mirror of
https://github.com/syncthing/syncthing.git
synced 2026-08-07 05:43:24 -04:00
Compare commits
391
Commits
v1.28.0-rc.2
...
v2.0.6
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
435f2d2178 | ||
|
|
8461ca539b | ||
|
|
fb977dc61d | ||
|
|
ee7ab4ce25 | ||
|
|
c3ce9713d9 | ||
|
|
6a147091c5 | ||
|
|
6208c36417 | ||
|
|
453fd20eeb | ||
|
|
28f0cffdb6 | ||
|
|
87c16c6cf5 | ||
|
|
5495c98e63 | ||
|
|
da7d5ce608 | ||
|
|
b300c297c6 | ||
|
|
124673f7a8 | ||
|
|
0395cf2bc0 | ||
|
|
36cd70040a | ||
|
|
1fbd396ffa | ||
|
|
2834bad85e | ||
|
|
516f3e29e8 | ||
|
|
ab20c16982 | ||
|
|
0c1df81ee9 | ||
|
|
d324b2ac86 | ||
|
|
0231089b99 | ||
|
|
74ffb85467 | ||
|
|
dcd280e6e2 | ||
|
|
4e56dbd883 | ||
|
|
13d7881b80 | ||
|
|
1d2c53bf3c | ||
|
|
9c449c966b | ||
|
|
ec2d4638e3 | ||
|
|
0ce92befc8 | ||
|
|
2167ce9656 | ||
|
|
0dc85d74aa | ||
|
|
b6e3f8037b | ||
|
|
00e7161a8f | ||
|
|
b5a7879eca | ||
|
|
4355dc69ea | ||
|
|
371ba69447 | ||
|
|
79ef57d0ea | ||
|
|
8bd6bdd397 | ||
|
|
ce3248cea7 | ||
|
|
99a6f3a5b6 | ||
|
|
c2e10dc156 | ||
|
|
fc914f3237 | ||
|
|
811d3752d0 | ||
|
|
00827dd5c1 | ||
|
|
a981c21d27 | ||
|
|
163dc122f3 | ||
|
|
83727e0824 | ||
|
|
529d3ef764 | ||
|
|
fefbf4dcc8 | ||
|
|
b9c6d3ae09 | ||
|
|
7bea8c758a | ||
|
|
479c0d3f16 | ||
|
|
d9ce7c3166 | ||
|
|
69979996d9 | ||
|
|
da58e5c50c | ||
|
|
44e259142f | ||
|
|
77970d5113 | ||
|
|
2b8ee4c7a5 | ||
|
|
be952e5f2d | ||
|
|
43ebac4242 | ||
|
|
f08a0ed01c | ||
|
|
612fdff377 | ||
|
|
8ccb7f1924 | ||
|
|
65d0ca8aa9 | ||
|
|
e82ed6e3d3 | ||
|
|
4b815fc086 | ||
|
|
7eaf843de2 | ||
|
|
110e1ae6f9 | ||
|
|
896f9725ec | ||
|
|
1a529e9d5d | ||
|
|
36ef17df8f | ||
|
|
955ac7775e | ||
|
|
8f69e874c4 | ||
|
|
ac06fd97e9 | ||
|
|
3726b7d112 | ||
|
|
377200591e | ||
|
|
4afc898c2f | ||
|
|
ff7e4fef55 | ||
|
|
9ffddb1923 | ||
|
|
896b857fc4 | ||
|
|
acc5d2675b | ||
|
|
6ece4c1fd2 | ||
|
|
cc09f0170d | ||
|
|
bb234d6c0e | ||
|
|
e6acc64758 | ||
|
|
f18cf545b9 | ||
|
|
6d64daaba3 | ||
|
|
47f48faed7 |
No files matched your search
@@ -1,6 +1,7 @@
|
|||||||
name: Feature request
|
name: Feature request
|
||||||
description: File a new feature request
|
description: File a new feature request
|
||||||
labels: ["enhancement", "needs-triage"]
|
labels: ["enhancement", "needs-triage"]
|
||||||
|
type: Feature
|
||||||
body:
|
body:
|
||||||
|
|
||||||
- type: textarea
|
- type: textarea
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
name: Bug report
|
name: Bug report
|
||||||
description: If you're actually looking for support instead, see "I need help / I have a question".
|
description: If you're actually looking for support instead, see "I need help / I have a question".
|
||||||
labels: ["bug", "needs-triage"]
|
labels: ["bug", "needs-triage"]
|
||||||
|
type: Bug
|
||||||
body:
|
body:
|
||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
|
|||||||
@@ -3,11 +3,11 @@ updates:
|
|||||||
- package-ecosystem: "github-actions"
|
- package-ecosystem: "github-actions"
|
||||||
directory: "/"
|
directory: "/"
|
||||||
schedule:
|
schedule:
|
||||||
interval: weekly
|
interval: monthly
|
||||||
open-pull-requests-limit: 10
|
open-pull-requests-limit: 10
|
||||||
|
|
||||||
- package-ecosystem: "gomod"
|
- package-ecosystem: "gomod"
|
||||||
directory: "/"
|
directory: "/"
|
||||||
schedule:
|
schedule:
|
||||||
interval: weekly
|
interval: monthly
|
||||||
open-pull-requests-limit: 10
|
open-pull-requests-limit: 10
|
||||||
@@ -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
|
||||||
@@ -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+
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
changelog:
|
||||||
|
exclude:
|
||||||
|
labels:
|
||||||
|
- dependencies
|
||||||
|
|
||||||
|
categories:
|
||||||
|
- title: Fixes
|
||||||
|
labels:
|
||||||
|
- bug
|
||||||
|
|
||||||
|
- title: Features
|
||||||
|
labels:
|
||||||
|
- enhancement
|
||||||
|
|
||||||
|
- title: Other
|
||||||
|
labels:
|
||||||
|
- '*'
|
||||||
@@ -7,11 +7,15 @@ on:
|
|||||||
- infra-*
|
- infra-*
|
||||||
|
|
||||||
env:
|
env:
|
||||||
GO_VERSION: "~1.23.0"
|
GO_VERSION: "~1.25.0"
|
||||||
CGO_ENABLED: "0"
|
CGO_ENABLED: "0"
|
||||||
BUILD_USER: docker
|
BUILD_USER: docker
|
||||||
BUILD_HOST: github.syncthing.net
|
BUILD_HOST: github.syncthing.net
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
docker-syncthing:
|
docker-syncthing:
|
||||||
name: Build and push Docker images
|
name: Build and push Docker images
|
||||||
@@ -41,6 +45,13 @@ jobs:
|
|||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Login to GHCR
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Build binaries
|
- name: Build binaries
|
||||||
run: |
|
run: |
|
||||||
for arch in arm64 amd64; do
|
for arch in arm64 amd64; do
|
||||||
@@ -53,13 +64,13 @@ jobs:
|
|||||||
|
|
||||||
- name: Set Docker tags (all branches)
|
- name: Set Docker tags (all branches)
|
||||||
run: |
|
run: |
|
||||||
tags=syncthing/${{ matrix.pkg }}:${{ github.sha }}
|
tags=docker.io/syncthing/${{ matrix.pkg }}:${{ github.sha }},ghcr.io/syncthing/infra/${{ matrix.pkg }}:${{ github.sha }}
|
||||||
echo "TAGS=$tags" >> $GITHUB_ENV
|
echo "TAGS=$tags" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Set Docker tags (latest)
|
- name: Set Docker tags (latest)
|
||||||
if: github.ref == 'refs/heads/infrastructure'
|
if: github.ref == 'refs/heads/infrastructure'
|
||||||
run: |
|
run: |
|
||||||
tags=syncthing/${{ matrix.pkg }}:latest,${{ env.TAGS }}
|
tags=docker.io/syncthing/${{ matrix.pkg }}:latest,ghcr.io/syncthing/infra/${{ matrix.pkg }}:latest,${{ env.TAGS }}
|
||||||
echo "TAGS=$tags" >> $GITHUB_ENV
|
echo "TAGS=$tags" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Build and push
|
- name: Build and push
|
||||||
|
|||||||
@@ -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
|
||||||
File diff suppressed because it is too large.
Load diff
@@ -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"
|
||||||
@@ -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 }}"
|
||||||
@@ -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@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
ref: ${{ github.ref }} # https://github.com/actions/checkout/issues/882
|
||||||
|
token: ${{ secrets.ACTIONS_GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- uses: actions/setup-go@v5
|
||||||
|
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 }}
|
||||||
@@ -17,5 +17,4 @@ deb
|
|||||||
*.bz2
|
*.bz2
|
||||||
/repos
|
/repos
|
||||||
/proto/scripts/protoc-gen-gosyncthing
|
/proto/scripts/protoc-gen-gosyncthing
|
||||||
/gui/next-gen-gui
|
|
||||||
/compat.json
|
/compat.json
|
||||||
+84
-20
@@ -1,26 +1,90 @@
|
|||||||
linters-settings:
|
version: "2"
|
||||||
maligned:
|
|
||||||
suggest-new: true
|
|
||||||
|
|
||||||
linters:
|
linters:
|
||||||
enable-all: true
|
default: all
|
||||||
disable:
|
disable:
|
||||||
- goimports
|
- cyclop
|
||||||
- depguard
|
- depguard
|
||||||
- lll
|
- err113
|
||||||
- gochecknoinits
|
- exhaustive
|
||||||
- gochecknoglobals
|
- exhaustruct
|
||||||
- gofmt
|
- forbidigo
|
||||||
- scopelint
|
- funcorder
|
||||||
- gocyclo
|
|
||||||
- funlen
|
- funlen
|
||||||
- wsl
|
- gochecknoglobals
|
||||||
|
- gochecknoinits
|
||||||
- gocognit
|
- gocognit
|
||||||
|
- goconst
|
||||||
|
- gocyclo
|
||||||
|
- godot
|
||||||
- godox
|
- godox
|
||||||
|
- gomoddirectives
|
||||||
service:
|
- inamedparam
|
||||||
golangci-lint-version: 1.21.x
|
- interfacebloat
|
||||||
prepare:
|
- ireturn
|
||||||
- rm -f go.sum # 1.12 -> 1.13 issues with QUIC-go
|
- lll
|
||||||
- GO111MODULE=on go mod vendor
|
- maintidx
|
||||||
- go run build.go assets
|
- mnd
|
||||||
|
- musttag
|
||||||
|
- nestif
|
||||||
|
- nlreturn
|
||||||
|
- noinlineerr
|
||||||
|
- nonamedreturns
|
||||||
|
- paralleltest
|
||||||
|
- prealloc
|
||||||
|
- predeclared
|
||||||
|
- protogetter
|
||||||
|
- recvcheck
|
||||||
|
- revive
|
||||||
|
- tagalign
|
||||||
|
- tagliatelle
|
||||||
|
- testpackage
|
||||||
|
- usetesting # go 1.24
|
||||||
|
- varnamelen
|
||||||
|
- whitespace
|
||||||
|
- wrapcheck
|
||||||
|
- wsl
|
||||||
|
- 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
|
||||||
|
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$
|
||||||
+6
-1
@@ -42,12 +42,14 @@ approval_rules:
|
|||||||
paths:
|
paths:
|
||||||
- ^[^/]+\.md
|
- ^[^/]+\.md
|
||||||
- ^\.policy\.yml
|
- ^\.policy\.yml
|
||||||
- ^\.github/
|
|
||||||
- ^LICENSE
|
- ^LICENSE
|
||||||
requires:
|
requires:
|
||||||
count: 1
|
count: 1
|
||||||
teams:
|
teams:
|
||||||
- syncthing/maintainers
|
- syncthing/maintainers
|
||||||
|
options:
|
||||||
|
ignore_update_merges: true
|
||||||
|
allow_contributor: true
|
||||||
|
|
||||||
# Regular pull requests require approval by an active contributor
|
# Regular pull requests require approval by an active contributor
|
||||||
- name: is approved by a syncthing contributor
|
- name: is approved by a syncthing contributor
|
||||||
@@ -55,6 +57,9 @@ approval_rules:
|
|||||||
count: 1
|
count: 1
|
||||||
teams:
|
teams:
|
||||||
- syncthing/contributors
|
- syncthing/contributors
|
||||||
|
options:
|
||||||
|
ignore_update_merges: true
|
||||||
|
allow_contributor: true
|
||||||
|
|
||||||
# Changes to some files (translations, dependencies, compatibility) do not
|
# Changes to some files (translations, dependencies, compatibility) do not
|
||||||
# require approval if they were proposed by a contributor and have a
|
# require approval if they were proposed by a contributor and have a
|
||||||
|
|||||||
@@ -13,117 +13,114 @@
|
|||||||
# contents of this file.
|
# 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>
|
||||||
|
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>
|
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>
|
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>
|
Aleksey Vasenev <margtu-fivt@ya.ru>
|
||||||
Alessandro G. (alessandro.g89) <alessandro.g89@gmail.com>
|
Alessandro G. (alessandro.g89) <alessandro.g89@gmail.com>
|
||||||
|
Alex Ionescu <github@ionescu.sh>
|
||||||
Alex Lindeman <139387+aelindeman@users.noreply.github.com>
|
Alex Lindeman <139387+aelindeman@users.noreply.github.com>
|
||||||
Alex Xu <alex.hello71@gmail.com>
|
Alex Xu <alex.hello71@gmail.com>
|
||||||
Alexander Graf (alex2108) <register-github@alex-graf.de>
|
|
||||||
Alexander Seiler <seileralex@gmail.com>
|
Alexander Seiler <seileralex@gmail.com>
|
||||||
Alexandre Alves <alexandrealvesdb.contact@gmail.com>
|
Alexandre Alves <alexandrealvesdb.contact@gmail.com>
|
||||||
Alexandre Viau (aviau) <alexandre@alexandreviau.net> <aviau@debian.org>
|
|
||||||
Aman Gupta <aman@tmm1.net>
|
Aman Gupta <aman@tmm1.net>
|
||||||
Anatoli Babenia <anatoli@rainforce.org>
|
|
||||||
Anderson Mesquita (andersonvom) <andersonvom@gmail.com>
|
|
||||||
Andreas Sommer <andreas.sommer87@googlemail.com>
|
Andreas Sommer <andreas.sommer87@googlemail.com>
|
||||||
andresvia <andres.via@gmail.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>
|
Andrew Rabert (nvllsvm) <ar@nullsum.net> <6550543+nvllsvm@users.noreply.github.com>
|
||||||
Andrey D (scienmind) <scintertech@cryptolab.net> <scienmind@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>
|
andyleap <andyleap@gmail.com>
|
||||||
Anjan Momi <anjan@momi.ca>
|
Anjan Momi <anjan@momi.ca>
|
||||||
Anthony Goeckner <agoeckner@users.noreply.github.com>
|
Anthony Goeckner <agoeckner@users.noreply.github.com>
|
||||||
Antoine Lamielle (0x010C) <antoine.lamielle@0x010c.fr> <gh@0x010c.fr>
|
Antoine Lamielle (0x010C) <antoine.lamielle@0x010c.fr> <gh@0x010c.fr>
|
||||||
Antony Male (canton7) <antony.male@gmail.com>
|
|
||||||
Anur <anurnomeru@163.com>
|
Anur <anurnomeru@163.com>
|
||||||
Aranjedeath <Aranjedeath@users.noreply.github.com>
|
Aranjedeath <Aranjedeath@users.noreply.github.com>
|
||||||
|
ardevd <ardevd@users.noreply.github.com>
|
||||||
Arkadiusz Tymiński <gevleeog@gmail.com>
|
Arkadiusz Tymiński <gevleeog@gmail.com>
|
||||||
Aroun <login@b-vo.fr>
|
Aroun <login@b-vo.fr>
|
||||||
Arthur Axel fREW Schmidt (frioux) <frew@afoolishmanifesto.com> <frioux@gmail.com>
|
Arthur Axel fREW Schmidt (frioux) <frew@afoolishmanifesto.com> <frioux@gmail.com>
|
||||||
Artur Zubilewicz <AkaZecik@users.noreply.github.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>
|
Aurélien Rainone <476650+arl@users.noreply.github.com>
|
||||||
BAHADIR YILMAZ <bahadiryilmaz32@gmail.com>
|
BAHADIR YILMAZ <bahadiryilmaz32@gmail.com>
|
||||||
Bart De Vries (mogwa1) <devriesb@gmail.com>
|
Bart De Vries (mogwa1) <devriesb@gmail.com>
|
||||||
Beat Reichenbach <44111292+beatreichenbach@users.noreply.github.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 Shepherd (benshep) <bjashepherd@gmail.com>
|
||||||
Ben Sidhom (bsidhom) <bsidhom@gmail.com>
|
Ben Sidhom (bsidhom) <bsidhom@gmail.com>
|
||||||
Benedikt Heine (bebehei) <bebe@bebehei.de>
|
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>
|
Benno Fünfstück <benno.fuenfstueck@gmail.com>
|
||||||
Benny Ng (tpng) <benny.tpng@gmail.com>
|
Benny Ng (tpng) <benny.tpng@gmail.com>
|
||||||
boomsquared <54829195+boomsquared@users.noreply.github.com>
|
boomsquared <54829195+boomsquared@users.noreply.github.com>
|
||||||
Boqin Qin <bobbqqin@bupt.edu.cn>
|
Boqin Qin <bobbqqin@bupt.edu.cn>
|
||||||
Boris Rybalkin <ribalkin@gmail.com>
|
Boris Rybalkin <ribalkin@gmail.com>
|
||||||
Brandon Philips (philips) <brandon@ifup.org>
|
|
||||||
Brendan Long (brendanlong) <self@brendanlong.com>
|
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>
|
Catfriend1 <16361913+Catfriend1@users.noreply.github.com>
|
||||||
Cathryne Linenweaver (Cathryne) <cathryne.linenweaver@gmail.com> <Cathryne@users.noreply.github.com> <katrinleinweber@MAC.local>
|
Cathryne Linenweaver (Cathryne) <cathryne.linenweaver@gmail.com> <Cathryne@users.noreply.github.com> <katrinleinweber@MAC.local>
|
||||||
Cedric Staniewski (xduugu) <cedric@gmx.ca>
|
Cedric Staniewski (xduugu) <cedric@gmx.ca>
|
||||||
chenrui <rui@meetup.com>
|
|
||||||
Chih-Hsuan Yen <yan12125@gmail.com> <1937689+yan12125@users.noreply.github.com>
|
Chih-Hsuan Yen <yan12125@gmail.com> <1937689+yan12125@users.noreply.github.com>
|
||||||
Choongkyu <choongkyu.kim+gh@gmail.com> <vapidlyrapid+gh@gmail.com>
|
Choongkyu <choongkyu.kim+gh@gmail.com> <vapidlyrapid+gh@gmail.com>
|
||||||
Chris Howie (cdhowie) <me@chrishowie.com>
|
Chris Howie (cdhowie) <me@chrishowie.com>
|
||||||
Chris Joel (cdata) <chris@scriptolo.gy>
|
Chris Joel (cdata) <chris@scriptolo.gy>
|
||||||
Chris Tonkinson <chris@masterbran.ch>
|
|
||||||
Christian Kujau <ckujau@users.noreply.github.com>
|
Christian Kujau <ckujau@users.noreply.github.com>
|
||||||
Christian Prescott <me@christianprescott.com>
|
Christian Prescott <me@christianprescott.com>
|
||||||
chucic <chucic@seznam.cz>
|
chucic <chucic@seznam.cz>
|
||||||
cjc7373 <niuchangcun@gmail.com>
|
cjc7373 <niuchangcun@gmail.com>
|
||||||
Colin Kennedy (moshen) <moshen.colin@gmail.com>
|
Colin Kennedy (moshen) <moshen.colin@gmail.com>
|
||||||
Cromefire_ <tim.l@nghorst.net> <26320625+cromefire@users.noreply.github.com>
|
Cromefire_ <tim.l@nghorst.net> <26320625+cromefire@users.noreply.github.com>
|
||||||
cui fliter <imcusg@gmail.com>
|
|
||||||
Cyprien Devillez <cypx@users.noreply.github.com>
|
Cyprien Devillez <cypx@users.noreply.github.com>
|
||||||
d-volution <49024624+d-volution@users.noreply.github.com>
|
d-volution <49024624+d-volution@users.noreply.github.com>
|
||||||
Dale Visser <dale.visser@live.com>
|
|
||||||
Dan <benda.daniel@gmail.com>
|
Dan <benda.daniel@gmail.com>
|
||||||
Daniel Barczyk <46358936+DanielBarczyk@users.noreply.github.com>
|
Daniel Barczyk <46358936+DanielBarczyk@users.noreply.github.com>
|
||||||
Daniel Bergmann (brgmnn) <dan.arne.bergmann@gmail.com> <brgmnn@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 Martí (mvdan) <mvdan@mvdan.cc>
|
||||||
Daniel Padrta <64928366+danpadcz@users.noreply.github.com>
|
Daniel Padrta <64928366+danpadcz@users.noreply.github.com>
|
||||||
|
Daniil Gentili <daniil@daniil.it>
|
||||||
Darshil Chanpura (dtchanpura) <dtchanpura@gmail.com> <dcprime314@gmail.com>
|
Darshil Chanpura (dtchanpura) <dtchanpura@gmail.com> <dcprime314@gmail.com>
|
||||||
|
dashangcun <907225865@qq.com>
|
||||||
David Rimmer (dinosore) <dinosore@dbrsoftware.co.uk>
|
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>
|
DeflateAwning <11021263+DeflateAwning@users.noreply.github.com>
|
||||||
Denis A. (dva) <denisva@gmail.com>
|
Denis A. (dva) <denisva@gmail.com>
|
||||||
Dennis Wilson (snnd) <dw@risu.io>
|
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>
|
derekriemer <derek.riemer@colorado.edu>
|
||||||
DerRockWolf <50499906+DerRockWolf@users.noreply.github.com>
|
DerRockWolf <50499906+DerRockWolf@users.noreply.github.com>
|
||||||
desbma <desbma@users.noreply.github.com>
|
desbma <desbma@users.noreply.github.com>
|
||||||
Devon G. Redekopp <devon@redekopp.com>
|
Devon G. Redekopp <devon@redekopp.com>
|
||||||
diemade <spamkill@posteo.ch>
|
|
||||||
digital <didev@dinid.net>
|
digital <didev@dinid.net>
|
||||||
Dimitri Papadopoulos Orfanos <3234522+DimitriPapadopoulos@users.noreply.github.com>
|
Dimitri Papadopoulos Orfanos <3234522+DimitriPapadopoulos@users.noreply.github.com>
|
||||||
Dmitry Saveliev (dsaveliev) <d.e.saveliev@gmail.com>
|
Dmitry Saveliev (dsaveliev) <d.e.saveliev@gmail.com>
|
||||||
|
domain <32405309+szu17dmy@users.noreply.github.com>
|
||||||
Domenic Horner <domenic@tgxn.net>
|
Domenic Horner <domenic@tgxn.net>
|
||||||
Dominik Heidler (asdil12) <dominik@heidler.eu>
|
Dominik Heidler (asdil12) <dominik@heidler.eu>
|
||||||
Elias Jarlebring (jarlebring) <jarlebring@gmail.com>
|
Elias Jarlebring (jarlebring) <jarlebring@gmail.com>
|
||||||
Elliot Huffman <thelich2@gmail.com>
|
Elliot Huffman <thelich2@gmail.com>
|
||||||
Emil Hessman (ceh) <emil@hessman.se>
|
Emil Hessman (ceh) <emil@hessman.se>
|
||||||
Emil Lundberg <emil@emlun.se>
|
|
||||||
Eng Zer Jun <engzerjun@gmail.com>
|
Eng Zer Jun <engzerjun@gmail.com>
|
||||||
entity0xfe <109791748+entity0xfe@users.noreply.github.com> <entity0xfe@my.domain>
|
entity0xfe <109791748+entity0xfe@users.noreply.github.com> <entity0xfe@my.domain>
|
||||||
Eric Lesiuta <elesiuta@gmail.com>
|
Eric Lesiuta <elesiuta@gmail.com>
|
||||||
Eric P <eric@kastelo.net>
|
|
||||||
Erik Meitner (WSGCSysadmin) <e.meitner@willystreet.coop>
|
Erik Meitner (WSGCSysadmin) <e.meitner@willystreet.coop>
|
||||||
Evan Spensley <94762716+0evan@users.noreply.github.com>
|
Evan Spensley <94762716+0evan@users.noreply.github.com>
|
||||||
Evgeny Kuznetsov <evgeny@kuznetsov.md>
|
|
||||||
Federico Castagnini (facastagnini) <federico.castagnini@gmail.com>
|
Federico Castagnini (facastagnini) <federico.castagnini@gmail.com>
|
||||||
Felix <53702818+f-eliks@users.noreply.github.com>
|
Felix <53702818+f-eliks@users.noreply.github.com>
|
||||||
Felix Ableitner (Nutomic) <me@nutomic.com>
|
Felix Ableitner (Nutomic) <me@nutomic.com>
|
||||||
@@ -137,7 +134,6 @@ ghjklw <malo@jaffre.info>
|
|||||||
Gilli Sigurdsson (gillisig) <gilli@vx.is>
|
Gilli Sigurdsson (gillisig) <gilli@vx.is>
|
||||||
Gleb Sinyavskiy <zhulik.gleb@gmail.com>
|
Gleb Sinyavskiy <zhulik.gleb@gmail.com>
|
||||||
Graham Miln (grahammiln) <graham.miln@dssw.co.uk> <graham.miln@miln.eu>
|
Graham Miln (grahammiln) <graham.miln@dssw.co.uk> <graham.miln@miln.eu>
|
||||||
greatroar <61184462+greatroar@users.noreply.github.com>
|
|
||||||
Greg <gco@jazzhaiku.com>
|
Greg <gco@jazzhaiku.com>
|
||||||
guangwu <guoguangwu@magic-shield.com>
|
guangwu <guoguangwu@magic-shield.com>
|
||||||
gudvinr <gudvinr@gmail.com>
|
gudvinr <gudvinr@gmail.com>
|
||||||
@@ -145,54 +141,42 @@ Gusted <postmaster@gusted.xyz> <williamzijl7@hotmail.com>
|
|||||||
Han Boetes <han@boetes.org>
|
Han Boetes <han@boetes.org>
|
||||||
HansK-p <42314815+HansK-p@users.noreply.github.com>
|
HansK-p <42314815+HansK-p@users.noreply.github.com>
|
||||||
Harrison Jones (harrisonhjones) <harrisonhjones@users.noreply.github.com>
|
Harrison Jones (harrisonhjones) <harrisonhjones@users.noreply.github.com>
|
||||||
|
Hazem Krimi <me@hazemkrimi.tech>
|
||||||
Heiko Zuerker (Smiley73) <heiko@zuerker.org>
|
Heiko Zuerker (Smiley73) <heiko@zuerker.org>
|
||||||
|
Hireworks <129852174+hireworksltd@users.noreply.github.com>
|
||||||
Hugo Locurcio <hugo.locurcio@hugo.pro>
|
Hugo Locurcio <hugo.locurcio@hugo.pro>
|
||||||
Iain Barnett <iainspeed@gmail.com>
|
Iain Barnett <iainspeed@gmail.com>
|
||||||
Ian Johnson (anonymouse64) <ian.johnson@canonical.com> <person.uwsome@gmail.com>
|
Ian Johnson (anonymouse64) <ian.johnson@canonical.com> <person.uwsome@gmail.com>
|
||||||
ignacy123 <ignacy.buczek@onet.pl>
|
ignacy123 <ignacy.buczek@onet.pl>
|
||||||
Ikko Ashimine <eltociear@gmail.com>
|
|
||||||
Ilya Brin <464157+ilyabrin@users.noreply.github.com>
|
|
||||||
Iskander Sharipov (Alex) <quasilyte@gmail.com>
|
Iskander Sharipov (Alex) <quasilyte@gmail.com>
|
||||||
Jaakko Hannikainen (jgke) <jgke@jgke.fi>
|
Jaakko Hannikainen (jgke) <jgke@jgke.fi>
|
||||||
Jacek Szafarkiewicz (hadogenes) <szafar@linux.pl>
|
|
||||||
Jack Croft <jccroft1@users.noreply.github.com>
|
Jack Croft <jccroft1@users.noreply.github.com>
|
||||||
Jacob <jyundt@gmail.com>
|
Jacob <jyundt@gmail.com>
|
||||||
Jake Peterson (acogdev) <jake@acogdev.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 O'Beirne <wild-github@au92.org>
|
||||||
James Patterson (jpjp) <jamespatterson@operamail.com> <jpjp@users.noreply.github.com>
|
James Patterson (jpjp) <jamespatterson@operamail.com> <jpjp@users.noreply.github.com>
|
||||||
janost <janost@tuta.io>
|
|
||||||
Jaroslav Lichtblau <svetlemodry@users.noreply.github.com>
|
Jaroslav Lichtblau <svetlemodry@users.noreply.github.com>
|
||||||
Jaroslav Malec (dzarda) <dzardacz@gmail.com>
|
Jaroslav Malec (dzarda) <dzardacz@gmail.com>
|
||||||
jaseg <githubaccount@jaseg.net>
|
|
||||||
Jaspitta <ste.scarpitta@gmail.com>
|
Jaspitta <ste.scarpitta@gmail.com>
|
||||||
Jauder Ho <jauderho@users.noreply.github.com>
|
|
||||||
Jaya Chithra (jayachithra) <s.k.jayachithra@gmail.com>
|
Jaya Chithra (jayachithra) <s.k.jayachithra@gmail.com>
|
||||||
Jaya Kumar <jaya.kumar@ict.nl>
|
Jaya Kumar <jaya.kumar@ict.nl>
|
||||||
Jeffery To <jeffery.to@gmail.com>
|
Jeffery To <jeffery.to@gmail.com>
|
||||||
jelle van der Waa <jelle@vdwaa.nl>
|
jelle van der Waa <jelle@vdwaa.nl>
|
||||||
Jens Diemer (jedie) <github.com@jensdiemer.de> <git@jensdiemer.de>
|
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>
|
Jochen Voss (seehuhn) <voss@seehuhn.de>
|
||||||
Johan Andersson <j@i19.se>
|
|
||||||
Johan Vromans (sciurius) <jvromans@squirrel.nl>
|
Johan Vromans (sciurius) <jvromans@squirrel.nl>
|
||||||
John Rinehart (fuzzybear3965) <johnrichardrinehart@gmail.com>
|
John Rinehart (fuzzybear3965) <johnrichardrinehart@gmail.com>
|
||||||
Jonas Thelemann <e-mail@jonas-thelemann.de>
|
Jonas Thelemann <e-mail@jonas-thelemann.de>
|
||||||
Jonathan <artback@protonmail.com> <jonagn@gmail.com>
|
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>
|
Jose Manuel Delicado (jmdaweb) <jmdaweb@hotmail.com> <jmdaweb@users.noreply.github.com>
|
||||||
jtagcat <git-514635f7@jtag.cat> <git-12dbd862@jtag.cat>
|
jtagcat <git-514635f7@jtag.cat> <git-12dbd862@jtag.cat>
|
||||||
Julian Lehrhuber <jul13579@users.noreply.github.com>
|
Julian Lehrhuber <jul13579@users.noreply.github.com>
|
||||||
Jörg Thalheim <Mic92@users.noreply.github.com>
|
Jörg Thalheim <Mic92@users.noreply.github.com>
|
||||||
Jędrzej Kula <kula.jedrek@gmail.com>
|
Jędrzej Kula <kula.jedrek@gmail.com>
|
||||||
K.B.Dharun Krishna <kbdharunkrishna@gmail.com>
|
Kapil Sareen <kapilsareen584@gmail.com>
|
||||||
Kalle Laine <pahakalle@protonmail.com>
|
|
||||||
Karol Różycki (krozycki) <rozycki.karol@gmail.com>
|
Karol Różycki (krozycki) <rozycki.karol@gmail.com>
|
||||||
Kebin Liu <lkebin@gmail.com>
|
Kebin Liu <lkebin@gmail.com>
|
||||||
Keith Harrison <keithh@protonmail.com>
|
Keith Harrison <keithh@protonmail.com>
|
||||||
Keith Turner <kturner@apache.org>
|
|
||||||
Kelong Cong (kc1212) <kc04bc@gmx.com> <kc1212@users.noreply.github.com>
|
Kelong Cong (kc1212) <kc04bc@gmx.com> <kc1212@users.noreply.github.com>
|
||||||
Ken'ichi Kamada (kamadak) <kamada@nanohz.org>
|
Ken'ichi Kamada (kamadak) <kamada@nanohz.org>
|
||||||
Kevin Allen (ironmig) <kma1660@gmail.com>
|
Kevin Allen (ironmig) <kma1660@gmail.com>
|
||||||
@@ -201,31 +185,27 @@ Kevin White, Jr. (kwhite17) <kevinwhite1710@gmail.com>
|
|||||||
klemens <ka7@github.com>
|
klemens <ka7@github.com>
|
||||||
Kurt Fitzner (Kudalufi) <kurt@va1der.ca> <kurt.fitzner@gmail.com>
|
Kurt Fitzner (Kudalufi) <kurt@va1der.ca> <kurt.fitzner@gmail.com>
|
||||||
kylosus <33132401+kylosus@users.noreply.github.com>
|
kylosus <33132401+kylosus@users.noreply.github.com>
|
||||||
Lars K.W. Gohlke (lkwg82) <lkwg82@gmx.de>
|
|
||||||
Lars Lehtonen <lars.lehtonen@gmail.com>
|
Lars Lehtonen <lars.lehtonen@gmail.com>
|
||||||
Laurent Arnoud <laurent@spkdev.net>
|
|
||||||
Laurent Etiemble (letiemble) <laurent.etiemble@gmail.com> <laurent.etiemble@monobjc.net>
|
Laurent Etiemble (letiemble) <laurent.etiemble@gmail.com> <laurent.etiemble@monobjc.net>
|
||||||
Leo Arias (elopio) <yo@elopio.net>
|
Leo Arias (elopio) <yo@elopio.net>
|
||||||
Liu Siyuan (liusy182) <liusy182@gmail.com> <liusy182@hotmail.com>
|
Liu Siyuan (liusy182) <liusy182@gmail.com> <liusy182@hotmail.com>
|
||||||
Lode Hoste (Zillode) <zillode@zillode.be>
|
|
||||||
Lord Landon Agahnim (LordLandon) <lordlandon@gmail.com>
|
Lord Landon Agahnim (LordLandon) <lordlandon@gmail.com>
|
||||||
LSmithx2 <42276854+lsmithx2@users.noreply.github.com>
|
LSmithx2 <42276854+lsmithx2@users.noreply.github.com>
|
||||||
luchenhan <168071714+luchenhan@users.noreply.github.com>
|
|
||||||
Lukas Lihotzki <lukas@lihotzki.de>
|
Lukas Lihotzki <lukas@lihotzki.de>
|
||||||
Luke Hamburg <1992842+luckman212@users.noreply.github.com>
|
Luke Hamburg <1992842+luckman212@users.noreply.github.com>
|
||||||
luzpaz <luzpaz@users.noreply.github.com>
|
luzpaz <luzpaz@users.noreply.github.com>
|
||||||
Majed Abdulaziz (majedev) <majed.alhajry@gmail.com>
|
Majed Abdulaziz (majedev) <majed.alhajry@gmail.com>
|
||||||
Marc Laporte (marclaporte) <marc@marclaporte.com> <marc@laporte.name>
|
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>
|
Marcin Dziadus (marcindziadus) <dziadus.marcin@gmail.com>
|
||||||
marco-m <marco.molteni@laposte.net>
|
Marcus B Spencer <marcus@marcusspencer.xyz> <marcus@marcusspencer.us>
|
||||||
Marcus Legendre <marcus.legendre@gmail.com>
|
Marcus Legendre <marcus.legendre@gmail.com>
|
||||||
Mario Majila <mariustshipichik@gmail.com>
|
Mario Majila <mariustshipichik@gmail.com>
|
||||||
Mark Pulford (mpx) <mark@kyne.com.au>
|
Mark Pulford (mpx) <mark@kyne.com.au>
|
||||||
Martchus <martchus@gmx.net>
|
Martchus <martchus@gmx.net>
|
||||||
Martin Polehla <p0l0us@users.noreply.github.com>
|
|
||||||
Mateusz Naściszewski (mateon1) <matin1111@wp.pl>
|
Mateusz Naściszewski (mateon1) <matin1111@wp.pl>
|
||||||
Mateusz Ż <thedead4fun@live.com>
|
Mateusz Ż <thedead4fun@live.com>
|
||||||
|
mathias4833 <67101597+mathias4833@users.noreply.github.com>
|
||||||
Matic Potočnik <hairyfotr@gmail.com>
|
Matic Potočnik <hairyfotr@gmail.com>
|
||||||
Matt Burke (burkemw3) <mburke@amplify.com> <burkemw3@gmail.com>
|
Matt Burke (burkemw3) <mburke@amplify.com> <burkemw3@gmail.com>
|
||||||
Matt Robenolt <matt@ydekproductions.com>
|
Matt Robenolt <matt@ydekproductions.com>
|
||||||
@@ -233,15 +213,10 @@ Matteo Ruina <matteo.ruina@gmail.com>
|
|||||||
Maurizio Tomasi <ziotom78@gmail.com>
|
Maurizio Tomasi <ziotom78@gmail.com>
|
||||||
Max <github@germancoding.com>
|
Max <github@germancoding.com>
|
||||||
Max Schulze (kralo) <max.schulze@online.de> <kralo@users.noreply.github.com>
|
Max Schulze (kralo) <max.schulze@online.de> <kralo@users.noreply.github.com>
|
||||||
maxice8 <30738253+maxice8@users.noreply.github.com>
|
|
||||||
MaximAL <almaximal@ya.ru>
|
MaximAL <almaximal@ya.ru>
|
||||||
Maxime Thirouin <m@moox.io>
|
|
||||||
Maximilian <maxi.rostock@outlook.de> <public@complexvector.space>
|
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 Jephcote (Rewt0r) <rewt0r@gmx.com> <Rewt0r@users.noreply.github.com>
|
||||||
Michael Ploujnikov (plouj) <ploujj@gmail.com>
|
|
||||||
Michael Rienstra <mrienstra@gmail.com>
|
Michael Rienstra <mrienstra@gmail.com>
|
||||||
Michael Tilli (pyfisch) <pyfisch@gmail.com>
|
|
||||||
MichaIng <micha@dietpi.com>
|
MichaIng <micha@dietpi.com>
|
||||||
Migelo <miha@filetki.si>
|
Migelo <miha@filetki.si>
|
||||||
Mike Boone <mike@boonedocks.net>
|
Mike Boone <mike@boonedocks.net>
|
||||||
@@ -250,7 +225,6 @@ MikolajTwarog <43782609+MikolajTwarog@users.noreply.github.com>
|
|||||||
Mingxuan Lin <gdlmx@users.noreply.github.com>
|
Mingxuan Lin <gdlmx@users.noreply.github.com>
|
||||||
mv1005 <49659413+mv1005@users.noreply.github.com>
|
mv1005 <49659413+mv1005@users.noreply.github.com>
|
||||||
Nate Morrison (nrm21) <natemorrison@gmail.com>
|
Nate Morrison (nrm21) <natemorrison@gmail.com>
|
||||||
Naveen <172697+naveensrinivasan@users.noreply.github.com>
|
|
||||||
nf <nf@wh3rd.net>
|
nf <nf@wh3rd.net>
|
||||||
Nicholas Rishel (PrototypeNM1) <rishel.nick@gmail.com> <PrototypeNM1@users.noreply.github.com>
|
Nicholas Rishel (PrototypeNM1) <rishel.nick@gmail.com> <PrototypeNM1@users.noreply.github.com>
|
||||||
Nick Busey <NickBusey@users.noreply.github.com>
|
Nick Busey <NickBusey@users.noreply.github.com>
|
||||||
@@ -265,14 +239,13 @@ NoLooseEnds <jon.koslung@gmail.com>
|
|||||||
Oliver Freyermuth <o.freyermuth@googlemail.com>
|
Oliver Freyermuth <o.freyermuth@googlemail.com>
|
||||||
orangekame3 <miya.org.0309@gmail.com>
|
orangekame3 <miya.org.0309@gmail.com>
|
||||||
otbutz <tbutz@optitool.de>
|
otbutz <tbutz@optitool.de>
|
||||||
Otiel <Otiel@users.noreply.github.com>
|
|
||||||
overkill <22098433+0verk1ll@users.noreply.github.com>
|
overkill <22098433+0verk1ll@users.noreply.github.com>
|
||||||
Oyebanji Jacob Mayowa <oyebanji05@gmail.com>
|
Oyebanji Jacob Mayowa <oyebanji05@gmail.com>
|
||||||
Pablo <pbaeyens31+github@gmail.com>
|
Pablo <pbaeyens31+github@gmail.com>
|
||||||
Pascal Jungblut (pascalj) <github@pascalj.com> <mail@pascal-jungblut.com>
|
Pascal Jungblut (pascalj) <github@pascalj.com> <mail@pascal-jungblut.com>
|
||||||
Paul Brit <paulbrit44@gmail.com>
|
Paul Brit <paulbrit44@gmail.com>
|
||||||
|
Paul Donald <newtwen+github@gmail.com>
|
||||||
Pawel Palenica (qepasa) <pawelpalenica11@gmail.com>
|
Pawel Palenica (qepasa) <pawelpalenica11@gmail.com>
|
||||||
Paweł Rozlach <vespian@users.noreply.github.com>
|
|
||||||
perewa <cavalcante.ten@gmail.com>
|
perewa <cavalcante.ten@gmail.com>
|
||||||
Peter Badida <KeyWeeUsr@users.noreply.github.com>
|
Peter Badida <KeyWeeUsr@users.noreply.github.com>
|
||||||
Peter Dave Hello <hsu@peterdavehello.org>
|
Peter Dave Hello <hsu@peterdavehello.org>
|
||||||
@@ -282,18 +255,16 @@ Phani Rithvij <phanirithvij2000@gmail.com>
|
|||||||
Phil Davis <phil.davis@inf.org>
|
Phil Davis <phil.davis@inf.org>
|
||||||
Philippe Schommers (filoozoom) <philippe@schommers.be>
|
Philippe Schommers (filoozoom) <philippe@schommers.be>
|
||||||
Phill Luby (pluby) <phill.luby@newredo.com>
|
Phill Luby (pluby) <phill.luby@newredo.com>
|
||||||
Pier Paolo Ramon <ramonpierre@gmail.com>
|
|
||||||
Piotr Bejda (piobpl) <piotrb10@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>
|
Quentin Hibon <qh.public@yahoo.com>
|
||||||
Rahmi Pruitt <rjpruitt16@gmail.com>
|
Rahmi Pruitt <rjpruitt16@gmail.com>
|
||||||
red_led <red-led@users.noreply.github.com>
|
red_led <red-led@users.noreply.github.com>
|
||||||
Richard Hartmann <RichiH@users.noreply.github.com>
|
|
||||||
Robert Carosi (nov1n) <robert@carosi.nl>
|
Robert Carosi (nov1n) <robert@carosi.nl>
|
||||||
Roberto Santalla <roobre@users.noreply.github.com>
|
Roberto Santalla <roobre@users.noreply.github.com>
|
||||||
Robin Schoonover <robin@cornhooves.org>
|
Robin Schoonover <robin@cornhooves.org>
|
||||||
Roman Zaynetdinov (zaynetro) <romanznet@gmail.com>
|
Roman Zaynetdinov (zaynetro) <romanznet@gmail.com>
|
||||||
Ross Smith II (rasa) <ross@smithii.com>
|
|
||||||
rubenbe <github-com-00ff86@vandamme.email>
|
rubenbe <github-com-00ff86@vandamme.email>
|
||||||
Ruslan Yevdokymov <38809160+ruslanye@users.noreply.github.com>
|
Ruslan Yevdokymov <38809160+ruslanye@users.noreply.github.com>
|
||||||
Ryan Qian <i@bitbili.net>
|
Ryan Qian <i@bitbili.net>
|
||||||
@@ -305,31 +276,27 @@ Sergey Mishin (ralder) <ralder@yandex.ru>
|
|||||||
Sertonix <83883937+Sertonix@users.noreply.github.com>
|
Sertonix <83883937+Sertonix@users.noreply.github.com>
|
||||||
Severin von Wnuck-Lipinski <ss7@live.de>
|
Severin von Wnuck-Lipinski <ss7@live.de>
|
||||||
Shaarad Dalvi <60266155+shaaraddalvi@users.noreply.github.com> <shdalv@microsoft.com>
|
Shaarad Dalvi <60266155+shaaraddalvi@users.noreply.github.com> <shdalv@microsoft.com>
|
||||||
Simon Frei (imsodin) <freisim93@gmail.com>
|
|
||||||
Simon Mwepu <simonmwepu@gmail.com>
|
Simon Mwepu <simonmwepu@gmail.com>
|
||||||
Simon Pickup <simon@pickupinfinity.com>
|
Simon Pickup <simon@pickupinfinity.com>
|
||||||
Sly_tom_cat <slytomcat@mail.ru>
|
Sly_tom_cat <slytomcat@mail.ru>
|
||||||
Sonu Kumar Saw <31889738+dev-saw99@users.noreply.github.com>
|
Sonu Kumar Saw <31889738+dev-saw99@users.noreply.github.com>
|
||||||
Stefan Kuntz (Stefan-Code) <stefan.github@gmail.com> <Stefan.github@gmail.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>
|
Steven Eckhoff <steven.eckhoff.opensource@gmail.com>
|
||||||
Suhas Gundimeda (snugghash) <suhas.gundimeda@gmail.com> <snugghash@gmail.com>
|
Suhas Gundimeda (snugghash) <suhas.gundimeda@gmail.com> <snugghash@gmail.com>
|
||||||
Sven Bachmann <dev@mcbachmann.de>
|
Sven Bachmann <dev@mcbachmann.de>
|
||||||
Syncthing Automation <automation@syncthing.net>
|
Sébastien WENSKE <sebastien@wenske.fr>
|
||||||
Syncthing Release Automation <release@syncthing.net>
|
|
||||||
Taylor Khan (nelsonkhan) <nelsonkhan@gmail.com>
|
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 <9749173+uhthomas@users.noreply.github.com>
|
||||||
Thomas Hipp <thomashipp@gmail.com>
|
Thomas Hipp <thomashipp@gmail.com>
|
||||||
Tim Abell (timabell) <tim@timwise.co.uk>
|
Tim Abell (timabell) <tim@timwise.co.uk>
|
||||||
Tim Howes (timhowes) <timhowes@berkeley.edu>
|
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 Klauser <tobias.klauser@gmail.com>
|
||||||
Tobias Nygren (tnn2) <tnn@nygren.pp.se>
|
Tobias Nygren (tnn2) <tnn@nygren.pp.se>
|
||||||
Tobias Tom (tobiastom) <t.tom@succont.de>
|
Tobias Tom (tobiastom) <t.tom@succont.de>
|
||||||
Tom Jakubowski <tom@crystae.net>
|
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>
|
Tully Robinson (tojrobinson) <tully@tojr.org>
|
||||||
Tyler Brazier (tylerbrazier) <tyler@tylerbrazier.com>
|
Tyler Brazier (tylerbrazier) <tyler@tylerbrazier.com>
|
||||||
Tyler Kropp <kropptyler@gmail.com>
|
Tyler Kropp <kropptyler@gmail.com>
|
||||||
@@ -346,10 +313,10 @@ WangXi <xib1102@icloud.com>
|
|||||||
Will Rouesnel <wrouesnel@wrouesnel.com>
|
Will Rouesnel <wrouesnel@wrouesnel.com>
|
||||||
William A. Kennington III (wkennington) <william@wkennington.com>
|
William A. Kennington III (wkennington) <william@wkennington.com>
|
||||||
wouter bolsterlee <wouter@bolsterl.ee>
|
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>
|
xarx00 <xarx00@users.noreply.github.com>
|
||||||
Xavier O. (damajor) <damajor@gmail.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>
|
Yannic A. (eipiminus1) <eipiminusone+github@gmail.com> <eipiminus1@users.noreply.github.com>
|
||||||
|
yparitcher <y@paritcher.com>
|
||||||
佛跳墙 <daoquan@qq.com>
|
佛跳墙 <daoquan@qq.com>
|
||||||
落心 <luoxin.ttt@gmail.com>
|
落心 <luoxin.ttt@gmail.com>
|
||||||
+151
-11
@@ -34,19 +34,163 @@ Note that the previously used service at
|
|||||||
retired and we kindly ask you to sign up on Weblate for continued
|
retired and we kindly ask you to sign up on Weblate for continued
|
||||||
involvement.
|
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
|
## Contributing Documentation
|
||||||
|
|
||||||
Updates to the [documentation site](https://docs.syncthing.net/) can be
|
Updates to the [documentation site](https://docs.syncthing.net/) can be
|
||||||
made as pull requests on the [documentation
|
made as pull requests on the [documentation
|
||||||
repository](https://github.com/syncthing/docs).
|
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
|
## Licensing
|
||||||
|
|
||||||
All contributions are made available under the same license as the already
|
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
|
- The documentation (man/...) is licensed under the Creative Commons
|
||||||
Attribution 4.0 International License.
|
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
|
Regardless of the license in effect, you retain the copyright to your
|
||||||
contribution.
|
contribution.
|
||||||
|
|
||||||
@@ -82,13 +82,11 @@ build process.
|
|||||||
|
|
||||||
## Signed Releases
|
## Signed Releases
|
||||||
|
|
||||||
As of v0.10.15 and onwards, release binaries are GPG signed with the key
|
Release binaries are GPG signed with the key available from
|
||||||
D26E6ED000654A3E, available from https://syncthing.net/security/ and
|
https://syncthing.net/security/. There is also a built-in automatic
|
||||||
most key servers.
|
upgrade mechanism (disabled in some distribution channels) which uses a
|
||||||
|
compiled in ECDSA signature. macOS and Windows binaries are also
|
||||||
There is also a built-in automatic upgrade mechanism (disabled in some
|
code-signed.
|
||||||
distribution channels) which uses a compiled in ECDSA signature. macOS
|
|
||||||
binaries are also properly code signed.
|
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
version: v2
|
||||||
|
managed:
|
||||||
|
enabled: true
|
||||||
|
override:
|
||||||
|
- file_option: go_package_prefix
|
||||||
|
value: github.com/syncthing/syncthing/internal/gen
|
||||||
|
plugins:
|
||||||
|
- remote: buf.build/protocolbuffers/go:v1.35.1
|
||||||
|
out: .
|
||||||
|
opt: module=github.com/syncthing/syncthing
|
||||||
|
inputs:
|
||||||
|
- directory: proto
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
version: v2
|
||||||
|
modules:
|
||||||
|
- path: proto
|
||||||
|
name: github.com/syncthing/syncthing
|
||||||
|
lint:
|
||||||
|
use:
|
||||||
|
- STANDARD
|
||||||
|
breaking:
|
||||||
|
use:
|
||||||
|
- WIRE_JSON
|
||||||
@@ -15,7 +15,6 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"compress/flate"
|
"compress/flate"
|
||||||
"compress/gzip"
|
"compress/gzip"
|
||||||
"encoding/base64"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"flag"
|
"flag"
|
||||||
@@ -39,27 +38,26 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
goarch string
|
goarch string
|
||||||
goos string
|
goos string
|
||||||
noupgrade bool
|
noupgrade bool
|
||||||
version string
|
version string
|
||||||
goCmd string
|
goCmd string
|
||||||
race bool
|
race bool
|
||||||
debug = os.Getenv("BUILDDEBUG") != ""
|
debug = os.Getenv("BUILDDEBUG") != ""
|
||||||
extraTags string
|
extraTags string
|
||||||
installSuffix string
|
installSuffix string
|
||||||
pkgdir string
|
pkgdir string
|
||||||
cc string
|
cc string
|
||||||
run string
|
run string
|
||||||
benchRun string
|
benchRun string
|
||||||
buildOut string
|
buildOut string
|
||||||
debugBinary bool
|
debugBinary bool
|
||||||
coverage bool
|
coverage bool
|
||||||
long bool
|
long bool
|
||||||
timeout = "120s"
|
timeout = "120s"
|
||||||
longTimeout = "600s"
|
longTimeout = "600s"
|
||||||
numVersions = 5
|
numVersions = 5
|
||||||
withNextGenGUI = os.Getenv("BUILD_NEXT_GEN_GUI") != ""
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type target struct {
|
type target struct {
|
||||||
@@ -86,7 +84,6 @@ var targets = map[string]target{
|
|||||||
"all": {
|
"all": {
|
||||||
// Only valid for the "build" and "install" commands as it lacks all
|
// Only valid for the "build" and "install" commands as it lacks all
|
||||||
// the archive creation stuff. buildPkgs gets filled out in init()
|
// the archive creation stuff. buildPkgs gets filled out in init()
|
||||||
tags: []string{"purego"},
|
|
||||||
},
|
},
|
||||||
"syncthing": {
|
"syncthing": {
|
||||||
// The default target for "build", "install", "tar", "zip", "deb", etc.
|
// The default target for "build", "install", "tar", "zip", "deb", etc.
|
||||||
@@ -97,40 +94,40 @@ var targets = map[string]target{
|
|||||||
buildPkgs: []string{"github.com/syncthing/syncthing/cmd/syncthing"},
|
buildPkgs: []string{"github.com/syncthing/syncthing/cmd/syncthing"},
|
||||||
binaryName: "syncthing", // .exe will be added automatically for Windows builds
|
binaryName: "syncthing", // .exe will be added automatically for Windows builds
|
||||||
archiveFiles: []archiveFile{
|
archiveFiles: []archiveFile{
|
||||||
{src: "{{binary}}", dst: "{{binary}}", perm: 0755},
|
{src: "{{binary}}", dst: "{{binary}}", perm: 0o755},
|
||||||
{src: "README.md", dst: "README.txt", perm: 0644},
|
{src: "README.md", dst: "README.txt", perm: 0o644},
|
||||||
{src: "LICENSE", dst: "LICENSE.txt", perm: 0644},
|
{src: "LICENSE", dst: "LICENSE.txt", perm: 0o644},
|
||||||
{src: "AUTHORS", dst: "AUTHORS.txt", perm: 0644},
|
{src: "AUTHORS", dst: "AUTHORS.txt", perm: 0o644},
|
||||||
// All files from etc/ and extra/ added automatically in init().
|
// All files from etc/ and extra/ added automatically in init().
|
||||||
},
|
},
|
||||||
systemdService: "syncthing@*.service",
|
systemdService: "syncthing@*.service",
|
||||||
installationFiles: []archiveFile{
|
installationFiles: []archiveFile{
|
||||||
{src: "{{binary}}", dst: "deb/usr/bin/{{binary}}", perm: 0755},
|
{src: "{{binary}}", dst: "deb/usr/bin/{{binary}}", perm: 0o755},
|
||||||
{src: "README.md", dst: "deb/usr/share/doc/syncthing/README.txt", perm: 0644},
|
{src: "README.md", dst: "deb/usr/share/doc/syncthing/README.txt", perm: 0o644},
|
||||||
{src: "LICENSE", dst: "deb/usr/share/doc/syncthing/LICENSE.txt", perm: 0644},
|
{src: "LICENSE", dst: "deb/usr/share/doc/syncthing/LICENSE.txt", perm: 0o644},
|
||||||
{src: "AUTHORS", dst: "deb/usr/share/doc/syncthing/AUTHORS.txt", perm: 0644},
|
{src: "AUTHORS", dst: "deb/usr/share/doc/syncthing/AUTHORS.txt", perm: 0o644},
|
||||||
{src: "man/syncthing.1", dst: "deb/usr/share/man/man1/syncthing.1", perm: 0644},
|
{src: "man/syncthing.1", dst: "deb/usr/share/man/man1/syncthing.1", perm: 0o644},
|
||||||
{src: "man/syncthing-config.5", dst: "deb/usr/share/man/man5/syncthing-config.5", perm: 0644},
|
{src: "man/syncthing-config.5", dst: "deb/usr/share/man/man5/syncthing-config.5", perm: 0o644},
|
||||||
{src: "man/syncthing-stignore.5", dst: "deb/usr/share/man/man5/syncthing-stignore.5", perm: 0644},
|
{src: "man/syncthing-stignore.5", dst: "deb/usr/share/man/man5/syncthing-stignore.5", perm: 0o644},
|
||||||
{src: "man/syncthing-device-ids.7", dst: "deb/usr/share/man/man7/syncthing-device-ids.7", perm: 0644},
|
{src: "man/syncthing-device-ids.7", dst: "deb/usr/share/man/man7/syncthing-device-ids.7", perm: 0o644},
|
||||||
{src: "man/syncthing-event-api.7", dst: "deb/usr/share/man/man7/syncthing-event-api.7", perm: 0644},
|
{src: "man/syncthing-event-api.7", dst: "deb/usr/share/man/man7/syncthing-event-api.7", perm: 0o644},
|
||||||
{src: "man/syncthing-faq.7", dst: "deb/usr/share/man/man7/syncthing-faq.7", perm: 0644},
|
{src: "man/syncthing-faq.7", dst: "deb/usr/share/man/man7/syncthing-faq.7", perm: 0o644},
|
||||||
{src: "man/syncthing-networking.7", dst: "deb/usr/share/man/man7/syncthing-networking.7", perm: 0644},
|
{src: "man/syncthing-networking.7", dst: "deb/usr/share/man/man7/syncthing-networking.7", perm: 0o644},
|
||||||
{src: "man/syncthing-rest-api.7", dst: "deb/usr/share/man/man7/syncthing-rest-api.7", perm: 0644},
|
{src: "man/syncthing-rest-api.7", dst: "deb/usr/share/man/man7/syncthing-rest-api.7", perm: 0o644},
|
||||||
{src: "man/syncthing-security.7", dst: "deb/usr/share/man/man7/syncthing-security.7", perm: 0644},
|
{src: "man/syncthing-security.7", dst: "deb/usr/share/man/man7/syncthing-security.7", perm: 0o644},
|
||||||
{src: "man/syncthing-versioning.7", dst: "deb/usr/share/man/man7/syncthing-versioning.7", perm: 0644},
|
{src: "man/syncthing-versioning.7", dst: "deb/usr/share/man/man7/syncthing-versioning.7", perm: 0o644},
|
||||||
{src: "etc/linux-systemd/system/syncthing@.service", dst: "deb/lib/systemd/system/syncthing@.service", perm: 0644},
|
{src: "etc/linux-systemd/system/syncthing@.service", dst: "deb/lib/systemd/system/syncthing@.service", perm: 0o644},
|
||||||
{src: "etc/linux-systemd/user/syncthing.service", dst: "deb/usr/lib/systemd/user/syncthing.service", perm: 0644},
|
{src: "etc/linux-systemd/user/syncthing.service", dst: "deb/usr/lib/systemd/user/syncthing.service", perm: 0o644},
|
||||||
{src: "etc/linux-sysctl/30-syncthing.conf", dst: "deb/usr/lib/sysctl.d/30-syncthing.conf", perm: 0644},
|
{src: "etc/linux-sysctl/30-syncthing.conf", dst: "deb/usr/lib/sysctl.d/30-syncthing.conf", perm: 0o644},
|
||||||
{src: "etc/firewall-ufw/syncthing", dst: "deb/etc/ufw/applications.d/syncthing", perm: 0644},
|
{src: "etc/firewall-ufw/syncthing", dst: "deb/etc/ufw/applications.d/syncthing", perm: 0o644},
|
||||||
{src: "etc/linux-desktop/syncthing-start.desktop", dst: "deb/usr/share/applications/syncthing-start.desktop", perm: 0644},
|
{src: "etc/linux-desktop/syncthing-start.desktop", dst: "deb/usr/share/applications/syncthing-start.desktop", perm: 0o644},
|
||||||
{src: "etc/linux-desktop/syncthing-ui.desktop", dst: "deb/usr/share/applications/syncthing-ui.desktop", perm: 0644},
|
{src: "etc/linux-desktop/syncthing-ui.desktop", dst: "deb/usr/share/applications/syncthing-ui.desktop", perm: 0o644},
|
||||||
{src: "assets/logo-32.png", dst: "deb/usr/share/icons/hicolor/32x32/apps/syncthing.png", perm: 0644},
|
{src: "assets/logo-32.png", dst: "deb/usr/share/icons/hicolor/32x32/apps/syncthing.png", perm: 0o644},
|
||||||
{src: "assets/logo-64.png", dst: "deb/usr/share/icons/hicolor/64x64/apps/syncthing.png", perm: 0644},
|
{src: "assets/logo-64.png", dst: "deb/usr/share/icons/hicolor/64x64/apps/syncthing.png", perm: 0o644},
|
||||||
{src: "assets/logo-128.png", dst: "deb/usr/share/icons/hicolor/128x128/apps/syncthing.png", perm: 0644},
|
{src: "assets/logo-128.png", dst: "deb/usr/share/icons/hicolor/128x128/apps/syncthing.png", perm: 0o644},
|
||||||
{src: "assets/logo-256.png", dst: "deb/usr/share/icons/hicolor/256x256/apps/syncthing.png", perm: 0644},
|
{src: "assets/logo-256.png", dst: "deb/usr/share/icons/hicolor/256x256/apps/syncthing.png", perm: 0o644},
|
||||||
{src: "assets/logo-512.png", dst: "deb/usr/share/icons/hicolor/512x512/apps/syncthing.png", perm: 0644},
|
{src: "assets/logo-512.png", dst: "deb/usr/share/icons/hicolor/512x512/apps/syncthing.png", perm: 0o644},
|
||||||
{src: "assets/logo-only.svg", dst: "deb/usr/share/icons/hicolor/scalable/apps/syncthing.svg", perm: 0644},
|
{src: "assets/logo-only.svg", dst: "deb/usr/share/icons/hicolor/scalable/apps/syncthing.svg", perm: 0o644},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"stdiscosrv": {
|
"stdiscosrv": {
|
||||||
@@ -142,23 +139,22 @@ var targets = map[string]target{
|
|||||||
buildPkgs: []string{"github.com/syncthing/syncthing/cmd/stdiscosrv"},
|
buildPkgs: []string{"github.com/syncthing/syncthing/cmd/stdiscosrv"},
|
||||||
binaryName: "stdiscosrv", // .exe will be added automatically for Windows builds
|
binaryName: "stdiscosrv", // .exe will be added automatically for Windows builds
|
||||||
archiveFiles: []archiveFile{
|
archiveFiles: []archiveFile{
|
||||||
{src: "{{binary}}", dst: "{{binary}}", perm: 0755},
|
{src: "{{binary}}", dst: "{{binary}}", perm: 0o755},
|
||||||
{src: "cmd/stdiscosrv/README.md", dst: "README.txt", perm: 0644},
|
{src: "cmd/stdiscosrv/README.md", dst: "README.txt", perm: 0o644},
|
||||||
{src: "LICENSE", dst: "LICENSE.txt", perm: 0644},
|
{src: "LICENSE", dst: "LICENSE.txt", perm: 0o644},
|
||||||
{src: "AUTHORS", dst: "AUTHORS.txt", perm: 0644},
|
{src: "AUTHORS", dst: "AUTHORS.txt", perm: 0o644},
|
||||||
},
|
},
|
||||||
systemdService: "stdiscosrv.service",
|
systemdService: "stdiscosrv.service",
|
||||||
installationFiles: []archiveFile{
|
installationFiles: []archiveFile{
|
||||||
{src: "{{binary}}", dst: "deb/usr/bin/{{binary}}", perm: 0755},
|
{src: "{{binary}}", dst: "deb/usr/bin/{{binary}}", perm: 0o755},
|
||||||
{src: "cmd/stdiscosrv/README.md", dst: "deb/usr/share/doc/syncthing-discosrv/README.txt", perm: 0644},
|
{src: "cmd/stdiscosrv/README.md", dst: "deb/usr/share/doc/syncthing-discosrv/README.txt", perm: 0o644},
|
||||||
{src: "LICENSE", dst: "deb/usr/share/doc/syncthing-discosrv/LICENSE.txt", perm: 0644},
|
{src: "LICENSE", dst: "deb/usr/share/doc/syncthing-discosrv/LICENSE.txt", perm: 0o644},
|
||||||
{src: "AUTHORS", dst: "deb/usr/share/doc/syncthing-discosrv/AUTHORS.txt", perm: 0644},
|
{src: "AUTHORS", dst: "deb/usr/share/doc/syncthing-discosrv/AUTHORS.txt", perm: 0o644},
|
||||||
{src: "man/stdiscosrv.1", dst: "deb/usr/share/man/man1/stdiscosrv.1", perm: 0644},
|
{src: "man/stdiscosrv.1", dst: "deb/usr/share/man/man1/stdiscosrv.1", perm: 0o644},
|
||||||
{src: "cmd/stdiscosrv/etc/linux-systemd/stdiscosrv.service", dst: "deb/lib/systemd/system/stdiscosrv.service", perm: 0644},
|
{src: "cmd/stdiscosrv/etc/linux-systemd/stdiscosrv.service", dst: "deb/lib/systemd/system/stdiscosrv.service", perm: 0o644},
|
||||||
{src: "cmd/stdiscosrv/etc/linux-systemd/default", dst: "deb/etc/default/syncthing-discosrv", perm: 0644},
|
{src: "cmd/stdiscosrv/etc/linux-systemd/default", dst: "deb/etc/default/syncthing-discosrv", perm: 0o644},
|
||||||
{src: "cmd/stdiscosrv/etc/firewall-ufw/stdiscosrv", dst: "deb/etc/ufw/applications.d/stdiscosrv", perm: 0644},
|
{src: "cmd/stdiscosrv/etc/firewall-ufw/stdiscosrv", dst: "deb/etc/ufw/applications.d/stdiscosrv", perm: 0o644},
|
||||||
},
|
},
|
||||||
tags: []string{"purego"},
|
|
||||||
},
|
},
|
||||||
"strelaysrv": {
|
"strelaysrv": {
|
||||||
name: "strelaysrv",
|
name: "strelaysrv",
|
||||||
@@ -169,44 +165,30 @@ var targets = map[string]target{
|
|||||||
buildPkgs: []string{"github.com/syncthing/syncthing/cmd/strelaysrv"},
|
buildPkgs: []string{"github.com/syncthing/syncthing/cmd/strelaysrv"},
|
||||||
binaryName: "strelaysrv", // .exe will be added automatically for Windows builds
|
binaryName: "strelaysrv", // .exe will be added automatically for Windows builds
|
||||||
archiveFiles: []archiveFile{
|
archiveFiles: []archiveFile{
|
||||||
{src: "{{binary}}", dst: "{{binary}}", perm: 0755},
|
{src: "{{binary}}", dst: "{{binary}}", perm: 0o755},
|
||||||
{src: "cmd/strelaysrv/README.md", dst: "README.txt", perm: 0644},
|
{src: "cmd/strelaysrv/README.md", dst: "README.txt", perm: 0o644},
|
||||||
{src: "cmd/strelaysrv/LICENSE", dst: "LICENSE.txt", perm: 0644},
|
{src: "cmd/strelaysrv/LICENSE", dst: "LICENSE.txt", perm: 0o644},
|
||||||
{src: "LICENSE", dst: "LICENSE.txt", perm: 0644},
|
{src: "LICENSE", dst: "LICENSE.txt", perm: 0o644},
|
||||||
{src: "AUTHORS", dst: "AUTHORS.txt", perm: 0644},
|
{src: "AUTHORS", dst: "AUTHORS.txt", perm: 0o644},
|
||||||
},
|
},
|
||||||
systemdService: "strelaysrv.service",
|
systemdService: "strelaysrv.service",
|
||||||
installationFiles: []archiveFile{
|
installationFiles: []archiveFile{
|
||||||
{src: "{{binary}}", dst: "deb/usr/bin/{{binary}}", perm: 0755},
|
{src: "{{binary}}", dst: "deb/usr/bin/{{binary}}", perm: 0o755},
|
||||||
{src: "cmd/strelaysrv/README.md", dst: "deb/usr/share/doc/syncthing-relaysrv/README.txt", perm: 0644},
|
{src: "cmd/strelaysrv/README.md", dst: "deb/usr/share/doc/syncthing-relaysrv/README.txt", perm: 0o644},
|
||||||
{src: "cmd/strelaysrv/LICENSE", dst: "deb/usr/share/doc/syncthing-relaysrv/LICENSE.txt", perm: 0644},
|
{src: "cmd/strelaysrv/LICENSE", dst: "deb/usr/share/doc/syncthing-relaysrv/LICENSE.txt", perm: 0o644},
|
||||||
{src: "LICENSE", dst: "deb/usr/share/doc/syncthing-relaysrv/LICENSE.txt", perm: 0644},
|
{src: "LICENSE", dst: "deb/usr/share/doc/syncthing-relaysrv/LICENSE.txt", perm: 0o644},
|
||||||
{src: "AUTHORS", dst: "deb/usr/share/doc/syncthing-relaysrv/AUTHORS.txt", perm: 0644},
|
{src: "AUTHORS", dst: "deb/usr/share/doc/syncthing-relaysrv/AUTHORS.txt", perm: 0o644},
|
||||||
{src: "man/strelaysrv.1", dst: "deb/usr/share/man/man1/strelaysrv.1", perm: 0644},
|
{src: "man/strelaysrv.1", dst: "deb/usr/share/man/man1/strelaysrv.1", perm: 0o644},
|
||||||
{src: "cmd/strelaysrv/etc/linux-systemd/strelaysrv.service", dst: "deb/lib/systemd/system/strelaysrv.service", perm: 0644},
|
{src: "cmd/strelaysrv/etc/linux-systemd/strelaysrv.service", dst: "deb/lib/systemd/system/strelaysrv.service", perm: 0o644},
|
||||||
{src: "cmd/strelaysrv/etc/linux-systemd/default", dst: "deb/etc/default/syncthing-relaysrv", perm: 0644},
|
{src: "cmd/strelaysrv/etc/linux-systemd/default", dst: "deb/etc/default/syncthing-relaysrv", perm: 0o644},
|
||||||
{src: "cmd/strelaysrv/etc/firewall-ufw/strelaysrv", dst: "deb/etc/ufw/applications.d/strelaysrv", perm: 0644},
|
{src: "cmd/strelaysrv/etc/firewall-ufw/strelaysrv", dst: "deb/etc/ufw/applications.d/strelaysrv", perm: 0o644},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"strelaypoolsrv": {
|
"strelaypoolsrv": {
|
||||||
name: "strelaypoolsrv",
|
name: "strelaypoolsrv",
|
||||||
debname: "syncthing-relaypoolsrv",
|
|
||||||
debdeps: []string{"libc6"},
|
|
||||||
description: "Syncthing Relay Pool Server",
|
description: "Syncthing Relay Pool Server",
|
||||||
buildPkgs: []string{"github.com/syncthing/syncthing/cmd/infra/strelaypoolsrv"},
|
buildPkgs: []string{"github.com/syncthing/syncthing/cmd/infra/strelaypoolsrv"},
|
||||||
binaryName: "strelaypoolsrv", // .exe will be added automatically for Windows builds
|
binaryName: "strelaypoolsrv",
|
||||||
archiveFiles: []archiveFile{
|
|
||||||
{src: "{{binary}}", dst: "{{binary}}", perm: 0755},
|
|
||||||
{src: "cmd/infra/strelaypoolsrv/README.md", dst: "README.txt", perm: 0644},
|
|
||||||
{src: "cmd/infra/strelaypoolsrv/LICENSE", dst: "LICENSE.txt", perm: 0644},
|
|
||||||
{src: "AUTHORS", dst: "AUTHORS.txt", perm: 0644},
|
|
||||||
},
|
|
||||||
installationFiles: []archiveFile{
|
|
||||||
{src: "{{binary}}", dst: "deb/usr/bin/{{binary}}", perm: 0755},
|
|
||||||
{src: "cmd/infra/strelaypoolsrv/README.md", dst: "deb/usr/share/doc/syncthing-relaypoolsrv/README.txt", perm: 0644},
|
|
||||||
{src: "cmd/infra/strelaypoolsrv/LICENSE", dst: "deb/usr/share/doc/syncthing-relaypoolsrv/LICENSE.txt", perm: 0644},
|
|
||||||
{src: "AUTHORS", dst: "deb/usr/share/doc/syncthing-relaypoolsrv/AUTHORS.txt", perm: 0644},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
"stupgrades": {
|
"stupgrades": {
|
||||||
name: "stupgrades",
|
name: "stupgrades",
|
||||||
@@ -244,13 +226,13 @@ func initTargets() {
|
|||||||
// and "extra" dirs.
|
// and "extra" dirs.
|
||||||
syncthingPkg := targets["syncthing"]
|
syncthingPkg := targets["syncthing"]
|
||||||
for _, file := range listFiles("etc") {
|
for _, file := range listFiles("etc") {
|
||||||
syncthingPkg.archiveFiles = append(syncthingPkg.archiveFiles, archiveFile{src: file, dst: file, perm: 0644})
|
syncthingPkg.archiveFiles = append(syncthingPkg.archiveFiles, archiveFile{src: file, dst: file, perm: 0o644})
|
||||||
}
|
}
|
||||||
for _, file := range listFiles("extra") {
|
for _, file := range listFiles("extra") {
|
||||||
syncthingPkg.archiveFiles = append(syncthingPkg.archiveFiles, archiveFile{src: file, dst: file, perm: 0644})
|
syncthingPkg.archiveFiles = append(syncthingPkg.archiveFiles, archiveFile{src: file, dst: file, perm: 0o644})
|
||||||
}
|
}
|
||||||
for _, file := range listFiles("extra") {
|
for _, file := range listFiles("extra") {
|
||||||
syncthingPkg.installationFiles = append(syncthingPkg.installationFiles, archiveFile{src: file, dst: "deb/usr/share/doc/syncthing/" + filepath.Base(file), perm: 0644})
|
syncthingPkg.installationFiles = append(syncthingPkg.installationFiles, archiveFile{src: file, dst: "deb/usr/share/doc/syncthing/" + filepath.Base(file), perm: 0o644})
|
||||||
}
|
}
|
||||||
targets["syncthing"] = syncthingPkg
|
targets["syncthing"] = syncthingPkg
|
||||||
}
|
}
|
||||||
@@ -306,10 +288,10 @@ func runCommand(cmd string, target target) {
|
|||||||
build(target, tags)
|
build(target, tags)
|
||||||
|
|
||||||
case "test":
|
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":
|
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":
|
case "integration":
|
||||||
integration(false)
|
integration(false)
|
||||||
@@ -347,7 +329,7 @@ func runCommand(cmd string, target target) {
|
|||||||
writeCompatJSON()
|
writeCompatJSON()
|
||||||
|
|
||||||
case "deb":
|
case "deb":
|
||||||
buildDeb(target)
|
buildDeb(target, tags)
|
||||||
|
|
||||||
case "vet":
|
case "vet":
|
||||||
metalintShort()
|
metalintShort()
|
||||||
@@ -397,7 +379,6 @@ func parseFlags() {
|
|||||||
flag.IntVar(&numVersions, "num-versions", numVersions, "Number of versions for changelog command")
|
flag.IntVar(&numVersions, "num-versions", numVersions, "Number of versions for changelog command")
|
||||||
flag.StringVar(&run, "run", "", "Specify which tests to run")
|
flag.StringVar(&run, "run", "", "Specify which tests to run")
|
||||||
flag.StringVar(&benchRun, "bench", "", "Specify which benchmarks 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.StringVar(&buildOut, "build-out", "", "Set the '-o' value for 'go build'")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
}
|
}
|
||||||
@@ -405,7 +386,6 @@ func parseFlags() {
|
|||||||
func test(tags []string, pkgs ...string) {
|
func test(tags []string, pkgs ...string) {
|
||||||
lazyRebuildAssets()
|
lazyRebuildAssets()
|
||||||
|
|
||||||
tags = append(tags, "purego")
|
|
||||||
args := []string{"test", "-tags", strings.Join(tags, " ")}
|
args := []string{"test", "-tags", strings.Join(tags, " ")}
|
||||||
if long {
|
if long {
|
||||||
timeout = longTimeout
|
timeout = longTimeout
|
||||||
@@ -439,7 +419,7 @@ func bench(tags []string, pkgs ...string) {
|
|||||||
func integration(bench bool) {
|
func integration(bench bool) {
|
||||||
lazyRebuildAssets()
|
lazyRebuildAssets()
|
||||||
args := []string{"test", "-v", "-timeout", "60m", "-tags"}
|
args := []string{"test", "-v", "-timeout", "60m", "-tags"}
|
||||||
tags := "purego,integration"
|
tags := "integration"
|
||||||
if bench {
|
if bench {
|
||||||
tags += ",benchmark"
|
tags += ",benchmark"
|
||||||
}
|
}
|
||||||
@@ -471,10 +451,6 @@ func benchArgs() []string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func install(target target, tags []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()
|
lazyRebuildAssets()
|
||||||
|
|
||||||
tags = append(target.tags, tags...)
|
tags = append(target.tags, tags...)
|
||||||
@@ -498,16 +474,12 @@ func install(target target, tags []string) {
|
|||||||
defer shouldCleanupSyso(sysoPath)
|
defer shouldCleanupSyso(sysoPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
args := []string{"install", "-v"}
|
args := []string{"install"}
|
||||||
args = appendParameters(args, tags, target.buildPkgs...)
|
args = appendParameters(args, tags, target.buildPkgs...)
|
||||||
runPrint(goCmd, args...)
|
runPrint(goCmd, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func build(target target, tags []string) {
|
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()
|
lazyRebuildAssets()
|
||||||
tags = append(target.tags, tags...)
|
tags = append(target.tags, tags...)
|
||||||
|
|
||||||
@@ -530,7 +502,7 @@ func build(target target, tags []string) {
|
|||||||
defer shouldCleanupSyso(sysoPath)
|
defer shouldCleanupSyso(sysoPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
args := []string{"build", "-v"}
|
args := []string{"build"}
|
||||||
if buildOut != "" {
|
if buildOut != "" {
|
||||||
args = append(args, "-o", buildOut)
|
args = append(args, "-o", buildOut)
|
||||||
}
|
}
|
||||||
@@ -542,13 +514,6 @@ func setBuildEnvVars() {
|
|||||||
os.Setenv("GOOS", goos)
|
os.Setenv("GOOS", goos)
|
||||||
os.Setenv("GOARCH", goarch)
|
os.Setenv("GOARCH", goarch)
|
||||||
os.Setenv("CC", cc)
|
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 {
|
func appendParameters(args []string, tags []string, pkgs ...string) []string {
|
||||||
@@ -627,7 +592,7 @@ func buildZip(target target, tags []string) {
|
|||||||
fmt.Println(filename)
|
fmt.Println(filename)
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildDeb(target target) {
|
func buildDeb(target target, tags []string) {
|
||||||
os.RemoveAll("deb")
|
os.RemoveAll("deb")
|
||||||
|
|
||||||
// "goarch" here is set to whatever the Debian packages expect. We correct
|
// "goarch" here is set to whatever the Debian packages expect. We correct
|
||||||
@@ -641,7 +606,7 @@ func buildDeb(target target) {
|
|||||||
goarch = "arm"
|
goarch = "arm"
|
||||||
}
|
}
|
||||||
|
|
||||||
build(target, []string{"noupgrade"})
|
build(target, append(tags, "noupgrade"))
|
||||||
|
|
||||||
for i := range target.installationFiles {
|
for i := range target.installationFiles {
|
||||||
target.installationFiles[i].src = strings.Replace(target.installationFiles[i].src, "{{binary}}", target.BinaryName(), 1)
|
target.installationFiles[i].src = strings.Replace(target.installationFiles[i].src, "{{binary}}", target.BinaryName(), 1)
|
||||||
@@ -663,6 +628,9 @@ func buildDeb(target target) {
|
|||||||
// than just 0.14.26. This rectifies that.
|
// than just 0.14.26. This rectifies that.
|
||||||
debver = strings.Replace(debver, "-", "~", -1)
|
debver = strings.Replace(debver, "-", "~", -1)
|
||||||
}
|
}
|
||||||
|
if strings.Contains(debver, "_") {
|
||||||
|
debver = strings.Replace(debver, "_", "~", -1)
|
||||||
|
}
|
||||||
args := []string{
|
args := []string{
|
||||||
"-t", "deb",
|
"-t", "deb",
|
||||||
"-s", "dir",
|
"-s", "dir",
|
||||||
@@ -750,7 +718,7 @@ func shouldBuildSyso(dir string) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
jsonPath := filepath.Join(dir, "versioninfo.json")
|
jsonPath := filepath.Join(dir, "versioninfo.json")
|
||||||
err = os.WriteFile(jsonPath, bs, 0644)
|
err = os.WriteFile(jsonPath, bs, 0o644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", errors.New("failed to create " + jsonPath + ": " + err.Error())
|
return "", errors.New("failed to create " + jsonPath + ": " + err.Error())
|
||||||
}
|
}
|
||||||
@@ -764,12 +732,9 @@ func shouldBuildSyso(dir string) (string, error) {
|
|||||||
sysoPath := filepath.Join(dir, "cmd", "syncthing", "resource.syso")
|
sysoPath := filepath.Join(dir, "cmd", "syncthing", "resource.syso")
|
||||||
|
|
||||||
// See https://github.com/josephspurrier/goversioninfo#command-line-flags
|
// See https://github.com/josephspurrier/goversioninfo#command-line-flags
|
||||||
armOption := ""
|
arm := strings.HasPrefix(goarch, "arm")
|
||||||
if strings.Contains(goarch, "arm") {
|
a64 := strings.Contains(goarch, "64")
|
||||||
armOption = "-arm=true"
|
if _, err := runError("goversioninfo", "-o", sysoPath, fmt.Sprintf("-arm=%v", arm), fmt.Sprintf("-64=%v", a64)); err != nil {
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := runError("goversioninfo", "-o", sysoPath, armOption); err != nil {
|
|
||||||
return "", errors.New("failed to create " + sysoPath + ": " + err.Error())
|
return "", errors.New("failed to create " + sysoPath + ": " + err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -809,7 +774,7 @@ func copyFile(src, dst string, perm os.FileMode) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
copy:
|
copy:
|
||||||
os.MkdirAll(filepath.Dir(dst), 0777)
|
os.MkdirAll(filepath.Dir(dst), 0o777)
|
||||||
if err := os.WriteFile(dst, in, perm); err != nil {
|
if err := os.WriteFile(dst, in, perm); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -841,43 +806,11 @@ func lazyRebuildAssets() {
|
|||||||
shouldRebuild := shouldRebuildAssets("lib/api/auto/gui.files.go", "gui") ||
|
shouldRebuild := shouldRebuildAssets("lib/api/auto/gui.files.go", "gui") ||
|
||||||
shouldRebuildAssets("cmd/infra/strelaypoolsrv/auto/gui.files.go", "cmd/infra/strelaypoolsrv/gui")
|
shouldRebuildAssets("cmd/infra/strelaypoolsrv/auto/gui.files.go", "cmd/infra/strelaypoolsrv/gui")
|
||||||
|
|
||||||
if withNextGenGUI {
|
|
||||||
shouldRebuild = buildNextGenGUI() || shouldRebuild
|
|
||||||
}
|
|
||||||
|
|
||||||
if shouldRebuild {
|
if shouldRebuild {
|
||||||
rebuildAssets()
|
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, 0644); err != nil {
|
|
||||||
fmt.Println("copy:", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func shouldRebuildAssets(target, srcdir string) bool {
|
func shouldRebuildAssets(target, srcdir string) bool {
|
||||||
info, err := os.Stat(target)
|
info, err := os.Stat(target)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -925,22 +858,9 @@ func updateDependencies() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func proto() {
|
func proto() {
|
||||||
pv := protobufVersion()
|
// buf needs to be installed
|
||||||
repo := "https://github.com/gogo/protobuf.git"
|
// https://buf.build/docs/installation/
|
||||||
path := filepath.Join("repos", "protobuf")
|
runPrint("buf", "generate")
|
||||||
|
|
||||||
runPrint(goCmd, "install", fmt.Sprintf("github.com/gogo/protobuf/protoc-gen-gogofast@%v", pv))
|
|
||||||
os.MkdirAll("repos", 0755)
|
|
||||||
|
|
||||||
if _, err := os.Stat(path); err != nil {
|
|
||||||
runPrint("git", "clone", repo, path)
|
|
||||||
} else {
|
|
||||||
runPrintInDir(path, "git", "fetch")
|
|
||||||
}
|
|
||||||
runPrintInDir(path, "git", "checkout", pv)
|
|
||||||
|
|
||||||
runPrint(goCmd, "generate", "github.com/syncthing/syncthing/cmd/stdiscosrv")
|
|
||||||
runPrint(goCmd, "generate", "proto/generate.go")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func testmocks() {
|
func testmocks() {
|
||||||
@@ -950,7 +870,6 @@ func testmocks() {
|
|||||||
"github.com/syncthing/syncthing/lib/connections",
|
"github.com/syncthing/syncthing/lib/connections",
|
||||||
"github.com/syncthing/syncthing/lib/discover",
|
"github.com/syncthing/syncthing/lib/discover",
|
||||||
"github.com/syncthing/syncthing/lib/events",
|
"github.com/syncthing/syncthing/lib/events",
|
||||||
"github.com/syncthing/syncthing/lib/logger",
|
|
||||||
"github.com/syncthing/syncthing/lib/model",
|
"github.com/syncthing/syncthing/lib/model",
|
||||||
"github.com/syncthing/syncthing/lib/protocol",
|
"github.com/syncthing/syncthing/lib/protocol",
|
||||||
}
|
}
|
||||||
@@ -981,6 +900,7 @@ func weblate() {
|
|||||||
func ldflags(tags []string) string {
|
func ldflags(tags []string) string {
|
||||||
b := new(strings.Builder)
|
b := new(strings.Builder)
|
||||||
b.WriteString("-w")
|
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.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.Stamp=%d", buildStamp())
|
||||||
fmt.Fprintf(b, " -X github.com/syncthing/syncthing/lib/build.User=%s", buildUser())
|
fmt.Fprintf(b, " -X github.com/syncthing/syncthing/lib/build.User=%s", buildUser())
|
||||||
@@ -1002,6 +922,9 @@ func rmr(paths ...string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getReleaseVersion() (string, error) {
|
func getReleaseVersion() (string, error) {
|
||||||
|
if ver := os.Getenv("VERSION"); ver != "" {
|
||||||
|
return strings.TrimSpace(ver), nil
|
||||||
|
}
|
||||||
bs, err := os.ReadFile("RELEASE")
|
bs, err := os.ReadFile("RELEASE")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
@@ -1046,7 +969,7 @@ func getGitVersion() (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getVersion() string {
|
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 {
|
if ver, err := getReleaseVersion(); err == nil {
|
||||||
return ver
|
return ver
|
||||||
}
|
}
|
||||||
@@ -1375,10 +1298,7 @@ func zipFile(out string, files []archiveFile) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func codesign(target target) {
|
func codesign(target target) {
|
||||||
switch goos {
|
if goos == "darwin" {
|
||||||
case "windows":
|
|
||||||
windowsCodesign(target.BinaryName())
|
|
||||||
case "darwin":
|
|
||||||
macosCodesign(target.BinaryName())
|
macosCodesign(target.BinaryName())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1402,70 +1322,6 @@ func macosCodesign(file string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func windowsCodesign(file string) {
|
|
||||||
st := "signtool.exe"
|
|
||||||
|
|
||||||
if path := os.Getenv("CODESIGN_SIGNTOOL"); path != "" {
|
|
||||||
st = path
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, algo := range []string{"sha1", "sha256"} {
|
|
||||||
args := []string{"sign", "/fd", algo}
|
|
||||||
if f := os.Getenv("CODESIGN_CERTIFICATE_FILE"); f != "" {
|
|
||||||
args = append(args, "/f", f)
|
|
||||||
} else if b := os.Getenv("CODESIGN_CERTIFICATE_BASE64"); b != "" {
|
|
||||||
// Decode the PFX certificate from base64.
|
|
||||||
bs, err := base64.RawStdEncoding.DecodeString(b)
|
|
||||||
if err != nil {
|
|
||||||
log.Println("Codesign: signing failed: decoding base64:", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write it to a temporary file
|
|
||||||
f, err := os.CreateTemp("", "codesign-*.pfx")
|
|
||||||
if err != nil {
|
|
||||||
log.Println("Codesign: signing failed: creating temp file:", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_ = f.Chmod(0600) // best effort remove other users' access
|
|
||||||
defer os.Remove(f.Name())
|
|
||||||
if _, err := f.Write(bs); err != nil {
|
|
||||||
log.Println("Codesign: signing failed: writing temp file:", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err := f.Close(); err != nil {
|
|
||||||
log.Println("Codesign: signing failed: closing temp file:", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use that when signing
|
|
||||||
args = append(args, "/f", f.Name())
|
|
||||||
}
|
|
||||||
if p := os.Getenv("CODESIGN_CERTIFICATE_PASSWORD"); p != "" {
|
|
||||||
args = append(args, "/p", p)
|
|
||||||
}
|
|
||||||
if tr := os.Getenv("CODESIGN_TIMESTAMP_SERVER"); tr != "" {
|
|
||||||
switch algo {
|
|
||||||
case "sha256":
|
|
||||||
args = append(args, "/tr", tr, "/td", algo)
|
|
||||||
default:
|
|
||||||
args = append(args, "/t", tr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if i > 0 {
|
|
||||||
args = append(args, "/as")
|
|
||||||
}
|
|
||||||
args = append(args, file)
|
|
||||||
|
|
||||||
bs, err := runError(st, args...)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Codesign: signing failed: %v: %s", err, string(bs))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
log.Println("Codesign: successfully signed", file, "using", algo)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func metalint() {
|
func metalint() {
|
||||||
lazyRebuildAssets()
|
lazyRebuildAssets()
|
||||||
runPrint(goCmd, "test", "-run", "Metalint", "./meta")
|
runPrint(goCmd, "test", "-run", "Metalint", "./meta")
|
||||||
@@ -1483,14 +1339,6 @@ func (t target) BinaryName() string {
|
|||||||
return t.binaryName
|
return t.binaryName
|
||||||
}
|
}
|
||||||
|
|
||||||
func protobufVersion() string {
|
|
||||||
bs, err := runError(goCmd, "list", "-f", "{{.Version}}", "-m", "github.com/gogo/protobuf")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("Getting protobuf version:", err)
|
|
||||||
}
|
|
||||||
return string(bs)
|
|
||||||
}
|
|
||||||
|
|
||||||
func currentAndLatestVersions(n int) ([]string, error) {
|
func currentAndLatestVersions(n int) ([]string, error) {
|
||||||
bs, err := runError("git", "tag", "--sort", "taggerdate")
|
bs, err := runError("git", "tag", "--sort", "taggerdate")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ case "${1:-default}" in
|
|||||||
|
|
||||||
prerelease)
|
prerelease)
|
||||||
script authors
|
script authors
|
||||||
|
script copyrights
|
||||||
build weblate
|
build weblate
|
||||||
pushd man ; ./refresh.sh ; popd
|
pushd man ; ./refresh.sh ; popd
|
||||||
git add -A gui man AUTHORS
|
git add -A gui man AUTHORS
|
||||||
|
|||||||
+12
-8
@@ -15,6 +15,9 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
|
|
||||||
|
"github.com/syncthing/syncthing/internal/gen/discoproto"
|
||||||
_ "github.com/syncthing/syncthing/lib/automaxprocs"
|
_ "github.com/syncthing/syncthing/lib/automaxprocs"
|
||||||
"github.com/syncthing/syncthing/lib/beacon"
|
"github.com/syncthing/syncthing/lib/beacon"
|
||||||
"github.com/syncthing/syncthing/lib/discover"
|
"github.com/syncthing/syncthing/lib/discover"
|
||||||
@@ -75,20 +78,21 @@ func recv(bc beacon.Interface) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
var ann discover.Announce
|
var ann discoproto.Announce
|
||||||
ann.Unmarshal(data[4:])
|
proto.Unmarshal(data[4:], &ann)
|
||||||
|
|
||||||
if ann.ID == myID {
|
id, _ := protocol.DeviceIDFromBytes(ann.Id)
|
||||||
|
if id == myID {
|
||||||
// This is one of our own fake packets, don't print it.
|
// This is one of our own fake packets, don't print it.
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Print announcement details for the first packet from a given
|
// Print announcement details for the first packet from a given
|
||||||
// device ID and source address, or if -all was given.
|
// device ID and source address, or if -all was given.
|
||||||
key := ann.ID.String() + src.String()
|
key := id.String() + src.String()
|
||||||
if all || !seen[key] {
|
if all || !seen[key] {
|
||||||
log.Printf("Announcement from %v\n", src)
|
log.Printf("Announcement from %v\n", src)
|
||||||
log.Printf(" %v at %s\n", ann.ID, strings.Join(ann.Addresses, ", "))
|
log.Printf(" %v at %s\n", id, strings.Join(ann.Addresses, ", "))
|
||||||
seen[key] = true
|
seen[key] = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -96,11 +100,11 @@ func recv(bc beacon.Interface) {
|
|||||||
|
|
||||||
// sends fake discovery announcements once every second
|
// sends fake discovery announcements once every second
|
||||||
func send(bc beacon.Interface) {
|
func send(bc beacon.Interface) {
|
||||||
ann := discover.Announce{
|
ann := &discoproto.Announce{
|
||||||
ID: myID,
|
Id: myID[:],
|
||||||
Addresses: []string{"tcp://fake.example.com:12345"},
|
Addresses: []string{"tcp://fake.example.com:12345"},
|
||||||
}
|
}
|
||||||
bs, _ := ann.Marshal()
|
bs, _ := proto.Marshal(ann)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
bc.Send(bs)
|
bc.Send(bs)
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ func main() {
|
|||||||
if *standardBlocks || blockSize < protocol.MinBlockSize {
|
if *standardBlocks || blockSize < protocol.MinBlockSize {
|
||||||
blockSize = protocol.BlockSize(fi.Size())
|
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 {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"cmp"
|
||||||
"compress/gzip"
|
"compress/gzip"
|
||||||
"context"
|
"context"
|
||||||
"io"
|
"io"
|
||||||
@@ -15,7 +16,7 @@ import (
|
|||||||
"math"
|
"math"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sort"
|
"slices"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -177,8 +178,8 @@ func (d *diskStore) inventory() error {
|
|||||||
})
|
})
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
sort.Slice(d.currentFiles, func(i, j int) bool {
|
slices.SortFunc(d.currentFiles, func(a, b currentFile) int {
|
||||||
return d.currentFiles[i].mtime < d.currentFiles[j].mtime
|
return cmp.Compare(a.mtime, b.mtime)
|
||||||
})
|
})
|
||||||
var oldest time.Duration
|
var oldest time.Duration
|
||||||
if len(d.currentFiles) > 0 {
|
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"`
|
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"`
|
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"`
|
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() {
|
func main() {
|
||||||
@@ -68,9 +68,9 @@ func main() {
|
|||||||
go ss.Serve(context.Background())
|
go ss.Serve(context.Background())
|
||||||
|
|
||||||
var ip *ignorePatterns
|
var ip *ignorePatterns
|
||||||
if params.IngorePatterns != "" {
|
if params.IgnorePatterns != "" {
|
||||||
var err error
|
var err error
|
||||||
ip, err = loadIgnorePatterns(params.IngorePatterns)
|
ip, err = loadIgnorePatterns(params.IgnorePatterns)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Failed to load ignore patterns: %v", err)
|
log.Fatalf("Failed to load ignore patterns: %v", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,7 +71,6 @@ func (l *githubSourceCodeLoader) Load(filename string, line, context int) ([][]b
|
|||||||
|
|
||||||
url := urlPrefix + l.version + filename[idx:]
|
url := urlPrefix + l.version + filename[idx:]
|
||||||
resp, err := l.client.Get(url)
|
resp, err := l.client.Get(url)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("Loading source:", err)
|
fmt.Println("Loading source:", err)
|
||||||
return nil, 0
|
return nil, 0
|
||||||
|
|||||||
@@ -52,5 +52,5 @@ func compressAndWrite(bs []byte, fullPath string) error {
|
|||||||
gw.Close()
|
gw.Close()
|
||||||
|
|
||||||
// Create an output file with the compressed report
|
// Create an output file with the compressed report
|
||||||
return os.WriteFile(fullPath, buf.Bytes(), 0644)
|
return os.WriteFile(fullPath, buf.Bytes(), 0o644)
|
||||||
}
|
}
|
||||||
@@ -10,7 +10,7 @@ to NAT or firewall issues.
|
|||||||
|
|
||||||
There is very little reason why you'd want to run this yourself, as
|
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
|
`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
|
check the documentation for
|
||||||
[relaysrv](https://github.com/syncthing/relaysrv), which also explains how
|
[relaysrv](https://github.com/syncthing/relaysrv), which also explains how
|
||||||
to join the default public pool.
|
to join the default public pool.
|
||||||
|
|||||||
@@ -17,19 +17,21 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
lru "github.com/hashicorp/golang-lru/v2"
|
lru "github.com/hashicorp/golang-lru/v2"
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||||
|
|
||||||
"github.com/syncthing/syncthing/cmd/infra/strelaypoolsrv/auto"
|
"github.com/syncthing/syncthing/cmd/infra/strelaypoolsrv/auto"
|
||||||
"github.com/syncthing/syncthing/lib/assets"
|
"github.com/syncthing/syncthing/lib/assets"
|
||||||
_ "github.com/syncthing/syncthing/lib/automaxprocs"
|
_ "github.com/syncthing/syncthing/lib/automaxprocs"
|
||||||
"github.com/syncthing/syncthing/lib/geoip"
|
"github.com/syncthing/syncthing/lib/geoip"
|
||||||
"github.com/syncthing/syncthing/lib/protocol"
|
"github.com/syncthing/syncthing/lib/protocol"
|
||||||
|
"github.com/syncthing/syncthing/lib/rand"
|
||||||
"github.com/syncthing/syncthing/lib/relay/client"
|
"github.com/syncthing/syncthing/lib/relay/client"
|
||||||
"github.com/syncthing/syncthing/lib/sync"
|
|
||||||
"github.com/syncthing/syncthing/lib/tlsutil"
|
"github.com/syncthing/syncthing/lib/tlsutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -109,10 +111,11 @@ var (
|
|||||||
requestProcessors = 8
|
requestProcessors = 8
|
||||||
geoipLicenseKey = os.Getenv("GEOIP_LICENSE_KEY")
|
geoipLicenseKey = os.Getenv("GEOIP_LICENSE_KEY")
|
||||||
geoipAccountID, _ = strconv.Atoi(os.Getenv("GEOIP_ACCOUNT_ID"))
|
geoipAccountID, _ = strconv.Atoi(os.Getenv("GEOIP_ACCOUNT_ID"))
|
||||||
|
maxRelaysReturned = 100
|
||||||
|
|
||||||
requests chan request
|
requests chan request
|
||||||
|
|
||||||
mut = sync.NewRWMutex()
|
mut sync.RWMutex
|
||||||
knownRelays = make([]*relay, 0)
|
knownRelays = make([]*relay, 0)
|
||||||
permanentRelays = make([]*relay, 0)
|
permanentRelays = make([]*relay, 0)
|
||||||
evictionTimers = make(map[string]*time.Timer)
|
evictionTimers = make(map[string]*time.Timer)
|
||||||
@@ -140,6 +143,7 @@ func main() {
|
|||||||
flag.IntVar(&requestQueueLen, "request-queue", requestQueueLen, "Queue length for incoming test requests")
|
flag.IntVar(&requestQueueLen, "request-queue", requestQueueLen, "Queue length for incoming test requests")
|
||||||
flag.IntVar(&requestProcessors, "request-processors", requestProcessors, "Number of request processor routines")
|
flag.IntVar(&requestProcessors, "request-processors", requestProcessors, "Number of request processor routines")
|
||||||
flag.StringVar(&geoipLicenseKey, "geoip-license-key", geoipLicenseKey, "License key for GeoIP database")
|
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()
|
flag.Parse()
|
||||||
|
|
||||||
@@ -330,6 +334,10 @@ func handleEndpointShort(rw http.ResponseWriter, r *http.Request) {
|
|||||||
relays = append(relays, relayShort{URL: slimURL(r.URL)})
|
relays = append(relays, relayShort{URL: slimURL(r.URL)})
|
||||||
}
|
}
|
||||||
mut.RUnlock()
|
mut.RUnlock()
|
||||||
|
if len(relays) > maxRelaysReturned {
|
||||||
|
rand.Shuffle(relays)
|
||||||
|
relays = relays[:maxRelaysReturned]
|
||||||
|
}
|
||||||
|
|
||||||
_ = json.NewEncoder(rw).Encode(map[string][]relayShort{
|
_ = json.NewEncoder(rw).Encode(map[string][]relayShort{
|
||||||
"relays": relays,
|
"relays": relays,
|
||||||
@@ -612,7 +620,7 @@ func createTestCertificate() tls.Certificate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
certFile, keyFile := filepath.Join(tmpDir, "cert.pem"), filepath.Join(tmpDir, "key.pem")
|
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 {
|
if err != nil {
|
||||||
log.Fatalln("Failed to create test X509 key pair:", err)
|
log.Fatalln("Failed to create test X509 key pair:", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import (
|
|||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -28,8 +27,6 @@ func init() {
|
|||||||
{URL: "known2"},
|
{URL: "known2"},
|
||||||
{URL: "known3"},
|
{URL: "known3"},
|
||||||
}
|
}
|
||||||
|
|
||||||
mut = new(sync.RWMutex)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Regression test: handleGetRequest should not modify permanentRelays.
|
// Regression test: handleGetRequest should not modify permanentRelays.
|
||||||
|
|||||||
@@ -6,10 +6,10 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
"github.com/syncthing/syncthing/lib/sync"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -104,7 +104,7 @@ func refreshStats() {
|
|||||||
mut.RUnlock()
|
mut.RUnlock()
|
||||||
|
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
wg := sync.NewWaitGroup()
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
results := make(chan statsFetchResult, len(relays))
|
results := make(chan statsFetchResult, len(relays))
|
||||||
for _, rel := range relays {
|
for _, rel := range relays {
|
||||||
@@ -173,7 +173,7 @@ func fetchStats(relay *relay) *stats {
|
|||||||
|
|
||||||
var stats 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 nil
|
||||||
}
|
}
|
||||||
return &stats
|
return &stats
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import (
|
|||||||
|
|
||||||
"github.com/alecthomas/kong"
|
"github.com/alecthomas/kong"
|
||||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||||
|
"github.com/syncthing/syncthing/internal/slogutil"
|
||||||
_ "github.com/syncthing/syncthing/lib/automaxprocs"
|
_ "github.com/syncthing/syncthing/lib/automaxprocs"
|
||||||
"github.com/syncthing/syncthing/lib/httpcache"
|
"github.com/syncthing/syncthing/lib/httpcache"
|
||||||
"github.com/syncthing/syncthing/lib/upgrade"
|
"github.com/syncthing/syncthing/lib/upgrade"
|
||||||
@@ -58,10 +59,10 @@ func server(params *cli) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("metrics: %w", err)
|
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() {
|
go func() {
|
||||||
if err := http.Serve(metricsListen, mux); err != nil {
|
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() {
|
go func() {
|
||||||
for range time.NewTicker(params.CacheTime).C {
|
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 {
|
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 {
|
if err != nil {
|
||||||
return fmt.Errorf("listen: %w", err)
|
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)
|
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")
|
osv := req.Header.Get("Syncthing-Os-Version")
|
||||||
if ua != "" && osv != "" {
|
if ua != "" && osv != "" {
|
||||||
// We should determine the compatibility of the releases.
|
// We should determine the compatibility of the releases.
|
||||||
rels = filterForCompabitility(rels, ua, osv)
|
rels = filterForCompatibility(rels, ua, osv)
|
||||||
} else {
|
} else {
|
||||||
metricFilterCalls.WithLabelValues("no-ua-or-osversion").Inc()
|
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.
|
// looking for a prerelease at all.
|
||||||
func filterForLatest(rels []upgrade.Release) []upgrade.Release {
|
func filterForLatest(rels []upgrade.Release) []upgrade.Release {
|
||||||
var filtered []upgrade.Release
|
var filtered []upgrade.Release
|
||||||
var havePre bool
|
havePre := make(map[string]bool)
|
||||||
|
haveStable := make(map[string]bool)
|
||||||
for _, rel := range rels {
|
for _, rel := range rels {
|
||||||
if !rel.Prerelease {
|
major, _, _ := strings.Cut(rel.Tag, ".")
|
||||||
// We found a stable version, we're good now.
|
if !rel.Prerelease && !haveStable[major] {
|
||||||
|
// Remember the first non-pre for each major
|
||||||
filtered = append(filtered, rel)
|
filtered = append(filtered, rel)
|
||||||
break
|
haveStable[major] = true
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
if rel.Prerelease && !havePre {
|
if rel.Prerelease && !havePre[major] && !haveStable[major] {
|
||||||
// We remember the first prerelease we find.
|
// We remember the first prerelease we find, unless we've
|
||||||
|
// already found a non-pre of the same major.
|
||||||
filtered = append(filtered, rel)
|
filtered = append(filtered, rel)
|
||||||
havePre = true
|
havePre[major] = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return filtered
|
return filtered
|
||||||
@@ -219,7 +224,7 @@ func filterForLatest(rels []upgrade.Release) []upgrade.Release {
|
|||||||
|
|
||||||
var userAgentOSArchExp = regexp.MustCompile(`^syncthing.*\(.+ (\w+)-(\w+)\)$`)
|
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)
|
osArch := userAgentOSArchExp.FindStringSubmatch(ua)
|
||||||
if len(osArch) != 3 {
|
if len(osArch) != 3 {
|
||||||
metricFilterCalls.WithLabelValues("bad-os-arch").Inc()
|
metricFilterCalls.WithLabelValues("bad-os-arch").Inc()
|
||||||
@@ -227,7 +232,7 @@ func filterForCompabitility(rels []upgrade.Release, ua, osv string) []upgrade.Re
|
|||||||
}
|
}
|
||||||
os := osArch[1]
|
os := osArch[1]
|
||||||
|
|
||||||
filtered := rels[:0]
|
var filtered []upgrade.Release
|
||||||
for _, rel := range rels {
|
for _, rel := range rels {
|
||||||
if rel.Compatibility == nil {
|
if rel.Compatibility == nil {
|
||||||
// No requirements means it's compatible with everything.
|
// No requirements means it's compatible with everything.
|
||||||
@@ -258,9 +263,10 @@ func filterForCompabitility(rels []upgrade.Release, ua, osv string) []upgrade.Re
|
|||||||
}
|
}
|
||||||
|
|
||||||
type cachedReleases struct {
|
type cachedReleases struct {
|
||||||
url string
|
url string
|
||||||
mut sync.RWMutex
|
mut sync.RWMutex
|
||||||
current []upgrade.Release
|
current []upgrade.Release
|
||||||
|
latestRel, latestPre string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *cachedReleases) Releases() []upgrade.Release {
|
func (c *cachedReleases) Releases() []upgrade.Release {
|
||||||
@@ -274,8 +280,26 @@ func (c *cachedReleases) Update(ctx context.Context) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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.mut.Lock()
|
||||||
c.current = rels
|
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()
|
c.mut.Unlock()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,4 +27,10 @@ var (
|
|||||||
Subsystem: "upgrade",
|
Subsystem: "upgrade",
|
||||||
Name: "http_requests",
|
Name: "http_requests",
|
||||||
}, []string{"target", "result"})
|
}, []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",
|
Subsystem: "ursrv_v2",
|
||||||
Name: "collect_seconds_last",
|
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{
|
metricsWriteSecondsLast = promauto.NewGauge(prometheus.GaugeOpts{
|
||||||
Namespace: "syncthing",
|
Namespace: "syncthing",
|
||||||
Subsystem: "ursrv_v2",
|
Subsystem: "ursrv_v2",
|
||||||
|
|||||||
@@ -7,6 +7,8 @@
|
|||||||
package serve
|
package serve
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"log/slog"
|
||||||
"reflect"
|
"reflect"
|
||||||
"slices"
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -28,7 +30,7 @@ type metricsSet struct {
|
|||||||
gaugeVecLabels map[string][]string
|
gaugeVecLabels map[string][]string
|
||||||
summaries map[string]*metricSummary
|
summaries map[string]*metricSummary
|
||||||
|
|
||||||
collectMut sync.Mutex
|
collectMut sync.RWMutex
|
||||||
collectCutoff time.Duration
|
collectCutoff time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,6 +110,60 @@ func nameConstLabels(name string) (string, prometheus.Labels) {
|
|||||||
return name, m
|
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) {
|
func (s *metricsSet) addReport(r *contract.Report) {
|
||||||
gaugeVecs := make(map[string][]string)
|
gaugeVecs := make(map[string][]string)
|
||||||
s.addReportStruct(reflect.ValueOf(r).Elem(), gaugeVecs)
|
s.addReportStruct(reflect.ValueOf(r).Elem(), gaugeVecs)
|
||||||
@@ -198,8 +254,8 @@ func (s *metricsSet) Describe(c chan<- *prometheus.Desc) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *metricsSet) Collect(c chan<- prometheus.Metric) {
|
func (s *metricsSet) Collect(c chan<- prometheus.Metric) {
|
||||||
s.collectMut.Lock()
|
s.collectMut.RLock()
|
||||||
defer s.collectMut.Unlock()
|
defer s.collectMut.RUnlock()
|
||||||
|
|
||||||
t0 := time.Now()
|
t0 := time.Now()
|
||||||
defer func() {
|
defer func() {
|
||||||
@@ -209,26 +265,6 @@ func (s *metricsSet) Collect(c chan<- prometheus.Metric) {
|
|||||||
metricsCollectsTotal.Inc()
|
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 {
|
for _, g := range s.gauges {
|
||||||
c <- g
|
c <- g
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,21 +17,23 @@ import (
|
|||||||
"log/slog"
|
"log/slog"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
_ "net/http/pprof"
|
||||||
"os"
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
_ "net/http/pprof"
|
|
||||||
|
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||||
"github.com/puzpuzpuz/xsync/v3"
|
"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/build"
|
||||||
"github.com/syncthing/syncthing/lib/geoip"
|
"github.com/syncthing/syncthing/lib/geoip"
|
||||||
"github.com/syncthing/syncthing/lib/s3"
|
|
||||||
"github.com/syncthing/syncthing/lib/ur/contract"
|
"github.com/syncthing/syncthing/lib/ur/contract"
|
||||||
|
"github.com/thejerf/suture/v4"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CLI struct {
|
type CLI struct {
|
||||||
@@ -42,11 +44,15 @@ type CLI struct {
|
|||||||
DumpFile string `env:"UR_DUMP_FILE" default:"reports.jsons.gz"`
|
DumpFile string `env:"UR_DUMP_FILE" default:"reports.jsons.gz"`
|
||||||
DumpInterval time.Duration `env:"UR_DUMP_INTERVAL" default:"5m"`
|
DumpInterval time.Duration `env:"UR_DUMP_INTERVAL" default:"5m"`
|
||||||
|
|
||||||
S3Endpoint string `name:"s3-endpoint" hidden:"true" env:"UR_S3_ENDPOINT"`
|
S3Endpoint string `name:"s3-endpoint" env:"UR_S3_ENDPOINT"`
|
||||||
S3Region string `name:"s3-region" hidden:"true" env:"UR_S3_REGION"`
|
S3Region string `name:"s3-region" env:"UR_S3_REGION"`
|
||||||
S3Bucket string `name:"s3-bucket" hidden:"true" env:"UR_S3_BUCKET"`
|
S3Bucket string `name:"s3-bucket" env:"UR_S3_BUCKET"`
|
||||||
S3AccessKeyID string `name:"s3-access-key-id" hidden:"true" env:"UR_S3_ACCESS_KEY_ID"`
|
S3AccessKeyID string `name:"s3-access-key-id" env:"UR_S3_ACCESS_KEY_ID"`
|
||||||
S3SecretKey string `name:"s3-secret-key" hidden:"true" env:"UR_S3_SECRET_KEY"`
|
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 (
|
var (
|
||||||
@@ -79,6 +85,7 @@ var (
|
|||||||
{regexp.MustCompile(`\ssyncthing@archlinux`), "Arch (3rd party)"},
|
{regexp.MustCompile(`\ssyncthing@archlinux`), "Arch (3rd party)"},
|
||||||
{regexp.MustCompile(`@debian`), "Debian (3rd party)"},
|
{regexp.MustCompile(`@debian`), "Debian (3rd party)"},
|
||||||
{regexp.MustCompile(`@fedora`), "Fedora (3rd party)"},
|
{regexp.MustCompile(`@fedora`), "Fedora (3rd party)"},
|
||||||
|
{regexp.MustCompile(`@openSUSE`), "openSUSE (3rd party)"},
|
||||||
{regexp.MustCompile(`\sbrew@`), "Homebrew (3rd party)"},
|
{regexp.MustCompile(`\sbrew@`), "Homebrew (3rd party)"},
|
||||||
{regexp.MustCompile(`\sroot@buildkitsandbox`), "LinuxServer.io (3rd party)"},
|
{regexp.MustCompile(`\sroot@buildkitsandbox`), "LinuxServer.io (3rd party)"},
|
||||||
{regexp.MustCompile(`\sports@freebsd`), "FreeBSD (3rd party)"},
|
{regexp.MustCompile(`\sports@freebsd`), "FreeBSD (3rd party)"},
|
||||||
@@ -99,42 +106,48 @@ func (cli *CLI) Run() error {
|
|||||||
|
|
||||||
urListener, err := net.Listen("tcp", cli.Listen)
|
urListener, err := net.Listen("tcp", cli.Listen)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.Error("Failed to listen (usage reports)", "error", err)
|
slog.Error("Failed to listen (usage reports)", slogutil.Error(err))
|
||||||
return 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)
|
internalListener, err := net.Listen("tcp", cli.ListenInternal)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.Error("Failed to listen (internal)", "error", err)
|
slog.Error("Failed to listen (internal)", slogutil.Error(err))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
slog.Info("Listening (internal)", "address", internalListener.Addr())
|
slog.Info("Listening (internal)", slogutil.Address(internalListener.Addr()))
|
||||||
|
|
||||||
var geo *geoip.Provider
|
var geo *geoip.Provider
|
||||||
if cli.GeoIPAccountID != 0 && cli.GeoIPLicenseKey != "" {
|
if cli.GeoIPAccountID != 0 && cli.GeoIPLicenseKey != "" {
|
||||||
geo, err = geoip.NewGeoLite2CityProvider(context.Background(), cli.GeoIPAccountID, cli.GeoIPLicenseKey, os.TempDir())
|
geo, err = geoip.NewGeoLite2CityProvider(context.Background(), cli.GeoIPAccountID, cli.GeoIPLicenseKey, os.TempDir())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.Error("Failed to load GeoIP", "error", err)
|
slog.Error("Failed to load GeoIP", slogutil.Error(err))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
go geo.Serve(context.TODO())
|
go geo.Serve(context.TODO())
|
||||||
}
|
}
|
||||||
|
|
||||||
// s3
|
// Blob storage
|
||||||
|
|
||||||
var s3sess *s3.Session
|
var blobs blob.Store
|
||||||
if cli.S3Endpoint != "" {
|
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 {
|
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
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := os.Stat(cli.DumpFile); err != nil && s3sess != nil {
|
if _, err := os.Stat(cli.DumpFile); err != nil && blobs != nil {
|
||||||
if err := cli.downloadDumpFile(s3sess); err != nil {
|
if err := cli.downloadDumpFile(blobs); err != nil {
|
||||||
slog.Error("Failed to download dump file", "error", err)
|
slog.Error("Failed to download dump file", slogutil.Error(err))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,8 +168,8 @@ func (cli *CLI) Run() error {
|
|||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
for range time.Tick(cli.DumpInterval) {
|
for range time.Tick(cli.DumpInterval) {
|
||||||
if err := cli.saveDumpFile(srv, s3sess); err != nil {
|
if err := cli.saveDumpFile(srv, blobs); err != nil {
|
||||||
slog.Error("Failed to write dump file", "error", err)
|
slog.Error("Failed to write dump file", slogutil.Error(err))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
@@ -175,7 +188,12 @@ func (cli *CLI) Run() error {
|
|||||||
// New external metrics endpoint accepts reports from clients and serves
|
// New external metrics endpoint accepts reports from clients and serves
|
||||||
// aggregated usage reporting metrics.
|
// aggregated usage reporting metrics.
|
||||||
|
|
||||||
|
main := suture.NewSimple("main")
|
||||||
|
main.ServeBackground(context.Background())
|
||||||
|
|
||||||
ms := newMetricsSet(srv)
|
ms := newMetricsSet(srv)
|
||||||
|
main.Add(ms)
|
||||||
|
|
||||||
reg := prometheus.NewRegistry()
|
reg := prometheus.NewRegistry()
|
||||||
reg.MustRegister(ms)
|
reg.MustRegister(ms)
|
||||||
|
|
||||||
@@ -186,7 +204,7 @@ func (cli *CLI) Run() error {
|
|||||||
|
|
||||||
metricsSrv := http.Server{
|
metricsSrv := http.Server{
|
||||||
ReadTimeout: 5 * time.Second,
|
ReadTimeout: 5 * time.Second,
|
||||||
WriteTimeout: 15 * time.Second,
|
WriteTimeout: 60 * time.Second,
|
||||||
Handler: mux,
|
Handler: mux,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,8 +212,8 @@ func (cli *CLI) Run() error {
|
|||||||
return metricsSrv.Serve(urListener)
|
return metricsSrv.Serve(urListener)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cli *CLI) downloadDumpFile(s3sess *s3.Session) error {
|
func (cli *CLI) downloadDumpFile(blobs blob.Store) error {
|
||||||
latestKey, err := s3sess.LatestKey()
|
latestKey, err := blobs.LatestKey(context.Background())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("list latest S3 key: %w", err)
|
return fmt.Errorf("list latest S3 key: %w", err)
|
||||||
}
|
}
|
||||||
@@ -203,7 +221,7 @@ func (cli *CLI) downloadDumpFile(s3sess *s3.Session) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("create dump file: %w", err)
|
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()
|
_ = fd.Close()
|
||||||
return fmt.Errorf("download dump file: %w", err)
|
return fmt.Errorf("download dump file: %w", err)
|
||||||
}
|
}
|
||||||
@@ -214,7 +232,12 @@ func (cli *CLI) downloadDumpFile(s3sess *s3.Session) error {
|
|||||||
return nil
|
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")
|
fd, err := os.Create(cli.DumpFile + ".tmp")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("creating dump file: %w", err)
|
return fmt.Errorf("creating dump file: %w", err)
|
||||||
@@ -233,19 +256,20 @@ func (cli *CLI) saveDumpFile(srv *server, s3sess *s3.Session) error {
|
|||||||
if err := os.Rename(cli.DumpFile+".tmp", cli.DumpFile); err != nil {
|
if err := os.Rename(cli.DumpFile+".tmp", cli.DumpFile); err != nil {
|
||||||
return fmt.Errorf("renaming dump file: %w", err)
|
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"))
|
key := fmt.Sprintf("reports-%s.jsons.gz", time.Now().UTC().Format("2006-01-02"))
|
||||||
fd, err := os.Open(cli.DumpFile)
|
fd, err := os.Open(cli.DumpFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("opening dump file: %w", err)
|
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)
|
return fmt.Errorf("uploading dump file: %w", err)
|
||||||
}
|
}
|
||||||
_ = fd.Close()
|
_ = fd.Close()
|
||||||
slog.Info("Dump file uploaded")
|
slog.Info("Dump file uploaded", "d", time.Since(t1).String())
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -296,7 +320,7 @@ func (s *server) handleNewData(w http.ResponseWriter, r *http.Request) {
|
|||||||
lr := &io.LimitedReader{R: r.Body, N: 40 * 1024}
|
lr := &io.LimitedReader{R: r.Body, N: 40 * 1024}
|
||||||
bs, _ := io.ReadAll(lr)
|
bs, _ := io.ReadAll(lr)
|
||||||
if err := json.Unmarshal(bs, &rep); err != nil {
|
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)
|
http.Error(w, "JSON Decode Error", http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -306,7 +330,7 @@ func (s *server) handleNewData(w http.ResponseWriter, r *http.Request) {
|
|||||||
rep.Address = addr
|
rep.Address = addr
|
||||||
|
|
||||||
if err := rep.Validate(); err != nil {
|
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)
|
http.Error(w, "Validation Error", http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -353,6 +377,16 @@ func (s *server) addReport(rep *contract.Report) bool {
|
|||||||
break
|
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)
|
_, loaded := s.reports.LoadAndStore(rep.UniqueID, rep)
|
||||||
return loaded
|
return loaded
|
||||||
@@ -373,6 +407,7 @@ func (s *server) save(w io.Writer) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *server) load(r io.Reader) {
|
func (s *server) load(r io.Reader) {
|
||||||
|
t0 := time.Now()
|
||||||
dec := json.NewDecoder(r)
|
dec := json.NewDecoder(r)
|
||||||
s.reports.Clear()
|
s.reports.Clear()
|
||||||
for {
|
for {
|
||||||
@@ -380,12 +415,12 @@ func (s *server) load(r io.Reader) {
|
|||||||
if err := dec.Decode(&rep); errors.Is(err, io.EOF) {
|
if err := dec.Decode(&rep); errors.Is(err, io.EOF) {
|
||||||
break
|
break
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
slog.Error("Failed to load record", "error", err)
|
slog.Error("Failed to load record", slogutil.Error(err))
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
s.addReport(&rep)
|
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 (
|
var (
|
||||||
|
|||||||
+16
-12
@@ -13,8 +13,12 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
|
|
||||||
amqp "github.com/rabbitmq/amqp091-go"
|
amqp "github.com/rabbitmq/amqp091-go"
|
||||||
"github.com/syncthing/syncthing/lib/protocol"
|
|
||||||
"github.com/thejerf/suture/v4"
|
"github.com/thejerf/suture/v4"
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
|
|
||||||
|
"github.com/syncthing/syncthing/internal/gen/discosrv"
|
||||||
|
"github.com/syncthing/syncthing/internal/protoutil"
|
||||||
|
"github.com/syncthing/syncthing/lib/protocol"
|
||||||
)
|
)
|
||||||
|
|
||||||
type amqpReplicator struct {
|
type amqpReplicator struct {
|
||||||
@@ -22,7 +26,7 @@ type amqpReplicator struct {
|
|||||||
broker string
|
broker string
|
||||||
sender *amqpSender
|
sender *amqpSender
|
||||||
receiver *amqpReceiver
|
receiver *amqpReceiver
|
||||||
outbox chan ReplicationRecord
|
outbox chan *discosrv.ReplicationRecord
|
||||||
}
|
}
|
||||||
|
|
||||||
func newAMQPReplicator(broker, clientID string, db database) *amqpReplicator {
|
func newAMQPReplicator(broker, clientID string, db database) *amqpReplicator {
|
||||||
@@ -31,7 +35,7 @@ func newAMQPReplicator(broker, clientID string, db database) *amqpReplicator {
|
|||||||
sender := &amqpSender{
|
sender := &amqpSender{
|
||||||
broker: broker,
|
broker: broker,
|
||||||
clientID: clientID,
|
clientID: clientID,
|
||||||
outbox: make(chan ReplicationRecord, replicationOutboxSize),
|
outbox: make(chan *discosrv.ReplicationRecord, replicationOutboxSize),
|
||||||
}
|
}
|
||||||
svc.Add(sender)
|
svc.Add(sender)
|
||||||
|
|
||||||
@@ -47,18 +51,18 @@ func newAMQPReplicator(broker, clientID string, db database) *amqpReplicator {
|
|||||||
broker: broker,
|
broker: broker,
|
||||||
sender: sender,
|
sender: sender,
|
||||||
receiver: receiver,
|
receiver: receiver,
|
||||||
outbox: make(chan ReplicationRecord, replicationOutboxSize),
|
outbox: make(chan *discosrv.ReplicationRecord, replicationOutboxSize),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *amqpReplicator) send(key *protocol.DeviceID, ps []DatabaseAddress, seen int64) {
|
func (s *amqpReplicator) send(key *protocol.DeviceID, ps []*discosrv.DatabaseAddress, seen int64) {
|
||||||
s.sender.send(key, ps, seen)
|
s.sender.send(key, ps, seen)
|
||||||
}
|
}
|
||||||
|
|
||||||
type amqpSender struct {
|
type amqpSender struct {
|
||||||
broker string
|
broker string
|
||||||
clientID string
|
clientID string
|
||||||
outbox chan ReplicationRecord
|
outbox chan *discosrv.ReplicationRecord
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *amqpSender) Serve(ctx context.Context) error {
|
func (s *amqpSender) Serve(ctx context.Context) error {
|
||||||
@@ -73,12 +77,12 @@ func (s *amqpSender) Serve(ctx context.Context) error {
|
|||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case rec := <-s.outbox:
|
case rec := <-s.outbox:
|
||||||
size := rec.Size()
|
size := proto.Size(rec)
|
||||||
if len(buf) < size {
|
if len(buf) < size {
|
||||||
buf = make([]byte, size)
|
buf = make([]byte, size)
|
||||||
}
|
}
|
||||||
|
|
||||||
n, err := rec.MarshalTo(buf)
|
n, err := protoutil.MarshalTo(buf, rec)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
replicationSendsTotal.WithLabelValues("error").Inc()
|
replicationSendsTotal.WithLabelValues("error").Inc()
|
||||||
return fmt.Errorf("replication marshal: %w", err)
|
return fmt.Errorf("replication marshal: %w", err)
|
||||||
@@ -111,8 +115,8 @@ func (s *amqpSender) String() string {
|
|||||||
return fmt.Sprintf("amqpSender(%q)", s.broker)
|
return fmt.Sprintf("amqpSender(%q)", s.broker)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *amqpSender) send(key *protocol.DeviceID, ps []DatabaseAddress, seen int64) {
|
func (s *amqpSender) send(key *protocol.DeviceID, ps []*discosrv.DatabaseAddress, seen int64) {
|
||||||
item := ReplicationRecord{
|
item := &discosrv.ReplicationRecord{
|
||||||
Key: key[:],
|
Key: key[:],
|
||||||
Addresses: ps,
|
Addresses: ps,
|
||||||
Seen: seen,
|
Seen: seen,
|
||||||
@@ -158,8 +162,8 @@ func (s *amqpReceiver) Serve(ctx context.Context) error {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
var rec ReplicationRecord
|
var rec discosrv.ReplicationRecord
|
||||||
if err := rec.Unmarshal(msg.Body); err != nil {
|
if err := proto.Unmarshal(msg.Body, &rec); err != nil {
|
||||||
replicationRecvsTotal.WithLabelValues("error").Inc()
|
replicationRecvsTotal.WithLabelValues("error").Inc()
|
||||||
return fmt.Errorf("replication unmarshal: %w", err)
|
return fmt.Errorf("replication unmarshal: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
+25
-11
@@ -28,6 +28,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/syncthing/syncthing/internal/gen/discosrv"
|
||||||
"github.com/syncthing/syncthing/lib/protocol"
|
"github.com/syncthing/syncthing/lib/protocol"
|
||||||
"github.com/syncthing/syncthing/lib/stringutil"
|
"github.com/syncthing/syncthing/lib/stringutil"
|
||||||
)
|
)
|
||||||
@@ -52,7 +53,7 @@ type apiSrv struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type replicator interface {
|
type replicator interface {
|
||||||
send(key *protocol.DeviceID, addrs []DatabaseAddress, seen int64)
|
send(key *protocol.DeviceID, addrs []*discosrv.DatabaseAddress, seen int64)
|
||||||
}
|
}
|
||||||
|
|
||||||
type requestID int64
|
type requestID int64
|
||||||
@@ -65,7 +66,7 @@ type contextKey int
|
|||||||
|
|
||||||
const idKey contextKey = iota
|
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{
|
return &apiSrv{
|
||||||
addr: addr,
|
addr: addr,
|
||||||
cert: cert,
|
cert: cert,
|
||||||
@@ -76,13 +77,13 @@ func newAPISrv(addr string, cert tls.Certificate, db database, repl replicator,
|
|||||||
seenTracker: &retryAfterTracker{
|
seenTracker: &retryAfterTracker{
|
||||||
name: "seenTracker",
|
name: "seenTracker",
|
||||||
bucketStarts: time.Now(),
|
bucketStarts: time.Now(),
|
||||||
desiredRate: 250,
|
desiredRate: desiredNotFoundRate / 2,
|
||||||
currentDelay: notFoundRetryUnknownMinSeconds,
|
currentDelay: notFoundRetryUnknownMinSeconds,
|
||||||
},
|
},
|
||||||
notSeenTracker: &retryAfterTracker{
|
notSeenTracker: &retryAfterTracker{
|
||||||
name: "notSeenTracker",
|
name: "notSeenTracker",
|
||||||
bucketStarts: time.Now(),
|
bucketStarts: time.Now(),
|
||||||
desiredRate: 250,
|
desiredRate: desiredNotFoundRate / 2,
|
||||||
currentDelay: notFoundRetryUnknownMaxSeconds / 2,
|
currentDelay: notFoundRetryUnknownMaxSeconds / 2,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -119,7 +120,9 @@ func (s *apiSrv) Serve(ctx context.Context) error {
|
|||||||
ReadTimeout: httpReadTimeout,
|
ReadTimeout: httpReadTimeout,
|
||||||
WriteTimeout: httpWriteTimeout,
|
WriteTimeout: httpWriteTimeout,
|
||||||
MaxHeaderBytes: httpMaxHeaderBytes,
|
MaxHeaderBytes: httpMaxHeaderBytes,
|
||||||
ErrorLog: log.New(io.Discard, "", 0),
|
}
|
||||||
|
if !debug {
|
||||||
|
srv.ErrorLog = log.New(io.Discard, "", 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
@@ -195,7 +198,7 @@ func (s *apiSrv) handleGET(w http.ResponseWriter, req *http.Request) {
|
|||||||
deviceID, err := protocol.DeviceIDFromString(req.URL.Query().Get("device"))
|
deviceID, err := protocol.DeviceIDFromString(req.URL.Query().Get("device"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if debug {
|
if debug {
|
||||||
log.Println(reqID, "bad device param")
|
log.Println(reqID, "bad device param:", err)
|
||||||
}
|
}
|
||||||
lookupRequestsTotal.WithLabelValues("bad_request").Inc()
|
lookupRequestsTotal.WithLabelValues("bad_request").Inc()
|
||||||
w.Header().Set("Retry-After", errorRetryAfterString())
|
w.Header().Set("Retry-After", errorRetryAfterString())
|
||||||
@@ -280,6 +283,9 @@ func (s *apiSrv) handlePOST(remoteAddr *net.TCPAddr, w http.ResponseWriter, req
|
|||||||
|
|
||||||
addresses := fixupAddresses(remoteAddr, ann.Addresses)
|
addresses := fixupAddresses(remoteAddr, ann.Addresses)
|
||||||
if len(addresses) == 0 {
|
if len(addresses) == 0 {
|
||||||
|
if debug {
|
||||||
|
log.Println(reqID, "no addresses")
|
||||||
|
}
|
||||||
announceRequestsTotal.WithLabelValues("bad_request").Inc()
|
announceRequestsTotal.WithLabelValues("bad_request").Inc()
|
||||||
w.Header().Set("Retry-After", errorRetryAfterString())
|
w.Header().Set("Retry-After", errorRetryAfterString())
|
||||||
http.Error(w, "Bad Request", http.StatusBadRequest)
|
http.Error(w, "Bad Request", http.StatusBadRequest)
|
||||||
@@ -287,6 +293,9 @@ func (s *apiSrv) handlePOST(remoteAddr *net.TCPAddr, w http.ResponseWriter, req
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err := s.handleAnnounce(deviceID, addresses); err != nil {
|
if err := s.handleAnnounce(deviceID, addresses); err != nil {
|
||||||
|
if debug {
|
||||||
|
log.Println(reqID, "handle:", err)
|
||||||
|
}
|
||||||
announceRequestsTotal.WithLabelValues("internal_error").Inc()
|
announceRequestsTotal.WithLabelValues("internal_error").Inc()
|
||||||
w.Header().Set("Retry-After", errorRetryAfterString())
|
w.Header().Set("Retry-After", errorRetryAfterString())
|
||||||
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
|
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
|
||||||
@@ -297,6 +306,9 @@ func (s *apiSrv) handlePOST(remoteAddr *net.TCPAddr, w http.ResponseWriter, req
|
|||||||
|
|
||||||
w.Header().Set("Reannounce-After", reannounceAfterString())
|
w.Header().Set("Reannounce-After", reannounceAfterString())
|
||||||
w.WriteHeader(http.StatusNoContent)
|
w.WriteHeader(http.StatusNoContent)
|
||||||
|
if debug {
|
||||||
|
log.Println(reqID, "announced", deviceID, addresses)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *apiSrv) Stop() {
|
func (s *apiSrv) Stop() {
|
||||||
@@ -312,10 +324,12 @@ func (s *apiSrv) handleAnnounce(deviceID protocol.DeviceID, addresses []string)
|
|||||||
slices.Sort(addresses)
|
slices.Sort(addresses)
|
||||||
addresses = slices.Compact(addresses)
|
addresses = slices.Compact(addresses)
|
||||||
|
|
||||||
dbAddrs := make([]DatabaseAddress, len(addresses))
|
dbAddrs := make([]*discosrv.DatabaseAddress, len(addresses))
|
||||||
for i := range addresses {
|
for i := range addresses {
|
||||||
dbAddrs[i].Address = addresses[i]
|
dbAddrs[i] = &discosrv.DatabaseAddress{
|
||||||
dbAddrs[i].Expires = expire
|
Address: addresses[i],
|
||||||
|
Expires: expire,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
seen := now.UnixNano()
|
seen := now.UnixNano()
|
||||||
@@ -326,7 +340,7 @@ func (s *apiSrv) handleAnnounce(deviceID protocol.DeviceID, addresses []string)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func handlePing(w http.ResponseWriter, _ *http.Request) {
|
func handlePing(w http.ResponseWriter, _ *http.Request) {
|
||||||
w.WriteHeader(204)
|
w.WriteHeader(http.StatusNoContent)
|
||||||
}
|
}
|
||||||
|
|
||||||
func certificateBytes(req *http.Request) ([]byte, error) {
|
func certificateBytes(req *http.Request) ([]byte, error) {
|
||||||
@@ -511,7 +525,7 @@ func (lrw *loggingResponseWriter) WriteHeader(code int) {
|
|||||||
lrw.ResponseWriter.WriteHeader(code)
|
lrw.ResponseWriter.WriteHeader(code)
|
||||||
}
|
}
|
||||||
|
|
||||||
func addressStrs(dbAddrs []DatabaseAddress) []string {
|
func addressStrs(dbAddrs []*discosrv.DatabaseAddress) []string {
|
||||||
res := make([]string, len(dbAddrs))
|
res := make([]string, len(dbAddrs))
|
||||||
for i, a := range dbAddrs {
|
for i, a := range dbAddrs {
|
||||||
res[i] = a.Address
|
res[i] = a.Address
|
||||||
|
|||||||
@@ -111,11 +111,11 @@ func BenchmarkAPIRequests(b *testing.B) {
|
|||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
go db.Serve(ctx)
|
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))
|
srv := httptest.NewServer(http.HandlerFunc(api.handler))
|
||||||
|
|
||||||
kf := b.TempDir() + "/cert"
|
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 {
|
if err != nil {
|
||||||
b.Fatal(err)
|
b.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
+48
-46
@@ -4,9 +4,6 @@
|
|||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
// 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/.
|
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
//go:generate go run ../../proto/scripts/protofmt.go database.proto
|
|
||||||
//go:generate protoc -I ../../ -I . --gogofast_out=. database.proto
|
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -25,9 +22,13 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/puzpuzpuz/xsync/v3"
|
"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/protocol"
|
||||||
"github.com/syncthing/syncthing/lib/rand"
|
"github.com/syncthing/syncthing/lib/rand"
|
||||||
"github.com/syncthing/syncthing/lib/s3"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type clock interface {
|
type clock interface {
|
||||||
@@ -41,48 +42,48 @@ func (defaultClock) Now() time.Time {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type database interface {
|
type database interface {
|
||||||
put(key *protocol.DeviceID, rec DatabaseRecord) error
|
put(key *protocol.DeviceID, rec *discosrv.DatabaseRecord) error
|
||||||
merge(key *protocol.DeviceID, addrs []DatabaseAddress, seen int64) error
|
merge(key *protocol.DeviceID, addrs []*discosrv.DatabaseAddress, seen int64) error
|
||||||
get(key *protocol.DeviceID) (DatabaseRecord, error)
|
get(key *protocol.DeviceID) (*discosrv.DatabaseRecord, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type inMemoryStore struct {
|
type inMemoryStore struct {
|
||||||
m *xsync.MapOf[protocol.DeviceID, DatabaseRecord]
|
m *xsync.MapOf[protocol.DeviceID, *discosrv.DatabaseRecord]
|
||||||
dir string
|
dir string
|
||||||
flushInterval time.Duration
|
flushInterval time.Duration
|
||||||
s3 *s3.Session
|
blobs blob.Store
|
||||||
objKey string
|
objKey string
|
||||||
clock clock
|
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()
|
hn, err := os.Hostname()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
hn = rand.String(8)
|
hn = rand.String(8)
|
||||||
}
|
}
|
||||||
s := &inMemoryStore{
|
s := &inMemoryStore{
|
||||||
m: xsync.NewMapOf[protocol.DeviceID, DatabaseRecord](),
|
m: xsync.NewMapOf[protocol.DeviceID, *discosrv.DatabaseRecord](),
|
||||||
dir: dir,
|
dir: dir,
|
||||||
flushInterval: flushInterval,
|
flushInterval: flushInterval,
|
||||||
s3: s3sess,
|
blobs: blobs,
|
||||||
objKey: hn + ".db",
|
objKey: hn + ".db",
|
||||||
clock: defaultClock{},
|
clock: defaultClock{},
|
||||||
}
|
}
|
||||||
nr, err := s.read()
|
nr, err := s.read()
|
||||||
if os.IsNotExist(err) && s3sess != nil {
|
if os.IsNotExist(err) && blobs != nil {
|
||||||
// Try to read from AWS
|
// Try to read from blob storage
|
||||||
latestKey, cerr := s3sess.LatestKey()
|
latestKey, cerr := blobs.LatestKey(context.Background())
|
||||||
if cerr != nil {
|
if cerr != nil {
|
||||||
log.Println("Error reading database from S3:", err)
|
log.Println("Error finding database from blob storage:", cerr)
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
fd, cerr := os.Create(path.Join(s.dir, "records.db"))
|
fd, cerr := os.Create(path.Join(s.dir, "records.db"))
|
||||||
if cerr != nil {
|
if cerr != nil {
|
||||||
log.Println("Error creating database file:", err)
|
log.Println("Error creating database file:", cerr)
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
if cerr := s3sess.Download(fd, latestKey); cerr != nil {
|
if cerr := blobs.Download(context.Background(), latestKey, fd); cerr != nil {
|
||||||
log.Printf("Error reading database from S3: %v", err)
|
log.Printf("Error downloading database from blob storage: %v", cerr)
|
||||||
}
|
}
|
||||||
_ = fd.Close()
|
_ = fd.Close()
|
||||||
nr, err = s.read()
|
nr, err = s.read()
|
||||||
@@ -95,7 +96,7 @@ func newInMemoryStore(dir string, flushInterval time.Duration, s3sess *s3.Sessio
|
|||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *inMemoryStore) put(key *protocol.DeviceID, rec DatabaseRecord) error {
|
func (s *inMemoryStore) put(key *protocol.DeviceID, rec *discosrv.DatabaseRecord) error {
|
||||||
t0 := time.Now()
|
t0 := time.Now()
|
||||||
s.m.Store(*key, rec)
|
s.m.Store(*key, rec)
|
||||||
databaseOperations.WithLabelValues(dbOpPut, dbResSuccess).Inc()
|
databaseOperations.WithLabelValues(dbOpPut, dbResSuccess).Inc()
|
||||||
@@ -103,16 +104,17 @@ func (s *inMemoryStore) put(key *protocol.DeviceID, rec DatabaseRecord) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *inMemoryStore) merge(key *protocol.DeviceID, addrs []DatabaseAddress, seen int64) error {
|
func (s *inMemoryStore) merge(key *protocol.DeviceID, addrs []*discosrv.DatabaseAddress, seen int64) error {
|
||||||
t0 := time.Now()
|
t0 := time.Now()
|
||||||
|
|
||||||
newRec := DatabaseRecord{
|
newRec := &discosrv.DatabaseRecord{
|
||||||
Addresses: addrs,
|
Addresses: addrs,
|
||||||
Seen: seen,
|
Seen: seen,
|
||||||
}
|
}
|
||||||
|
|
||||||
oldRec, _ := s.m.Load(*key)
|
if oldRec, ok := s.m.Load(*key); ok {
|
||||||
newRec = merge(oldRec, newRec)
|
newRec = merge(oldRec, newRec)
|
||||||
|
}
|
||||||
s.m.Store(*key, newRec)
|
s.m.Store(*key, newRec)
|
||||||
|
|
||||||
databaseOperations.WithLabelValues(dbOpMerge, dbResSuccess).Inc()
|
databaseOperations.WithLabelValues(dbOpMerge, dbResSuccess).Inc()
|
||||||
@@ -121,7 +123,7 @@ func (s *inMemoryStore) merge(key *protocol.DeviceID, addrs []DatabaseAddress, s
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *inMemoryStore) get(key *protocol.DeviceID) (DatabaseRecord, error) {
|
func (s *inMemoryStore) get(key *protocol.DeviceID) (*discosrv.DatabaseRecord, error) {
|
||||||
t0 := time.Now()
|
t0 := time.Now()
|
||||||
defer func() {
|
defer func() {
|
||||||
databaseOperationSeconds.WithLabelValues(dbOpGet).Observe(time.Since(t0).Seconds())
|
databaseOperationSeconds.WithLabelValues(dbOpGet).Observe(time.Since(t0).Seconds())
|
||||||
@@ -130,7 +132,7 @@ func (s *inMemoryStore) get(key *protocol.DeviceID) (DatabaseRecord, error) {
|
|||||||
rec, ok := s.m.Load(*key)
|
rec, ok := s.m.Load(*key)
|
||||||
if !ok {
|
if !ok {
|
||||||
databaseOperations.WithLabelValues(dbOpGet, dbResNotFound).Inc()
|
databaseOperations.WithLabelValues(dbOpGet, dbResNotFound).Inc()
|
||||||
return DatabaseRecord{}, nil
|
return &discosrv.DatabaseRecord{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
rec.Addresses = expire(rec.Addresses, s.clock.Now())
|
rec.Addresses = expire(rec.Addresses, s.clock.Now())
|
||||||
@@ -176,7 +178,7 @@ func (s *inMemoryStore) expireAndCalculateStatistics() {
|
|||||||
current, currentIPv4, currentIPv6, currentIPv6GUA, last24h, last1w := 0, 0, 0, 0, 0, 0
|
current, currentIPv4, currentIPv6, currentIPv6GUA, last24h, last1w := 0, 0, 0, 0, 0, 0
|
||||||
|
|
||||||
n := 0
|
n := 0
|
||||||
s.m.Range(func(key protocol.DeviceID, rec DatabaseRecord) bool {
|
s.m.Range(func(key protocol.DeviceID, rec *discosrv.DatabaseRecord) bool {
|
||||||
if n%1000 == 0 {
|
if n%1000 == 0 {
|
||||||
runtime.Gosched()
|
runtime.Gosched()
|
||||||
}
|
}
|
||||||
@@ -261,7 +263,7 @@ func (s *inMemoryStore) write() (err error) {
|
|||||||
now := s.clock.Now()
|
now := s.clock.Now()
|
||||||
cutoff1w := now.Add(-7 * 24 * time.Hour).UnixNano()
|
cutoff1w := now.Add(-7 * 24 * time.Hour).UnixNano()
|
||||||
n := 0
|
n := 0
|
||||||
s.m.Range(func(key protocol.DeviceID, value DatabaseRecord) bool {
|
s.m.Range(func(key protocol.DeviceID, value *discosrv.DatabaseRecord) bool {
|
||||||
if n%1000 == 0 {
|
if n%1000 == 0 {
|
||||||
runtime.Gosched()
|
runtime.Gosched()
|
||||||
}
|
}
|
||||||
@@ -271,16 +273,16 @@ func (s *inMemoryStore) write() (err error) {
|
|||||||
// drop the record if it's older than a week
|
// drop the record if it's older than a week
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
rec := ReplicationRecord{
|
rec := &discosrv.ReplicationRecord{
|
||||||
Key: key[:],
|
Key: key[:],
|
||||||
Addresses: value.Addresses,
|
Addresses: value.Addresses,
|
||||||
Seen: value.Seen,
|
Seen: value.Seen,
|
||||||
}
|
}
|
||||||
s := rec.Size()
|
s := proto.Size(rec)
|
||||||
if s+4 > len(buf) {
|
if s+4 > len(buf) {
|
||||||
buf = make([]byte, s+4)
|
buf = make([]byte, s+4)
|
||||||
}
|
}
|
||||||
n, err := rec.MarshalTo(buf[4:])
|
n, err := protoutil.MarshalTo(buf[4:], rec)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
rangeErr = err
|
rangeErr = err
|
||||||
return false
|
return false
|
||||||
@@ -308,16 +310,16 @@ func (s *inMemoryStore) write() (err error) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Upload to S3
|
// Upload to blob storage
|
||||||
if s.s3 != nil {
|
if s.blobs != nil {
|
||||||
fd, err = os.Open(dbf)
|
fd, err = os.Open(dbf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error uploading database to S3: %v", err)
|
log.Printf("Error uploading database to blob storage: %v", err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
defer fd.Close()
|
defer fd.Close()
|
||||||
if err := s.s3.Upload(fd, s.objKey); err != nil {
|
if err := s.blobs.Upload(context.Background(), s.objKey, fd); err != nil {
|
||||||
log.Printf("Error uploading database to S3: %v", err)
|
log.Printf("Error uploading database to blob storage: %v", err)
|
||||||
}
|
}
|
||||||
log.Println("Finished uploading database")
|
log.Println("Finished uploading database")
|
||||||
}
|
}
|
||||||
@@ -349,8 +351,8 @@ func (s *inMemoryStore) read() (int, error) {
|
|||||||
if _, err := io.ReadFull(br, buf[:n]); err != nil {
|
if _, err := io.ReadFull(br, buf[:n]); err != nil {
|
||||||
return nr, err
|
return nr, err
|
||||||
}
|
}
|
||||||
rec := ReplicationRecord{}
|
rec := &discosrv.ReplicationRecord{}
|
||||||
if err := rec.Unmarshal(buf[:n]); err != nil {
|
if err := proto.Unmarshal(buf[:n], rec); err != nil {
|
||||||
return nr, err
|
return nr, err
|
||||||
}
|
}
|
||||||
key, err := protocol.DeviceIDFromBytes(rec.Key)
|
key, err := protocol.DeviceIDFromBytes(rec.Key)
|
||||||
@@ -362,9 +364,9 @@ func (s *inMemoryStore) read() (int, error) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
slices.SortFunc(rec.Addresses, DatabaseAddress.Cmp)
|
slices.SortFunc(rec.Addresses, Cmp)
|
||||||
rec.Addresses = slices.CompactFunc(rec.Addresses, DatabaseAddress.Equal)
|
rec.Addresses = slices.CompactFunc(rec.Addresses, Equal)
|
||||||
s.m.Store(key, DatabaseRecord{
|
s.m.Store(key, &discosrv.DatabaseRecord{
|
||||||
Addresses: expire(rec.Addresses, s.clock.Now()),
|
Addresses: expire(rec.Addresses, s.clock.Now()),
|
||||||
Seen: rec.Seen,
|
Seen: rec.Seen,
|
||||||
})
|
})
|
||||||
@@ -377,7 +379,7 @@ func (s *inMemoryStore) read() (int, error) {
|
|||||||
// result is the union of the two address sets, with the newer expiry time
|
// result is the union of the two address sets, with the newer expiry time
|
||||||
// chosen for any duplicates. The address list in a is overwritten and
|
// chosen for any duplicates. The address list in a is overwritten and
|
||||||
// reused for the result.
|
// reused for the result.
|
||||||
func merge(a, b DatabaseRecord) DatabaseRecord {
|
func merge(a, b *discosrv.DatabaseRecord) *discosrv.DatabaseRecord {
|
||||||
// Both lists must be sorted for this to work.
|
// Both lists must be sorted for this to work.
|
||||||
|
|
||||||
a.Seen = max(a.Seen, b.Seen)
|
a.Seen = max(a.Seen, b.Seen)
|
||||||
@@ -396,7 +398,7 @@ func merge(a, b DatabaseRecord) DatabaseRecord {
|
|||||||
aIdx++
|
aIdx++
|
||||||
case 1:
|
case 1:
|
||||||
// a > b, insert b before a
|
// a > b, insert b before a
|
||||||
a.Addresses = append(a.Addresses[:aIdx], append([]DatabaseAddress{b.Addresses[bIdx]}, a.Addresses[aIdx:]...)...)
|
a.Addresses = append(a.Addresses[:aIdx], append([]*discosrv.DatabaseAddress{b.Addresses[bIdx]}, a.Addresses[aIdx:]...)...)
|
||||||
bIdx++
|
bIdx++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -410,7 +412,7 @@ func merge(a, b DatabaseRecord) DatabaseRecord {
|
|||||||
// expire returns the list of addresses after removing expired entries.
|
// expire returns the list of addresses after removing expired entries.
|
||||||
// Expiration happen in place, so the slice given as the parameter is
|
// Expiration happen in place, so the slice given as the parameter is
|
||||||
// destroyed. Internal order is preserved.
|
// destroyed. Internal order is preserved.
|
||||||
func expire(addrs []DatabaseAddress, now time.Time) []DatabaseAddress {
|
func expire(addrs []*discosrv.DatabaseAddress, now time.Time) []*discosrv.DatabaseAddress {
|
||||||
cutoff := now.UnixNano()
|
cutoff := now.UnixNano()
|
||||||
naddrs := addrs[:0]
|
naddrs := addrs[:0]
|
||||||
for i := range addrs {
|
for i := range addrs {
|
||||||
@@ -428,13 +430,13 @@ func expire(addrs []DatabaseAddress, now time.Time) []DatabaseAddress {
|
|||||||
return naddrs
|
return naddrs
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d DatabaseAddress) Cmp(other DatabaseAddress) (n int) {
|
func Cmp(d, other *discosrv.DatabaseAddress) (n int) {
|
||||||
if c := cmp.Compare(d.Address, other.Address); c != 0 {
|
if c := cmp.Compare(d.Address, other.Address); c != 0 {
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
return cmp.Compare(d.Expires, other.Expires)
|
return cmp.Compare(d.Expires, other.Expires)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d DatabaseAddress) Equal(other DatabaseAddress) bool {
|
func Equal(d, other *discosrv.DatabaseAddress) bool {
|
||||||
return d.Address == other.Address
|
return d.Address == other.Address
|
||||||
}
|
}
|
||||||
@@ -1,792 +0,0 @@
|
|||||||
// Code generated by protoc-gen-gogo. DO NOT EDIT.
|
|
||||||
// source: database.proto
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
fmt "fmt"
|
|
||||||
_ "github.com/gogo/protobuf/gogoproto"
|
|
||||||
proto "github.com/gogo/protobuf/proto"
|
|
||||||
io "io"
|
|
||||||
math "math"
|
|
||||||
math_bits "math/bits"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Reference imports to suppress errors if they are not otherwise used.
|
|
||||||
var _ = proto.Marshal
|
|
||||||
var _ = fmt.Errorf
|
|
||||||
var _ = math.Inf
|
|
||||||
|
|
||||||
// This is a compile-time assertion to ensure that this generated file
|
|
||||||
// is compatible with the proto package it is being compiled against.
|
|
||||||
// A compilation error at this line likely means your copy of the
|
|
||||||
// proto package needs to be updated.
|
|
||||||
const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
|
|
||||||
|
|
||||||
type DatabaseRecord struct {
|
|
||||||
Addresses []DatabaseAddress `protobuf:"bytes,1,rep,name=addresses,proto3" json:"addresses"`
|
|
||||||
Seen int64 `protobuf:"varint,3,opt,name=seen,proto3" json:"seen,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *DatabaseRecord) Reset() { *m = DatabaseRecord{} }
|
|
||||||
func (m *DatabaseRecord) String() string { return proto.CompactTextString(m) }
|
|
||||||
func (*DatabaseRecord) ProtoMessage() {}
|
|
||||||
func (*DatabaseRecord) Descriptor() ([]byte, []int) {
|
|
||||||
return fileDescriptor_b90fe3356ea5df07, []int{0}
|
|
||||||
}
|
|
||||||
func (m *DatabaseRecord) XXX_Unmarshal(b []byte) error {
|
|
||||||
return m.Unmarshal(b)
|
|
||||||
}
|
|
||||||
func (m *DatabaseRecord) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
|
||||||
if deterministic {
|
|
||||||
return xxx_messageInfo_DatabaseRecord.Marshal(b, m, deterministic)
|
|
||||||
} else {
|
|
||||||
b = b[:cap(b)]
|
|
||||||
n, err := m.MarshalToSizedBuffer(b)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return b[:n], nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
func (m *DatabaseRecord) XXX_Merge(src proto.Message) {
|
|
||||||
xxx_messageInfo_DatabaseRecord.Merge(m, src)
|
|
||||||
}
|
|
||||||
func (m *DatabaseRecord) XXX_Size() int {
|
|
||||||
return m.Size()
|
|
||||||
}
|
|
||||||
func (m *DatabaseRecord) XXX_DiscardUnknown() {
|
|
||||||
xxx_messageInfo_DatabaseRecord.DiscardUnknown(m)
|
|
||||||
}
|
|
||||||
|
|
||||||
var xxx_messageInfo_DatabaseRecord proto.InternalMessageInfo
|
|
||||||
|
|
||||||
type ReplicationRecord struct {
|
|
||||||
Key []byte `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
|
|
||||||
Addresses []DatabaseAddress `protobuf:"bytes,2,rep,name=addresses,proto3" json:"addresses"`
|
|
||||||
Seen int64 `protobuf:"varint,3,opt,name=seen,proto3" json:"seen,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *ReplicationRecord) Reset() { *m = ReplicationRecord{} }
|
|
||||||
func (m *ReplicationRecord) String() string { return proto.CompactTextString(m) }
|
|
||||||
func (*ReplicationRecord) ProtoMessage() {}
|
|
||||||
func (*ReplicationRecord) Descriptor() ([]byte, []int) {
|
|
||||||
return fileDescriptor_b90fe3356ea5df07, []int{1}
|
|
||||||
}
|
|
||||||
func (m *ReplicationRecord) XXX_Unmarshal(b []byte) error {
|
|
||||||
return m.Unmarshal(b)
|
|
||||||
}
|
|
||||||
func (m *ReplicationRecord) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
|
||||||
if deterministic {
|
|
||||||
return xxx_messageInfo_ReplicationRecord.Marshal(b, m, deterministic)
|
|
||||||
} else {
|
|
||||||
b = b[:cap(b)]
|
|
||||||
n, err := m.MarshalToSizedBuffer(b)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return b[:n], nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
func (m *ReplicationRecord) XXX_Merge(src proto.Message) {
|
|
||||||
xxx_messageInfo_ReplicationRecord.Merge(m, src)
|
|
||||||
}
|
|
||||||
func (m *ReplicationRecord) XXX_Size() int {
|
|
||||||
return m.Size()
|
|
||||||
}
|
|
||||||
func (m *ReplicationRecord) XXX_DiscardUnknown() {
|
|
||||||
xxx_messageInfo_ReplicationRecord.DiscardUnknown(m)
|
|
||||||
}
|
|
||||||
|
|
||||||
var xxx_messageInfo_ReplicationRecord proto.InternalMessageInfo
|
|
||||||
|
|
||||||
type DatabaseAddress struct {
|
|
||||||
Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"`
|
|
||||||
Expires int64 `protobuf:"varint,2,opt,name=expires,proto3" json:"expires,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *DatabaseAddress) Reset() { *m = DatabaseAddress{} }
|
|
||||||
func (m *DatabaseAddress) String() string { return proto.CompactTextString(m) }
|
|
||||||
func (*DatabaseAddress) ProtoMessage() {}
|
|
||||||
func (*DatabaseAddress) Descriptor() ([]byte, []int) {
|
|
||||||
return fileDescriptor_b90fe3356ea5df07, []int{2}
|
|
||||||
}
|
|
||||||
func (m *DatabaseAddress) XXX_Unmarshal(b []byte) error {
|
|
||||||
return m.Unmarshal(b)
|
|
||||||
}
|
|
||||||
func (m *DatabaseAddress) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
|
||||||
if deterministic {
|
|
||||||
return xxx_messageInfo_DatabaseAddress.Marshal(b, m, deterministic)
|
|
||||||
} else {
|
|
||||||
b = b[:cap(b)]
|
|
||||||
n, err := m.MarshalToSizedBuffer(b)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return b[:n], nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
func (m *DatabaseAddress) XXX_Merge(src proto.Message) {
|
|
||||||
xxx_messageInfo_DatabaseAddress.Merge(m, src)
|
|
||||||
}
|
|
||||||
func (m *DatabaseAddress) XXX_Size() int {
|
|
||||||
return m.Size()
|
|
||||||
}
|
|
||||||
func (m *DatabaseAddress) XXX_DiscardUnknown() {
|
|
||||||
xxx_messageInfo_DatabaseAddress.DiscardUnknown(m)
|
|
||||||
}
|
|
||||||
|
|
||||||
var xxx_messageInfo_DatabaseAddress proto.InternalMessageInfo
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
proto.RegisterType((*DatabaseRecord)(nil), "main.DatabaseRecord")
|
|
||||||
proto.RegisterType((*ReplicationRecord)(nil), "main.ReplicationRecord")
|
|
||||||
proto.RegisterType((*DatabaseAddress)(nil), "main.DatabaseAddress")
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() { proto.RegisterFile("database.proto", fileDescriptor_b90fe3356ea5df07) }
|
|
||||||
|
|
||||||
var fileDescriptor_b90fe3356ea5df07 = []byte{
|
|
||||||
// 243 bytes of a gzipped FileDescriptorProto
|
|
||||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x4b, 0x49, 0x2c, 0x49,
|
|
||||||
0x4c, 0x4a, 0x2c, 0x4e, 0xd5, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0xc9, 0x4d, 0xcc, 0xcc,
|
|
||||||
0x93, 0x52, 0x2e, 0x4a, 0x2d, 0xc8, 0x2f, 0xd6, 0x07, 0x0b, 0x25, 0x95, 0xa6, 0xe9, 0xa7, 0xe7,
|
|
||||||
0xa7, 0xe7, 0x83, 0x39, 0x60, 0x16, 0x44, 0xa9, 0x52, 0x3c, 0x17, 0x9f, 0x0b, 0x54, 0x73, 0x50,
|
|
||||||
0x6a, 0x72, 0x7e, 0x51, 0x8a, 0x90, 0x25, 0x17, 0x67, 0x62, 0x4a, 0x4a, 0x51, 0x6a, 0x71, 0x71,
|
|
||||||
0x6a, 0xb1, 0x04, 0xa3, 0x02, 0xb3, 0x06, 0xb7, 0x91, 0xa8, 0x1e, 0xc8, 0x40, 0x3d, 0x98, 0x42,
|
|
||||||
0x47, 0x88, 0xb4, 0x13, 0xcb, 0x89, 0x7b, 0xf2, 0x0c, 0x41, 0x08, 0xd5, 0x42, 0x42, 0x5c, 0x2c,
|
|
||||||
0xc5, 0xa9, 0xa9, 0x79, 0x12, 0xcc, 0x0a, 0x8c, 0x1a, 0xcc, 0x41, 0x60, 0xb6, 0x52, 0x09, 0x97,
|
|
||||||
0x60, 0x50, 0x6a, 0x41, 0x4e, 0x66, 0x72, 0x62, 0x49, 0x66, 0x7e, 0x1e, 0xd4, 0x0e, 0x01, 0x2e,
|
|
||||||
0xe6, 0xec, 0xd4, 0x4a, 0x09, 0x46, 0x05, 0x46, 0x0d, 0x9e, 0x20, 0x10, 0x13, 0xd5, 0x56, 0x26,
|
|
||||||
0x8a, 0x6d, 0x75, 0xe5, 0xe2, 0x47, 0xd3, 0x27, 0x24, 0xc1, 0xc5, 0x0e, 0xd5, 0x03, 0xb6, 0x97,
|
|
||||||
0x33, 0x08, 0xc6, 0x05, 0xc9, 0xa4, 0x56, 0x14, 0x64, 0x16, 0x81, 0x6d, 0x06, 0x99, 0x01, 0xe3,
|
|
||||||
0x3a, 0xc9, 0x9c, 0x78, 0x28, 0xc7, 0x70, 0xe2, 0x91, 0x1c, 0xe3, 0x85, 0x47, 0x72, 0x8c, 0x0f,
|
|
||||||
0x1e, 0xc9, 0x31, 0x4e, 0x78, 0x2c, 0xc7, 0x70, 0xe1, 0xb1, 0x1c, 0xc3, 0x8d, 0xc7, 0x72, 0x0c,
|
|
||||||
0x49, 0x6c, 0xe0, 0x20, 0x34, 0x06, 0x04, 0x00, 0x00, 0xff, 0xff, 0xc6, 0x0b, 0x9b, 0x77, 0x7f,
|
|
||||||
0x01, 0x00, 0x00,
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *DatabaseRecord) Marshal() (dAtA []byte, err error) {
|
|
||||||
size := m.Size()
|
|
||||||
dAtA = make([]byte, size)
|
|
||||||
n, err := m.MarshalToSizedBuffer(dAtA[:size])
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return dAtA[:n], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *DatabaseRecord) MarshalTo(dAtA []byte) (int, error) {
|
|
||||||
size := m.Size()
|
|
||||||
return m.MarshalToSizedBuffer(dAtA[:size])
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *DatabaseRecord) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
|
||||||
i := len(dAtA)
|
|
||||||
_ = i
|
|
||||||
var l int
|
|
||||||
_ = l
|
|
||||||
if m.Seen != 0 {
|
|
||||||
i = encodeVarintDatabase(dAtA, i, uint64(m.Seen))
|
|
||||||
i--
|
|
||||||
dAtA[i] = 0x18
|
|
||||||
}
|
|
||||||
if len(m.Addresses) > 0 {
|
|
||||||
for iNdEx := len(m.Addresses) - 1; iNdEx >= 0; iNdEx-- {
|
|
||||||
{
|
|
||||||
size, err := m.Addresses[iNdEx].MarshalToSizedBuffer(dAtA[:i])
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
i -= size
|
|
||||||
i = encodeVarintDatabase(dAtA, i, uint64(size))
|
|
||||||
}
|
|
||||||
i--
|
|
||||||
dAtA[i] = 0xa
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return len(dAtA) - i, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *ReplicationRecord) Marshal() (dAtA []byte, err error) {
|
|
||||||
size := m.Size()
|
|
||||||
dAtA = make([]byte, size)
|
|
||||||
n, err := m.MarshalToSizedBuffer(dAtA[:size])
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return dAtA[:n], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *ReplicationRecord) MarshalTo(dAtA []byte) (int, error) {
|
|
||||||
size := m.Size()
|
|
||||||
return m.MarshalToSizedBuffer(dAtA[:size])
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *ReplicationRecord) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
|
||||||
i := len(dAtA)
|
|
||||||
_ = i
|
|
||||||
var l int
|
|
||||||
_ = l
|
|
||||||
if m.Seen != 0 {
|
|
||||||
i = encodeVarintDatabase(dAtA, i, uint64(m.Seen))
|
|
||||||
i--
|
|
||||||
dAtA[i] = 0x18
|
|
||||||
}
|
|
||||||
if len(m.Addresses) > 0 {
|
|
||||||
for iNdEx := len(m.Addresses) - 1; iNdEx >= 0; iNdEx-- {
|
|
||||||
{
|
|
||||||
size, err := m.Addresses[iNdEx].MarshalToSizedBuffer(dAtA[:i])
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
i -= size
|
|
||||||
i = encodeVarintDatabase(dAtA, i, uint64(size))
|
|
||||||
}
|
|
||||||
i--
|
|
||||||
dAtA[i] = 0x12
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(m.Key) > 0 {
|
|
||||||
i -= len(m.Key)
|
|
||||||
copy(dAtA[i:], m.Key)
|
|
||||||
i = encodeVarintDatabase(dAtA, i, uint64(len(m.Key)))
|
|
||||||
i--
|
|
||||||
dAtA[i] = 0xa
|
|
||||||
}
|
|
||||||
return len(dAtA) - i, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *DatabaseAddress) Marshal() (dAtA []byte, err error) {
|
|
||||||
size := m.Size()
|
|
||||||
dAtA = make([]byte, size)
|
|
||||||
n, err := m.MarshalToSizedBuffer(dAtA[:size])
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return dAtA[:n], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *DatabaseAddress) MarshalTo(dAtA []byte) (int, error) {
|
|
||||||
size := m.Size()
|
|
||||||
return m.MarshalToSizedBuffer(dAtA[:size])
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *DatabaseAddress) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
|
||||||
i := len(dAtA)
|
|
||||||
_ = i
|
|
||||||
var l int
|
|
||||||
_ = l
|
|
||||||
if m.Expires != 0 {
|
|
||||||
i = encodeVarintDatabase(dAtA, i, uint64(m.Expires))
|
|
||||||
i--
|
|
||||||
dAtA[i] = 0x10
|
|
||||||
}
|
|
||||||
if len(m.Address) > 0 {
|
|
||||||
i -= len(m.Address)
|
|
||||||
copy(dAtA[i:], m.Address)
|
|
||||||
i = encodeVarintDatabase(dAtA, i, uint64(len(m.Address)))
|
|
||||||
i--
|
|
||||||
dAtA[i] = 0xa
|
|
||||||
}
|
|
||||||
return len(dAtA) - i, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func encodeVarintDatabase(dAtA []byte, offset int, v uint64) int {
|
|
||||||
offset -= sovDatabase(v)
|
|
||||||
base := offset
|
|
||||||
for v >= 1<<7 {
|
|
||||||
dAtA[offset] = uint8(v&0x7f | 0x80)
|
|
||||||
v >>= 7
|
|
||||||
offset++
|
|
||||||
}
|
|
||||||
dAtA[offset] = uint8(v)
|
|
||||||
return base
|
|
||||||
}
|
|
||||||
func (m *DatabaseRecord) Size() (n int) {
|
|
||||||
if m == nil {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
var l int
|
|
||||||
_ = l
|
|
||||||
if len(m.Addresses) > 0 {
|
|
||||||
for _, e := range m.Addresses {
|
|
||||||
l = e.Size()
|
|
||||||
n += 1 + l + sovDatabase(uint64(l))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if m.Seen != 0 {
|
|
||||||
n += 1 + sovDatabase(uint64(m.Seen))
|
|
||||||
}
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *ReplicationRecord) Size() (n int) {
|
|
||||||
if m == nil {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
var l int
|
|
||||||
_ = l
|
|
||||||
l = len(m.Key)
|
|
||||||
if l > 0 {
|
|
||||||
n += 1 + l + sovDatabase(uint64(l))
|
|
||||||
}
|
|
||||||
if len(m.Addresses) > 0 {
|
|
||||||
for _, e := range m.Addresses {
|
|
||||||
l = e.Size()
|
|
||||||
n += 1 + l + sovDatabase(uint64(l))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if m.Seen != 0 {
|
|
||||||
n += 1 + sovDatabase(uint64(m.Seen))
|
|
||||||
}
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *DatabaseAddress) Size() (n int) {
|
|
||||||
if m == nil {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
var l int
|
|
||||||
_ = l
|
|
||||||
l = len(m.Address)
|
|
||||||
if l > 0 {
|
|
||||||
n += 1 + l + sovDatabase(uint64(l))
|
|
||||||
}
|
|
||||||
if m.Expires != 0 {
|
|
||||||
n += 1 + sovDatabase(uint64(m.Expires))
|
|
||||||
}
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
|
|
||||||
func sovDatabase(x uint64) (n int) {
|
|
||||||
return (math_bits.Len64(x|1) + 6) / 7
|
|
||||||
}
|
|
||||||
func sozDatabase(x uint64) (n int) {
|
|
||||||
return sovDatabase(uint64((x << 1) ^ uint64((int64(x) >> 63))))
|
|
||||||
}
|
|
||||||
func (m *DatabaseRecord) Unmarshal(dAtA []byte) error {
|
|
||||||
l := len(dAtA)
|
|
||||||
iNdEx := 0
|
|
||||||
for iNdEx < l {
|
|
||||||
preIndex := iNdEx
|
|
||||||
var wire uint64
|
|
||||||
for shift := uint(0); ; shift += 7 {
|
|
||||||
if shift >= 64 {
|
|
||||||
return ErrIntOverflowDatabase
|
|
||||||
}
|
|
||||||
if iNdEx >= l {
|
|
||||||
return io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
b := dAtA[iNdEx]
|
|
||||||
iNdEx++
|
|
||||||
wire |= uint64(b&0x7F) << shift
|
|
||||||
if b < 0x80 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fieldNum := int32(wire >> 3)
|
|
||||||
wireType := int(wire & 0x7)
|
|
||||||
if wireType == 4 {
|
|
||||||
return fmt.Errorf("proto: DatabaseRecord: wiretype end group for non-group")
|
|
||||||
}
|
|
||||||
if fieldNum <= 0 {
|
|
||||||
return fmt.Errorf("proto: DatabaseRecord: illegal tag %d (wire type %d)", fieldNum, wire)
|
|
||||||
}
|
|
||||||
switch fieldNum {
|
|
||||||
case 1:
|
|
||||||
if wireType != 2 {
|
|
||||||
return fmt.Errorf("proto: wrong wireType = %d for field Addresses", wireType)
|
|
||||||
}
|
|
||||||
var msglen int
|
|
||||||
for shift := uint(0); ; shift += 7 {
|
|
||||||
if shift >= 64 {
|
|
||||||
return ErrIntOverflowDatabase
|
|
||||||
}
|
|
||||||
if iNdEx >= l {
|
|
||||||
return io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
b := dAtA[iNdEx]
|
|
||||||
iNdEx++
|
|
||||||
msglen |= int(b&0x7F) << shift
|
|
||||||
if b < 0x80 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if msglen < 0 {
|
|
||||||
return ErrInvalidLengthDatabase
|
|
||||||
}
|
|
||||||
postIndex := iNdEx + msglen
|
|
||||||
if postIndex < 0 {
|
|
||||||
return ErrInvalidLengthDatabase
|
|
||||||
}
|
|
||||||
if postIndex > l {
|
|
||||||
return io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
m.Addresses = append(m.Addresses, DatabaseAddress{})
|
|
||||||
if err := m.Addresses[len(m.Addresses)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
iNdEx = postIndex
|
|
||||||
case 3:
|
|
||||||
if wireType != 0 {
|
|
||||||
return fmt.Errorf("proto: wrong wireType = %d for field Seen", wireType)
|
|
||||||
}
|
|
||||||
m.Seen = 0
|
|
||||||
for shift := uint(0); ; shift += 7 {
|
|
||||||
if shift >= 64 {
|
|
||||||
return ErrIntOverflowDatabase
|
|
||||||
}
|
|
||||||
if iNdEx >= l {
|
|
||||||
return io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
b := dAtA[iNdEx]
|
|
||||||
iNdEx++
|
|
||||||
m.Seen |= int64(b&0x7F) << shift
|
|
||||||
if b < 0x80 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
iNdEx = preIndex
|
|
||||||
skippy, err := skipDatabase(dAtA[iNdEx:])
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
|
||||||
return ErrInvalidLengthDatabase
|
|
||||||
}
|
|
||||||
if (iNdEx + skippy) > l {
|
|
||||||
return io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
iNdEx += skippy
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if iNdEx > l {
|
|
||||||
return io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
func (m *ReplicationRecord) Unmarshal(dAtA []byte) error {
|
|
||||||
l := len(dAtA)
|
|
||||||
iNdEx := 0
|
|
||||||
for iNdEx < l {
|
|
||||||
preIndex := iNdEx
|
|
||||||
var wire uint64
|
|
||||||
for shift := uint(0); ; shift += 7 {
|
|
||||||
if shift >= 64 {
|
|
||||||
return ErrIntOverflowDatabase
|
|
||||||
}
|
|
||||||
if iNdEx >= l {
|
|
||||||
return io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
b := dAtA[iNdEx]
|
|
||||||
iNdEx++
|
|
||||||
wire |= uint64(b&0x7F) << shift
|
|
||||||
if b < 0x80 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fieldNum := int32(wire >> 3)
|
|
||||||
wireType := int(wire & 0x7)
|
|
||||||
if wireType == 4 {
|
|
||||||
return fmt.Errorf("proto: ReplicationRecord: wiretype end group for non-group")
|
|
||||||
}
|
|
||||||
if fieldNum <= 0 {
|
|
||||||
return fmt.Errorf("proto: ReplicationRecord: illegal tag %d (wire type %d)", fieldNum, wire)
|
|
||||||
}
|
|
||||||
switch fieldNum {
|
|
||||||
case 1:
|
|
||||||
if wireType != 2 {
|
|
||||||
return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType)
|
|
||||||
}
|
|
||||||
var byteLen int
|
|
||||||
for shift := uint(0); ; shift += 7 {
|
|
||||||
if shift >= 64 {
|
|
||||||
return ErrIntOverflowDatabase
|
|
||||||
}
|
|
||||||
if iNdEx >= l {
|
|
||||||
return io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
b := dAtA[iNdEx]
|
|
||||||
iNdEx++
|
|
||||||
byteLen |= int(b&0x7F) << shift
|
|
||||||
if b < 0x80 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if byteLen < 0 {
|
|
||||||
return ErrInvalidLengthDatabase
|
|
||||||
}
|
|
||||||
postIndex := iNdEx + byteLen
|
|
||||||
if postIndex < 0 {
|
|
||||||
return ErrInvalidLengthDatabase
|
|
||||||
}
|
|
||||||
if postIndex > l {
|
|
||||||
return io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
m.Key = append(m.Key[:0], dAtA[iNdEx:postIndex]...)
|
|
||||||
if m.Key == nil {
|
|
||||||
m.Key = []byte{}
|
|
||||||
}
|
|
||||||
iNdEx = postIndex
|
|
||||||
case 2:
|
|
||||||
if wireType != 2 {
|
|
||||||
return fmt.Errorf("proto: wrong wireType = %d for field Addresses", wireType)
|
|
||||||
}
|
|
||||||
var msglen int
|
|
||||||
for shift := uint(0); ; shift += 7 {
|
|
||||||
if shift >= 64 {
|
|
||||||
return ErrIntOverflowDatabase
|
|
||||||
}
|
|
||||||
if iNdEx >= l {
|
|
||||||
return io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
b := dAtA[iNdEx]
|
|
||||||
iNdEx++
|
|
||||||
msglen |= int(b&0x7F) << shift
|
|
||||||
if b < 0x80 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if msglen < 0 {
|
|
||||||
return ErrInvalidLengthDatabase
|
|
||||||
}
|
|
||||||
postIndex := iNdEx + msglen
|
|
||||||
if postIndex < 0 {
|
|
||||||
return ErrInvalidLengthDatabase
|
|
||||||
}
|
|
||||||
if postIndex > l {
|
|
||||||
return io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
m.Addresses = append(m.Addresses, DatabaseAddress{})
|
|
||||||
if err := m.Addresses[len(m.Addresses)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
iNdEx = postIndex
|
|
||||||
case 3:
|
|
||||||
if wireType != 0 {
|
|
||||||
return fmt.Errorf("proto: wrong wireType = %d for field Seen", wireType)
|
|
||||||
}
|
|
||||||
m.Seen = 0
|
|
||||||
for shift := uint(0); ; shift += 7 {
|
|
||||||
if shift >= 64 {
|
|
||||||
return ErrIntOverflowDatabase
|
|
||||||
}
|
|
||||||
if iNdEx >= l {
|
|
||||||
return io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
b := dAtA[iNdEx]
|
|
||||||
iNdEx++
|
|
||||||
m.Seen |= int64(b&0x7F) << shift
|
|
||||||
if b < 0x80 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
iNdEx = preIndex
|
|
||||||
skippy, err := skipDatabase(dAtA[iNdEx:])
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
|
||||||
return ErrInvalidLengthDatabase
|
|
||||||
}
|
|
||||||
if (iNdEx + skippy) > l {
|
|
||||||
return io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
iNdEx += skippy
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if iNdEx > l {
|
|
||||||
return io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
func (m *DatabaseAddress) Unmarshal(dAtA []byte) error {
|
|
||||||
l := len(dAtA)
|
|
||||||
iNdEx := 0
|
|
||||||
for iNdEx < l {
|
|
||||||
preIndex := iNdEx
|
|
||||||
var wire uint64
|
|
||||||
for shift := uint(0); ; shift += 7 {
|
|
||||||
if shift >= 64 {
|
|
||||||
return ErrIntOverflowDatabase
|
|
||||||
}
|
|
||||||
if iNdEx >= l {
|
|
||||||
return io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
b := dAtA[iNdEx]
|
|
||||||
iNdEx++
|
|
||||||
wire |= uint64(b&0x7F) << shift
|
|
||||||
if b < 0x80 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fieldNum := int32(wire >> 3)
|
|
||||||
wireType := int(wire & 0x7)
|
|
||||||
if wireType == 4 {
|
|
||||||
return fmt.Errorf("proto: DatabaseAddress: wiretype end group for non-group")
|
|
||||||
}
|
|
||||||
if fieldNum <= 0 {
|
|
||||||
return fmt.Errorf("proto: DatabaseAddress: illegal tag %d (wire type %d)", fieldNum, wire)
|
|
||||||
}
|
|
||||||
switch fieldNum {
|
|
||||||
case 1:
|
|
||||||
if wireType != 2 {
|
|
||||||
return fmt.Errorf("proto: wrong wireType = %d for field Address", wireType)
|
|
||||||
}
|
|
||||||
var stringLen uint64
|
|
||||||
for shift := uint(0); ; shift += 7 {
|
|
||||||
if shift >= 64 {
|
|
||||||
return ErrIntOverflowDatabase
|
|
||||||
}
|
|
||||||
if iNdEx >= l {
|
|
||||||
return io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
b := dAtA[iNdEx]
|
|
||||||
iNdEx++
|
|
||||||
stringLen |= uint64(b&0x7F) << shift
|
|
||||||
if b < 0x80 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
intStringLen := int(stringLen)
|
|
||||||
if intStringLen < 0 {
|
|
||||||
return ErrInvalidLengthDatabase
|
|
||||||
}
|
|
||||||
postIndex := iNdEx + intStringLen
|
|
||||||
if postIndex < 0 {
|
|
||||||
return ErrInvalidLengthDatabase
|
|
||||||
}
|
|
||||||
if postIndex > l {
|
|
||||||
return io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
m.Address = string(dAtA[iNdEx:postIndex])
|
|
||||||
iNdEx = postIndex
|
|
||||||
case 2:
|
|
||||||
if wireType != 0 {
|
|
||||||
return fmt.Errorf("proto: wrong wireType = %d for field Expires", wireType)
|
|
||||||
}
|
|
||||||
m.Expires = 0
|
|
||||||
for shift := uint(0); ; shift += 7 {
|
|
||||||
if shift >= 64 {
|
|
||||||
return ErrIntOverflowDatabase
|
|
||||||
}
|
|
||||||
if iNdEx >= l {
|
|
||||||
return io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
b := dAtA[iNdEx]
|
|
||||||
iNdEx++
|
|
||||||
m.Expires |= int64(b&0x7F) << shift
|
|
||||||
if b < 0x80 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
iNdEx = preIndex
|
|
||||||
skippy, err := skipDatabase(dAtA[iNdEx:])
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
|
||||||
return ErrInvalidLengthDatabase
|
|
||||||
}
|
|
||||||
if (iNdEx + skippy) > l {
|
|
||||||
return io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
iNdEx += skippy
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if iNdEx > l {
|
|
||||||
return io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
func skipDatabase(dAtA []byte) (n int, err error) {
|
|
||||||
l := len(dAtA)
|
|
||||||
iNdEx := 0
|
|
||||||
depth := 0
|
|
||||||
for iNdEx < l {
|
|
||||||
var wire uint64
|
|
||||||
for shift := uint(0); ; shift += 7 {
|
|
||||||
if shift >= 64 {
|
|
||||||
return 0, ErrIntOverflowDatabase
|
|
||||||
}
|
|
||||||
if iNdEx >= l {
|
|
||||||
return 0, io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
b := dAtA[iNdEx]
|
|
||||||
iNdEx++
|
|
||||||
wire |= (uint64(b) & 0x7F) << shift
|
|
||||||
if b < 0x80 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
wireType := int(wire & 0x7)
|
|
||||||
switch wireType {
|
|
||||||
case 0:
|
|
||||||
for shift := uint(0); ; shift += 7 {
|
|
||||||
if shift >= 64 {
|
|
||||||
return 0, ErrIntOverflowDatabase
|
|
||||||
}
|
|
||||||
if iNdEx >= l {
|
|
||||||
return 0, io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
iNdEx++
|
|
||||||
if dAtA[iNdEx-1] < 0x80 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case 1:
|
|
||||||
iNdEx += 8
|
|
||||||
case 2:
|
|
||||||
var length int
|
|
||||||
for shift := uint(0); ; shift += 7 {
|
|
||||||
if shift >= 64 {
|
|
||||||
return 0, ErrIntOverflowDatabase
|
|
||||||
}
|
|
||||||
if iNdEx >= l {
|
|
||||||
return 0, io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
b := dAtA[iNdEx]
|
|
||||||
iNdEx++
|
|
||||||
length |= (int(b) & 0x7F) << shift
|
|
||||||
if b < 0x80 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if length < 0 {
|
|
||||||
return 0, ErrInvalidLengthDatabase
|
|
||||||
}
|
|
||||||
iNdEx += length
|
|
||||||
case 3:
|
|
||||||
depth++
|
|
||||||
case 4:
|
|
||||||
if depth == 0 {
|
|
||||||
return 0, ErrUnexpectedEndOfGroupDatabase
|
|
||||||
}
|
|
||||||
depth--
|
|
||||||
case 5:
|
|
||||||
iNdEx += 4
|
|
||||||
default:
|
|
||||||
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
|
|
||||||
}
|
|
||||||
if iNdEx < 0 {
|
|
||||||
return 0, ErrInvalidLengthDatabase
|
|
||||||
}
|
|
||||||
if depth == 0 {
|
|
||||||
return iNdEx, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0, io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrInvalidLengthDatabase = fmt.Errorf("proto: negative length found during unmarshaling")
|
|
||||||
ErrIntOverflowDatabase = fmt.Errorf("proto: integer overflow")
|
|
||||||
ErrUnexpectedEndOfGroupDatabase = fmt.Errorf("proto: unexpected end of group")
|
|
||||||
)
|
|
||||||
@@ -1,32 +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/.
|
|
||||||
|
|
||||||
syntax = "proto3";
|
|
||||||
|
|
||||||
package main;
|
|
||||||
|
|
||||||
import "repos/protobuf/gogoproto/gogo.proto";
|
|
||||||
|
|
||||||
option (gogoproto.goproto_getters_all) = false;
|
|
||||||
option (gogoproto.goproto_unkeyed_all) = false;
|
|
||||||
option (gogoproto.goproto_unrecognized_all) = false;
|
|
||||||
option (gogoproto.goproto_sizecache_all) = false;
|
|
||||||
|
|
||||||
message DatabaseRecord {
|
|
||||||
repeated DatabaseAddress addresses = 1 [(gogoproto.nullable) = false];
|
|
||||||
int64 seen = 3; // Unix nanos, last device announce
|
|
||||||
}
|
|
||||||
|
|
||||||
message ReplicationRecord {
|
|
||||||
bytes key = 1; // raw 32 byte device ID
|
|
||||||
repeated DatabaseAddress addresses = 2 [(gogoproto.nullable) = false];
|
|
||||||
int64 seen = 3; // Unix nanos, last device announce
|
|
||||||
}
|
|
||||||
|
|
||||||
message DatabaseAddress {
|
|
||||||
string address = 1;
|
|
||||||
int64 expires = 2; // Unix nanos
|
|
||||||
}
|
|
||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/syncthing/syncthing/internal/gen/discosrv"
|
||||||
"github.com/syncthing/syncthing/lib/protocol"
|
"github.com/syncthing/syncthing/lib/protocol"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -39,7 +40,7 @@ func TestDatabaseGetSet(t *testing.T) {
|
|||||||
|
|
||||||
// Put a record
|
// Put a record
|
||||||
|
|
||||||
rec.Addresses = []DatabaseAddress{
|
rec.Addresses = []*discosrv.DatabaseAddress{
|
||||||
{Address: "tcp://1.2.3.4:5", Expires: tc.Now().Add(time.Minute).UnixNano()},
|
{Address: "tcp://1.2.3.4:5", Expires: tc.Now().Add(time.Minute).UnixNano()},
|
||||||
}
|
}
|
||||||
if err := db.put(&protocol.EmptyDeviceID, rec); err != nil {
|
if err := db.put(&protocol.EmptyDeviceID, rec); err != nil {
|
||||||
@@ -65,7 +66,7 @@ func TestDatabaseGetSet(t *testing.T) {
|
|||||||
|
|
||||||
tc.wind(30 * time.Second)
|
tc.wind(30 * time.Second)
|
||||||
|
|
||||||
addrs := []DatabaseAddress{
|
addrs := []*discosrv.DatabaseAddress{
|
||||||
{Address: "tcp://6.7.8.9:0", Expires: tc.Now().Add(time.Minute).UnixNano()},
|
{Address: "tcp://6.7.8.9:0", Expires: tc.Now().Add(time.Minute).UnixNano()},
|
||||||
}
|
}
|
||||||
if err := db.merge(&protocol.EmptyDeviceID, addrs, tc.Now().UnixNano()); err != nil {
|
if err := db.merge(&protocol.EmptyDeviceID, addrs, tc.Now().UnixNano()); err != nil {
|
||||||
@@ -112,7 +113,7 @@ func TestDatabaseGetSet(t *testing.T) {
|
|||||||
|
|
||||||
// Set an address
|
// Set an address
|
||||||
|
|
||||||
addrs = []DatabaseAddress{
|
addrs = []*discosrv.DatabaseAddress{
|
||||||
{Address: "tcp://6.7.8.9:0", Expires: tc.Now().Add(time.Minute).UnixNano()},
|
{Address: "tcp://6.7.8.9:0", Expires: tc.Now().Add(time.Minute).UnixNano()},
|
||||||
}
|
}
|
||||||
if err := db.merge(&protocol.GlobalDeviceID, addrs, tc.Now().UnixNano()); err != nil {
|
if err := db.merge(&protocol.GlobalDeviceID, addrs, tc.Now().UnixNano()); err != nil {
|
||||||
@@ -134,28 +135,28 @@ func TestDatabaseGetSet(t *testing.T) {
|
|||||||
func TestFilter(t *testing.T) {
|
func TestFilter(t *testing.T) {
|
||||||
// all cases are expired with t=10
|
// all cases are expired with t=10
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
a []DatabaseAddress
|
a []*discosrv.DatabaseAddress
|
||||||
b []DatabaseAddress
|
b []*discosrv.DatabaseAddress
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
a: nil,
|
a: nil,
|
||||||
b: nil,
|
b: nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
a: []DatabaseAddress{{Address: "a", Expires: 9}, {Address: "b", Expires: 9}, {Address: "c", Expires: 9}},
|
a: []*discosrv.DatabaseAddress{{Address: "a", Expires: 9}, {Address: "b", Expires: 9}, {Address: "c", Expires: 9}},
|
||||||
b: []DatabaseAddress{},
|
b: []*discosrv.DatabaseAddress{},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
a: []DatabaseAddress{{Address: "a", Expires: 10}},
|
a: []*discosrv.DatabaseAddress{{Address: "a", Expires: 10}},
|
||||||
b: []DatabaseAddress{{Address: "a", Expires: 10}},
|
b: []*discosrv.DatabaseAddress{{Address: "a", Expires: 10}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
a: []DatabaseAddress{{Address: "a", Expires: 10}, {Address: "b", Expires: 10}, {Address: "c", Expires: 10}},
|
a: []*discosrv.DatabaseAddress{{Address: "a", Expires: 10}, {Address: "b", Expires: 10}, {Address: "c", Expires: 10}},
|
||||||
b: []DatabaseAddress{{Address: "a", Expires: 10}, {Address: "b", Expires: 10}, {Address: "c", Expires: 10}},
|
b: []*discosrv.DatabaseAddress{{Address: "a", Expires: 10}, {Address: "b", Expires: 10}, {Address: "c", Expires: 10}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
a: []DatabaseAddress{{Address: "a", Expires: 5}, {Address: "b", Expires: 15}, {Address: "c", Expires: 5}, {Address: "d", Expires: 15}, {Address: "e", Expires: 5}},
|
a: []*discosrv.DatabaseAddress{{Address: "a", Expires: 5}, {Address: "b", Expires: 15}, {Address: "c", Expires: 5}, {Address: "d", Expires: 15}, {Address: "e", Expires: 5}},
|
||||||
b: []DatabaseAddress{{Address: "b", Expires: 15}, {Address: "d", Expires: 15}},
|
b: []*discosrv.DatabaseAddress{{Address: "b", Expires: 15}, {Address: "d", Expires: 15}},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,62 +170,62 @@ func TestFilter(t *testing.T) {
|
|||||||
|
|
||||||
func TestMerge(t *testing.T) {
|
func TestMerge(t *testing.T) {
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
a, b, res []DatabaseAddress
|
a, b, res []*discosrv.DatabaseAddress
|
||||||
}{
|
}{
|
||||||
{nil, nil, nil},
|
{nil, nil, nil},
|
||||||
{
|
{
|
||||||
nil,
|
nil,
|
||||||
[]DatabaseAddress{{Address: "a", Expires: 10}},
|
[]*discosrv.DatabaseAddress{{Address: "a", Expires: 10}},
|
||||||
[]DatabaseAddress{{Address: "a", Expires: 10}},
|
[]*discosrv.DatabaseAddress{{Address: "a", Expires: 10}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
nil,
|
nil,
|
||||||
[]DatabaseAddress{{Address: "a", Expires: 10}, {Address: "b", Expires: 10}, {Address: "c", Expires: 10}},
|
[]*discosrv.DatabaseAddress{{Address: "a", Expires: 10}, {Address: "b", Expires: 10}, {Address: "c", Expires: 10}},
|
||||||
[]DatabaseAddress{{Address: "a", Expires: 10}, {Address: "b", Expires: 10}, {Address: "c", Expires: 10}},
|
[]*discosrv.DatabaseAddress{{Address: "a", Expires: 10}, {Address: "b", Expires: 10}, {Address: "c", Expires: 10}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
[]DatabaseAddress{{Address: "a", Expires: 10}},
|
[]*discosrv.DatabaseAddress{{Address: "a", Expires: 10}},
|
||||||
[]DatabaseAddress{{Address: "a", Expires: 15}},
|
[]*discosrv.DatabaseAddress{{Address: "a", Expires: 15}},
|
||||||
[]DatabaseAddress{{Address: "a", Expires: 15}},
|
[]*discosrv.DatabaseAddress{{Address: "a", Expires: 15}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
[]DatabaseAddress{{Address: "a", Expires: 10}},
|
[]*discosrv.DatabaseAddress{{Address: "a", Expires: 10}},
|
||||||
[]DatabaseAddress{{Address: "b", Expires: 15}},
|
[]*discosrv.DatabaseAddress{{Address: "b", Expires: 15}},
|
||||||
[]DatabaseAddress{{Address: "a", Expires: 10}, {Address: "b", Expires: 15}},
|
[]*discosrv.DatabaseAddress{{Address: "a", Expires: 10}, {Address: "b", Expires: 15}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
[]DatabaseAddress{{Address: "a", Expires: 10}, {Address: "b", Expires: 15}},
|
[]*discosrv.DatabaseAddress{{Address: "a", Expires: 10}, {Address: "b", Expires: 15}},
|
||||||
[]DatabaseAddress{{Address: "a", Expires: 15}, {Address: "b", Expires: 15}},
|
[]*discosrv.DatabaseAddress{{Address: "a", Expires: 15}, {Address: "b", Expires: 15}},
|
||||||
[]DatabaseAddress{{Address: "a", Expires: 15}, {Address: "b", Expires: 15}},
|
[]*discosrv.DatabaseAddress{{Address: "a", Expires: 15}, {Address: "b", Expires: 15}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
[]DatabaseAddress{{Address: "a", Expires: 10}, {Address: "b", Expires: 15}},
|
[]*discosrv.DatabaseAddress{{Address: "a", Expires: 10}, {Address: "b", Expires: 15}},
|
||||||
[]DatabaseAddress{{Address: "b", Expires: 15}, {Address: "c", Expires: 20}},
|
[]*discosrv.DatabaseAddress{{Address: "b", Expires: 15}, {Address: "c", Expires: 20}},
|
||||||
[]DatabaseAddress{{Address: "a", Expires: 10}, {Address: "b", Expires: 15}, {Address: "c", Expires: 20}},
|
[]*discosrv.DatabaseAddress{{Address: "a", Expires: 10}, {Address: "b", Expires: 15}, {Address: "c", Expires: 20}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
[]DatabaseAddress{{Address: "a", Expires: 10}, {Address: "b", Expires: 15}},
|
[]*discosrv.DatabaseAddress{{Address: "a", Expires: 10}, {Address: "b", Expires: 15}},
|
||||||
[]DatabaseAddress{{Address: "b", Expires: 5}, {Address: "c", Expires: 20}},
|
[]*discosrv.DatabaseAddress{{Address: "b", Expires: 5}, {Address: "c", Expires: 20}},
|
||||||
[]DatabaseAddress{{Address: "a", Expires: 10}, {Address: "b", Expires: 15}, {Address: "c", Expires: 20}},
|
[]*discosrv.DatabaseAddress{{Address: "a", Expires: 10}, {Address: "b", Expires: 15}, {Address: "c", Expires: 20}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
[]DatabaseAddress{{Address: "y", Expires: 10}, {Address: "z", Expires: 10}},
|
[]*discosrv.DatabaseAddress{{Address: "y", Expires: 10}, {Address: "z", Expires: 10}},
|
||||||
[]DatabaseAddress{{Address: "a", Expires: 5}, {Address: "b", Expires: 15}},
|
[]*discosrv.DatabaseAddress{{Address: "a", Expires: 5}, {Address: "b", Expires: 15}},
|
||||||
[]DatabaseAddress{{Address: "a", Expires: 5}, {Address: "b", Expires: 15}, {Address: "y", Expires: 10}, {Address: "z", Expires: 10}},
|
[]*discosrv.DatabaseAddress{{Address: "a", Expires: 5}, {Address: "b", Expires: 15}, {Address: "y", Expires: 10}, {Address: "z", Expires: 10}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
[]DatabaseAddress{{Address: "a", Expires: 10}, {Address: "b", Expires: 15}, {Address: "d", Expires: 10}},
|
[]*discosrv.DatabaseAddress{{Address: "a", Expires: 10}, {Address: "b", Expires: 15}, {Address: "d", Expires: 10}},
|
||||||
[]DatabaseAddress{{Address: "b", Expires: 5}, {Address: "c", Expires: 20}},
|
[]*discosrv.DatabaseAddress{{Address: "b", Expires: 5}, {Address: "c", Expires: 20}},
|
||||||
[]DatabaseAddress{{Address: "a", Expires: 10}, {Address: "b", Expires: 15}, {Address: "c", Expires: 20}, {Address: "d", Expires: 10}},
|
[]*discosrv.DatabaseAddress{{Address: "a", Expires: 10}, {Address: "b", Expires: 15}, {Address: "c", Expires: 20}, {Address: "d", Expires: 10}},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range cases {
|
for _, tc := range cases {
|
||||||
rec := merge(DatabaseRecord{Addresses: tc.a}, DatabaseRecord{Addresses: tc.b})
|
rec := merge(&discosrv.DatabaseRecord{Addresses: tc.a}, &discosrv.DatabaseRecord{Addresses: tc.b})
|
||||||
if fmt.Sprint(rec.Addresses) != fmt.Sprint(tc.res) {
|
if fmt.Sprint(rec.Addresses) != fmt.Sprint(tc.res) {
|
||||||
t.Errorf("Incorrect result %v, expected %v", rec.Addresses, tc.res)
|
t.Errorf("Incorrect result %v, expected %v", rec.Addresses, tc.res)
|
||||||
}
|
}
|
||||||
rec = merge(DatabaseRecord{Addresses: tc.b}, DatabaseRecord{Addresses: tc.a})
|
rec = merge(&discosrv.DatabaseRecord{Addresses: tc.b}, &discosrv.DatabaseRecord{Addresses: tc.a})
|
||||||
if fmt.Sprint(rec.Addresses) != fmt.Sprint(tc.res) {
|
if fmt.Sprint(rec.Addresses) != fmt.Sprint(tc.res) {
|
||||||
t.Errorf("Incorrect result %v, expected %v", rec.Addresses, tc.res)
|
t.Errorf("Incorrect result %v, expected %v", rec.Addresses, tc.res)
|
||||||
}
|
}
|
||||||
@@ -233,9 +234,9 @@ func TestMerge(t *testing.T) {
|
|||||||
|
|
||||||
func BenchmarkMergeEqual(b *testing.B) {
|
func BenchmarkMergeEqual(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
ar := []DatabaseAddress{{Address: "a", Expires: 10}, {Address: "b", Expires: 15}}
|
ar := []*discosrv.DatabaseAddress{{Address: "a", Expires: 10}, {Address: "b", Expires: 15}}
|
||||||
br := []DatabaseAddress{{Address: "a", Expires: 15}, {Address: "b", Expires: 10}}
|
br := []*discosrv.DatabaseAddress{{Address: "a", Expires: 15}, {Address: "b", Expires: 10}}
|
||||||
res := merge(DatabaseRecord{Addresses: ar}, DatabaseRecord{Addresses: br})
|
res := merge(&discosrv.DatabaseRecord{Addresses: ar}, &discosrv.DatabaseRecord{Addresses: br})
|
||||||
if len(res.Addresses) != 2 {
|
if len(res.Addresses) != 2 {
|
||||||
b.Fatal("wrong length")
|
b.Fatal("wrong length")
|
||||||
}
|
}
|
||||||
|
|||||||
+29
-20
@@ -11,22 +11,24 @@ import (
|
|||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
_ "net/http/pprof"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"runtime"
|
"runtime"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
_ "net/http/pprof"
|
|
||||||
|
|
||||||
"github.com/alecthomas/kong"
|
"github.com/alecthomas/kong"
|
||||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
"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/lib/automaxprocs"
|
_ "github.com/syncthing/syncthing/lib/automaxprocs"
|
||||||
"github.com/syncthing/syncthing/lib/build"
|
"github.com/syncthing/syncthing/lib/build"
|
||||||
"github.com/syncthing/syncthing/lib/protocol"
|
"github.com/syncthing/syncthing/lib/protocol"
|
||||||
"github.com/syncthing/syncthing/lib/rand"
|
"github.com/syncthing/syncthing/lib/rand"
|
||||||
"github.com/syncthing/syncthing/lib/s3"
|
|
||||||
"github.com/syncthing/syncthing/lib/tlsutil"
|
"github.com/syncthing/syncthing/lib/tlsutil"
|
||||||
"github.com/thejerf/suture/v4"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -58,12 +60,13 @@ const (
|
|||||||
var debug = false
|
var debug = false
|
||||||
|
|
||||||
type CLI struct {
|
type CLI struct {
|
||||||
Cert string `group:"Listen" help:"Certificate file" default:"./cert.pem" env:"DISCOVERY_CERT_FILE"`
|
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"`
|
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"`
|
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"`
|
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"`
|
Listen string `group:"Listen" help:"Listen address" default:":8443" env:"DISCOVERY_LISTEN"`
|
||||||
MetricsListen string `group:"Listen" help:"Metrics listen address" env:"DISCOVERY_METRICS_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"`
|
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"`
|
DBFlushInterval time.Duration `group:"Database" help:"Interval between database flushes" default:"5m" env:"DISCOVERY_DB_FLUSH_INTERVAL"`
|
||||||
@@ -74,6 +77,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"`
|
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"`
|
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"`
|
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"`
|
Debug bool `short:"d" help:"Print debug output" env:"DISCOVERY_DEBUG"`
|
||||||
@@ -100,7 +107,7 @@ func main() {
|
|||||||
cert, err = tls.LoadX509KeyPair(cli.Cert, cli.Key)
|
cert, err = tls.LoadX509KeyPair(cli.Cert, cli.Key)
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
log.Println("Failed to load keypair. Generating one, this might take a while...")
|
log.Println("Failed to load keypair. Generating one, this might take a while...")
|
||||||
cert, err = tlsutil.NewCertificate(cli.Cert, cli.Key, "stdiscosrv", 20*365)
|
cert, err = tlsutil.NewCertificate(cli.Cert, cli.Key, "stdiscosrv", 20*365, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalln("Failed to generate X509 key pair:", err)
|
log.Fatalln("Failed to generate X509 key pair:", err)
|
||||||
}
|
}
|
||||||
@@ -117,18 +124,20 @@ func main() {
|
|||||||
Timeout: 2 * time.Minute,
|
Timeout: 2 * time.Minute,
|
||||||
})
|
})
|
||||||
|
|
||||||
// If configured, use S3 for database backups.
|
// If configured, use blob storage for database backups.
|
||||||
var s3c *s3.Session
|
var blobs blob.Store
|
||||||
|
var err error
|
||||||
if cli.DBS3Endpoint != "" {
|
if cli.DBS3Endpoint != "" {
|
||||||
var err error
|
blobs, err = s3.NewSession(cli.DBS3Endpoint, cli.DBS3Region, cli.DBS3Bucket, cli.DBS3AccessKeyID, cli.DBS3SecretKey)
|
||||||
s3c, err = s3.NewSession(cli.DBS3Endpoint, cli.DBS3Region, cli.DBS3Bucket, cli.DBS3AccessKeyID, cli.DBS3SecretKey)
|
} else if cli.DBAzureBlobAccount != "" {
|
||||||
if err != nil {
|
blobs, err = azureblob.NewBlobStore(cli.DBAzureBlobAccount, cli.DBAzureBlobKey, cli.DBAzureBlobContainer)
|
||||||
log.Fatalf("Failed to create S3 session: %v", err)
|
}
|
||||||
}
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to create blob store: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start the database.
|
// Start the database.
|
||||||
db := newInMemoryStore(cli.DBDir, cli.DBFlushInterval, s3c)
|
db := newInMemoryStore(cli.DBDir, cli.DBFlushInterval, blobs)
|
||||||
main.Add(db)
|
main.Add(db)
|
||||||
|
|
||||||
// If we have an AMQP broker for replication, start that
|
// If we have an AMQP broker for replication, start that
|
||||||
@@ -141,7 +150,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Start the main API server.
|
// 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)
|
main.Add(qs)
|
||||||
|
|
||||||
// If we have a metrics port configured, start a metrics handler.
|
// If we have a metrics port configured, start a metrics handler.
|
||||||
|
|||||||
@@ -12,9 +12,8 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
syncthingprotocol "github.com/syncthing/syncthing/lib/protocol"
|
syncthingprotocol "github.com/syncthing/syncthing/lib/protocol"
|
||||||
"github.com/syncthing/syncthing/lib/tlsutil"
|
|
||||||
|
|
||||||
"github.com/syncthing/syncthing/lib/relay/protocol"
|
"github.com/syncthing/syncthing/lib/relay/protocol"
|
||||||
|
"github.com/syncthing/syncthing/lib/tlsutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -185,7 +184,7 @@ func protocolConnectionHandler(tcpConn net.Conn, config *tls.Config, token strin
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// requestedPeer is the server, id is the client
|
// requestedPeer is the server, id is the client
|
||||||
ses := newSession(requestedPeer, id, sessionLimiter, globalLimiter)
|
ses := newSession(requestedPeer, id, sessionLimitBps, globalLimiter)
|
||||||
|
|
||||||
go ses.Serve()
|
go ses.Serve()
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/time/rate"
|
||||||
|
|
||||||
_ "github.com/syncthing/syncthing/lib/automaxprocs"
|
_ "github.com/syncthing/syncthing/lib/automaxprocs"
|
||||||
"github.com/syncthing/syncthing/lib/build"
|
"github.com/syncthing/syncthing/lib/build"
|
||||||
"github.com/syncthing/syncthing/lib/config"
|
"github.com/syncthing/syncthing/lib/config"
|
||||||
@@ -30,7 +32,6 @@ import (
|
|||||||
"github.com/syncthing/syncthing/lib/relay/protocol"
|
"github.com/syncthing/syncthing/lib/relay/protocol"
|
||||||
"github.com/syncthing/syncthing/lib/tlsutil"
|
"github.com/syncthing/syncthing/lib/tlsutil"
|
||||||
_ "github.com/syncthing/syncthing/lib/upnp"
|
_ "github.com/syncthing/syncthing/lib/upnp"
|
||||||
"golang.org/x/time/rate"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -50,7 +51,6 @@ var (
|
|||||||
globalLimitBps int
|
globalLimitBps int
|
||||||
overLimit atomic.Bool
|
overLimit atomic.Bool
|
||||||
descriptorLimit int64
|
descriptorLimit int64
|
||||||
sessionLimiter *rate.Limiter
|
|
||||||
globalLimiter *rate.Limiter
|
globalLimiter *rate.Limiter
|
||||||
networkBufferSize int
|
networkBufferSize int
|
||||||
|
|
||||||
@@ -157,7 +157,7 @@ func main() {
|
|||||||
cert, err := tls.LoadX509KeyPair(certFile, keyFile)
|
cert, err := tls.LoadX509KeyPair(certFile, keyFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("Failed to load keypair. Generating one, this might take a while...")
|
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 {
|
if err != nil {
|
||||||
log.Fatalln("Failed to generate X509 key pair:", err)
|
log.Fatalln("Failed to generate X509 key pair:", err)
|
||||||
}
|
}
|
||||||
@@ -227,9 +227,6 @@ func main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if sessionLimitBps > 0 {
|
|
||||||
sessionLimiter = rate.NewLimiter(rate.Limit(sessionLimitBps), 2*sessionLimitBps)
|
|
||||||
}
|
|
||||||
if globalLimitBps > 0 {
|
if globalLimitBps > 0 {
|
||||||
globalLimiter = rate.NewLimiter(rate.Limit(globalLimitBps), 2*globalLimitBps)
|
globalLimiter = rate.NewLimiter(rate.Limit(globalLimitBps), 2*globalLimitBps)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ var (
|
|||||||
bytesProxied atomic.Int64
|
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)
|
serverkey := make([]byte, 32)
|
||||||
_, err := rand.Read(serverkey)
|
_, err := rand.Read(serverkey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -40,12 +40,17 @@ func newSession(serverid, clientid syncthingprotocol.DeviceID, sessionRateLimit,
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var sessionRateLimit *rate.Limiter
|
||||||
|
if sessionLimitBps > 0 {
|
||||||
|
sessionRateLimit = rate.NewLimiter(rate.Limit(sessionLimitBps), 2*sessionLimitBps)
|
||||||
|
}
|
||||||
ses := &session{
|
ses := &session{
|
||||||
serverkey: serverkey,
|
serverkey: serverkey,
|
||||||
serverid: serverid,
|
serverid: serverid,
|
||||||
clientkey: clientkey,
|
clientkey: clientkey,
|
||||||
clientid: clientid,
|
clientid: clientid,
|
||||||
rateLimit: makeRateLimitFunc(sessionRateLimit, globalRateLimit),
|
rateLimit: makeRateLimitFunc(sessionRateLimit, globalRateLimit),
|
||||||
|
limiter: sessionRateLimit,
|
||||||
connsChan: make(chan net.Conn),
|
connsChan: make(chan net.Conn),
|
||||||
conns: make([]net.Conn, 0, 2),
|
conns: make([]net.Conn, 0, 2),
|
||||||
}
|
}
|
||||||
@@ -68,7 +73,6 @@ func findSession(key string) *session {
|
|||||||
ses, ok := pendingSessions[key]
|
ses, ok := pendingSessions[key]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
}
|
}
|
||||||
delete(pendingSessions, key)
|
delete(pendingSessions, key)
|
||||||
return ses
|
return ses
|
||||||
@@ -110,6 +114,7 @@ type session struct {
|
|||||||
clientid syncthingprotocol.DeviceID
|
clientid syncthingprotocol.DeviceID
|
||||||
|
|
||||||
rateLimit func(bytes int)
|
rateLimit func(bytes int)
|
||||||
|
limiter *rate.Limiter
|
||||||
|
|
||||||
connsChan chan net.Conn
|
connsChan chan net.Conn
|
||||||
conns []net.Conn
|
conns []net.Conn
|
||||||
|
|||||||
@@ -8,11 +8,14 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"runtime/pprof"
|
"runtime/pprof"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/syncthing/syncthing/internal/slogutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
func startBlockProfiler() {
|
func startBlockProfiler() {
|
||||||
@@ -20,10 +23,10 @@ func startBlockProfiler() {
|
|||||||
if profiler == nil {
|
if profiler == nil {
|
||||||
panic("Couldn't find block profiler")
|
panic("Couldn't find block profiler")
|
||||||
}
|
}
|
||||||
l.Debugln("Starting block profiling")
|
slog.Debug("Starting block profiling")
|
||||||
go func() {
|
go func() {
|
||||||
err := saveBlockingProfiles(profiler) // Only returns on error
|
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")
|
panic("Block profiler failed")
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,5 +41,4 @@ func (p *profileCommand) Run(ctx Context) error {
|
|||||||
type debugCommand struct {
|
type debugCommand struct {
|
||||||
File fileCommand `cmd:"" help:"Show information about a file (or directory/symlink)"`
|
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"`
|
Profile profileCommand `cmd:"" help:"Save a profile to help figuring out what Syncthing does"`
|
||||||
Index indexCommand `cmd:"" help:"Show information about the index (database)"`
|
|
||||||
}
|
}
|
||||||
@@ -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,158 +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"
|
|
||||||
|
|
||||||
"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 protocol.FileInfo
|
|
||||||
err := f.Unmarshal(it.Value())
|
|
||||||
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 db.VersionList
|
|
||||||
flv.Unmarshal(it.Value())
|
|
||||||
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 db.CountsSet
|
|
||||||
if err := cs.Unmarshal(it.Value()); 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 protocol.Vector
|
|
||||||
err := v.Unmarshal(it.Value())
|
|
||||||
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 db.ObservedFolder
|
|
||||||
of.Unmarshal(it.Value())
|
|
||||||
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 db.ObservedDevice
|
|
||||||
od.Unmarshal(it.Value())
|
|
||||||
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,355 +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"
|
|
||||||
|
|
||||||
"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]protocol.FileInfo)
|
|
||||||
globals := make(map[globalKey]db.VersionList)
|
|
||||||
sequences := make(map[sequenceKey]string)
|
|
||||||
needs := make(map[globalKey]struct{})
|
|
||||||
blocklists := make(map[string]struct{})
|
|
||||||
versions := make(map[string]protocol.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 protocol.FileInfo
|
|
||||||
err := f.Unmarshal(it.Value())
|
|
||||||
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 db.VersionList
|
|
||||||
if err := flv.Unmarshal(it.Value()); 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 protocol.Vector
|
|
||||||
if err := v.Unmarshal(it.Value()); 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 !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
|
|
||||||
}
|
|
||||||
if fi.IsInvalid() != invalid {
|
|
||||||
fmt.Printf("VersionList %q, folder %q, entry %d, FileInfo invalid mismatch, %v (VersionList) != %v (FileInfo)\n", gk.name, folder, i, invalid, fi.IsInvalid())
|
|
||||||
success = false
|
|
||||||
}
|
|
||||||
if fi.IsDeleted() != deleted {
|
|
||||||
fmt.Printf("VersionList %q, folder %q, entry %d, FileInfo deleted mismatch, %v (VersionList) != %v (FileInfo)\n", gk.name, folder, i, deleted, fi.IsDeleted())
|
|
||||||
success = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for i, fv := range vl.RawVersions {
|
|
||||||
for _, device := range fv.Devices {
|
|
||||||
checkGlobal(i, device, fv.Version, false, fv.Deleted)
|
|
||||||
}
|
|
||||||
for _, device := range fv.InvalidDevices {
|
|
||||||
checkGlobal(i, device, fv.Version, 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, _ := vl.GetGlobal()
|
|
||||||
devB, _ := fv.FirstDevice()
|
|
||||||
dev := deviceToIDs[string(devB)]
|
|
||||||
fi := 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 db.VersionList) bool {
|
|
||||||
gfv, gok := vl.GetGlobal()
|
|
||||||
if !gok { // That's weird, but we hardly need something non-existent
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
fv, ok := vl.Get(protocol.LocalDeviceID[:])
|
|
||||||
return db.Need(gfv, ok, fv.Version)
|
|
||||||
}
|
|
||||||
@@ -14,15 +14,12 @@ import (
|
|||||||
"github.com/alecthomas/kong"
|
"github.com/alecthomas/kong"
|
||||||
"github.com/kballard/go-shellquote"
|
"github.com/kballard/go-shellquote"
|
||||||
|
|
||||||
"github.com/syncthing/syncthing/cmd/syncthing/cmdutil"
|
|
||||||
"github.com/syncthing/syncthing/lib/config"
|
"github.com/syncthing/syncthing/lib/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CLI struct {
|
type CLI struct {
|
||||||
cmdutil.CommonOptions
|
GUIAddress string `name:"gui-address" env:"STGUIADDRESS"`
|
||||||
DataDir string `name:"data" placeholder:"PATH" env:"STDATADIR" help:"Set data directory (database and logs)"`
|
GUIAPIKey string `name:"gui-apikey" env:"STGUIAPIKEY"`
|
||||||
GUIAddress string `name:"gui-address"`
|
|
||||||
GUIAPIKey string `name:"gui-apikey"`
|
|
||||||
|
|
||||||
Show showCommand `cmd:"" help:"Show command group"`
|
Show showCommand `cmd:"" help:"Show command group"`
|
||||||
Debug debugCommand `cmd:"" help:"Debug command group"`
|
Debug debugCommand `cmd:"" help:"Debug command group"`
|
||||||
@@ -37,11 +34,6 @@ type Context struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (cli CLI) AfterApply(kongCtx *kong.Context) error {
|
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{
|
clientFactory := &apiClientFactory{
|
||||||
cfg: config.GUIConfiguration{
|
cfg: config.GUIConfiguration{
|
||||||
RawAddress: cli.GUIAddress,
|
RawAddress: cli.GUIAddress,
|
||||||
|
|||||||
@@ -17,8 +17,6 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/syncthing/syncthing/lib/config"
|
"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) {
|
func responseToBArray(response *http.Response) ([]byte, error) {
|
||||||
@@ -133,19 +131,6 @@ func prettyPrintResponse(response *http.Response) error {
|
|||||||
return prettyPrintJSON(data)
|
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 {
|
func normalizePath(path string) string {
|
||||||
return filepath.ToSlash(filepath.Clean(path))
|
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"
|
"context"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sort"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/syncthing/syncthing/internal/slogutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -33,11 +36,13 @@ const (
|
|||||||
func uploadPanicLogs(ctx context.Context, urlBase, dir string) {
|
func uploadPanicLogs(ctx context.Context, urlBase, dir string) {
|
||||||
files, err := filepath.Glob(filepath.Join(dir, "panic-*.log"))
|
files, err := filepath.Glob(filepath.Join(dir, "panic-*.log"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.Warnln("Failed to list panic logs:", err)
|
slog.ErrorContext(ctx, "Failed to list panic logs", slogutil.Error(err))
|
||||||
return
|
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 {
|
for _, file := range files {
|
||||||
if strings.Contains(file, ".reported.") {
|
if strings.Contains(file, ".reported.") {
|
||||||
// We've already sent this file. It'll be cleaned out at some
|
// 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 {
|
if err := uploadPanicLog(ctx, urlBase, file); err != nil {
|
||||||
l.Warnln("Reporting crash:", err)
|
slog.ErrorContext(ctx, "Reporting crash", slogutil.Error(err))
|
||||||
} else {
|
} else {
|
||||||
// Rename the log so we don't have to try to report it again. This
|
// 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.
|
// 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)
|
data = filterLogLines(data)
|
||||||
|
|
||||||
hash := fmt.Sprintf("%x", sha256.Sum256(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)
|
url := fmt.Sprintf("%s/%s", urlBase, hash)
|
||||||
headReq, err := http.NewRequest(http.MethodHead, url, nil)
|
headReq, err := http.NewRequest(http.MethodHead, url, nil)
|
||||||
|
|||||||
@@ -6,10 +6,6 @@
|
|||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import "github.com/syncthing/syncthing/internal/slogutil"
|
||||||
"github.com/syncthing/syncthing/lib/logger"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
func init() { slogutil.RegisterPackage("Main package") }
|
||||||
l = logger.DefaultLogger.NewFacility("main", "Main package")
|
|
||||||
)
|
|
||||||
@@ -17,6 +17,9 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
|
|
||||||
|
"github.com/syncthing/syncthing/internal/gen/bep"
|
||||||
"github.com/syncthing/syncthing/lib/config"
|
"github.com/syncthing/syncthing/lib/config"
|
||||||
"github.com/syncthing/syncthing/lib/fs"
|
"github.com/syncthing/syncthing/lib/fs"
|
||||||
"github.com/syncthing/syncthing/lib/osutil"
|
"github.com/syncthing/syncthing/lib/osutil"
|
||||||
@@ -235,7 +238,7 @@ func (c *CLI) decryptFile(encFi *protocol.FileInfo, plainFi *protocol.FileInfo,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Verify the hash against the plaintext block info
|
// 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
|
// The block decrypted correctly but fails the hash check. This
|
||||||
// is odd and unexpected, but it it's still a valid block from
|
// is odd and unexpected, but it it's still a valid block from
|
||||||
// the source. The file might have changed while we pulled it?
|
// the source. The file might have changed while we pulled it?
|
||||||
@@ -280,10 +283,11 @@ func loadEncryptedFileInfo(fd fs.File) (*protocol.FileInfo, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var encFi protocol.FileInfo
|
var encFi bep.FileInfo
|
||||||
if err := encFi.Unmarshal(trailer); err != nil {
|
if err := proto.Unmarshal(trailer, &encFi); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
fi := protocol.FileInfoFromWire(&encFi)
|
||||||
|
|
||||||
return &encFi, nil
|
return &fi, nil
|
||||||
}
|
}
|
||||||
@@ -11,42 +11,25 @@ import (
|
|||||||
"bufio"
|
"bufio"
|
||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/syncthing/syncthing/cmd/syncthing/cmdutil"
|
|
||||||
"github.com/syncthing/syncthing/lib/config"
|
"github.com/syncthing/syncthing/lib/config"
|
||||||
"github.com/syncthing/syncthing/lib/events"
|
"github.com/syncthing/syncthing/lib/events"
|
||||||
"github.com/syncthing/syncthing/lib/fs"
|
"github.com/syncthing/syncthing/lib/fs"
|
||||||
"github.com/syncthing/syncthing/lib/locations"
|
"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/protocol"
|
||||||
"github.com/syncthing/syncthing/lib/syncthing"
|
"github.com/syncthing/syncthing/lib/syncthing"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CLI struct {
|
type CLI struct {
|
||||||
cmdutil.CommonOptions
|
GUIUser string `placeholder:"STRING" help:"Specify new GUI authentication user name"`
|
||||||
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)"`
|
||||||
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 {
|
func (c *CLI) Run() 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)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Support reading the password from a pipe or similar
|
// Support reading the password from a pipe or similar
|
||||||
if c.GUIPassword == "-" {
|
if c.GUIPassword == "-" {
|
||||||
reader := bufio.NewReader(os.Stdin)
|
reader := bufio.NewReader(os.Stdin)
|
||||||
@@ -57,13 +40,13 @@ func (c *CLI) Run(l logger.Logger) error {
|
|||||||
c.GUIPassword = string(password)
|
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 fmt.Errorf("failed to generate config and keys: %w", err)
|
||||||
}
|
}
|
||||||
return nil
|
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)
|
dir, err := fs.ExpandTilde(confDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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)
|
certFile, keyFile := locations.Get(locations.CertFile), locations.Get(locations.KeyFile)
|
||||||
cert, err := tls.LoadX509KeyPair(certFile, keyFile)
|
cert, err := tls.LoadX509KeyPair(certFile, keyFile)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
l.Warnln("Key exists; will not overwrite.")
|
slog.Warn("Key exists; will not overwrite")
|
||||||
} else {
|
} else {
|
||||||
cert, err = syncthing.GenerateCertificate(certFile, keyFile)
|
cert, err = syncthing.GenerateCertificate(certFile, keyFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("create certificate: %w", err)
|
return fmt.Errorf("create certificate: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
myID = protocol.NewDeviceID(cert.Certificate[0])
|
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)
|
cfgFile := locations.Get(locations.ConfigFile)
|
||||||
cfg, _, err := config.Load(cfgFile, myID, events.NoopLogger)
|
cfg, _, err := config.Load(cfgFile, myID, events.NoopLogger)
|
||||||
if fs.IsNotExist(err) {
|
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)
|
return fmt.Errorf("create config: %w", err)
|
||||||
}
|
}
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
@@ -104,7 +88,7 @@ func Generate(l logger.Logger, confDir, guiUser, guiPassword string, noDefaultFo
|
|||||||
|
|
||||||
var updateErr error
|
var updateErr error
|
||||||
waiter, err := cfg.Modify(func(cfg *config.Configuration) {
|
waiter, err := cfg.Modify(func(cfg *config.Configuration) {
|
||||||
updateErr = updateGUIAuthentication(l, &cfg.GUI, guiUser, guiPassword)
|
updateErr = updateGUIAuthentication(&cfg.GUI, guiUser, guiPassword)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("modify config: %w", err)
|
return fmt.Errorf("modify config: %w", err)
|
||||||
@@ -120,17 +104,17 @@ func Generate(l logger.Logger, confDir, guiUser, guiPassword string, noDefaultFo
|
|||||||
return nil
|
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 {
|
if guiUser != "" && guiCfg.User != 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 guiPassword != "" && guiCfg.Password != guiPassword {
|
||||||
if err := guiCfg.SetPassword(guiPassword); err != nil {
|
if err := guiCfg.SetPassword(guiPassword); err != nil {
|
||||||
return fmt.Errorf("failed to set GUI authentication password: %w", err)
|
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
|
return nil
|
||||||
}
|
}
|
||||||
@@ -8,18 +8,21 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"runtime/pprof"
|
"runtime/pprof"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/syncthing/syncthing/internal/slogutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
func startHeapProfiler() {
|
func startHeapProfiler() {
|
||||||
l.Debugln("Starting heap profiling")
|
slog.Debug("Starting heap profiling")
|
||||||
go func() {
|
go func() {
|
||||||
err := saveHeapProfiles(1) // Only returns on error
|
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")
|
panic("Heap profiler failed")
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,8 +7,8 @@
|
|||||||
//go:build !windows
|
//go:build !windows
|
||||||
// +build !windows
|
// +build !windows
|
||||||
|
|
||||||
package cmdutil
|
package main
|
||||||
|
|
||||||
type buildCommonOptions struct {
|
type buildSpecificOptions struct {
|
||||||
HideConsole bool `hidden:""`
|
HideConsole bool `hidden:""`
|
||||||
}
|
}
|
||||||
@@ -4,8 +4,8 @@
|
|||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
// 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/.
|
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
package cmdutil
|
package main
|
||||||
|
|
||||||
type buildCommonOptions struct {
|
type buildSpecificOptions struct {
|
||||||
HideConsole bool `name:"no-console" help:"Hide console window"`
|
HideConsole bool `name:"no-console" help:"Hide console window" env:"STHIDECONSOLE"`
|
||||||
}
|
}
|
||||||
+444
-313
File diff suppressed because it is too large.
Load diff
+43
-62
@@ -11,26 +11,28 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"log/slog"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/syncthing/syncthing/internal/slogutil"
|
||||||
"github.com/syncthing/syncthing/lib/build"
|
"github.com/syncthing/syncthing/lib/build"
|
||||||
"github.com/syncthing/syncthing/lib/fs"
|
"github.com/syncthing/syncthing/lib/fs"
|
||||||
"github.com/syncthing/syncthing/lib/locations"
|
"github.com/syncthing/syncthing/lib/locations"
|
||||||
"github.com/syncthing/syncthing/lib/osutil"
|
"github.com/syncthing/syncthing/lib/osutil"
|
||||||
"github.com/syncthing/syncthing/lib/svcutil"
|
"github.com/syncthing/syncthing/lib/svcutil"
|
||||||
"github.com/syncthing/syncthing/lib/sync"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
stdoutFirstLines []string // The first 10 lines of stdout
|
stdoutFirstLines []string // The first 10 lines of stdout
|
||||||
stdoutLastLines []string // The last 50 lines of stdout
|
stdoutLastLines []string // The last 50 lines of stdout
|
||||||
stdoutMut = sync.NewMutex()
|
stdoutMut sync.Mutex
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -43,9 +45,7 @@ const (
|
|||||||
panicUploadNoticeWait = 10 * time.Second
|
panicUploadNoticeWait = 10 * time.Second
|
||||||
)
|
)
|
||||||
|
|
||||||
func monitorMain(options serveOptions) {
|
func (c *serveCmd) monitorMain() {
|
||||||
l.SetPrefix("[monitor] ")
|
|
||||||
|
|
||||||
var dst io.Writer = os.Stdout
|
var dst io.Writer = os.Stdout
|
||||||
|
|
||||||
logFile := locations.Get(locations.LogFile)
|
logFile := locations.Get(locations.LogFile)
|
||||||
@@ -58,13 +58,13 @@ func monitorMain(options serveOptions) {
|
|||||||
open := func(name string) (io.WriteCloser, error) {
|
open := func(name string) (io.WriteCloser, error) {
|
||||||
return newAutoclosedFile(name, logFileAutoCloseDelay, logFileMaxOpenTime)
|
return newAutoclosedFile(name, logFileAutoCloseDelay, logFileMaxOpenTime)
|
||||||
}
|
}
|
||||||
if options.LogMaxSize > 0 {
|
if c.LogMaxSize > 0 {
|
||||||
fileDst, err = newRotatedFile(logFile, open, int64(options.LogMaxSize), options.LogMaxFiles)
|
fileDst, err = newRotatedFile(logFile, open, int64(c.LogMaxSize), c.LogMaxFiles)
|
||||||
} else {
|
} else {
|
||||||
fileDst, err = open(logFile)
|
fileDst, err = open(logFile)
|
||||||
}
|
}
|
||||||
if err != nil {
|
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 {
|
} else {
|
||||||
if build.IsWindows {
|
if build.IsWindows {
|
||||||
// Translate line breaks to Windows standard
|
// Translate line breaks to Windows standard
|
||||||
@@ -78,14 +78,14 @@ func monitorMain(options serveOptions) {
|
|||||||
// Log to both stdout and file.
|
// Log to both stdout and file.
|
||||||
dst = io.MultiWriter(dst, fileDst)
|
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
|
args := os.Args
|
||||||
binary, err := getBinary(args[0])
|
binary, err := getBinary(args[0])
|
||||||
if err != nil {
|
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")
|
panic("Error starting the main Syncthing process")
|
||||||
}
|
}
|
||||||
var restarts [restartCounts]time.Time
|
var restarts [restartCounts]time.Time
|
||||||
@@ -102,7 +102,7 @@ func monitorMain(options serveOptions) {
|
|||||||
maybeReportPanics()
|
maybeReportPanics()
|
||||||
|
|
||||||
if t := time.Since(restarts[0]); t < restartLoopThreshold {
|
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())
|
os.Exit(svcutil.ExitError.AsInt())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,10 +122,10 @@ func monitorMain(options serveOptions) {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
l.Debugln("Starting syncthing")
|
slog.Debug("Starting syncthing")
|
||||||
err = cmd.Start()
|
err = cmd.Start()
|
||||||
if err != nil {
|
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")
|
panic("Error starting the main Syncthing process")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,7 +134,7 @@ func monitorMain(options serveOptions) {
|
|||||||
stdoutLastLines = make([]string, 0, 50)
|
stdoutLastLines = make([]string, 0, 50)
|
||||||
stdoutMut.Unlock()
|
stdoutMut.Unlock()
|
||||||
|
|
||||||
wg := sync.NewWaitGroup()
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
@@ -158,13 +158,13 @@ func monitorMain(options serveOptions) {
|
|||||||
stopped := false
|
stopped := false
|
||||||
select {
|
select {
|
||||||
case s := <-stopSign:
|
case s := <-stopSign:
|
||||||
l.Infof("Signal %d received; exiting", s)
|
slog.Info("Received signal; exiting", "signal", s)
|
||||||
cmd.Process.Signal(sigTerm)
|
cmd.Process.Signal(sigTerm)
|
||||||
err = <-exit
|
err = <-exit
|
||||||
stopped = true
|
stopped = true
|
||||||
|
|
||||||
case s := <-restartSign:
|
case s := <-restartSign:
|
||||||
l.Infof("Signal %d received; restarting", s)
|
slog.Info("Received signal; restarting", "signal", s)
|
||||||
cmd.Process.Signal(sigHup)
|
cmd.Process.Signal(sigHup)
|
||||||
err = <-exit
|
err = <-exit
|
||||||
|
|
||||||
@@ -178,25 +178,25 @@ func monitorMain(options serveOptions) {
|
|||||||
|
|
||||||
if exiterr, ok := err.(*exec.ExitError); ok {
|
if exiterr, ok := err.(*exec.ExitError); ok {
|
||||||
exitCode := exiterr.ExitCode()
|
exitCode := exiterr.ExitCode()
|
||||||
if stopped || options.NoRestart {
|
if stopped || c.NoRestart {
|
||||||
os.Exit(exitCode)
|
os.Exit(exitCode)
|
||||||
}
|
}
|
||||||
if exitCode == svcutil.ExitUpgrade.AsInt() {
|
if exitCode == svcutil.ExitUpgrade.AsInt() {
|
||||||
// Restart the monitor process to release the .old
|
// Restart the monitor process to release the .old
|
||||||
// binary as part of the upgrade process.
|
// binary as part of the upgrade process.
|
||||||
l.Infoln("Restarting monitor...")
|
slog.Info("Restarting monitor...")
|
||||||
if err = restartMonitor(binary, args); err != nil {
|
if err = restartMonitor(binary, args); err != nil {
|
||||||
l.Warnln("Restart:", err)
|
slog.Error("Failed to restart monitor", slogutil.Error(err))
|
||||||
}
|
}
|
||||||
os.Exit(exitCode)
|
os.Exit(exitCode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if options.NoRestart {
|
if c.NoRestart {
|
||||||
os.Exit(svcutil.ExitError.AsInt())
|
os.Exit(svcutil.ExitError.AsInt())
|
||||||
}
|
}
|
||||||
|
|
||||||
l.Infoln("Syncthing exited:", err)
|
slog.Info("Syncthing exited", slogutil.Error(err))
|
||||||
time.Sleep(restartPause)
|
time.Sleep(restartPause)
|
||||||
|
|
||||||
if first {
|
if first {
|
||||||
@@ -238,47 +238,29 @@ func copyStderr(stderr io.Reader, dst io.Writer) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if panicFd == nil {
|
dst.Write([]byte(line))
|
||||||
dst.Write([]byte(line))
|
|
||||||
|
|
||||||
if strings.HasPrefix(line, "panic:") || strings.HasPrefix(line, "fatal error:") {
|
if panicFd == nil && (strings.HasPrefix(line, "panic:") || strings.HasPrefix(line, "fatal error:")) {
|
||||||
panicFd, err = os.Create(locations.GetTimestamped(locations.PanicLog))
|
panicFd, err = os.Create(locations.GetTimestamped(locations.PanicLog))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.Warnln("Create panic log:", err)
|
slog.Error("Failed to create panic log", slogutil.Error(err))
|
||||||
continue
|
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()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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")
|
panicFd.WriteString("Panic at " + time.Now().Format(time.RFC3339) + "\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -448,7 +430,6 @@ func newAutoclosedFile(name string, closeDelay, maxOpenTime time.Duration) (*aut
|
|||||||
name: name,
|
name: name,
|
||||||
closeDelay: closeDelay,
|
closeDelay: closeDelay,
|
||||||
maxOpenTime: maxOpenTime,
|
maxOpenTime: maxOpenTime,
|
||||||
mut: sync.NewMutex(),
|
|
||||||
closed: make(chan struct{}),
|
closed: make(chan struct{}),
|
||||||
closeTimer: time.NewTimer(time.Minute),
|
closeTimer: time.NewTimer(time.Minute),
|
||||||
}
|
}
|
||||||
@@ -556,7 +537,7 @@ func maybeReportPanics() {
|
|||||||
// Try to get a config to see if/where panics should be reported.
|
// Try to get a config to see if/where panics should be reported.
|
||||||
cfg, err := loadOrDefaultConfig()
|
cfg, err := loadOrDefaultConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.Warnln("Couldn't load config; not reporting crash")
|
slog.Error("Couldn't load config; not reporting crash")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -576,7 +557,7 @@ func maybeReportPanics() {
|
|||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return
|
return
|
||||||
case <-time.After(panicUploadNoticeWait):
|
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")
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ func checkNotExist(t *testing.T, name string) {
|
|||||||
func TestAutoClosedFile(t *testing.T) {
|
func TestAutoClosedFile(t *testing.T) {
|
||||||
os.RemoveAll("_autoclose")
|
os.RemoveAll("_autoclose")
|
||||||
defer os.RemoveAll("_autoclose")
|
defer os.RemoveAll("_autoclose")
|
||||||
os.Mkdir("_autoclose", 0755)
|
os.Mkdir("_autoclose", 0o755)
|
||||||
file := filepath.FromSlash("_autoclose/tmp")
|
file := filepath.FromSlash("_autoclose/tmp")
|
||||||
data := []byte("hello, world\n")
|
data := []byte("hello, world\n")
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,10 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/syncthing/syncthing/lib/build"
|
||||||
|
"github.com/syncthing/syncthing/lib/locations"
|
||||||
"github.com/syncthing/syncthing/lib/protocol"
|
"github.com/syncthing/syncthing/lib/protocol"
|
||||||
|
"golang.org/x/exp/constraints"
|
||||||
)
|
)
|
||||||
|
|
||||||
func startPerfStats() {
|
func startPerfStats() {
|
||||||
@@ -29,37 +32,73 @@ func savePerfStats(file string) {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var prevUsage int64
|
var prevTime time.Time
|
||||||
var prevTime int64
|
var curRus, prevRus syscall.Rusage
|
||||||
var rusage syscall.Rusage
|
var curMem, prevMem runtime.MemStats
|
||||||
var memstats runtime.MemStats
|
|
||||||
var prevIn, prevOut int64
|
var prevIn, prevOut int64
|
||||||
|
|
||||||
t0 := time.Now()
|
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 {
|
for t := range time.NewTicker(250 * time.Millisecond).C {
|
||||||
if err := syscall.Getrusage(syscall.RUSAGE_SELF, &rusage); err != nil {
|
syscall.Getrusage(syscall.RUSAGE_SELF, &curRus)
|
||||||
continue
|
runtime.ReadMemStats(&curMem)
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
in, out := protocol.TotalInOut()
|
in, out := protocol.TotalInOut()
|
||||||
var inRate, outRate float64
|
timeDiff := t.Sub(prevTime)
|
||||||
if timeDiff > 0 {
|
|
||||||
inRate = float64(in-prevIn) / (float64(timeDiff) / 1e9) // bytes per second
|
rss := curRus.Maxrss
|
||||||
outRate = float64(out-prevOut) / (float64(timeDiff) / 1e9) // bytes per second
|
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),
|
||||||
|
dirsize(locations.Get(locations.Database))/1024,
|
||||||
|
)
|
||||||
|
|
||||||
|
prevTime = t
|
||||||
|
prevRus = curRus
|
||||||
|
prevMem = curMem
|
||||||
prevIn, prevOut = in, out
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
func dirsize(location string) int64 {
|
||||||
|
entries, err := os.ReadDir(location)
|
||||||
|
if err != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
var size int64
|
||||||
|
for _, entry := range entries {
|
||||||
|
fi, err := entry.Info()
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
size += fi.Size()
|
||||||
|
}
|
||||||
|
|
||||||
|
return size
|
||||||
|
}
|
||||||
+13
@@ -26,3 +26,16 @@
|
|||||||
darwin: "20"
|
darwin: "20"
|
||||||
linux: "2.6.32"
|
linux: "2.6.32"
|
||||||
windows: "10.0"
|
windows: "10.0"
|
||||||
|
|
||||||
|
- runtime: go1.24
|
||||||
|
requirements:
|
||||||
|
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=Start Syncthing
|
Name=Start Syncthing
|
||||||
GenericName=File synchronization
|
GenericName=File synchronization
|
||||||
Comment=Starts the main syncthing process in the background.
|
Comment=Starts the main syncthing process in the background.
|
||||||
Exec=/usr/bin/syncthing serve --no-browser --logfile=default
|
Exec=syncthing serve --no-browser --logfile=default
|
||||||
Icon=syncthing
|
Icon=syncthing
|
||||||
Terminal=false
|
Terminal=false
|
||||||
Type=Application
|
Type=Application
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Name=Syncthing Web UI
|
Name=Syncthing Web UI
|
||||||
GenericName=File synchronization UI
|
GenericName=File synchronization UI
|
||||||
Comment=Opens Syncthing's Web UI in the default browser (Syncthing must already be started).
|
Comment=Opens Syncthing's Web UI in the default browser (Syncthing must already be started).
|
||||||
Exec=/usr/bin/syncthing --browser-only
|
Exec=syncthing browser
|
||||||
Icon=syncthing
|
Icon=syncthing
|
||||||
Terminal=false
|
Terminal=false
|
||||||
Type=Application
|
Type=Application
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ StartLimitBurst=4
|
|||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
User=%i
|
User=%i
|
||||||
ExecStart=/usr/bin/syncthing serve --no-browser --no-restart --logflags=0
|
ExecStart=/usr/bin/syncthing serve --no-browser --no-restart
|
||||||
Restart=on-failure
|
Restart=on-failure
|
||||||
RestartSec=1
|
RestartSec=1
|
||||||
SuccessExitStatus=3 4
|
SuccessExitStatus=3 4
|
||||||
|
|||||||
@@ -18,4 +18,4 @@ env STNORESTART=yes
|
|||||||
respawn
|
respawn
|
||||||
|
|
||||||
# the syncthing command Upstart is to execute when it is started up
|
# the syncthing command Upstart is to execute when it is started up
|
||||||
exec $SYNCTHING_EXE -no-browser
|
exec $SYNCTHING_EXE --no-browser
|
||||||
@@ -1,101 +1,122 @@
|
|||||||
module github.com/syncthing/syncthing
|
module github.com/syncthing/syncthing
|
||||||
|
|
||||||
go 1.22.0
|
go 1.24.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/AudriusButkevicius/recli v0.0.7-0.20220911121932-d000ce8fbf0f
|
github.com/AudriusButkevicius/recli v0.0.7
|
||||||
github.com/alecthomas/kong v1.2.1
|
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.2
|
||||||
github.com/aws/aws-sdk-go v1.55.5
|
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/incontainer v1.0.0
|
||||||
github.com/calmh/xdr v1.2.0
|
github.com/calmh/xdr v1.2.0
|
||||||
github.com/ccding/go-stun v0.1.5
|
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/d4l3k/messagediff v1.2.1
|
||||||
github.com/getsentry/raven-go v0.2.0
|
github.com/getsentry/raven-go v0.2.0
|
||||||
github.com/go-ldap/ldap/v3 v3.4.8
|
github.com/go-ldap/ldap/v3 v3.4.11
|
||||||
github.com/gobwas/glob v0.2.3
|
github.com/gobwas/glob v0.2.3
|
||||||
github.com/gogo/protobuf v1.3.2
|
github.com/gofrs/flock v0.12.1
|
||||||
github.com/greatroar/blobloom v0.8.0
|
|
||||||
github.com/hashicorp/golang-lru/v2 v2.0.7
|
github.com/hashicorp/golang-lru/v2 v2.0.7
|
||||||
github.com/jackpal/gateway v1.0.15
|
github.com/jackpal/gateway v1.0.16
|
||||||
github.com/jackpal/go-nat-pmp v1.0.2
|
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/julienschmidt/httprouter v1.3.0
|
||||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
|
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
|
||||||
github.com/maruel/panicparse/v2 v2.3.1
|
github.com/maruel/panicparse/v2 v2.5.0
|
||||||
github.com/maxbrunsfeld/counterfeiter/v6 v6.8.1
|
github.com/mattn/go-sqlite3 v1.14.31
|
||||||
github.com/maxmind/geoipupdate/v6 v6.1.0
|
github.com/maxmind/geoipupdate/v6 v6.1.0
|
||||||
github.com/miscreant/miscreant.go v0.0.0-20200214223636-26d376326b75
|
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.21
|
github.com/pierrec/lz4/v4 v4.1.22
|
||||||
github.com/prometheus/client_golang v1.20.4
|
github.com/prometheus/client_golang v1.23.0
|
||||||
github.com/puzpuzpuz/xsync/v3 v3.4.0
|
github.com/puzpuzpuz/xsync/v3 v3.5.1
|
||||||
github.com/quic-go/quic-go v0.47.0
|
github.com/quic-go/quic-go v0.52.0
|
||||||
github.com/rabbitmq/amqp091-go v1.10.0
|
github.com/rabbitmq/amqp091-go v1.10.0
|
||||||
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475
|
github.com/rcrowley/go-metrics v0.0.0-20250401214520-65e299d6c5c9
|
||||||
github.com/shirou/gopsutil/v4 v4.24.9
|
github.com/shirou/gopsutil/v4 v4.25.6 // https://github.com/shirou/gopsutil/issues/1898
|
||||||
github.com/syncthing/notify v0.0.0-20210616190510-c6b7342338d2
|
github.com/syncthing/notify v0.0.0-20250528144937-c7027d4f7465
|
||||||
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d
|
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d
|
||||||
github.com/thejerf/suture/v4 v4.0.5
|
github.com/thejerf/suture/v4 v4.0.6
|
||||||
github.com/urfave/cli v1.22.15
|
github.com/urfave/cli v1.22.17
|
||||||
github.com/vitrun/qart v0.0.0-20160531060029-bf64b92db6b0
|
github.com/vitrun/qart v0.0.0-20160531060029-bf64b92db6b0
|
||||||
github.com/willabides/kongplete v0.4.0
|
github.com/willabides/kongplete v0.4.0
|
||||||
|
github.com/wlynxg/anet v0.0.5
|
||||||
go.uber.org/automaxprocs v1.6.0
|
go.uber.org/automaxprocs v1.6.0
|
||||||
golang.org/x/crypto v0.27.0
|
golang.org/x/crypto v0.41.0
|
||||||
golang.org/x/net v0.29.0
|
golang.org/x/exp v0.0.0-20250811191247-51f88131bc50
|
||||||
golang.org/x/sys v0.25.0
|
golang.org/x/net v0.43.0
|
||||||
golang.org/x/text v0.18.0
|
golang.org/x/sys v0.35.0
|
||||||
golang.org/x/time v0.6.0
|
golang.org/x/text v0.28.0
|
||||||
golang.org/x/tools v0.25.0
|
golang.org/x/time v0.12.0
|
||||||
google.golang.org/protobuf v1.34.2
|
google.golang.org/protobuf v1.36.7
|
||||||
sigs.k8s.io/yaml v1.4.0
|
modernc.org/sqlite v1.38.2
|
||||||
|
sigs.k8s.io/yaml v1.6.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
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/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||||
github.com/certifi/gocertifi v0.0.0-20210507211836-431795d63e8d // indirect
|
github.com/certifi/gocertifi v0.0.0-20210507211836-431795d63e8d // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.3.0 // 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/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/ebitengine/purego v0.8.0 // 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/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-ole/go-ole v1.3.0 // indirect
|
||||||
github.com/go-task/slim-sprig/v3 v3.0.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/golang/snappy v0.0.4 // indirect
|
||||||
github.com/google/pprof v0.0.0-20241001023024-f4c0cfd0cf1d // indirect
|
github.com/google/pprof v0.0.0-20250423184734-337e5dd93bb4 // indirect
|
||||||
github.com/google/uuid v1.6.0 // indirect
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||||
github.com/klauspost/compress v1.17.10 // indirect
|
|
||||||
github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683 // 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.11.3 // indirect
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // 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/nxadm/tail v1.4.11 // indirect
|
||||||
github.com/onsi/ginkgo/v2 v2.20.2 // indirect
|
github.com/onsi/ginkgo/v2 v2.23.4 // indirect
|
||||||
github.com/oschwald/maxminddb-golang v1.13.1 // indirect
|
github.com/oschwald/maxminddb-golang v1.13.1 // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/posener/complete v1.2.3 // indirect
|
github.com/posener/complete v1.2.3 // indirect
|
||||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
|
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
|
||||||
github.com/prometheus/client_model v0.6.1 // indirect
|
github.com/prometheus/client_model v0.6.2 // indirect
|
||||||
github.com/prometheus/common v0.60.0 // indirect
|
github.com/prometheus/common v0.65.0 // indirect
|
||||||
github.com/prometheus/procfs v0.15.1 // 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/riywo/loginshell v0.0.0-20200815045211-7d26008be1ab // indirect
|
||||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||||
github.com/stretchr/objx v0.5.2 // indirect
|
github.com/stretchr/objx v0.5.2 // indirect
|
||||||
github.com/stretchr/testify v1.9.0 // indirect
|
github.com/stretchr/testify v1.10.0 // indirect
|
||||||
github.com/tklauser/go-sysconf v0.3.14 // indirect
|
github.com/tklauser/go-sysconf v0.3.15 // indirect
|
||||||
github.com/tklauser/numcpus v0.8.0 // indirect
|
github.com/tklauser/numcpus v0.10.0 // indirect
|
||||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||||
go.uber.org/mock v0.4.0 // indirect
|
go.uber.org/mock v0.5.2 // indirect
|
||||||
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // indirect
|
go.yaml.in/yaml/v2 v2.4.2 // indirect
|
||||||
golang.org/x/mod v0.21.0 // indirect
|
golang.org/x/mod v0.27.0 // indirect
|
||||||
golang.org/x/sync v0.8.0 // indirect
|
golang.org/x/sync v0.16.0 // indirect
|
||||||
|
golang.org/x/tools v0.36.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // 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
|
// https://github.com/gobwas/glob/pull/55
|
||||||
replace github.com/gobwas/glob v0.2.3 => github.com/calmh/glob v0.0.0-20220615080505-1d823af5017b
|
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
|
||||||
|
)
|
||||||
@@ -1,22 +1,38 @@
|
|||||||
github.com/AudriusButkevicius/recli v0.0.7-0.20220911121932-d000ce8fbf0f h1:GmH5lT+moM7PbAJFBq57nH9WJ+wRnBXr/tyaYWbSAx8=
|
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||||
github.com/AudriusButkevicius/recli v0.0.7-0.20220911121932-d000ce8fbf0f/go.mod h1:Nhfib1j/VFnLrXL9cHgA+/n2O6P5THuWelOnbfPNd78=
|
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 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8=
|
||||||
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
|
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
|
||||||
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 h1:oygO0locgZJe7PpYPXT5A29ZkwJaPqcva7BVeemZOZs=
|
||||||
github.com/alecthomas/assert/v2 v2.10.0 h1:jjRCHsj6hBJhkmhznrCzoNpbA3zqy0fYiUcYZP/GkPY=
|
github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
|
||||||
github.com/alecthomas/assert/v2 v2.10.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
|
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||||
github.com/alecthomas/kong v1.2.1 h1:E8jH4Tsgv6wCRX2nGrdPyHDUCSG83WH2qE4XLACD33Q=
|
github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0=
|
||||||
github.com/alecthomas/kong v1.2.1/go.mod h1:rKTSFhbdp3Ryefn8x5MOEprnRFQ7nlmMC01GKhehhBM=
|
github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
|
||||||
|
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 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc=
|
||||||
github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
|
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 h1:LHTHcTQiSGT7VVbI0o4wBRNQIgn917usHWOd6VAffYI=
|
||||||
github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4=
|
github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4=
|
||||||
github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU=
|
github.com/aws/aws-sdk-go v1.55.8 h1:JRmEUbU52aJQZ2AjX4q4Wu7t4uZjOu71uyNmaWlUkJQ=
|
||||||
github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
|
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 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
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 h1:Fjm4GuJ+TGMgqfGHN42IQArJb77CfD/mAwLbDUoJe6g=
|
||||||
github.com/calmh/glob v0.0.0-20220615080505-1d823af5017b/go.mod h1:91K7jfEsgJSyfSrX+gmrRfZMtntx6JsHolWubGXDopg=
|
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 h1:g2cTUtZuFGmMGX8GoykPkN1Judj2uw8/3/aEtq4Z/rg=
|
||||||
github.com/calmh/incontainer v1.0.0/go.mod h1:eOhqnw15c9X+4RNBe0W3HlUZFfX16O0EDsCOInTndHY=
|
github.com/calmh/incontainer v1.0.0/go.mod h1:eOhqnw15c9X+4RNBe0W3HlUZFfX16O0EDsCOInTndHY=
|
||||||
github.com/calmh/xdr v1.2.0 h1:GaGSNH4ZDw9kNdYqle6+RcAENiaQ8/611Ok+jQbBEeU=
|
github.com/calmh/xdr v1.2.0 h1:GaGSNH4ZDw9kNdYqle6+RcAENiaQ8/611Ok+jQbBEeU=
|
||||||
@@ -29,21 +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/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 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
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/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/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/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc=
|
github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
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 h1:ZcAIMYsUg0EAp9X+tt8/enBE/Q8Yd5kzPynLyKptt9U=
|
||||||
github.com/d4l3k/messagediff v1.2.1/go.mod h1:Oozbb1TVXFac9FtSIxHBMnBCq2qeH/2KkEQxENCrlLo=
|
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.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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/ebitengine/purego v0.8.0 h1:JbqvnEzRvPpxhCJzJJ2y0RbiZ8nyjccVUrSM3q+GvvE=
|
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||||
github.com/ebitengine/purego v0.8.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
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.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.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||||
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
|
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
|
||||||
@@ -52,23 +69,24 @@ 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/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 h1:no+xWJRb5ZI7eE8TWgIq1jLulQiIoLG0IfYxv5JYMGs=
|
||||||
github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
|
github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
|
||||||
github.com/go-asn1-ber/asn1-ber v1.5.5/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
|
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.7 h1:DTX+lbVTWaTw1hQ+PbZPlnDZPEIs0SS/GCZAl535dDk=
|
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
|
||||||
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.11 h1:4k0Yxweg+a3OyBLjdYn5OKglv18JNvfDykSoI8bW0gU=
|
||||||
github.com/go-ldap/ldap/v3 v3.4.8 h1:loKJyspcRezt2Q3ZRMq2p/0v8iOurlmeXDPw6fikSvQ=
|
github.com/go-ldap/ldap/v3 v3.4.11/go.mod h1:bY7t0FLK8OAVpp/vV6sSlpz3EQDGcQwc8pF0ujLgKvM=
|
||||||
github.com/go-ldap/ldap/v3 v3.4.8/go.mod h1:qS3Sjlu76eHfHGpUdWkAXQTw4beih+cHsco2jXlIXrk=
|
|
||||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
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-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
github.com/go-ole/go-ole v1.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 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
|
||||||
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
|
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 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 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
||||||
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
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 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E=
|
||||||
github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0=
|
github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0=
|
||||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
github.com/golang-jwt/jwt/v5 v5.2.3 h1:kkGXqQOBSDDWRhWNXTFpqGSCMyh/PLnqUvMGJPDJDs0=
|
||||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
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.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/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||||
@@ -84,26 +102,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.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.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.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
|
|
||||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
|
||||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
|
||||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/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-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||||
github.com/google/pprof v0.0.0-20241001023024-f4c0cfd0cf1d h1:Jaz2JzpQaQXyET0AjLBXShrthbpqMkhGiEfkcQAiAUs=
|
github.com/google/pprof v0.0.0-20250423184734-337e5dd93bb4 h1:gD0vax+4I+mAj+jEChEf25Ia07Jq7kYOFO5PPhAxFl4=
|
||||||
github.com/google/pprof v0.0.0-20241001023024-f4c0cfd0cf1d/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
|
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 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
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.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 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
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.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 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
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 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
|
||||||
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
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=
|
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
|
||||||
@@ -112,8 +124,8 @@ github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUq
|
|||||||
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
|
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||||
github.com/jackpal/gateway v1.0.15 h1:yb4Gltgr8ApHWWnSyybnDL1vURbqw7ooo7IIL5VZSeg=
|
github.com/jackpal/gateway v1.0.16 h1:mTBRuHSW8qviVqX7kXnxKevqlfS/OA01ys6k6fxSX7w=
|
||||||
github.com/jackpal/gateway v1.0.15/go.mod h1:dbyEDcDhHUh9EmjB9ung81elMUZfG0SoNc2TfTbcj4c=
|
github.com/jackpal/gateway v1.0.16/go.mod h1:IOn1OUbso/cGYmnCBZbCEqhNCLSz0xxdtIpUpri5/nA=
|
||||||
github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus=
|
github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus=
|
||||||
github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
|
github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
|
||||||
github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8=
|
github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8=
|
||||||
@@ -132,35 +144,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 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 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
|
||||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
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 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
|
||||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
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 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
|
||||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||||
github.com/klauspost/compress v1.17.10 h1:oXAz+Vh0PMUvJczoi+flxpnBEPxoER1IaAnU/NMPtT0=
|
|
||||||
github.com/klauspost/compress v1.17.10/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
|
|
||||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
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 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
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 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
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 h1:7UMa6KCCMjZEMDtTVdcGu0B1GmmC7QJKiCCjyTAWQy0=
|
||||||
github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k=
|
github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k=
|
||||||
github.com/maruel/panicparse/v2 v2.3.1 h1:NtJavmbMn0DyzmmSStE8yUsmPZrZmudPH7kplxBinOA=
|
github.com/maruel/panicparse/v2 v2.5.0 h1:yCtuS0FWjfd0RTYMXGpDvWcb0kINm8xJGu18/xMUh00=
|
||||||
github.com/maruel/panicparse/v2 v2.3.1/go.mod h1:s3UmQB9Fm/n7n/prcD2xBGDkwXD6y2LeZnhbEXvs9Dg=
|
github.com/maruel/panicparse/v2 v2.5.0/go.mod h1:DA2fDiBk63bKfBf4CVZP9gb4fuvzdPbLDsSI873hweQ=
|
||||||
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
github.com/maxbrunsfeld/counterfeiter/v6 v6.8.1 h1:NicmruxkeqHjDv03SfSxqmaLuisddudfP3h5wdXFbhM=
|
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||||
github.com/maxbrunsfeld/counterfeiter/v6 v6.8.1/go.mod h1:eyp4DdUJAKkr9tvxR3jWhw2mDK7CWABMG5r9uyaKC7I=
|
github.com/maxbrunsfeld/counterfeiter/v6 v6.11.3 h1:Eaq36EIyJNp7b3qDhjV7jmDVq/yPeW2v4pTqzGbOGB4=
|
||||||
|
github.com/maxbrunsfeld/counterfeiter/v6 v6.11.3/go.mod h1:6KKUoQBZBW6PDXJtNfqeEjPXMj/ITTk+cWK9t9uS5+E=
|
||||||
github.com/maxmind/geoipupdate/v6 v6.1.0 h1:sdtTHzzQNJlXF5+fd/EoPTucRHyMonYt/Cok8xzzfqA=
|
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/maxmind/geoipupdate/v6 v6.1.0/go.mod h1:cZYCDzfMzTY4v6dKRdV7KTB6SStxtn3yFkiJ1btTGGc=
|
||||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
|
||||||
github.com/miscreant/miscreant.go v0.0.0-20200214223636-26d376326b75 h1:cUVxyR+UfmdEAZGJ8IiKld1O0dbGotEnkMolG5hfMSY=
|
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/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 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
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.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||||
github.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY=
|
github.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY=
|
||||||
@@ -171,20 +187,22 @@ 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 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
||||||
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
|
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.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.23.4 h1:ktYTpKJAVZnDT4VjxSbiBenUjmlL/5QkBEocaWXiQus=
|
||||||
github.com/onsi/ginkgo/v2 v2.20.2/go.mod h1:K9gyxPIlb+aIvnZ8bd9Ak+YP18w3APlR+5coaZoE2ag=
|
github.com/onsi/ginkgo/v2 v2.23.4/go.mod h1:Bt66ApGPBFzHyR+JO10Zbt0Gsp4uWxu5mIOTusL46e8=
|
||||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
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.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.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
|
||||||
github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro=
|
github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro=
|
||||||
github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
|
github.com/onsi/gomega v1.37.0 h1:CdEG8g0S133B4OswTDC/5XPSzE1OeP29QOioj2PID2Y=
|
||||||
github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
|
github.com/onsi/gomega v1.37.0/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0=
|
||||||
github.com/oschwald/geoip2-golang v1.11.0 h1:hNENhCn1Uyzhf9PTmquXENiWS6AlxAEnBII6r8krA3w=
|
github.com/oschwald/geoip2-golang v1.13.0 h1:Q44/Ldc703pasJeP5V9+aFSZFmBN7DKHbNsSFzQATJI=
|
||||||
github.com/oschwald/geoip2-golang v1.11.0/go.mod h1:P9zG+54KPEFOliZ29i7SeYZ/GM6tfEL+rgSn03hYuUo=
|
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 h1:G3wwjdN9JmIK2o/ermkHM+98oX5fS+k5MbwsmL4MRQE=
|
||||||
github.com/oschwald/maxminddb-golang v1.13.1/go.mod h1:K4pgV9N/GcK694KSTmVSDTODk4IsCNThNdTmnaBZ/F8=
|
github.com/oschwald/maxminddb-golang v1.13.1/go.mod h1:K4pgV9N/GcK694KSTmVSDTODk4IsCNThNdTmnaBZ/F8=
|
||||||
github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ=
|
github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU=
|
||||||
github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
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.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
@@ -196,32 +214,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/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 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
|
||||||
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
|
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
|
||||||
github.com/prometheus/client_golang v1.20.4 h1:Tgh3Yr67PaOv/uTqloMsCEdeuFTatm5zIq5+qNN23vI=
|
github.com/prometheus/client_golang v1.23.0 h1:ust4zpdl9r4trLY/gSjlm07PuiBq2ynaXXlptpfy8Uc=
|
||||||
github.com/prometheus/client_golang v1.20.4/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
|
github.com/prometheus/client_golang v1.23.0/go.mod h1:i/o0R9ByOnHX0McrTMTyhYvKE4haaf2mW08I+jGAjEE=
|
||||||
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
|
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
|
||||||
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
|
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
||||||
github.com/prometheus/common v0.60.0 h1:+V9PAREWNvJMAuJ1x1BaWl9dewMW4YrHZQbx0sJNllA=
|
github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE=
|
||||||
github.com/prometheus/common v0.60.0/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw=
|
github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8=
|
||||||
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
|
github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
|
||||||
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
|
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
|
||||||
github.com/puzpuzpuz/xsync/v3 v3.4.0 h1:DuVBAdXuGFHv8adVXjWWZ63pJq+NRXOWVXlKDBZ+mJ4=
|
github.com/puzpuzpuz/xsync/v3 v3.5.1 h1:GJYJZwO6IdxN/IKbneznS6yPkVC+c3zyY/j19c++5Fg=
|
||||||
github.com/puzpuzpuz/xsync/v3 v3.4.0/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA=
|
github.com/puzpuzpuz/xsync/v3 v3.5.1/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA=
|
||||||
github.com/quic-go/quic-go v0.47.0 h1:yXs3v7r2bm1wmPTYNLKAAJTHMYkPEsfYJmTazXrCZ7Y=
|
github.com/quic-go/quic-go v0.52.0 h1:/SlHrCRElyaU6MaEPKqKr9z83sBg2v4FLLvWM+Z47pA=
|
||||||
github.com/quic-go/quic-go v0.47.0/go.mod h1:3bCapYsJvXGZcipOHuu7plYtaV6tnF+z7wIFsU0WK9E=
|
github.com/quic-go/quic-go v0.52.0/go.mod h1:MFlGGpcpJqRAfmYi6NC2cptDPSxRWTOGNuP4wqrWmzQ=
|
||||||
github.com/rabbitmq/amqp091-go v1.10.0 h1:STpn5XsHlHGcecLmMFCtg7mqq0RnD+zFr4uzukfVhBw=
|
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/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-20250401214520-65e299d6c5c9 h1:bsUq1dX0N8AOIL7EB/X911+m4EHsnWEHeJ0c+3TTBrg=
|
||||||
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/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 h1:ZjX6I48eZSFetPb41dHudEyVr5v953N15TsNZXlkcWY=
|
||||||
github.com/riywo/loginshell v0.0.0-20200815045211-7d26008be1ab/go.mod h1:/PfPXh0EntGc3QAAyUaviy4S9tzy4Zp0e2ilq4voC6E=
|
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.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
||||||
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
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 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
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 h1:z/Q9idDcay5m5irkZ28M7PtQM4aOISzOpj4bUPkDee8=
|
||||||
github.com/sclevine/spec v1.4.0/go.mod h1:LvpgJaFyvQzRvc1kaDs0bulYwzC70PbiYjC4QnFHkOM=
|
github.com/sclevine/spec v1.4.0/go.mod h1:LvpgJaFyvQzRvc1kaDs0bulYwzC70PbiYjC4QnFHkOM=
|
||||||
github.com/shirou/gopsutil/v4 v4.24.9 h1:KIV+/HaHD5ka5f570RZq+2SaeFsb/pq+fp2DGNWYoOI=
|
github.com/shirou/gopsutil/v4 v4.25.6 h1:kLysI2JsKorfaFPcYmcJqbzROzsBWEOAtw6A7dIfqXs=
|
||||||
github.com/shirou/gopsutil/v4 v4.24.9/go.mod h1:3fkaHNeYsUFCGZ8+9vZVWtbyM1k2eRnlL+bWO8Bxa/Q=
|
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.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.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
@@ -232,82 +252,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.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.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.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.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/syncthing/notify v0.0.0-20210616190510-c6b7342338d2 h1:F4snRP//nIuTTW9LYEzVH4HVwDG9T3M4t8y/2nqMbiY=
|
github.com/syncthing/notify v0.0.0-20250528144937-c7027d4f7465 h1:yhxdTGmFkAM2TFA65c3NgGwpnIkUM8oVqPX2e9S7IVg=
|
||||||
github.com/syncthing/notify v0.0.0-20210616190510-c6b7342338d2/go.mod h1:J0q59IWjLtpRIJulohwqEZvjzwOfTEPp8SVhDJl+y0Y=
|
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 h1:vfofYNRScrDdvS342BElfbETmL1Aiz3i2t0zfRj16Hs=
|
||||||
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48=
|
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48=
|
||||||
github.com/thejerf/suture/v4 v4.0.5 h1:F1E/4FZwXWqvlWDKEUo6/ndLtxGAUzMmNqkrMknZbAA=
|
github.com/thejerf/suture/v4 v4.0.6 h1:QsuCEsCqb03xF9tPAsWAj8QOAJBgQI1c0VqJNaingg8=
|
||||||
github.com/thejerf/suture/v4 v4.0.5/go.mod h1:gu9Y4dXNUWFrByqRt30Rm9/UZ0wzRSt9AJS6xu/ZGxU=
|
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.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4=
|
||||||
github.com/tklauser/go-sysconf v0.3.14/go.mod h1:1ym4lWMLUOhuBOPGtRcJm7tEGX4SCYNEEEtghGG/8uY=
|
github.com/tklauser/go-sysconf v0.3.15/go.mod h1:Dmjwr6tYFIseJw7a3dRLJfsHAMXZ3nEnL/aZY+0IuI4=
|
||||||
github.com/tklauser/numcpus v0.8.0 h1:Mx4Wwe/FjZLeQsK/6kt2EOepwwSl7SmJrK5bV/dXYgY=
|
github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfjso=
|
||||||
github.com/tklauser/numcpus v0.8.0/go.mod h1:ZJZlAY+dmR4eut8epnzf0u/VwodKmryxR8txiloSqBE=
|
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.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||||
github.com/urfave/cli v1.22.15 h1:nuqt+pdC/KqswQKhETJjo7pvn/k4xMUxgW6liI7XpnM=
|
github.com/urfave/cli v1.22.17 h1:SYzXoiPfQjHBbkYxbew5prZHS1TOLT3ierW8SYLqtVQ=
|
||||||
github.com/urfave/cli v1.22.15/go.mod h1:wSan1hmo5zeyLGBjRJbzRTNk8gwoYa2B9n4q9dmRIc0=
|
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 h1:okhMind4q9H1OxF44gNegWkiP4H/gsTFLalHFa4OOUI=
|
||||||
github.com/vitrun/qart v0.0.0-20160531060029-bf64b92db6b0/go.mod h1:TTbGUfE+cXXceWtbTHq6lqcTvYPBKLNejBEbnUsQJtU=
|
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 h1:eivXxkp5ud5+4+NVN9e4goxC5mSh3n1RHov+gsblM2g=
|
||||||
github.com/willabides/kongplete v0.4.0/go.mod h1:0P0jtWD9aTsqPSUAl4de35DLghrr57XcayPyvqSi2X8=
|
github.com/willabides/kongplete v0.4.0/go.mod h1:0P0jtWD9aTsqPSUAl4de35DLghrr57XcayPyvqSi2X8=
|
||||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
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.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 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
||||||
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
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 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
|
||||||
go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
|
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 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||||
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
|
go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko=
|
||||||
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
|
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.3 h1:bXOww4E/J3f66rav3pX3m8w6jDE4knZjGOw8b5Y6iNE=
|
||||||
|
go.yaml.in/yaml/v3 v3.0.3/go.mod h1:tBHosrYAkRZjRAOREWbDnBXUf08JOwYq++0QNwQiWzI=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
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-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-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.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
|
||||||
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
|
||||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
golang.org/x/exp v0.0.0-20250811191247-51f88131bc50 h1:3yiSh9fhy5/RhCSntf4Sy0Tnx50DmMpQ4MQdKKk4yg4=
|
||||||
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
golang.org/x/exp v0.0.0-20250811191247-51f88131bc50/go.mod h1:rT6SFzZ7oxADUDx58pcaKFTcZ+inxAa9fTrYx/uVYwg=
|
||||||
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
|
|
||||||
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
|
|
||||||
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk=
|
|
||||||
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY=
|
|
||||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
|
||||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.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.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ=
|
||||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc=
|
||||||
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
|
|
||||||
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
|
||||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
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-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-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-20200226121028-0de0cce0169b/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-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-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-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-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-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.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
|
||||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
|
||||||
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.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
|
||||||
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
|
||||||
golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo=
|
|
||||||
golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
|
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
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-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-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.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
|
||||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
|
||||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
|
||||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
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-20180926160741-c2ed4eda69e7/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
@@ -324,47 +328,29 @@ golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f/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-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-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.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.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
|
||||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
|
||||||
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
|
|
||||||
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-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.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.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
|
||||||
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
|
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
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.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.6/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.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.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
|
||||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
|
||||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
|
||||||
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
|
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||||
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
|
||||||
golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
|
|
||||||
golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-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-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
|
||||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg=
|
||||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s=
|
||||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
|
||||||
golang.org/x/tools v0.25.0 h1:oFU9pkj/iJgs+0DT+VMHrx+oBKs/LJMV+Uvg78sl+fE=
|
|
||||||
golang.org/x/tools v0.25.0/go.mod h1:/vtpO8WL1N9cQC3FN5zPqb//fRXskFHbLKk4OW1Q7rg=
|
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
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-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
@@ -378,8 +364,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.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-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.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
google.golang.org/protobuf v1.36.7 h1:IgrO7UwFQGJdRNXH/sQux4R1Dj1WAKcLElzeeRaXV2A=
|
||||||
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
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 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 h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
@@ -395,5 +381,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.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 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
|
modernc.org/cc/v4 v4.26.2 h1:991HMkLjJzYBIfha6ECZdjrIYz2/1ayr+FL8GN+CNzM=
|
||||||
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
|
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=
|
||||||
@@ -224,7 +224,6 @@ code.ng-binding{
|
|||||||
}
|
}
|
||||||
|
|
||||||
.well, .form-control[readonly="readonly"], .popover { /* read-only fields*/
|
.well, .form-control[readonly="readonly"], .popover { /* read-only fields*/
|
||||||
color: #666 !important;
|
|
||||||
border-color: #444 !important;
|
border-color: #444 !important;
|
||||||
background-color: #111 !important;
|
background-color: #111 !important;
|
||||||
}
|
}
|
||||||
@@ -278,3 +277,17 @@ code.ng-binding{
|
|||||||
.reception {
|
.reception {
|
||||||
filter: invert(77%) sepia(0%) saturate(724%) hue-rotate(146deg) brightness(91%) contrast(85%);
|
filter: invert(77%) sepia(0%) saturate(724%) hue-rotate(146deg) brightness(91%) contrast(85%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Disabled checkbox panels */
|
||||||
|
|
||||||
|
.checkbox[disabled] {
|
||||||
|
background-color: #222222;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox[disabled] * {
|
||||||
|
color: #666666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox[disabled] .help-block {
|
||||||
|
color: #666666 !important;
|
||||||
|
}
|
||||||
@@ -228,7 +228,6 @@ code.ng-binding{
|
|||||||
}
|
}
|
||||||
|
|
||||||
.well, .form-control[readonly="readonly"], .popover { /* read-only fields*/
|
.well, .form-control[readonly="readonly"], .popover { /* read-only fields*/
|
||||||
color: #666 !important;
|
|
||||||
border-color: #424242 !important;
|
border-color: #424242 !important;
|
||||||
background-color: #3B3B3B !important;
|
background-color: #3B3B3B !important;
|
||||||
}
|
}
|
||||||
@@ -289,4 +288,18 @@ code.ng-binding{
|
|||||||
/* Remote Devices 'connection type'-icon color set to #aaa */
|
/* Remote Devices 'connection type'-icon color set to #aaa */
|
||||||
.reception {
|
.reception {
|
||||||
filter: invert(77%) sepia(0%) saturate(724%) hue-rotate(146deg) brightness(91%) contrast(85%);
|
filter: invert(77%) sepia(0%) saturate(724%) hue-rotate(146deg) brightness(91%) contrast(85%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Disabled checkbox panels */
|
||||||
|
|
||||||
|
.checkbox[disabled] {
|
||||||
|
background-color: #3B3B3B;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox[disabled] * {
|
||||||
|
color: #999999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox[disabled] .help-block {
|
||||||
|
color: #999999 !important;
|
||||||
|
}
|
||||||
@@ -448,7 +448,6 @@ ul.three-columns li, ul.two-columns li {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width:479px) {
|
@media (max-width:479px) {
|
||||||
|
|
||||||
nav .dropdown-toggle {
|
nav .dropdown-toggle {
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
}
|
}
|
||||||
@@ -456,13 +455,7 @@ ul.three-columns li, ul.two-columns li {
|
|||||||
.navbar-nav .open .dropdown-menu > li > a {
|
.navbar-nav .open .dropdown-menu > li > a {
|
||||||
padding: 12px 15px 12px 25px;
|
padding: 12px 15px 12px 25px;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.tab-content {
|
|
||||||
padding-top: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 419px) {
|
|
||||||
/* The selectors are build to target only the content of folder and device
|
/* The selectors are build to target only the content of folder and device
|
||||||
panels as it would "destroy" e.g. out of sync or recent changes listings.
|
panels as it would "destroy" e.g. out of sync or recent changes listings.
|
||||||
The !important is needed to override .visible-xs that sets display to a
|
The !important is needed to override .visible-xs that sets display to a
|
||||||
@@ -513,6 +506,10 @@ ul.three-columns li, ul.two-columns li {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tab-content {
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
.form-horizontal .form-group {
|
.form-horizontal .form-group {
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"A device with that ID is already added.": "أضيف هذا الجهاز بالفعل.",
|
"A device with that ID is already added.": "أضيف هذا الجهاز بالفعل.",
|
||||||
"A negative number of days doesn't make sense.": "لا يمكن استخدام قيمة سالبة لعدد الأيام.",
|
"A negative number of days doesn't make sense.": "لا يمكن استخدام قيمة سالبة لعدد الأيام.",
|
||||||
"A new major version may not be compatible with previous versions.": "الإصدار الجديد قد لا يتوافق مع الإصدارات السابقة.",
|
"A new major version may not be compatible with previous versions.": "الإصدار الجديد قد لا يتوافق مع الإصدارات السابقة.",
|
||||||
"API Key": "مفتاح API",
|
"API Key": "مفتاح واجهة برمجة التطبيقات \"API\"",
|
||||||
"About": "حول",
|
"About": "حول",
|
||||||
"Action": "إجراء",
|
"Action": "إجراء",
|
||||||
"Actions": "الإجراءات",
|
"Actions": "الإجراءات",
|
||||||
@@ -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%}\".": "مفعل دائمًا عندما يكون نوع المجلد هو \"{{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%}\".": "لا يمكن تفعيله عندما يكون نوع المجلد هو \"{{foldertype}}\".",
|
||||||
"Changelog": "سجل التغيير",
|
"Changelog": "سجل التغيير",
|
||||||
"Clean out after": "نظف بعد",
|
"Clean out after": "نظف بعد",
|
||||||
"Cleaning Versions": "إصدارات نظيفة",
|
"Cleaning Versions": "إصدارات نظيفة",
|
||||||
@@ -154,7 +156,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 set up, retrying": "فشل الإعداد، تجري المحاولة مرة أخرى",
|
||||||
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "يُتوقع فشل الاتصال بخوادم IPv6، إذا لم يكن IPv6 متاحا.",
|
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "يُتوقع فشل الاتصال بخوادم IPv6، إذا لم يكن IPv6 متاحا.",
|
||||||
"File Pull Order": "ترتيب استيراد الملفات",
|
"File Pull Order": "ترتيب استيراد الملفات",
|
||||||
"File Versioning": "إصدارات الملف",
|
"File Versioning": "إصدارات الملف",
|
||||||
@@ -368,7 +370,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": "إغلاق",
|
"Shut Down": "إغلاق",
|
||||||
"Shutdown Complete": "أُغلِق",
|
"Shutdown Complete": "أُغلِق",
|
||||||
"Simple": "بسيط",
|
"Simple": "بسيط",
|
||||||
"Simple File Versioning": "التقسيم البسيط لإصدارات الملفات",
|
"Simple File Versioning": "التقسيم البسيط لإصدارات الملفات",
|
||||||
|
|||||||
@@ -152,7 +152,7 @@
|
|||||||
"Show ID": "Паказаць ID",
|
"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 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.",
|
"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": "Выключэньне завершанае",
|
"Shutdown Complete": "Выключэньне завершанае",
|
||||||
"Simple File Versioning": "Простае захоўваньне вэрсій",
|
"Simple File Versioning": "Простае захоўваньне вэрсій",
|
||||||
"Single level wildcard (matches within a directory only)": "Single level wildcard (matches within a directory only)",
|
"Single level wildcard (matches within a directory only)": "Single level wildcard (matches within a directory only)",
|
||||||
|
|||||||
@@ -22,11 +22,12 @@
|
|||||||
"Advanced Configuration": "Разширени настройки",
|
"Advanced Configuration": "Разширени настройки",
|
||||||
"All Data": "Всички данни",
|
"All Data": "Всички данни",
|
||||||
"All Time": "През цялото време",
|
"All Time": "През цялото време",
|
||||||
"All folders shared with this device must be protected by a password, such that all sent data is unreadable without the given password.": "Всички папки, споделени с устройството трябва да бъдат защитени с парола, така че данните да са недостъпни без нея.",
|
"All folders shared with this device must be protected by a password, such that all sent data is unreadable without the given password.": "Папките, споделени с устройството трябва да бъдат защитени с парола, така че данните да са недостъпни без нея.",
|
||||||
"Allow Anonymous Usage Reporting?": "Разрешавате ли анонимно отчитане на употребата?",
|
"Allow Anonymous Usage Reporting?": "Разрешавате ли анонимно отчитане на употребата?",
|
||||||
"Allowed Networks": "Разрешени мрежи",
|
"Allowed Networks": "Разрешени мрежи",
|
||||||
"Alphabetic": "Азбучен ред",
|
"Alphabetic": "Азбучен ред",
|
||||||
"Altered by ignoring deletes.": "Промяна чрез пренебрегване на премахваните елементи.",
|
"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.": "Външна команда управлява версиите. Тя трябва да премахне файла от синхронизираната папка. Ако в пътя до приложението има интервали, то той трябва да бъде поставен в кавички.",
|
"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%}\".": "Не мож да бъде включено, ако вида на папката е „{{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": "Отстраняване на дефекти",
|
||||||
"Debugging Facilities": "Отстраняване на дефекти",
|
"Debugging Facilities": "Отстраняване на дефекти",
|
||||||
"Default": "По подразбиране",
|
"Default": "По подразбиране",
|
||||||
"Default Configuration": "Настройки по подразбиране",
|
"Default Configuration": "Настройки по подразбиране",
|
||||||
@@ -111,7 +114,7 @@
|
|||||||
"Disabled periodic scanning and failed setting up watching for changes, retrying every 1m:": "Изключено периодично обхождане и грешка при започване на наблюдението за промени, прави се опит всяка минута:",
|
"Disabled periodic scanning and failed setting up watching for changes, retrying every 1m:": "Изключено периодично обхождане и грешка при започване на наблюдението за промени, прави се опит всяка минута:",
|
||||||
"Disables comparing and syncing file permissions. Useful on systems with nonexistent or custom permissions (e.g. FAT, exFAT, Synology, Android).": "Изключва сравняването и синхронизацията на правата на файловете. Полезно за системи с липсващи или специфични права (като FAT, exFAT, Synology, Android).",
|
"Disables comparing and syncing file permissions. Useful on systems with nonexistent or custom permissions (e.g. FAT, exFAT, Synology, Android).": "Изключва сравняването и синхронизацията на правата на файловете. Полезно за системи с липсващи или специфични права (като FAT, exFAT, Synology, Android).",
|
||||||
"Discard": "Отказване",
|
"Discard": "Отказване",
|
||||||
"Disconnected": "Не е свързано",
|
"Disconnected": "Няма връзка",
|
||||||
"Disconnected (Inactive)": "Не е свързано (неизползвано)",
|
"Disconnected (Inactive)": "Не е свързано (неизползвано)",
|
||||||
"Disconnected (Unused)": "Не е свързано (неизползвано)",
|
"Disconnected (Unused)": "Не е свързано (неизползвано)",
|
||||||
"Discovered": "Открит",
|
"Discovered": "Открит",
|
||||||
@@ -134,8 +137,8 @@
|
|||||||
"Edit Folder Defaults": "За нови папки",
|
"Edit Folder Defaults": "За нови папки",
|
||||||
"Editing {%path%}.": "Променяне на {{path}}.",
|
"Editing {%path%}.": "Променяне на {{path}}.",
|
||||||
"Enable Crash Reporting": "Включване на доклад за срив",
|
"Enable Crash Reporting": "Включване на доклад за срив",
|
||||||
"Enable NAT traversal": "Преминаване през NAT",
|
"Enable NAT traversal": "Обхождане на NAT",
|
||||||
"Enable Relaying": "Препращане",
|
"Enable Relaying": "Разрешаване на ретранслатори",
|
||||||
"Enabled": "Включено",
|
"Enabled": "Включено",
|
||||||
"Enables sending extended attributes to other devices, and applying incoming extended attributes. May require running with elevated privileges.": "Когато е отметнато разширените атрибути се изпращат към другите устройства, а получените разширени атрибути се прилагат. Обикновено изисква съответните за целта права.",
|
"Enables sending extended attributes to other devices, and applying incoming extended attributes. May require running with elevated privileges.": "Когато е отметнато разширените атрибути се изпращат към другите устройства, а получените разширени атрибути се прилагат. Обикновено изисква съответните за целта права.",
|
||||||
"Enables sending extended attributes to other devices, but not applying incoming extended attributes. This can have a significant performance impact. Always enabled when \"Sync Extended Attributes\" is enabled.": "Когато е отметнато разширените атрибути се изпращат към другите устройства, но получените разширени атрибути не се прилагат. Може да има значително неблагоприятно влияние върху производителността. Винаги е отметнато когато „Синхронизиране на разширени атрибути“ е отметнато.",
|
"Enables sending 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.": "Когато е отметнато разширените атрибути се изпращат към другите устройства, но получените разширени атрибути не се прилагат. Може да има значително неблагоприятно влияние върху производителността. Винаги е отметнато когато „Синхронизиране на разширени атрибути“ е отметнато.",
|
||||||
@@ -154,12 +157,12 @@
|
|||||||
"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 set up, retrying": "Грешка при настройване, извършва се повторен опит",
|
||||||
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Неуспешна връзка към сървъри по IPv6 може да се очаква ако няма свързаност по IPv6.",
|
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Неуспешна връзка към сървъри по IPv6 може да се очаква ако няма свързаност по IPv6.",
|
||||||
"File Pull Order": "Ред на изтегляне",
|
"File Pull Order": "Ред на изтегляне",
|
||||||
"File Versioning": "Версии на файловете",
|
"File Versioning": "Версии на файловете",
|
||||||
"Files are moved to .stversions directory when replaced or deleted by Syncthing.": "Файловете биват преместени в папка .stversions при заменяне или изтриване от Syncthing.",
|
"Files are moved to .stversions directory when replaced or deleted by Syncthing.": "Когато Syncthing замени или премахне файл, негова версия се копира в папка .stversions.",
|
||||||
"Files are moved to date stamped versions in a .stversions directory when replaced or deleted by Syncthing.": "Когато Syncthing замени или изтрие файл той бива преместен в папката .stversions и преименуван чрез добавяне на датата и часа.",
|
"Files are moved to date stamped versions in a .stversions directory when replaced or deleted by Syncthing.": "Когато Syncthing замени или премахне файл, негова версия се копира в папка .stversions, като в името му се добавят датата и часът.",
|
||||||
"Files are protected from changes made on other devices, but changes made on this device will be sent to the rest of the cluster.": "Предпазва местните файлове от промени, идващи от другите устройства, но местните промени се изпращат.",
|
"Files are protected from changes made on other devices, but changes made on this device will be sent to the rest of the cluster.": "Предпазва местните файлове от промени, идващи от другите устройства, но местните промени се изпращат.",
|
||||||
"Files are synchronized from the cluster, but any changes made locally will not be sent to other devices.": "Файловете се синхронизират от другите устройства, но местните промени не се изпращат.",
|
"Files are synchronized from the cluster, but any changes made locally will not be sent to other devices.": "Файловете се синхронизират от другите устройства, но местните промени не се изпращат.",
|
||||||
"Filesystem Watcher Errors": "Грешка при наблюдаване на файловата система",
|
"Filesystem Watcher Errors": "Грешка при наблюдаване на файловата система",
|
||||||
@@ -205,9 +208,10 @@
|
|||||||
"Ignored Folders": "Пренебрегнати папки",
|
"Ignored Folders": "Пренебрегнати папки",
|
||||||
"Ignored at": "Пренебрегнато на",
|
"Ignored at": "Пренебрегнато на",
|
||||||
"Included Software": "Използван софтуер",
|
"Included Software": "Използван софтуер",
|
||||||
"Incoming Rate Limit (KiB/s)": "Ограничение при изтегляне (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": "Сведения",
|
||||||
"Internally used paths:": "Вътрешно използвани пътища:",
|
"Internally used paths:": "Вътрешно използвани пътища:",
|
||||||
"Introduced By": "Предложено от",
|
"Introduced By": "Предложено от",
|
||||||
"Introducer": "Поръчител",
|
"Introducer": "Поръчител",
|
||||||
@@ -215,7 +219,7 @@
|
|||||||
"Inversion of the given condition (i.e. do not exclude)": "Обръща значението на условието (напр. да не се отхвърля)",
|
"Inversion of the given condition (i.e. do not exclude)": "Обръща значението на условието (напр. да не се отхвърля)",
|
||||||
"Keep Versions": "Пазени версии",
|
"Keep Versions": "Пазени версии",
|
||||||
"LDAP": "LDAP",
|
"LDAP": "LDAP",
|
||||||
"Largest First": "Първо най-големи",
|
"Largest First": "Първо най-големите",
|
||||||
"Last 30 Days": "Последните 30 дена",
|
"Last 30 Days": "Последните 30 дена",
|
||||||
"Last 7 Days": "Последните 7 дена",
|
"Last 7 Days": "Последните 7 дена",
|
||||||
"Last Month": "Миналия месец",
|
"Last Month": "Миналия месец",
|
||||||
@@ -225,6 +229,7 @@
|
|||||||
"Learn more": "Научете повече",
|
"Learn more": "Научете повече",
|
||||||
"Learn more at {%url%}": "Научете повече на {{url}}",
|
"Learn more at {%url%}": "Научете повече на {{url}}",
|
||||||
"Limit": "Ограничение",
|
"Limit": "Ограничение",
|
||||||
|
"Limit Bandwidth in LAN": "Огранич. на скоростта в местната мрежа",
|
||||||
"Listener Failures": "Грешки при очакване на връзка",
|
"Listener Failures": "Грешки при очакване на връзка",
|
||||||
"Listener Status": "Очакване на връзка",
|
"Listener Status": "Очакване на връзка",
|
||||||
"Listeners": "Очакване на връзка",
|
"Listeners": "Очакване на връзка",
|
||||||
@@ -261,7 +266,7 @@
|
|||||||
"Never": "никога",
|
"Never": "никога",
|
||||||
"New Device": "Ново устройство",
|
"New Device": "Ново устройство",
|
||||||
"New Folder": "Нова папка",
|
"New Folder": "Нова папка",
|
||||||
"Newest First": "Първо най-нови",
|
"Newest First": "Първо най-новите",
|
||||||
"No": "Не",
|
"No": "Не",
|
||||||
"No File Versioning": "Без пазене на версии",
|
"No File Versioning": "Без пазене на версии",
|
||||||
"No files will be deleted as a result of this operation.": "В резултат на операцията няма да бъдат премахнати файлове.",
|
"No files will be deleted as a result of this operation.": "В резултат на операцията няма да бъдат премахнати файлове.",
|
||||||
@@ -272,12 +277,12 @@
|
|||||||
"Number of Connections": "Брой на връзките",
|
"Number of Connections": "Брой на връзките",
|
||||||
"OK": "Добре",
|
"OK": "Добре",
|
||||||
"Off": "Изключено",
|
"Off": "Изключено",
|
||||||
"Oldest First": "Първо най-стари",
|
"Oldest First": "Първо най-старите",
|
||||||
"Optional descriptive label for the folder. Can be different on each device.": "Незадължително име на папката. Може да бъде различно на всяко устройство.",
|
"Optional descriptive label for the folder. Can be different on each device.": "Незадължително име на папката. Може да бъде различно на всяко устройство.",
|
||||||
"Options": "Настройки",
|
"Options": "Настройки",
|
||||||
"Out of Sync": "Несинхронизирано",
|
"Out of Sync": "Несинхронизирано",
|
||||||
"Out of Sync Items": "Несинхронизирани елементи",
|
"Out of Sync Items": "Несинхронизирани елементи",
|
||||||
"Outgoing Rate Limit (KiB/s)": "Ограничение при качване (KiB/s)",
|
"Outgoing Rate Limit (KiB/s)": "Ограничение при качване (КиБ/с)",
|
||||||
"Override": "Налагане",
|
"Override": "Налагане",
|
||||||
"Override Changes": "Налагане на местни промени",
|
"Override Changes": "Налагане на местни промени",
|
||||||
"Ownership": "Собственост",
|
"Ownership": "Собственост",
|
||||||
@@ -307,11 +312,11 @@
|
|||||||
"QUIC LAN": "QUIC LAN",
|
"QUIC LAN": "QUIC LAN",
|
||||||
"QUIC WAN": "QUIC WAN",
|
"QUIC WAN": "QUIC WAN",
|
||||||
"Quick guide to supported patterns": "Кратък наръчник на поддържаните шаблони",
|
"Quick guide to supported patterns": "Кратък наръчник на поддържаните шаблони",
|
||||||
"Random": "Произволен",
|
"Random": "В случаен ред",
|
||||||
"Receive Encrypted": "Приема шифровани данни",
|
"Receive Encrypted": "Получава шифровани данни",
|
||||||
"Receive Only": "Само получава",
|
"Receive Only": "Само получава",
|
||||||
"Received data is already encrypted": "Получените данни вече са шифровани",
|
"Received data is already encrypted": "Получените данни вече са шифровани",
|
||||||
"Recent Changes": "Последни промени",
|
"Recent Changes": "Скорошни промени",
|
||||||
"Reduced by ignore patterns": "Наложени са шаблони за пренебрегване",
|
"Reduced by ignore patterns": "Наложени са шаблони за пренебрегване",
|
||||||
"Relay LAN": "Препращане по LAN",
|
"Relay LAN": "Препращане по LAN",
|
||||||
"Relay WAN": "Препращане по WAN",
|
"Relay WAN": "Препращане по WAN",
|
||||||
@@ -368,13 +373,13 @@
|
|||||||
"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": "Изключване",
|
"Shut Down": "Изключване",
|
||||||
"Shutdown Complete": "Спирането завършено",
|
"Shutdown Complete": "Спирането завършено",
|
||||||
"Simple": "Обикновени",
|
"Simple": "Обикновени",
|
||||||
"Simple File Versioning": "Обикновени версии",
|
"Simple File Versioning": "Обикновени версии",
|
||||||
"Single level wildcard (matches within a directory only)": "Заместващ символ за едно ниво (съвпада само с папка)",
|
"Single level wildcard (matches within a directory only)": "Заместващ символ за едно ниво (съвпада само с папка)",
|
||||||
"Size": "Размер",
|
"Size": "Размер",
|
||||||
"Smallest First": "Първо най-малки",
|
"Smallest First": "Първо най-малките",
|
||||||
"Some discovery methods could not be established for finding other devices or announcing this device:": "Следните методи за откриване не могат да бъдат използвани за намиране на други устройства или за обявяване на това устройство, за да бъде открито от останалите:",
|
"Some discovery methods could not be established for finding other devices or announcing this device:": "Следните методи за откриване не могат да бъдат използвани за намиране на други устройства или за обявяване на това устройство, за да бъде открито от останалите:",
|
||||||
"Some items could not be restored:": "Някои елементи не могат да бъдат възстановени:",
|
"Some items could not be restored:": "Някои елементи не могат да бъдат възстановени:",
|
||||||
"Some listening addresses could not be enabled to accept connections:": "Някои от адресите, на които Syncthing очаква връзка не могат да бъдат настроени да получават входящи връзки:",
|
"Some listening addresses could not be enabled to accept connections:": "Някои от адресите, на които Syncthing очаква връзка не могат да бъдат настроени да получават входящи връзки:",
|
||||||
@@ -384,7 +389,7 @@
|
|||||||
"Stable releases only": "Само стабилни версии",
|
"Stable releases only": "Само стабилни версии",
|
||||||
"Staggered": "Разпределени",
|
"Staggered": "Разпределени",
|
||||||
"Staggered File Versioning": "Разпределени версии",
|
"Staggered File Versioning": "Разпределени версии",
|
||||||
"Start Browser": "Отваряне в мрежов четец",
|
"Start Browser": "Отваряне на мрежов четец",
|
||||||
"Statistics": "Статистика",
|
"Statistics": "Статистика",
|
||||||
"Stay logged in": "Оставане в системата",
|
"Stay logged in": "Оставане в системата",
|
||||||
"Stopped": "Спряна",
|
"Stopped": "Спряна",
|
||||||
@@ -437,11 +442,11 @@
|
|||||||
"The interval must be a positive number of seconds.": "Интервалът трябва да е положителен брой секунди.",
|
"The interval must be a positive number of seconds.": "Интервалът трябва да е положителен брой секунди.",
|
||||||
"The interval, in seconds, for running cleanup in the versions directory. Zero to disable periodic cleaning.": "Интервал, в секунди, на почистване на папката с версии. Нула изключва периодичното почистване.",
|
"The interval, in seconds, for running cleanup in the versions directory. Zero to disable periodic cleaning.": "Интервал, в секунди, на почистване на папката с версии. Нула изключва периодичното почистване.",
|
||||||
"The maximum age must be a number and cannot be blank.": "Максималната възраст трябва да е число, полето не може да бъде празно.",
|
"The maximum age must be a number and cannot be blank.": "Максималната възраст трябва да е число, полето не може да бъде празно.",
|
||||||
"The maximum time to keep a version (in days, set to 0 to keep versions forever).": "Максимална продължителност за пазене на версия (в дни, за да не бъдат изтривани версии задайте 0).",
|
"The maximum time to keep a version (in days, set to 0 to keep versions forever).": "Максимален срок за пазене на версия (в дни, 0 – без ограничение).",
|
||||||
"The number of connections must be a non-negative number.": "Броят на връзките трябва да бъде положително число.",
|
"The number of connections must be a non-negative number.": "Броят на връзките трябва да бъде положително число.",
|
||||||
"The number of days must be a number and cannot be blank.": "Броят дни трябва да бъде число и не може да бъде празно.",
|
"The number of days must be a number and cannot be blank.": "Броят дни трябва да бъде число и не може да бъде празно.",
|
||||||
"The number of days to keep files in the trash can. Zero means forever.": "Брой дни за пазене на файловете в кошчето. Нула значи завинаги.",
|
"The number of days to keep files in the trash can. Zero means forever.": "Брой дни за пазене на файловете в кошчето. Нула значи завинаги.",
|
||||||
"The number of old versions to keep, per file.": "Брой стари версии, които да бъдат пазени за всеки файл.",
|
"The number of old versions to keep, per file.": "Брой пазени стари версии на файл.",
|
||||||
"The number of versions must be a number and cannot be blank.": "Броят версии трябва да бъде число и не може да бъде празно.",
|
"The number of versions must be a number and cannot be blank.": "Броят версии трябва да бъде число и не може да бъде празно.",
|
||||||
"The path cannot be blank.": "Пътят не може да бъде празен.",
|
"The path cannot be blank.": "Пътят не може да бъде празен.",
|
||||||
"The rate limit is applied to the accumulated traffic of all connections to this device.": "Ограничението се прилага към общия трафик от всички връзки към това устройство.",
|
"The rate limit is applied to the accumulated traffic of all connections to this device.": "Ограничението се прилага към общия трафик от всички връзки към това устройство.",
|
||||||
@@ -474,7 +479,7 @@
|
|||||||
"Unexpected Items": "Неочаквани елементи",
|
"Unexpected Items": "Неочаквани елементи",
|
||||||
"Unexpected items have been found in this folder.": "В папката са намерени неочаквани елементи.",
|
"Unexpected items have been found in this folder.": "В папката са намерени неочаквани елементи.",
|
||||||
"Unignore": "Отменяне на пренебрегване",
|
"Unignore": "Отменяне на пренебрегване",
|
||||||
"Unknown": "Неясно",
|
"Unknown": "Неизвестно",
|
||||||
"Unshared": "Несподелена",
|
"Unshared": "Несподелена",
|
||||||
"Unshared Devices": "Устройства, с които не е споделена",
|
"Unshared Devices": "Устройства, с които не е споделена",
|
||||||
"Unshared Folders": "Несподелени папки",
|
"Unshared Folders": "Несподелени папки",
|
||||||
@@ -498,7 +503,7 @@
|
|||||||
"Using a direct TCP connection over WAN": "Използване на директна свързаност с TCP през широкодостъпна мрежа",
|
"Using a direct TCP connection over WAN": "Използване на директна свързаност с TCP през широкодостъпна мрежа",
|
||||||
"Version": "Издание",
|
"Version": "Издание",
|
||||||
"Versions": "Версии",
|
"Versions": "Версии",
|
||||||
"Versions Path": "Път до версиите",
|
"Versions Path": "Папка с версиите",
|
||||||
"Versions are automatically deleted if they are older than the maximum age or exceed the number of files allowed in an interval.": "Версиите биват изтривани автоматично ако са по-стари от максималната възраст или надминават броя разрешени версии за определено време.",
|
"Versions are automatically deleted if they are older than the maximum age or exceed the number of files allowed in an interval.": "Версиите биват изтривани автоматично ако са по-стари от максималната възраст или надминават броя разрешени версии за определено време.",
|
||||||
"Waiting to Clean": "Изчаква за почистване",
|
"Waiting to Clean": "Изчаква за почистване",
|
||||||
"Waiting to Scan": "Изчаква за обхождане",
|
"Waiting to Scan": "Изчаква за обхождане",
|
||||||
@@ -526,7 +531,7 @@
|
|||||||
"You have unsaved changes. Do you really want to discard them?": "Има незапазени промени. Желаете ли да се откажете от тях?",
|
"You have unsaved changes. Do you really want to discard them?": "Има незапазени промени. Желаете ли да се откажете от тях?",
|
||||||
"You must keep at least one version.": "Необходимо е да запазите поне една версия.",
|
"You must keep at least one version.": "Необходимо е да запазите поне една версия.",
|
||||||
"You should never add or change anything locally in a \"{%receiveEncrypted%}\" folder.": "Никога не трябва да променяте нищо в папка от вида „{{receiveEncrypted}}“.",
|
"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.": "Пощенският клиент би трябвало да се отвори, да ви даде възможност да изберете получател, за да изпратите съобщението от вашия адрес за електронна поща.",
|
"Your email app should open to let you choose the recipient and send it from your own address.": "Пощенският клиент би трябвало да се отвори, да ви даде възможност да изберете получател, за да изпратите съобщението от вашия адрес за електронна поща.",
|
||||||
"days": "дни",
|
"days": "дни",
|
||||||
"deleted": "премахнато",
|
"deleted": "премахнато",
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
"Allowed Networks": "Xarxes permeses",
|
"Allowed Networks": "Xarxes permeses",
|
||||||
"Alphabetic": "Alfabètic",
|
"Alphabetic": "Alfabètic",
|
||||||
"Altered by ignoring deletes.": "S'ha alterat ignorant les supressions.",
|
"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.",
|
"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 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?",
|
"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:",
|
"Body:": "Cos de text:",
|
||||||
"Bugs": "Errors (Bugs)",
|
"Bugs": "Errors (Bugs)",
|
||||||
"Cancel": "Cancel·la",
|
"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",
|
"Changelog": "Historial de canvis",
|
||||||
"Clean out after": "Netejar després",
|
"Clean out after": "Netejar després",
|
||||||
"Cleaning Versions": "Netejant versions",
|
"Cleaning Versions": "Netejant versions",
|
||||||
@@ -154,7 +156,7 @@
|
|||||||
"Failed Items": "Elements fallats",
|
"Failed Items": "Elements fallats",
|
||||||
"Failed to load file versions.": "No s'han pogut carregar les versions dels fitxers.",
|
"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 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.",
|
"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 Pull Order": "Ordre d'agafar fitxers",
|
||||||
"File Versioning": "Versionat de Fitxers",
|
"File Versioning": "Versionat de Fitxers",
|
||||||
@@ -368,7 +370,7 @@
|
|||||||
"Show diff with previous version": "Mostra la diferència amb la versió anterior",
|
"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 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.",
|
"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",
|
"Shutdown Complete": "Apagat complet",
|
||||||
"Simple": "Simple",
|
"Simple": "Simple",
|
||||||
"Simple File Versioning": "Versionat de Fitxers Senzill",
|
"Simple File Versioning": "Versionat de Fitxers Senzill",
|
||||||
|
|||||||
@@ -150,7 +150,7 @@
|
|||||||
"Failed Items": "Objectes fallits",
|
"Failed Items": "Objectes fallits",
|
||||||
"Failed to load file versions.": "No s'han pogut carregar les versions dels fitxers.",
|
"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 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.",
|
"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 Pull Order": "Ordre de fitxers del pull",
|
||||||
"File Versioning": "Versionat de fitxer",
|
"File Versioning": "Versionat de fitxer",
|
||||||
@@ -353,7 +353,7 @@
|
|||||||
"Show diff with previous version": "Mostrar les diferències amb la versió prèvia",
|
"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 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.",
|
"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",
|
"Shutdown Complete": "Apagar completament",
|
||||||
"Simple": "Senzill",
|
"Simple": "Senzill",
|
||||||
"Simple File Versioning": "Versionat de fitxers senzill",
|
"Simple File Versioning": "Versionat de fitxers senzill",
|
||||||
|
|||||||
@@ -154,7 +154,7 @@
|
|||||||
"Failed Items": "Nezdařené položky",
|
"Failed Items": "Nezdařené položky",
|
||||||
"Failed to load file versions.": "Nepodařilo se nahrát verze souboru.",
|
"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 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.",
|
"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 Pull Order": "Pořadí stahování souborů",
|
||||||
"File Versioning": "Správa verzí souborů",
|
"File Versioning": "Správa verzí souborů",
|
||||||
@@ -221,13 +221,14 @@
|
|||||||
"Last seen": "Naposledy spatřen",
|
"Last seen": "Naposledy spatřen",
|
||||||
"Latest Change": "Poslední změna",
|
"Latest Change": "Poslední změna",
|
||||||
"Learn more": "Zjistěte více",
|
"Learn more": "Zjistěte více",
|
||||||
|
"Learn more at {%url%}": "Více na {{url}}",
|
||||||
"Limit": "Limit",
|
"Limit": "Limit",
|
||||||
"Listener Failures": "Selhání při naslouchání",
|
"Listener Failures": "Selhání při naslouchání",
|
||||||
"Listener Status": "Stav naslouchání",
|
"Listener Status": "Stav naslouchání",
|
||||||
"Listeners": "Naslouchající",
|
"Listeners": "Naslouchající",
|
||||||
"Loading data...": "Načítání dat…",
|
"Loading data...": "Načítání dat…",
|
||||||
"Loading...": "Načítání…",
|
"Loading...": "Načítání…",
|
||||||
"Local Additions": "Místní příbytky",
|
"Local Additions": "Místní přebytky",
|
||||||
"Local Discovery": "Místní objevování",
|
"Local Discovery": "Místní objevování",
|
||||||
"Local State": "Místní status",
|
"Local State": "Místní status",
|
||||||
"Local State (Total)": "Místní status (Celkem)",
|
"Local State (Total)": "Místní status (Celkem)",
|
||||||
@@ -236,11 +237,16 @@
|
|||||||
"Log File": "Soubor logů",
|
"Log File": "Soubor logů",
|
||||||
"Log In": "Přihlásit se",
|
"Log In": "Přihlásit se",
|
||||||
"Log Out": "Odhlásit se",
|
"Log Out": "Odhlásit se",
|
||||||
|
"Log in to see paths information.": "Pro zobrazení informací o cestě se přihlaste.",
|
||||||
|
"Log in to see version information.": "Pro zobrazení informací o verzi se přihlaste.",
|
||||||
"Log tailing paused. Scroll to the bottom to continue.": "Zaznamenávání událostí pozastaveno. Sjeďte dolů pro pokračování.",
|
"Log tailing paused. Scroll to the bottom to continue.": "Zaznamenávání událostí pozastaveno. Sjeďte dolů pro pokračování.",
|
||||||
|
"Login failed, see Syncthing logs for details.": "Přihlášení selhalo, detaily najdete v Syncthing logu.",
|
||||||
"Logs": "Záznamy událostí",
|
"Logs": "Záznamy událostí",
|
||||||
"Major Upgrade": "Aktualizace hlavní verze",
|
"Major Upgrade": "Aktualizace hlavní verze",
|
||||||
"Mass actions": "Hromadné akce",
|
"Mass actions": "Hromadné akce",
|
||||||
"Maximum Age": "Maximální časový limit",
|
"Maximum Age": "Maximální časový limit",
|
||||||
|
"Maximum single entry size": "Maximální velikost jedné položky",
|
||||||
|
"Maximum total size": "Maximální celková velikost",
|
||||||
"Metadata Only": "Pouze metadata",
|
"Metadata Only": "Pouze metadata",
|
||||||
"Minimum Free Disk Space": "Minimální velikost volného místa na úložišti",
|
"Minimum Free Disk Space": "Minimální velikost volného místa na úložišti",
|
||||||
"Mod. Device": "Zařízení, které provedlo změnu",
|
"Mod. Device": "Zařízení, které provedlo změnu",
|
||||||
@@ -354,7 +360,7 @@
|
|||||||
"Show diff with previous version": "Ukázat rozdíl oproti předchozí verzi",
|
"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 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á.",
|
"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",
|
"Shutdown Complete": "Vypnutí dokončeno",
|
||||||
"Simple": "Jednoduché",
|
"Simple": "Jednoduché",
|
||||||
"Simple File Versioning": "Jednoduchá správa verzí souborů",
|
"Simple File Versioning": "Jednoduchá správa verzí souborů",
|
||||||
@@ -410,15 +416,17 @@
|
|||||||
"The folder path cannot be blank.": "Popis umístění složky nemůže zůstat nevyplněný.",
|
"The folder path cannot be blank.": "Popis umístění složky nemůže zůstat nevyplněný.",
|
||||||
"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.": "Jsou použity následující intervaly: za první hodinu jsou ponechány verze pro každých 30 sekund, za první den jsou ponechány verze pro každou hodinu, za prvních 30 dní jsou ponechány verze pro každý den a do nejvyššího nastaveného stáří jsou ponechány verze pro každý týden.",
|
"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.": "Jsou použity následující intervaly: za první hodinu jsou ponechány verze pro každých 30 sekund, za první den jsou ponechány verze pro každou hodinu, za prvních 30 dní jsou ponechány verze pro každý den a do nejvyššího nastaveného stáří jsou ponechány verze pro každý týden.",
|
||||||
"The following items could not be synchronized.": "Následující položky nemohly být synchronizovány.",
|
"The following items could not be synchronized.": "Následující položky nemohly být synchronizovány.",
|
||||||
"The following items were changed locally.": "Tyto položky byly změněny lokálně",
|
"The following items were changed locally.": "Tyto položky byly změněny lokálně.",
|
||||||
"The following methods are used to discover other devices on the network and announce this device to be found by others:": "K objevování ostatních zařízení a oznamování tohoto zařízení se používají následující metody:",
|
"The following methods are used to discover other devices on the network and announce this device to be found by others:": "K objevování ostatních zařízení a oznamování tohoto zařízení se používají následující metody:",
|
||||||
|
"The following text will automatically be inserted into a new message.": "Následující text bude automaticky vložen do nové zprávy.",
|
||||||
"The following unexpected items were found.": "Byly nalezeny tyto neočekávané položky.",
|
"The following unexpected items were found.": "Byly nalezeny tyto neočekávané položky.",
|
||||||
"The interval must be a positive number of seconds.": "Interval musí být kladný počet sekund.",
|
"The interval must be a positive number of seconds.": "Interval musí být kladný počet sekund.",
|
||||||
"The interval, in seconds, for running cleanup in the versions directory. Zero to disable periodic cleaning.": "Interval (v sekundách) pro spouštění čištění ve složce s verzemi. Nula pravidelné čištění vypíná.",
|
"The interval, in seconds, for running cleanup in the versions directory. Zero to disable periodic cleaning.": "Interval (v sekundách) pro spouštění čištění ve složce s verzemi. Nula pravidelné čištění vypíná.",
|
||||||
"The maximum age must be a number and cannot be blank.": "Nejvyšší stáří je třeba zadat v podobě čísla a nemůže být prázdné.",
|
"The maximum age must be a number and cannot be blank.": "Nejvyšší stáří je třeba zadat v podobě čísla a nemůže být prázdné.",
|
||||||
"The maximum time to keep a version (in days, set to 0 to keep versions forever).": "Maximální doba pro zachování verze (dny, zapsáním hodnoty 0 bude ponecháno navždy).",
|
"The maximum time to keep a version (in days, set to 0 to keep versions forever).": "Maximální doba pro zachování verze (dny, zapsáním hodnoty 0 bude ponecháno navždy).",
|
||||||
"The number of days must be a number and cannot be blank.": "Je třeba, aby počet dní bylo číslo a nemůže zůstat nevyplněné.",
|
"The number of connections must be a non-negative number.": "Počet spojení musí být nezáporné číslo.",
|
||||||
"The number of days to keep files in the trash can. Zero means forever.": "Počet dní, po který budou soubory uchovány v koši. Nula znamená navždy.",
|
"The number of days must be a number and cannot be blank.": "Počet dní musí být číslo a nemůže zůstat nevyplněné.",
|
||||||
|
"The number of days to keep files in the trash can. Zero means forever.": "Počet dní, po které budou soubory uchovány v koši. Nula znamená navždy.",
|
||||||
"The number of old versions to keep, per file.": "Počet uchovávaných starších verzí každého ze souborů.",
|
"The number of old versions to keep, per file.": "Počet uchovávaných starších verzí každého ze souborů.",
|
||||||
"The number of versions must be a number and cannot be blank.": "Je třeba, aby počet verzí bylo číslo a nemůže zůstat nevyplněné.",
|
"The number of versions must be a number and cannot be blank.": "Je třeba, aby počet verzí bylo číslo a nemůže zůstat nevyplněné.",
|
||||||
"The path cannot be blank.": "Popis umístění nemůže zůstat nevyplněný.",
|
"The path cannot be blank.": "Popis umístění nemůže zůstat nevyplněný.",
|
||||||
@@ -501,6 +509,8 @@
|
|||||||
"folder": "složka",
|
"folder": "složka",
|
||||||
"full documentation": "úplná dokumentace",
|
"full documentation": "úplná dokumentace",
|
||||||
"items": "položky",
|
"items": "položky",
|
||||||
|
"modified": "změněno",
|
||||||
|
"permit": "povolit",
|
||||||
"seconds": "sekund",
|
"seconds": "sekund",
|
||||||
"theme": {
|
"theme": {
|
||||||
"name": {
|
"name": {
|
||||||
|
|||||||
@@ -154,7 +154,7 @@
|
|||||||
"Failed Items": "Mislykkede filer",
|
"Failed Items": "Mislykkede filer",
|
||||||
"Failed to load file versions.": "Fil versioner kunne ikke indlæses.",
|
"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 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.",
|
"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 Pull Order": "Hentningsrækkefølge for filer",
|
||||||
"File Versioning": "Filversionering",
|
"File Versioning": "Filversionering",
|
||||||
@@ -368,7 +368,7 @@
|
|||||||
"Show diff with previous version": "Vis forskelle fra tidligere version",
|
"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 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.",
|
"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",
|
"Shutdown Complete": "Nedlukning fuldført",
|
||||||
"Simple": "Enkel",
|
"Simple": "Enkel",
|
||||||
"Simple File Versioning": "Simpel filversionering",
|
"Simple File Versioning": "Simpel filversionering",
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
"Allowed Networks": "Erlaubte Netzwerke",
|
"Allowed Networks": "Erlaubte Netzwerke",
|
||||||
"Alphabetic": "Alphabetisch",
|
"Alphabetic": "Alphabetisch",
|
||||||
"Altered by ignoring deletes.": "Weicht ab, weil Löschungen ignoriert werden.",
|
"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.",
|
"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 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?",
|
"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:",
|
"Body:": "Nachrichtentext:",
|
||||||
"Bugs": "Fehler",
|
"Bugs": "Fehler",
|
||||||
"Cancel": "Abbrechen",
|
"Cancel": "Abbrechen",
|
||||||
|
"Cannot be enabled when the folder type is \"{%foldertype%}\".": "Kann nicht aktiviert werden, wenn der Ordnertyp „{{foldertype}}“ ist.",
|
||||||
"Changelog": "Änderungsprotokoll",
|
"Changelog": "Änderungsprotokoll",
|
||||||
"Clean out after": "Löschen nach",
|
"Clean out after": "Löschen nach",
|
||||||
"Cleaning Versions": "Versionen bereinigen",
|
"Cleaning Versions": "Versionen bereinigen",
|
||||||
@@ -59,12 +61,12 @@
|
|||||||
"Click to see full identification string and QR code.": "Klicken, um die vollständige Kennung und den QR-Code anzuzeigen.",
|
"Click to see full identification string and QR code.": "Klicken, um die vollständige Kennung und den QR-Code anzuzeigen.",
|
||||||
"Close": "Schließen",
|
"Close": "Schließen",
|
||||||
"Command": "Befehl",
|
"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",
|
"Compression": "Komprimierung",
|
||||||
"Configuration Directory": "Konfigurationsverzeichnis",
|
"Configuration Directory": "Konfigurationsverzeichnis",
|
||||||
"Configuration File": "Konfigurationsdatei",
|
"Configuration File": "Konfigurationsdatei",
|
||||||
"Configured": "Konfiguriert",
|
"Configured": "Konfiguriert",
|
||||||
"Connected (Unused)": "Verbunden (Nicht genutzt)",
|
"Connected (Unused)": "Verbunden (nicht genutzt)",
|
||||||
"Connection Error": "Verbindungsfehler",
|
"Connection Error": "Verbindungsfehler",
|
||||||
"Connection Management": "Verbindungsverwaltung",
|
"Connection Management": "Verbindungsverwaltung",
|
||||||
"Connection Type": "Verbindungstyp",
|
"Connection Type": "Verbindungstyp",
|
||||||
@@ -80,6 +82,7 @@
|
|||||||
"Custom Range": "Eigener Zeitraum",
|
"Custom Range": "Eigener Zeitraum",
|
||||||
"Danger!": "Achtung!",
|
"Danger!": "Achtung!",
|
||||||
"Database Location": "Datenbank-Speicherort",
|
"Database Location": "Datenbank-Speicherort",
|
||||||
|
"Debug": "Debug",
|
||||||
"Debugging Facilities": "Debugging-Möglichkeiten",
|
"Debugging Facilities": "Debugging-Möglichkeiten",
|
||||||
"Default": "Vorgabe",
|
"Default": "Vorgabe",
|
||||||
"Default Configuration": "Vorgabekonfiguration",
|
"Default Configuration": "Vorgabekonfiguration",
|
||||||
@@ -95,14 +98,14 @@
|
|||||||
"Deselect folders to stop sharing with this device.": "Ordner abwählen, um sie nicht mehr für mit diesem Gerät zu teilen.",
|
"Deselect folders to stop sharing with this device.": "Ordner abwählen, um sie nicht mehr für mit diesem Gerät zu teilen.",
|
||||||
"Device": "Gerät",
|
"Device": "Gerät",
|
||||||
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "Gerät „{{name}}“ ({{device}} {{address}}) möchte sich verbinden. Gerät hinzufügen?",
|
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "Gerät „{{name}}“ ({{device}} {{address}}) möchte sich verbinden. Gerät hinzufügen?",
|
||||||
"Device Certificate": "Geräte-Zertifikat",
|
"Device Certificate": "Gerätezertifikat",
|
||||||
"Device ID": "Gerätekennung",
|
"Device ID": "Gerätekennung",
|
||||||
"Device Identification": "Geräteidentifikation",
|
"Device Identification": "Geräteidentifikation",
|
||||||
"Device Name": "Gerätename",
|
"Device Name": "Gerätename",
|
||||||
"Device Status": "Gerätestatus",
|
"Device Status": "Gerätestatus",
|
||||||
"Device is untrusted, enter encryption password": "Gerät wird nicht vertraut, Verschlüsselungspasswort eingeben",
|
"Device is untrusted, enter encryption password": "Gerät wird nicht vertraut, Verschlüsselungspasswort eingeben",
|
||||||
"Device rate limits": "Datenratenbegrenzungen fürs Gerät",
|
"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",
|
"Devices": "Geräte",
|
||||||
"Disable Crash Reporting": "Absturzmeldung deaktivieren",
|
"Disable Crash Reporting": "Absturzmeldung deaktivieren",
|
||||||
"Disabled": "Deaktiviert",
|
"Disabled": "Deaktiviert",
|
||||||
@@ -112,8 +115,8 @@
|
|||||||
"Disables comparing and syncing file permissions. Useful on systems with nonexistent or custom permissions (e.g. FAT, exFAT, Synology, Android).": "Deaktiviert Vergleich und Synchronisierung der Dateiberechtigungen. Dies ist hilfreich für Dateisysteme ohne konfigurierbare Berechtigungsparameter (z. B. FAT, exFAT, Synology, Android).",
|
"Disables comparing and syncing file permissions. Useful on systems with nonexistent or custom permissions (e.g. FAT, exFAT, Synology, Android).": "Deaktiviert Vergleich und Synchronisierung der Dateiberechtigungen. Dies ist hilfreich für Dateisysteme ohne konfigurierbare Berechtigungsparameter (z. B. FAT, exFAT, Synology, Android).",
|
||||||
"Discard": "Verwerfen",
|
"Discard": "Verwerfen",
|
||||||
"Disconnected": "Getrennt",
|
"Disconnected": "Getrennt",
|
||||||
"Disconnected (Inactive)": "Getrennt (Inaktiv)",
|
"Disconnected (Inactive)": "Getrennt (inaktiv)",
|
||||||
"Disconnected (Unused)": "Getrennt (Nicht genutzt)",
|
"Disconnected (Unused)": "Getrennt (nicht genutzt)",
|
||||||
"Discovered": "Ermittelt",
|
"Discovered": "Ermittelt",
|
||||||
"Discovery": "Gerätesuche",
|
"Discovery": "Gerätesuche",
|
||||||
"Discovery Failures": "Gerätesuchfehler",
|
"Discovery Failures": "Gerätesuchfehler",
|
||||||
@@ -132,7 +135,7 @@
|
|||||||
"Edit Device Defaults": "Gerätevorgaben bearbeiten",
|
"Edit Device Defaults": "Gerätevorgaben bearbeiten",
|
||||||
"Edit Folder": "Ordner bearbeiten",
|
"Edit Folder": "Ordner bearbeiten",
|
||||||
"Edit Folder Defaults": "Ordnervorgaben bearbeiten",
|
"Edit Folder Defaults": "Ordnervorgaben bearbeiten",
|
||||||
"Editing {%path%}.": "Bearbeite {{path}}.",
|
"Editing {%path%}.": "Bearbeiten von {{path}}.",
|
||||||
"Enable Crash Reporting": "Absturzmeldung aktivieren",
|
"Enable Crash Reporting": "Absturzmeldung aktivieren",
|
||||||
"Enable NAT traversal": "NAT-Durchdringung aktivieren",
|
"Enable NAT traversal": "NAT-Durchdringung aktivieren",
|
||||||
"Enable Relaying": "Weiterleitung aktivieren",
|
"Enable Relaying": "Weiterleitung aktivieren",
|
||||||
@@ -142,7 +145,7 @@
|
|||||||
"Enables sending ownership information to other devices, and applying incoming ownership information. Typically requires running with elevated privileges.": "Bewirkt das Senden der Besitzinformation an andere Geräte und das Anwenden empfangener Besitzinformation. Erfordert üblicherweise die Ausführung mit höheren Zugriffsrechten.",
|
"Enables sending ownership information to other devices, and applying incoming ownership information. Typically requires running with elevated privileges.": "Bewirkt das Senden der Besitzinformation an andere Geräte und das Anwenden empfangener Besitzinformation. Erfordert üblicherweise die Ausführung mit höheren Zugriffsrechten.",
|
||||||
"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.": "Bewirkt das Senden von Besitzinformation an andere Geräte, jedoch ohne empfangene Besitzinformation anzuwenden. Kann zu einem merklichen Leistungseinbruch führen. Immer aktiviert, wenn „Besitzinformation synchronisieren“ eingeschaltet ist.",
|
"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.": "Bewirkt das Senden von Besitzinformation an andere Geräte, jedoch ohne empfangene Besitzinformation anzuwenden. Kann zu einem merklichen Leistungseinbruch führen. Immer aktiviert, wenn „Besitzinformation synchronisieren“ eingeschaltet ist.",
|
||||||
"Enter a non-negative number (e.g., \"2.35\") and select a unit. Percentages are as part of the total disk size.": "Geben Sie eine positive Zahl ein (z. B. „2.35“) und wählen Sie eine Einheit. Prozentsätze sind Teil der gesamten Festplattengröße.",
|
"Enter a non-negative number (e.g., \"2.35\") and select a unit. Percentages are as part of the total disk size.": "Geben Sie eine positive Zahl ein (z. B. „2.35“) und wählen Sie eine Einheit. Prozentsätze sind Teil der gesamten Festplattengröße.",
|
||||||
"Enter a non-privileged port number (1024 - 65535).": "Geben Sie eine nichtprivilegierte Portnummer ein (1024 - 65535).",
|
"Enter a non-privileged port number (1024 - 65535).": "Geben Sie eine nicht privilegierte Portnummer ein (1024 - 65535).",
|
||||||
"Enter comma separated (\"tcp://ip:port\", \"tcp://host:port\") addresses or \"dynamic\" to perform automatic discovery of the address.": "Kommagetrennte Adressen („tcp://ip:port“, „tcp://host:port“) oder „dynamic“ eingeben, um die Adresse automatisch zu ermitteln.",
|
"Enter comma separated (\"tcp://ip:port\", \"tcp://host:port\") addresses or \"dynamic\" to perform automatic discovery of the address.": "Kommagetrennte Adressen („tcp://ip:port“, „tcp://host:port“) oder „dynamic“ eingeben, um die Adresse automatisch zu ermitteln.",
|
||||||
"Enter ignore patterns, one per line.": "Geben Sie Ignoriermuster ein, eines pro Zeile.",
|
"Enter ignore patterns, one per line.": "Geben Sie Ignoriermuster ein, eines pro Zeile.",
|
||||||
"Enter up to three octal digits.": "Tragen Sie bis zu drei oktale Ziffern ein.",
|
"Enter up to three octal digits.": "Tragen Sie bis zu drei oktale Ziffern ein.",
|
||||||
@@ -154,17 +157,17 @@
|
|||||||
"Failed Items": "Fehlgeschlagene Elemente",
|
"Failed Items": "Fehlgeschlagene Elemente",
|
||||||
"Failed to load file versions.": "Fehler beim Laden der Dateiversionen.",
|
"Failed to load file versions.": "Fehler beim Laden der Dateiversionen.",
|
||||||
"Failed to load ignore patterns.": "Fehler beim Laden der Ignoriermuster.",
|
"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.",
|
"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 Pull Order": "Dateiübertragungsreihenfolge",
|
||||||
"File Versioning": "Dateiversionierung",
|
"File Versioning": "Dateiversionierung",
|
||||||
"Files are moved to .stversions directory when replaced or deleted by Syncthing.": "Dateien werden in den .stversions Ordner verschoben, wenn sie von Syncthing ersetzt oder gelöscht wurden.",
|
"Files are moved to .stversions directory when replaced or deleted by Syncthing.": "Dateien werden in den Ordner .stversions verschoben, wenn sie von Syncthing ersetzt oder gelöscht wurden.",
|
||||||
"Files are moved to date stamped versions in a .stversions directory when replaced or deleted by Syncthing.": "Dateien werden mit Datumsstempel versioniert und in den .stversions Ordner verschoben, wenn sie von Syncthing ersetzt oder gelöscht wurden.",
|
"Files are moved to date stamped versions in a .stversions directory when replaced or deleted by Syncthing.": "Dateien werden mit Datumsstempel versioniert und in den Ordner .stversions verschoben, wenn sie von Syncthing ersetzt oder gelöscht wurden.",
|
||||||
"Files are protected from changes made on other devices, but changes made on this device will be sent to the rest of the cluster.": "Dateien sind auf diesem Gerät schreibgeschützt. Auf diesem Gerät durchgeführte Veränderungen werden aber auf den Rest des Verbunds übertragen.",
|
"Files are protected from changes made on other devices, but changes made on this device will be sent to the rest of the cluster.": "Dateien sind auf diesem Gerät schreibgeschützt. Auf diesem Gerät durchgeführte Veränderungen werden aber auf den Rest des Verbunds übertragen.",
|
||||||
"Files are synchronized from the cluster, but any changes made locally will not be sent to other devices.": "Dateien werden vom Verbund synchronisiert, aber lokal vorgenommene Änderungen werden nicht an andere Geräte gesendet.",
|
"Files are synchronized from the cluster, but any changes made locally will not be sent to other devices.": "Dateien werden vom Verbund synchronisiert, aber lokal vorgenommene Änderungen werden nicht an andere Geräte gesendet.",
|
||||||
"Filesystem Watcher Errors": "Fehler im Dateisystembeobachter",
|
"Filesystem Watcher Errors": "Fehler im Dateisystembeobachter",
|
||||||
"Filter by date": "Nach Datum sortieren",
|
"Filter by date": "Nach Datum sortieren",
|
||||||
"Filter by name": "Nach Name sortieren",
|
"Filter by name": "Nach Namen sortieren",
|
||||||
"Folder": "Ordner",
|
"Folder": "Ordner",
|
||||||
"Folder ID": "Ordnerkennung",
|
"Folder ID": "Ordnerkennung",
|
||||||
"Folder Label": "Ordnerbezeichnung",
|
"Folder Label": "Ordnerbezeichnung",
|
||||||
@@ -181,10 +184,10 @@
|
|||||||
"GUI / API HTTPS Certificate": "GUI / API HTTPS-Zertifikat",
|
"GUI / API HTTPS Certificate": "GUI / API HTTPS-Zertifikat",
|
||||||
"GUI Authentication Password": "Passwort für Zugang zur Benutzeroberfläche",
|
"GUI Authentication Password": "Passwort für Zugang zur Benutzeroberfläche",
|
||||||
"GUI Authentication User": "Benutzername 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": "Addresse der Benutzeroberfläche",
|
"GUI Listen Address": "Adresse der Benutzeroberfläche",
|
||||||
"GUI Override Directory": "GUI-Ersatz-Verzeichnis",
|
"GUI Override Directory": "GUI-Ersatz-Verzeichnis",
|
||||||
"GUI Theme": "GUI Design",
|
"GUI Theme": "GUI-Design",
|
||||||
"General": "Allgemein",
|
"General": "Allgemein",
|
||||||
"Generate": "Generieren",
|
"Generate": "Generieren",
|
||||||
"Global Discovery": "Globale Gerätesuche",
|
"Global Discovery": "Globale Gerätesuche",
|
||||||
@@ -208,11 +211,12 @@
|
|||||||
"Incoming Rate Limit (KiB/s)": "Eingehende Datenratenbegrenzung (KiB/s)",
|
"Incoming Rate Limit (KiB/s)": "Eingehende Datenratenbegrenzung (KiB/s)",
|
||||||
"Incorrect configuration may damage your folder contents and render Syncthing inoperable.": "Eine falsche Konfiguration kann den Ordnerinhalt beschädigen und Syncthing in einen unausführbaren Zustand versetzen.",
|
"Incorrect 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.",
|
"Incorrect user name or password.": "Falscher Benutzername oder Passwort.",
|
||||||
|
"Info": "Info",
|
||||||
"Internally used paths:": "Intern verwendete Pfade:",
|
"Internally used paths:": "Intern verwendete Pfade:",
|
||||||
"Introduced By": "Verteilt von",
|
"Introduced By": "Verteilt von",
|
||||||
"Introducer": "Verteilergerät",
|
"Introducer": "Verteilergerät",
|
||||||
"Introduction": "Einführung",
|
"Introduction": "Einführung",
|
||||||
"Inversion of the given condition (i.e. do not exclude)": "Umkehrung der angegebenen Bedingung (d. h. schließe nicht aus)",
|
"Inversion of the given condition (i.e. do not exclude)": "Umkehrung der angegebenen Bedingung (d. h. nicht ausschließen)",
|
||||||
"Keep Versions": "Versionen erhalten",
|
"Keep Versions": "Versionen erhalten",
|
||||||
"LDAP": "LDAP",
|
"LDAP": "LDAP",
|
||||||
"Largest First": "Größte zuerst",
|
"Largest First": "Größte zuerst",
|
||||||
@@ -223,8 +227,9 @@
|
|||||||
"Last seen": "Zuletzt online",
|
"Last seen": "Zuletzt online",
|
||||||
"Latest Change": "Letzte Änderung",
|
"Latest Change": "Letzte Änderung",
|
||||||
"Learn more": "Mehr erfahren",
|
"Learn more": "Mehr erfahren",
|
||||||
"Learn more at {%url%}": "Erfahre mehr unter {{url}}",
|
"Learn more at {%url%}": "Erfahren Sie mehr unter {{url}}",
|
||||||
"Limit": "Limit",
|
"Limit": "Limit",
|
||||||
|
"Limit Bandwidth in LAN": "Bandbreite im LAN begrenzen",
|
||||||
"Listener Failures": "Fehler bei Listener",
|
"Listener Failures": "Fehler bei Listener",
|
||||||
"Listener Status": "Status der Listener",
|
"Listener Status": "Status der Listener",
|
||||||
"Listeners": "Zuhörer",
|
"Listeners": "Zuhörer",
|
||||||
@@ -233,7 +238,7 @@
|
|||||||
"Local Additions": "Lokal hinzugefügte Elemente",
|
"Local Additions": "Lokal hinzugefügte Elemente",
|
||||||
"Local Discovery": "Lokale Gerätesuche",
|
"Local Discovery": "Lokale Gerätesuche",
|
||||||
"Local State": "Lokaler Status",
|
"Local State": "Lokaler Status",
|
||||||
"Local State (Total)": "Lokaler Status (Gesamt)",
|
"Local State (Total)": "Lokaler Status (gesamt)",
|
||||||
"Locally Changed Items": "Lokal geänderte Elemente",
|
"Locally Changed Items": "Lokal geänderte Elemente",
|
||||||
"Log": "Protokoll",
|
"Log": "Protokoll",
|
||||||
"Log File": "Protokolldatei",
|
"Log File": "Protokolldatei",
|
||||||
@@ -283,23 +288,23 @@
|
|||||||
"Ownership": "Besitzinformation",
|
"Ownership": "Besitzinformation",
|
||||||
"Password": "Passwort",
|
"Password": "Passwort",
|
||||||
"Path": "Pfad",
|
"Path": "Pfad",
|
||||||
"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": "Pfad zum Ordner auf dem lokalen Gerät. Ordner wird erzeugt, wenn er nicht existiert. Das Tilden-Zeichen (~) kann als Abkürzung benutzt werden für",
|
"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": "Pfad zum Ordner auf dem lokalen Gerät. Ordner wird erzeugt, wenn er nicht existiert. Das Tilde-Zeichen (~) kann als Abkürzung verwendet werden für",
|
||||||
"Path where versions should be stored (leave empty for the default .stversions directory in the shared folder).": "Pfad in dem Versionen gespeichert werden sollen (leer lassen, wenn der Standard .stversions Ordner für den geteilten Ordner verwendet werden soll).",
|
"Path where versions should be stored (leave empty for the default .stversions directory in the shared folder).": "Pfad, in dem Versionen gespeichert werden sollen (leer lassen für den Standardordner .stversions im freigegebenen Ordner).",
|
||||||
"Paths": "Pfade",
|
"Paths": "Pfade",
|
||||||
"Pause": "Pause",
|
"Pause": "Pause",
|
||||||
"Pause All": "Alles pausieren",
|
"Pause All": "Alles pausieren",
|
||||||
"Paused": "Pausiert",
|
"Paused": "Pausiert",
|
||||||
"Paused (Unused)": "Pausiert (Nicht genutzt)",
|
"Paused (Unused)": "Pausiert (nicht genutzt)",
|
||||||
"Pending changes": "Ausstehende Änderungen",
|
"Pending changes": "Ausstehende Änderungen",
|
||||||
"Periodic scanning at given interval and disabled watching for changes": "Periodischer Scan im angegebenen Intervall und Überwachung von Änderungen deaktiviert",
|
"Periodic scanning at given interval and disabled watching for changes": "Periodischer Scan im angegebenen Intervall und Überwachung von Änderungen deaktiviert",
|
||||||
"Periodic scanning at given interval and enabled watching for changes": "Periodischer Scan im angegebenen Intervall und Überwachung von Änderungen aktiviert",
|
"Periodic scanning at given interval and enabled watching for changes": "Periodischer Scan im angegebenen Intervall und Überwachung von Änderungen aktiviert",
|
||||||
"Periodic scanning at given interval and failed setting up watching for changes, retrying every 1m:": "Periodischer Scan im angegebenen Intervall, Überwachung von Änderungen fehlgeschlagen, erneuter Versuch jede Minute:",
|
"Periodic scanning at given interval and failed setting up watching for changes, retrying every 1m:": "Periodischer Scan im angegebenen Intervall, Überwachung von Änderungen fehlgeschlagen, erneuter Versuch jede Minute:",
|
||||||
"Permanently add it to the ignore list, suppressing further notifications.": "Permanent zur Ignorierliste hinzufügen, um weitere Benachrichtigungen zu unterdrücken.",
|
"Permanently add it to the ignore list, suppressing further notifications.": "Permanent zur Ignorierliste hinzufügen, um weitere Benachrichtigungen zu unterdrücken.",
|
||||||
"Please consult the release notes before performing a major upgrade.": "Bitte lesen Sie die Veröffentlichungshinweise bevor Sie eine Hauptversionsaktualisierung installieren.",
|
"Please consult the release notes before performing a major upgrade.": "Bitte lesen Sie die Veröffentlichungshinweise, bevor Sie eine Hauptversionsaktualisierung installieren.",
|
||||||
"Please set a GUI Authentication User and Password in the Settings dialog.": "Bitte lege einen Benutzer und ein Passwort für die Benutzeroberfläche in den Einstellungen fest.",
|
"Please set a GUI Authentication User and Password in the Settings dialog.": "Bitte legen Sie in den Einstellungen einen Benutzer und ein Passwort für die Benutzeroberfläche fest.",
|
||||||
"Please wait": "Bitte warten",
|
"Please wait": "Bitte warten",
|
||||||
"Prefix indicating that the file can be deleted if preventing directory removal": "Präfix, das anzeigt, dass die Datei gelöscht werden kann, wenn sie die Entfernung des Ordners verhindert",
|
"Prefix indicating that the file can be deleted if preventing directory removal": "Präfix, das anzeigt, dass die Datei gelöscht werden kann, wenn sie die Entfernung des Ordners verhindert",
|
||||||
"Prefix indicating that the pattern should be matched without case sensitivity": "Präfix, das anzeigt, dass das Muster ohne Beachtung der Groß- / Kleinschreibung übereinstimmen soll",
|
"Prefix indicating that the pattern should be matched without case sensitivity": "Präfix, das anzeigt, dass das Muster ohne Beachtung der Groß-/Kleinschreibung abgeglichen werden soll",
|
||||||
"Preparing to Sync": "Vorbereiten auf die Synchronisation",
|
"Preparing to Sync": "Vorbereiten auf die Synchronisation",
|
||||||
"Preview": "Vorschau",
|
"Preview": "Vorschau",
|
||||||
"Preview Usage Report": "Vorschau des Nutzungsberichts",
|
"Preview Usage Report": "Vorschau des Nutzungsberichts",
|
||||||
@@ -308,7 +313,7 @@
|
|||||||
"QUIC WAN": "QUIC WAN",
|
"QUIC WAN": "QUIC WAN",
|
||||||
"Quick guide to supported patterns": "Schnellanleitung zu den unterstützten Mustern",
|
"Quick guide to supported patterns": "Schnellanleitung zu den unterstützten Mustern",
|
||||||
"Random": "Zufall",
|
"Random": "Zufall",
|
||||||
"Receive Encrypted": "Empfange verschlüsselt",
|
"Receive Encrypted": "Verschlüsselt empfangen",
|
||||||
"Receive Only": "Nur empfangen",
|
"Receive Only": "Nur empfangen",
|
||||||
"Received data is already encrypted": "Empfangene Daten sind bereits verschlüsselt",
|
"Received data is already encrypted": "Empfangene Daten sind bereits verschlüsselt",
|
||||||
"Recent Changes": "Letzte Änderungen",
|
"Recent Changes": "Letzte Änderungen",
|
||||||
@@ -362,20 +367,20 @@
|
|||||||
"Shared With": "Geteilt mit",
|
"Shared With": "Geteilt mit",
|
||||||
"Sharing": "Teilen",
|
"Sharing": "Teilen",
|
||||||
"Show ID": "Eigene Kennung",
|
"Show ID": "Eigene Kennung",
|
||||||
"Show QR": "Zeige QR Code",
|
"Show QR": "QR-Code anzeigen",
|
||||||
"Show detailed discovery status": "Status der Gerätesuche anzeigen",
|
"Show detailed discovery status": "Status der Gerätesuche anzeigen",
|
||||||
"Show detailed listener status": "Detaillierten Listener-Status anzeigen",
|
"Show detailed listener status": "Detaillierten Listener-Status anzeigen",
|
||||||
"Show diff with previous version": "Unterschied zur vorherigen Version anzeigen",
|
"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 anstatt der Gerätekennung im Verbund-Status angezeigt. Wird als optionaler Standardname an andere Geräte bekannt gegeben.",
|
"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 auf diesem Gerät als Gerätename angezeigt und an die anderen Geräte im Geräte-Verbund weitergegeben. Wenn kein Gerätename angegeben wird, wird der Name des entfernten Gerätes genommen.",
|
"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",
|
"Shutdown Complete": "Vollständig heruntergefahren",
|
||||||
"Simple": "Einfach",
|
"Simple": "Einfach",
|
||||||
"Simple File Versioning": "Einfache Dateiversionierung",
|
"Simple File Versioning": "Einfache Dateiversionierung",
|
||||||
"Single level wildcard (matches within a directory only)": "Einzelnes Maskenzeichen (wird für einen einzelnen Ordner verwendet)",
|
"Single level wildcard (matches within a directory only)": "Einzelnes Maskenzeichen (wird für einen einzelnen Ordner verwendet)",
|
||||||
"Size": "Größe",
|
"Size": "Größe",
|
||||||
"Smallest First": "Kleinstes zuerst",
|
"Smallest First": "Kleinstes zuerst",
|
||||||
"Some discovery methods could not be established for finding other devices or announcing this device:": "Zum Auffinden anderer Geräte, oder um dieses Gerät anzukündigen, konnten manche Methoden nicht eingerichtet werden:",
|
"Some discovery methods could not be established for finding other devices or announcing this device:": "Einige Erkennungsmethoden zum Auffinden anderer Geräte oder zur Meldung dieses Geräts konnten nicht eingerichtet werden:",
|
||||||
"Some items could not be restored:": "Einige Elemente konnten nicht wiederhergestellt werden:",
|
"Some items could not be restored:": "Einige Elemente konnten nicht wiederhergestellt werden:",
|
||||||
"Some listening addresses could not be enabled to accept connections:": "An manchen Netzwerkadressen kann nicht gelauscht werden, um Verbindungen anzunehmen:",
|
"Some listening addresses could not be enabled to accept connections:": "An manchen Netzwerkadressen kann nicht gelauscht werden, um Verbindungen anzunehmen:",
|
||||||
"Source Code": "Quellcode",
|
"Source Code": "Quellcode",
|
||||||
@@ -388,7 +393,7 @@
|
|||||||
"Statistics": "Statistiken",
|
"Statistics": "Statistiken",
|
||||||
"Stay logged in": "Angemeldet bleiben",
|
"Stay logged in": "Angemeldet bleiben",
|
||||||
"Stopped": "Gestoppt",
|
"Stopped": "Gestoppt",
|
||||||
"Stores and syncs only encrypted data. Folders on all connected devices need to be set up with the same password or be of type \"{%receiveEncrypted%}\" too.": "Speichert und synchronisiert nur verschlüsselte Daten. Ordner auf allen verbundenen Geräten müssen mit dem selben Passwort eingerichtet werden oder vom Typ „{{receiveEncrypted}}“ sein.",
|
"Stores and syncs only encrypted data. Folders on all connected devices need to be set up with the same password or be of type \"{%receiveEncrypted%}\" too.": "Speichert und synchronisiert nur verschlüsselte Daten. Ordner auf allen verbundenen Geräten müssen mit demselben Passwort eingerichtet werden oder vom Typ „{{receiveEncrypted}}“ sein.",
|
||||||
"Subject:": "Betreff:",
|
"Subject:": "Betreff:",
|
||||||
"Support": "Support",
|
"Support": "Support",
|
||||||
"Support Bundle": "Supportpaket",
|
"Support Bundle": "Supportpaket",
|
||||||
@@ -396,7 +401,7 @@
|
|||||||
"Sync Ownership": "Besitzinformation synchronisieren",
|
"Sync Ownership": "Besitzinformation synchronisieren",
|
||||||
"Sync Protocol Listen Addresses": "Adresse(n) für das Synchronisierungsprotokoll",
|
"Sync Protocol Listen Addresses": "Adresse(n) für das Synchronisierungsprotokoll",
|
||||||
"Sync Status": "Status der Synchronisierung",
|
"Sync Status": "Status der Synchronisierung",
|
||||||
"Syncing": "Synchronisiere",
|
"Syncing": "Wird synchronisiert",
|
||||||
"Syncthing device ID for \"{%devicename%}\"": "Syncthing-Geräte-ID für „{{devicename}}“",
|
"Syncthing device ID for \"{%devicename%}\"": "Syncthing-Geräte-ID für „{{devicename}}“",
|
||||||
"Syncthing has been shut down.": "Syncthing wurde heruntergefahren.",
|
"Syncthing has been shut down.": "Syncthing wurde heruntergefahren.",
|
||||||
"Syncthing includes the following software or portions thereof:": "Syncthing enthält die folgende Software oder Teile von:",
|
"Syncthing includes the following software or portions thereof:": "Syncthing enthält die folgende Software oder Teile von:",
|
||||||
@@ -409,24 +414,24 @@
|
|||||||
"Syncthing is upgrading.": "Syncthing wird aktualisiert.",
|
"Syncthing is upgrading.": "Syncthing wird aktualisiert.",
|
||||||
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "Syncthing unterstützt jetzt automatische Absturzberichte an die Entwickler. Diese Funktion ist standardmäßig aktiviert.",
|
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "Syncthing unterstützt jetzt automatische Absturzberichte an die Entwickler. Diese Funktion ist standardmäßig aktiviert.",
|
||||||
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Syncthing scheint nicht erreichbar zu sein oder es gibt ein Problem mit der Internetverbindung. Erneuter Versuch …",
|
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Syncthing scheint nicht erreichbar zu sein oder es gibt ein Problem mit der Internetverbindung. Erneuter Versuch …",
|
||||||
"Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.": "Syncthing scheint ein Problem mit der Verarbeitung Deiner Eingabe zu haben. Bitte lade die Seite neu oder führe einen Neustart durch, falls das Problem weiterhin besteht.",
|
"Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.": "Syncthing scheint ein Problem mit der Verarbeitung Ihrer Eingabe zu haben. Laden Sie bitte die Seite neu oder führen einen Neustart durch, falls das Problem weiterhin besteht.",
|
||||||
"TCP LAN": "TCP LAN",
|
"TCP LAN": "TCP LAN",
|
||||||
"TCP WAN": "TCP WAN",
|
"TCP WAN": "TCP WAN",
|
||||||
"Take me back": "Führe mich zurück",
|
"Take me back": "Führen Sie mich zurück",
|
||||||
"The GUI address is overridden by startup options. Changes here will not take effect while the override is in place.": "Die GUI-Adresse wird durch Startoptionen überschrieben. Hier vorgenommene Änderungen werden nicht wirksam, solange die Überschreibung besteht.",
|
"The GUI address is overridden by startup options. Changes here will not take effect while the override is in place.": "Die GUI-Adresse wird durch Startoptionen überschrieben. Hier vorgenommene Änderungen werden nicht wirksam, solange die Überschreibung besteht.",
|
||||||
"The Syncthing Authors": "Die Syncthing-Autoren",
|
"The Syncthing Authors": "Die Syncthing-Autoren",
|
||||||
"The Syncthing admin interface is configured to allow remote access without a password.": "Die Syncthing-Oberfläche erlaubt mit den jetzigen Einstellungen einen Zugriff ohne Passwort.",
|
"The Syncthing admin interface is configured to allow remote access without a password.": "Die Syncthing-Oberfläche erlaubt mit den jetzigen Einstellungen einen Zugriff ohne Passwort.",
|
||||||
"The aggregated statistics are publicly available at the URL below.": "Die gesammelten Statistiken sind öffentlich unter der nachfolgenden URL verfügbar.",
|
"The aggregated statistics are publicly available at the URL below.": "Die gesammelten Statistiken sind öffentlich unter der nachfolgenden URL verfügbar.",
|
||||||
"The cleanup interval cannot be blank.": "Das Bereinigungsintervall darf nicht leer sein.",
|
"The cleanup interval cannot be blank.": "Das Bereinigungsintervall darf nicht leer sein.",
|
||||||
"The configuration has been saved but not activated. Syncthing must restart to activate the new configuration.": "Die Konfiguration wurde gespeichert, aber noch nicht aktiviert. Syncthing muss neugestartet werden, um die neue Konfiguration zu übernehmen.",
|
"The configuration has been saved but not activated. Syncthing must restart to activate the new configuration.": "Die Konfiguration wurde gespeichert, aber noch nicht aktiviert. Syncthing muss neu gestartet werden, um die neue Konfiguration zu übernehmen.",
|
||||||
"The device ID cannot be blank.": "Die Gerätekennung darf nicht leer sein.",
|
"The device ID cannot be blank.": "Die Gerätekennung darf nicht leer sein.",
|
||||||
"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).": "Die hier einzutragende Gerätekennung kann im Dialog „Aktionen > Kennung anzeigen“ auf dem anderen Gerät gefunden werden. Leerzeichen und Bindestriche sind optional (werden ignoriert).",
|
"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).": "Die hier einzutragende Gerätekennung kann im Dialog „Aktionen > Kennung anzeigen“ auf dem anderen Gerät gefunden werden. Leerzeichen und Bindestriche sind optional (werden ignoriert).",
|
||||||
"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.": "Der verschlüsselte Nutzungsbericht wird täglich gesendet. Er wird verwendet, um Statistiken über verwendete Betriebssysteme, Ordnergrößen und Programmversionen zu erstellen. Sollte der Bericht in Zukunft weitere Daten erfassen, wird dieses Fenster erneut angezeigt.",
|
"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.": "Der verschlüsselte Nutzungsbericht wird täglich gesendet. Er wird verwendet, um Statistiken über verwendete Betriebssysteme, Ordnergrößen und Programmversionen zu erstellen. Sollte der Bericht in Zukunft weitere Daten erfassen, wird dieses Fenster erneut angezeigt.",
|
||||||
"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.": "Die eingegebene Gerätekennung scheint nicht gültig zu sein. Es sollte eine 52 oder 56 stellige Zeichenkette aus Buchstaben und Nummern sein. Leerzeichen und Bindestriche sind optional.",
|
"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.": "Die eingegebene Gerätekennung scheint nicht gültig zu sein. Es sollte eine 52- oder 56-stellige Zeichenkette aus Buchstaben und Zahlen sein. Leerzeichen und Bindestriche sind optional.",
|
||||||
"The folder ID cannot be blank.": "Die Ordnerkennung darf nicht leer sein.",
|
"The folder ID cannot be blank.": "Die Ordnerkennung darf nicht leer sein.",
|
||||||
"The folder ID must be unique.": "Die Ordnerkennung muss eindeutig sein.",
|
"The folder ID must be unique.": "Die Ordnerkennung muss eindeutig sein.",
|
||||||
"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.": "Der Ordnerinhalt auf anderen Geräten wird überschrieben, um mit diesem Gerät identisch zu werden. Dateien, die hier nicht vorhanden sind, werden auf anderen Geräten gelöscht.",
|
"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.": "Der Ordnerinhalt auf anderen Geräten wird überschrieben, um mit diesem Gerät identisch zu werden. Dateien, die hier nicht vorhanden sind, werden auf anderen Geräten gelöscht.",
|
||||||
"The folder content on this device will be overwritten to become identical with other devices. Files newly added here will be deleted.": "Der Ordnerinhalt auf diesem Gerät wird überschrieben, um mit anderen Geräten identisch zu werden. Dateien, die hier neu hinzugefügt wurden, werden gelöscht.",
|
"The folder content on this device will be overwritten to become identical with other devices. Files newly added here will be deleted.": "Der Ordnerinhalt auf diesem Gerät wird überschrieben, damit er mit anderen Geräten identisch ist. Dateien, die hier neu hinzugefügt wurden, werden gelöscht.",
|
||||||
"The folder path cannot be blank.": "Der Ordnerpfad darf nicht leer sein.",
|
"The folder path cannot be blank.": "Der Ordnerpfad darf nicht leer sein.",
|
||||||
"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.": "Es wird in folgenden Abständen versioniert: In der ersten Stunde wird alle 30 Sekunden eine Version behalten, am ersten Tag eine jede Stunde, in den ersten 30 Tagen eine jeden Tag. Danach wird bis zum angegebenen Höchstalter eine Version pro Woche behalten.",
|
"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.": "Es wird in folgenden Abständen versioniert: In der ersten Stunde wird alle 30 Sekunden eine Version behalten, am ersten Tag eine jede Stunde, in den ersten 30 Tagen eine jeden Tag. Danach wird bis zum angegebenen Höchstalter eine Version pro Woche behalten.",
|
||||||
"The following items could not be synchronized.": "Die folgenden Elemente konnten nicht synchronisiert werden.",
|
"The following items could not be synchronized.": "Die folgenden Elemente konnten nicht synchronisiert werden.",
|
||||||
@@ -435,12 +440,12 @@
|
|||||||
"The following text will automatically be inserted into a new message.": "Der folgende Text wird automatisch in eine neue Nachricht eingefügt.",
|
"The following text will automatically be inserted into a new message.": "Der folgende Text wird automatisch in eine neue Nachricht eingefügt.",
|
||||||
"The following unexpected items were found.": "Die folgenden unerwarteten Elemente wurden gefunden.",
|
"The following unexpected items were found.": "Die folgenden unerwarteten Elemente wurden gefunden.",
|
||||||
"The interval must be a positive number of seconds.": "Das Intervall muss eine positive Zahl von Sekunden sein.",
|
"The interval must be a positive number of seconds.": "Das Intervall muss eine positive Zahl von Sekunden sein.",
|
||||||
"The interval, in seconds, for running cleanup in the versions directory. Zero to disable periodic cleaning.": "Das Intervall, in Sekunden, zwischen den Bereinigungen im Versionsverzeichnis. Null um das regelmäßige Bereinigen zu deaktivieren.",
|
"The interval, in seconds, for running cleanup in the versions directory. Zero to disable periodic cleaning.": "Das Intervall in Sekunden zwischen den Bereinigungen im Versionsverzeichnis. Null, um das regelmäßige Bereinigen zu deaktivieren.",
|
||||||
"The maximum age must be a number and cannot be blank.": "Das Höchstalter muss angegeben werden und eine Zahl sein.",
|
"The maximum age must be a number and cannot be blank.": "Das Höchstalter muss angegeben werden und eine Zahl sein.",
|
||||||
"The maximum time to keep a version (in days, set to 0 to keep versions forever).": "Die längste Zeit, die alte Versionen vorgehalten werden (in Tagen) (0 um alte Versionen für immer zu behalten).",
|
"The maximum time to keep a version (in days, set to 0 to keep versions forever).": "Die längste Zeit, die alte Versionen vorgehalten werden (in Tagen) (0, um alte Versionen für immer zu behalten).",
|
||||||
"The number of connections must be a non-negative number.": "Die Anzahl der Verbindungen muss eine nicht-negative Zahl sein.",
|
"The number of connections must be a non-negative number.": "Die Anzahl der Verbindungen muss eine nicht-negative Zahl sein.",
|
||||||
"The number of days must be a number and cannot be blank.": "Die Anzahl der Tage muss eine Ganzzahl sein und darf nicht leer sein.",
|
"The number of days must be a number and cannot be blank.": "Die Anzahl der Tage muss eine Ganzzahl sein und darf nicht leer sein.",
|
||||||
"The number of days to keep files in the trash can. Zero means forever.": "Dauer in Tagen für welche die Dateien aufgehoben werden sollen. 0 bedeutet für immer.",
|
"The number of days to keep files in the trash can. Zero means forever.": "Die Anzahl der Tage, die Dateien im Papierkorb verbleiben sollen. Null bedeutet für immer.",
|
||||||
"The number of old versions to keep, per file.": "Anzahl der alten Versionen, die von jeder Datei behalten werden sollen.",
|
"The number of old versions to keep, per file.": "Anzahl der alten Versionen, die von jeder Datei behalten werden sollen.",
|
||||||
"The number of versions must be a number and cannot be blank.": "Die Anzahl von Versionen muss eine Ganzzahl und darf nicht leer sein.",
|
"The number of versions must be a number and cannot be blank.": "Die Anzahl von Versionen muss eine Ganzzahl und darf nicht leer sein.",
|
||||||
"The path cannot be blank.": "Der Pfad darf nicht leer sein.",
|
"The path cannot be blank.": "Der Pfad darf nicht leer sein.",
|
||||||
@@ -456,16 +461,16 @@
|
|||||||
"This Device": "Dieses Gerät",
|
"This Device": "Dieses Gerät",
|
||||||
"This Month": "Dieser Monat",
|
"This Month": "Dieser Monat",
|
||||||
"This can easily give hackers access to read and change any files on your computer.": "Dies kann dazu führen, dass Unberechtigte relativ einfach auf Ihre Dateien zugreifen und diese ändern können.",
|
"This can easily give hackers access to read and change any files on your computer.": "Dies kann dazu führen, dass Unberechtigte relativ einfach auf Ihre Dateien zugreifen und diese ändern können.",
|
||||||
"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.": "Dieses Gerät kann nicht automatisch andere Geräte auffinden, oder seine eigene Adresse bekannt geben, um von anderen gefunden zu werden. Nur Geräte mit statisch konfigurierten Adressen können sich verbinden.",
|
"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.": "Dieses Gerät kann nicht automatisch andere Geräte auffinden oder seine eigene Adresse bekannt geben, um von anderen gefunden zu werden. Nur Geräte mit statisch konfigurierten Adressen können sich verbinden.",
|
||||||
"This is a major version upgrade.": "Dies ist eine Hauptversionsaktualisierung.",
|
"This is a major version upgrade.": "Dies ist eine Hauptversionsaktualisierung.",
|
||||||
"This setting controls the free space required on the home (i.e., index database) disk.": "Diese Einstellung regelt den freien Speicherplatz, der für den Systemordner (d.h. Indexdatenbank) erforderlich ist.",
|
"This setting controls the free space required on the home (i.e., index database) disk.": "Diese Einstellung regelt den freien Speicherplatz, der für den Systemordner (d. h. Indexdatenbank) erforderlich ist.",
|
||||||
"Time": "Zeit",
|
"Time": "Zeit",
|
||||||
"Time the item was last modified": "Zeit der letzten Änderung des Elements",
|
"Time the item was last modified": "Zeit der letzten Änderung des Elements",
|
||||||
"To connect with the Syncthing device named \"{%devicename%}\", add a new remote device on your end with this ID:": "Um sich mit dem Syncthing-Gerät namens „{{devicename}}“ zu verbinden, fügen Sie ein neues Gerät mit dieser ID hinzu:",
|
"To connect with the Syncthing device named \"{%devicename%}\", add a new remote device on your end with this ID:": "Um sich mit dem Syncthing-Gerät namens „{{devicename}}“ zu verbinden, fügen Sie ein neues Gerät mit dieser ID hinzu:",
|
||||||
"To permit a rule, have the checkbox checked. To deny a rule, leave it unchecked.": "Zum Erlauben einer Regel Häkchen setzen. Zum Verweigern einer Regel frei lassen.",
|
"To permit a rule, have the checkbox checked. To deny a rule, leave it unchecked.": "Zum Erlauben einer Regel Häkchen setzen. Zum Verweigern einer Regel frei lassen.",
|
||||||
"Today": "Heute",
|
"Today": "Heute",
|
||||||
"Trash Can": "Papierkorb",
|
"Trash Can": "Papierkorb",
|
||||||
"Trash Can File Versioning": "Papierkorb Dateiversionierung",
|
"Trash Can File Versioning": "Papierkorb-Dateiversionierung",
|
||||||
"Type": "Typ",
|
"Type": "Typ",
|
||||||
"UNIX Permissions": "UNIX-Berechtigungen",
|
"UNIX Permissions": "UNIX-Berechtigungen",
|
||||||
"Unavailable": "Nicht verfügbar",
|
"Unavailable": "Nicht verfügbar",
|
||||||
@@ -491,7 +496,7 @@
|
|||||||
"Use notifications from the filesystem to detect changed items.": "Benachrichtigungen des Dateisystems nutzen, um Änderungen zu erkennen.",
|
"Use notifications from the filesystem to detect changed items.": "Benachrichtigungen des Dateisystems nutzen, um Änderungen zu erkennen.",
|
||||||
"User": "Benutzer",
|
"User": "Benutzer",
|
||||||
"User Home": "Benutzer-Stammverzeichnis",
|
"User Home": "Benutzer-Stammverzeichnis",
|
||||||
"Username/Password has not been set for the GUI authentication. Please consider setting it up.": "Benutzername / Passwort wurde für die Benutzeroberfläche nicht gesetzt. Bitte erwägen Sie dies einzurichten.",
|
"Username/Password has not been set for the GUI authentication. Please consider setting it up.": "Benutzername / Passwort wurde für die Benutzeroberfläche nicht gesetzt. Bitte erwägen Sie, dies einzurichten.",
|
||||||
"Using a QUIC connection over LAN": "Verwendet eine QUIC-Verbindung über LAN",
|
"Using a QUIC connection over LAN": "Verwendet eine QUIC-Verbindung über LAN",
|
||||||
"Using a QUIC connection over WAN": "Verwendet eine QUIC-Verbindung über WAN",
|
"Using a QUIC connection over WAN": "Verwendet eine QUIC-Verbindung über WAN",
|
||||||
"Using a direct TCP connection over LAN": "Verwendet eine direkte TCP-Verbindung über LAN",
|
"Using a direct TCP connection over LAN": "Verwendet eine direkte TCP-Verbindung über LAN",
|
||||||
@@ -512,8 +517,8 @@
|
|||||||
"Watch for Changes": "Änderungen überwachen",
|
"Watch for Changes": "Änderungen überwachen",
|
||||||
"Watching for Changes": "Überwachung von Änderungen",
|
"Watching for Changes": "Überwachung von Änderungen",
|
||||||
"Watching for changes discovers most changes without periodic scanning.": "Das Überwachen von Änderungen entdeckt die meisten Änderungen ohne regelmäßiges Scannen.",
|
"Watching for changes discovers most changes without periodic scanning.": "Das Überwachen von Änderungen entdeckt die meisten Änderungen ohne regelmäßiges Scannen.",
|
||||||
"When adding a new device, keep in mind that this device must be added on the other side too.": "Beachte beim Hinzufügen eines neuen Gerätes, dass dieses Gerät auch auf den anderen Geräten hinzugefügt werden muss.",
|
"When adding a new device, keep in mind that this device must be added on the other side too.": "Beachten Sie beim Hinzufügen eines neuen Gerätes, dass dieses Gerät auch auf den anderen Geräten hinzugefügt werden muss.",
|
||||||
"When adding a new folder, keep in mind that the Folder ID is used to tie folders together between devices. They are case sensitive and must match exactly between all devices.": "Beachte bitte beim Hinzufügen eines neuen Ordners, dass die Ordnerkennung dazu verwendet wird, Ordner zwischen Geräten zu verbinden. Die Kennung muss also auf allen Geräten gleich sein, die Groß- und Kleinschreibung muss dabei beachtet werden.",
|
"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.": "Beachten Sie bitte beim Hinzufügen eines neuen Ordners, dass die Ordnerkennung dazu verwendet wird, Ordner zwischen Geräten zu verbinden. Die Kennung muss also auf allen Geräten gleich sein, die Groß- und Kleinschreibung muss dabei beachtet werden.",
|
||||||
"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.": "Wenn auf beiden Geräten der Wert höher als eins eingestellt ist, versucht Syncthing, mehrere gleichzeitige Verbindungen herzustellen. Wenn die Werte unterschiedlich sind, wird der höchste Wert verwendet. Den Wert auf Null setzen, um Syncthing entscheiden zu lassen.",
|
"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.": "Wenn auf beiden Geräten der Wert höher als eins eingestellt ist, versucht Syncthing, mehrere gleichzeitige Verbindungen herzustellen. Wenn die Werte unterschiedlich sind, wird der höchste Wert verwendet. Den Wert auf Null setzen, um Syncthing entscheiden zu lassen.",
|
||||||
"Yes": "Ja",
|
"Yes": "Ja",
|
||||||
"Yesterday": "Gestern",
|
"Yesterday": "Gestern",
|
||||||
@@ -524,8 +529,8 @@
|
|||||||
"You have no ignored devices.": "Sie haben keine ignorierten Geräte.",
|
"You have no ignored devices.": "Sie haben keine ignorierten Geräte.",
|
||||||
"You have no ignored folders.": "Sie haben keine ignorierten Ordner.",
|
"You have no ignored folders.": "Sie haben keine ignorierten Ordner.",
|
||||||
"You have unsaved changes. Do you really want to discard them?": "Sie haben nicht gespeicherte Änderungen. Wollen Sie diese wirklich verwerfen?",
|
"You have unsaved changes. Do you really want to discard them?": "Sie haben nicht gespeicherte Änderungen. Wollen Sie diese wirklich verwerfen?",
|
||||||
"You must keep at least one version.": "Du musst mindestens eine Version behalten.",
|
"You must keep at least one version.": "Sie müssen zumindest eine Version behalten.",
|
||||||
"You should never add or change anything locally in a \"{%receiveEncrypted%}\" folder.": "Sie sollten nie etwas im „{{receiveEncrypted}}“ Ordner lokal ändern oder hinzufügen.",
|
"You should never add or change anything locally in a \"{%receiveEncrypted%}\" folder.": "Sie sollten nie etwas im Ordner „{{receiveEncrypted}}“ lokal ändern oder hinzufügen.",
|
||||||
"Your SMS app should open to let you choose the recipient and send it from your own number.": "Ihre SMS-App sollte sich öffnen, damit Sie den Empfänger auswählen und die Nachricht von Ihrer eigenen Nummer aus versenden können.",
|
"Your SMS app should open to let you choose the recipient and send it from your own number.": "Ihre SMS-App sollte sich öffnen, damit Sie den Empfänger auswählen und die Nachricht von Ihrer eigenen Nummer aus versenden können.",
|
||||||
"Your email app should open to let you choose the recipient and send it from your own address.": "Ihre E-Mail-App sollte sich öffnen, damit Sie den Empfänger auswählen und die Nachricht von Ihrer eigenen Adresse aus versenden können.",
|
"Your email app should open to let you choose the recipient and send it from your own address.": "Ihre E-Mail-App sollte sich öffnen, damit Sie den Empfänger auswählen und die Nachricht von Ihrer eigenen Adresse aus versenden können.",
|
||||||
"days": "Tage",
|
"days": "Tage",
|
||||||
|
|||||||
@@ -154,7 +154,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 set up, retrying": "Αποτυχία ενεργοποίησης, γίνεται νέα προσπάθεια",
|
||||||
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Είναι φυσιολογική η αποτυχία σύνδεσης σε εξυπηρετητές IPv6 όταν δεν υπάρχει συνδεσιμότητα IPv6.",
|
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Είναι φυσιολογική η αποτυχία σύνδεσης σε εξυπηρετητές IPv6 όταν δεν υπάρχει συνδεσιμότητα IPv6.",
|
||||||
"File Pull Order": "Σειρά με την οποία θα κατεβαίνουν τα αρχεία",
|
"File Pull Order": "Σειρά με την οποία θα κατεβαίνουν τα αρχεία",
|
||||||
"File Versioning": "Τήρηση εκδόσεων αρχείων",
|
"File Versioning": "Τήρηση εκδόσεων αρχείων",
|
||||||
@@ -368,7 +368,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": "Απενεργοποίηση",
|
"Shut Down": "Απενεργοποίηση",
|
||||||
"Shutdown Complete": "Πλήρης απενεργοποίηση",
|
"Shutdown Complete": "Πλήρης απενεργοποίηση",
|
||||||
"Simple": "Απλό",
|
"Simple": "Απλό",
|
||||||
"Simple File Versioning": "Απλή τήρηση εκδόσεων",
|
"Simple File Versioning": "Απλή τήρηση εκδόσεων",
|
||||||
|
|||||||
@@ -150,7 +150,7 @@
|
|||||||
"Failed Items": "Failed Items",
|
"Failed Items": "Failed Items",
|
||||||
"Failed to load file versions.": "Failed to load file versions.",
|
"Failed to load file versions.": "Failed to load file versions.",
|
||||||
"Failed to load ignore patterns.": "Failed to load ignore patterns.",
|
"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.",
|
"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 Pull Order": "File Pull Order",
|
||||||
"File Versioning": "File Versioning",
|
"File Versioning": "File Versioning",
|
||||||
@@ -353,7 +353,7 @@
|
|||||||
"Show diff with previous version": "Show diff with previous version",
|
"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 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.",
|
"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",
|
"Shutdown Complete": "Shutdown Complete",
|
||||||
"Simple": "Simple",
|
"Simple": "Simple",
|
||||||
"Simple File Versioning": "Simple File Versioning",
|
"Simple File Versioning": "Simple File Versioning",
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
"Allowed Networks": "Allowed Networks",
|
"Allowed Networks": "Allowed Networks",
|
||||||
"Alphabetic": "Alphabetic",
|
"Alphabetic": "Alphabetic",
|
||||||
"Altered by ignoring deletes.": "Altered by ignoring deletes.",
|
"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.",
|
"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 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?",
|
"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:",
|
"Body:": "Body:",
|
||||||
"Bugs": "Bugs",
|
"Bugs": "Bugs",
|
||||||
"Cancel": "Cancel",
|
"Cancel": "Cancel",
|
||||||
|
"Cannot be enabled when the folder type is \"{%foldertype%}\".": "Cannot be enabled when the folder type is \"{{foldertype}}\".",
|
||||||
"Changelog": "Changelog",
|
"Changelog": "Changelog",
|
||||||
"Clean out after": "Clean out after",
|
"Clean out after": "Clean out after",
|
||||||
"Cleaning Versions": "Cleaning Versions",
|
"Cleaning Versions": "Cleaning Versions",
|
||||||
@@ -154,7 +156,7 @@
|
|||||||
"Failed Items": "Failed Items",
|
"Failed Items": "Failed Items",
|
||||||
"Failed to load file versions.": "Failed to load file versions.",
|
"Failed to load file versions.": "Failed to load file versions.",
|
||||||
"Failed to load ignore patterns.": "Failed to load ignore patterns.",
|
"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.",
|
"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 Pull Order": "File Pull Order",
|
||||||
"File Versioning": "File Versioning",
|
"File Versioning": "File Versioning",
|
||||||
@@ -225,6 +227,7 @@
|
|||||||
"Learn more": "Learn more",
|
"Learn more": "Learn more",
|
||||||
"Learn more at {%url%}": "Learn more at {{url}}",
|
"Learn more at {%url%}": "Learn more at {{url}}",
|
||||||
"Limit": "Limit",
|
"Limit": "Limit",
|
||||||
|
"Limit Bandwidth in LAN": "Limit Bandwidth in LAN",
|
||||||
"Listener Failures": "Listener Failures",
|
"Listener Failures": "Listener Failures",
|
||||||
"Listener Status": "Listener Status",
|
"Listener Status": "Listener Status",
|
||||||
"Listeners": "Listeners",
|
"Listeners": "Listeners",
|
||||||
@@ -368,7 +371,7 @@
|
|||||||
"Show diff with previous version": "Show diff with previous version",
|
"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 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.",
|
"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",
|
"Shutdown Complete": "Shutdown Complete",
|
||||||
"Simple": "Simple",
|
"Simple": "Simple",
|
||||||
"Simple File Versioning": "Simple File Versioning",
|
"Simple File Versioning": "Simple File Versioning",
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
"Allowed Networks": "Allowed Networks",
|
"Allowed Networks": "Allowed Networks",
|
||||||
"Alphabetic": "Alphabetic",
|
"Alphabetic": "Alphabetic",
|
||||||
"Altered by ignoring deletes.": "Altered by ignoring deletes.",
|
"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.",
|
"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 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?",
|
"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:",
|
"Body:": "Body:",
|
||||||
"Bugs": "Bugs",
|
"Bugs": "Bugs",
|
||||||
"Cancel": "Cancel",
|
"Cancel": "Cancel",
|
||||||
|
"Cannot be enabled when the folder type is \"{%foldertype%}\".": "Cannot be enabled when the folder type is \"{{foldertype}}\".",
|
||||||
"Changelog": "Changelog",
|
"Changelog": "Changelog",
|
||||||
"Clean out after": "Clean out after",
|
"Clean out after": "Clean out after",
|
||||||
"Cleaning Versions": "Cleaning Versions",
|
"Cleaning Versions": "Cleaning Versions",
|
||||||
@@ -80,6 +82,7 @@
|
|||||||
"Custom Range": "Custom Range",
|
"Custom Range": "Custom Range",
|
||||||
"Danger!": "Danger!",
|
"Danger!": "Danger!",
|
||||||
"Database Location": "Database Location",
|
"Database Location": "Database Location",
|
||||||
|
"Debug": "Debug",
|
||||||
"Debugging Facilities": "Debugging Facilities",
|
"Debugging Facilities": "Debugging Facilities",
|
||||||
"Default": "Default",
|
"Default": "Default",
|
||||||
"Default Configuration": "Default Configuration",
|
"Default Configuration": "Default Configuration",
|
||||||
@@ -154,7 +157,7 @@
|
|||||||
"Failed Items": "Failed Items",
|
"Failed Items": "Failed Items",
|
||||||
"Failed to load file versions.": "Failed to load file versions.",
|
"Failed to load file versions.": "Failed to load file versions.",
|
||||||
"Failed to load ignore patterns.": "Failed to load ignore patterns.",
|
"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.",
|
"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 Pull Order": "File Pull Order",
|
||||||
"File Versioning": "File Versioning",
|
"File Versioning": "File Versioning",
|
||||||
@@ -208,6 +211,7 @@
|
|||||||
"Incoming Rate Limit (KiB/s)": "Incoming Rate Limit (KiB/s)",
|
"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 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.",
|
"Incorrect user name or password.": "Incorrect user name or password.",
|
||||||
|
"Info": "Info",
|
||||||
"Internally used paths:": "Internally used paths:",
|
"Internally used paths:": "Internally used paths:",
|
||||||
"Introduced By": "Introduced By",
|
"Introduced By": "Introduced By",
|
||||||
"Introducer": "Introducer",
|
"Introducer": "Introducer",
|
||||||
@@ -225,6 +229,7 @@
|
|||||||
"Learn more": "Learn more",
|
"Learn more": "Learn more",
|
||||||
"Learn more at {%url%}": "Learn more at {{url}}",
|
"Learn more at {%url%}": "Learn more at {{url}}",
|
||||||
"Limit": "Limit",
|
"Limit": "Limit",
|
||||||
|
"Limit Bandwidth in LAN": "Limit Bandwidth in LAN",
|
||||||
"Listener Failures": "Listener Failures",
|
"Listener Failures": "Listener Failures",
|
||||||
"Listener Status": "Listener Status",
|
"Listener Status": "Listener Status",
|
||||||
"Listeners": "Listeners",
|
"Listeners": "Listeners",
|
||||||
@@ -368,7 +373,7 @@
|
|||||||
"Show diff with previous version": "Show diff with previous version",
|
"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 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.",
|
"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",
|
"Shutdown Complete": "Shutdown Complete",
|
||||||
"Simple": "Simple",
|
"Simple": "Simple",
|
||||||
"Simple File Versioning": "Simple File Versioning",
|
"Simple File Versioning": "Simple File Versioning",
|
||||||
|
|||||||
@@ -106,7 +106,7 @@
|
|||||||
"Error": "Eraro",
|
"Error": "Eraro",
|
||||||
"External File Versioning": "Ekstera Versionado de Dosiero",
|
"External File Versioning": "Ekstera Versionado de Dosiero",
|
||||||
"Failed Items": "Malsukcesaj Eroj",
|
"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.",
|
"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 Pull Order": "Ordo por Tiri Dosieron",
|
||||||
"File Versioning": "Versionado de Dosieroj",
|
"File Versioning": "Versionado de Dosieroj",
|
||||||
@@ -258,7 +258,7 @@
|
|||||||
"Show diff with previous version": "Montri diferenco kun antaŭa versio",
|
"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 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.",
|
"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",
|
"Shutdown Complete": "Sistemfermo Tuta",
|
||||||
"Simple File Versioning": "Simpla Versionado de Dosieroj",
|
"Simple File Versioning": "Simpla Versionado de Dosieroj",
|
||||||
"Single level wildcard (matches within a directory only)": "Ununivela ĵokero (egalas nur ene de dosierujo)",
|
"Single level wildcard (matches within a directory only)": "Ununivela ĵokero (egalas nur ene de dosierujo)",
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
"Add Device": "Agregar el dispositivo",
|
"Add Device": "Agregar el dispositivo",
|
||||||
"Add Folder": "Agregar Carpeta",
|
"Add Folder": "Agregar Carpeta",
|
||||||
"Add Remote Device": "Añadir un dispositivo remoto",
|
"Add Remote Device": "Añadir un dispositivo remoto",
|
||||||
"Add devices from the introducer to our device list, for mutually shared folders.": "Añadir dispositivos desde el introductor a nuestra lista de dispositivos, para las carpetas compartidas mutuamente.",
|
"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 filter entry": "Añadir una entrada al filtro",
|
||||||
"Add ignore patterns": "Agregar patrones a ignorar",
|
"Add ignore patterns": "Agregar patrones a ignorar",
|
||||||
"Add new folder?": "¿Agregar una carpeta nueva?",
|
"Add new folder?": "¿Agregar una carpeta nueva?",
|
||||||
@@ -29,7 +29,7 @@
|
|||||||
"Altered by ignoring deletes.": "Alterado ignorando eliminaciones.",
|
"Altered by ignoring deletes.": "Alterado ignorando eliminaciones.",
|
||||||
"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.",
|
"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 anónimo de uso",
|
||||||
"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. ¿Desearía usar el nuevo formato?",
|
"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",
|
"Applied to LAN": "Aplicado a la LAN",
|
||||||
"Apply": "Solicitar",
|
"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 override all remote changes?": "¿Está seguro(a) de que desea sobreescribir todos los cambios remotos?",
|
||||||
@@ -131,7 +131,7 @@
|
|||||||
"Edit Device": "Editar Dispositivo",
|
"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": "Editar Carpeta",
|
||||||
"Edit Folder Defaults": "Editar Valores Predeterminados de la Carpeta",
|
"Edit Folder Defaults": "Editar Valores Predeterminados de las Carpeta",
|
||||||
"Editing {%path%}.": "Editando {{path}}.",
|
"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 NAT traversal": "Permitir NAT transversal",
|
||||||
@@ -154,7 +154,7 @@
|
|||||||
"Failed Items": "Elementos fallidos",
|
"Failed Items": "Elementos fallidos",
|
||||||
"Failed to load file versions.": "Error al cargar las versiones de los archivos.",
|
"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 load ignore patterns.": "No se pudieron cargar los patrones de ignorar.",
|
||||||
"Failed to setup, retrying": "Fallo en la configuración, reintentando",
|
"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 un fallo al conectar a los servidores IPv6 si no hay conectividad IPv6.",
|
"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 Pull Order": "Orden de Obtención de los Archivos",
|
||||||
"File Versioning": "Versionado de ficheros",
|
"File Versioning": "Versionado de ficheros",
|
||||||
@@ -269,7 +269,7 @@
|
|||||||
"No upgrades": "Sin actualizaciones",
|
"No upgrades": "Sin actualizaciones",
|
||||||
"Not shared": "No Compartido(a)",
|
"Not shared": "No Compartido(a)",
|
||||||
"Notice": "Aviso",
|
"Notice": "Aviso",
|
||||||
"Number of Connections": "Número de las conexiones",
|
"Number of Connections": "Número de conexiones",
|
||||||
"OK": "De acuerdo",
|
"OK": "De acuerdo",
|
||||||
"Off": "Desactivado",
|
"Off": "Desactivado",
|
||||||
"Oldest First": "El más antiguo primero",
|
"Oldest First": "El más antiguo primero",
|
||||||
@@ -368,7 +368,7 @@
|
|||||||
"Show diff with previous version": "Mostrar la diferencia con la versión anterior",
|
"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 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.",
|
"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",
|
"Shut Down": "Apagar",
|
||||||
"Shutdown Complete": "Apagar completamente",
|
"Shutdown Complete": "Apagar completamente",
|
||||||
"Simple": "Sencillo",
|
"Simple": "Sencillo",
|
||||||
"Simple File Versioning": "Versionado simple de fichero",
|
"Simple File Versioning": "Versionado simple de fichero",
|
||||||
@@ -532,7 +532,7 @@
|
|||||||
"deleted": "eliminado",
|
"deleted": "eliminado",
|
||||||
"deny": "denegar",
|
"deny": "denegar",
|
||||||
"directories": "directorios",
|
"directories": "directorios",
|
||||||
"file": "expediente",
|
"file": "fichero",
|
||||||
"files": "archivos",
|
"files": "archivos",
|
||||||
"folder": "carpeta",
|
"folder": "carpeta",
|
||||||
"full documentation": "Documentación completa",
|
"full documentation": "Documentación completa",
|
||||||
|
|||||||
@@ -1,17 +1,26 @@
|
|||||||
{
|
{
|
||||||
"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.",
|
"A negative number of days doesn't make sense.": "Negatiivne päevade arv ei ole loogiline.",
|
||||||
"API Key": "API Võti",
|
"API Key": "API võti",
|
||||||
|
"About": "Rakenduse teave",
|
||||||
"Action": "Tegevus",
|
"Action": "Tegevus",
|
||||||
"Actions": "Tegevused",
|
"Actions": "Tegevused",
|
||||||
"Add": "Lisa",
|
"Add": "Lisa",
|
||||||
"Add Device": "Lisa seade",
|
"Add Device": "Lisa seade",
|
||||||
"Add Folder": "Lisa kaust",
|
"Add Folder": "Lisa kaust",
|
||||||
"Add new folder?": "Lisa uus kaust?",
|
"Add Remote Device": "Lisa kaugseade",
|
||||||
|
"Add new folder?": "Kas lisad uue kausta?",
|
||||||
"Address": "Aadress",
|
"Address": "Aadress",
|
||||||
|
"Addresses": "Aadressid",
|
||||||
|
"All Data": "Kõik andmed",
|
||||||
|
"All Time": "Kõik ajad",
|
||||||
|
"Allowed Networks": "Lubatud võrgud",
|
||||||
"Alphabetic": "Tähestikuline",
|
"Alphabetic": "Tähestikuline",
|
||||||
|
"Apply": "Rakenda",
|
||||||
"Automatic upgrades": "Automaatsed uuendused",
|
"Automatic upgrades": "Automaatsed uuendused",
|
||||||
"Be careful!": "Ettevaatust!",
|
"Be careful!": "Ettevaatust!",
|
||||||
|
"Cancel": "Loobu",
|
||||||
|
"Changelog": "Muudatuste nimekiri",
|
||||||
"Close": "Sulge",
|
"Close": "Sulge",
|
||||||
"Configured": "Seadistatud",
|
"Configured": "Seadistatud",
|
||||||
"Connection Error": "Ühenduse viga",
|
"Connection Error": "Ühenduse viga",
|
||||||
@@ -41,7 +50,7 @@
|
|||||||
"Folder Label": "Kausta Silt",
|
"Folder Label": "Kausta Silt",
|
||||||
"Folder Type": "Kausta Tüüp",
|
"Folder Type": "Kausta Tüüp",
|
||||||
"Folders": "Kaustad",
|
"Folders": "Kaustad",
|
||||||
"GUI": "GUI",
|
"GUI": "Kasutajaliides",
|
||||||
"GUI Authentication Password": "GUI Autentimise Salasõna",
|
"GUI Authentication Password": "GUI Autentimise Salasõna",
|
||||||
"GUI Authentication User": "GUI Autentimise Kasutajatunnus",
|
"GUI Authentication User": "GUI Autentimise Kasutajatunnus",
|
||||||
"GUI Theme": "GUI Teema",
|
"GUI Theme": "GUI Teema",
|
||||||
@@ -69,7 +78,7 @@
|
|||||||
"New Folder": "Uus Kaust",
|
"New Folder": "Uus Kaust",
|
||||||
"Newest First": "Uusimad Ennem",
|
"Newest First": "Uusimad Ennem",
|
||||||
"No": "Ei",
|
"No": "Ei",
|
||||||
"OK": "OK",
|
"OK": "Sobib",
|
||||||
"Oldest First": "Vanimad Ennem",
|
"Oldest First": "Vanimad Ennem",
|
||||||
"Options": "Valikud",
|
"Options": "Valikud",
|
||||||
"Outgoing Rate Limit (KiB/s)": "Väljuva Kiiruse Piirang (KiB/s)",
|
"Outgoing Rate Limit (KiB/s)": "Väljuva Kiiruse Piirang (KiB/s)",
|
||||||
@@ -110,7 +119,7 @@
|
|||||||
"The device ID cannot be blank.": "Seadme ID ei tohi olla tühi.",
|
"The device ID cannot be blank.": "Seadme ID ei tohi olla tühi.",
|
||||||
"The folder ID cannot be blank.": "Kausta ID ei tohi olla tühi.",
|
"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 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 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 maximum age must be a number and cannot be blank.": "Maksimaalne vanus peab olema arv ning ei tohi olla tühi.",
|
||||||
"Unknown": "Teadmata",
|
"Unknown": "Teadmata",
|
||||||
|
|||||||
@@ -130,7 +130,7 @@
|
|||||||
"External File Versioning": "Fitxategi bertsioen kanpoko kudeaketa",
|
"External File Versioning": "Fitxategi bertsioen kanpoko kudeaketa",
|
||||||
"Failed Items": "Huts egin duten fitxategiak",
|
"Failed Items": "Huts egin duten fitxategiak",
|
||||||
"Failed to load ignore patterns.": "Huts egin du baztertze ereduak kargatzean.",
|
"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",
|
"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 Pull Order": "Fitxategiak berreskuratzeko ordena",
|
||||||
"File Versioning": "Fitxategiak zaintzeko metodoa",
|
"File Versioning": "Fitxategiak zaintzeko metodoa",
|
||||||
@@ -309,7 +309,7 @@
|
|||||||
"Show diff with previous version": "Erakutsi aurreko bertsioarekiko aldeak",
|
"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 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",
|
"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!",
|
"Shutdown Complete": "Gelditua!",
|
||||||
"Simple File Versioning": "Bertsioen segitze sinplifikatua",
|
"Simple File Versioning": "Bertsioen segitze sinplifikatua",
|
||||||
"Single level wildcard (matches within a directory only)": "Hein bakar bateko jokerra (karpetaren barnean bakarrik dagokiona)",
|
"Single level wildcard (matches within a directory only)": "Hein bakar bateko jokerra (karpetaren barnean bakarrik dagokiona)",
|
||||||
|
|||||||
@@ -122,7 +122,7 @@
|
|||||||
"External": "Ulkoinen",
|
"External": "Ulkoinen",
|
||||||
"External File Versioning": "Ulkoinen tiedostoversionti",
|
"External File Versioning": "Ulkoinen tiedostoversionti",
|
||||||
"Failed Items": "Epäonnistuneet kohteet",
|
"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.",
|
"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 Pull Order": "Tiedostojen noutojärjestys",
|
||||||
"File Versioning": "Tiedostoversiointi",
|
"File Versioning": "Tiedostoversiointi",
|
||||||
@@ -288,7 +288,7 @@
|
|||||||
"Show diff with previous version": "Näytä muutokset edelliseen versioon",
|
"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 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.",
|
"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",
|
"Shutdown Complete": "Sammutus valmis",
|
||||||
"Simple File Versioning": "Yksinkertainen tiedostoversiointi",
|
"Simple File Versioning": "Yksinkertainen tiedostoversiointi",
|
||||||
"Single level wildcard (matches within a directory only)": "Yksitasoinen jokerimerkki (vaikuttaa vain kyseisen kansion sisällä)",
|
"Single level wildcard (matches within a directory only)": "Yksitasoinen jokerimerkki (vaikuttaa vain kyseisen kansion sisällä)",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"A device with that ID is already added.": "Nadagdag na ang device na may ganitong ID.",
|
"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 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 kasalukuyang bersyon.",
|
"A new major version may not be compatible with previous versions.": "Maaring hindi compatible ang isang bagong major na beryson sa mga nakaraang bersyon.",
|
||||||
"API Key": "API Key",
|
"API Key": "API Key",
|
||||||
"About": "Tungkol sa",
|
"About": "Tungkol sa",
|
||||||
"Action": "Aksyon",
|
"Action": "Aksyon",
|
||||||
@@ -27,6 +27,7 @@
|
|||||||
"Allowed Networks": "Mga Pinapayagang Network",
|
"Allowed Networks": "Mga Pinapayagang Network",
|
||||||
"Alphabetic": "Alpabetiko",
|
"Alphabetic": "Alpabetiko",
|
||||||
"Altered by ignoring deletes.": "Binago sa pamamagitan ng hindi pagpansin sa mga pagtanggal.",
|
"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.",
|
"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 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?",
|
"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,6 +53,7 @@
|
|||||||
"Body:": "Body:",
|
"Body:": "Body:",
|
||||||
"Bugs": "Mga Bug",
|
"Bugs": "Mga Bug",
|
||||||
"Cancel": "Kanselahin",
|
"Cancel": "Kanselahin",
|
||||||
|
"Cannot be enabled when the folder type is \"{%foldertype%}\".": "Hindi maaaring paganahin kapag ang uri ng folder ay \"{{foldertype}}\".",
|
||||||
"Changelog": "Mga Pagbabago",
|
"Changelog": "Mga Pagbabago",
|
||||||
"Clean out after": "Linisin pagkatapos",
|
"Clean out after": "Linisin pagkatapos",
|
||||||
"Cleaning Versions": "Mga Bersyon ng Paglinis",
|
"Cleaning Versions": "Mga Bersyon ng Paglinis",
|
||||||
@@ -80,6 +82,7 @@
|
|||||||
"Custom Range": "Custom na Saklaw",
|
"Custom Range": "Custom na Saklaw",
|
||||||
"Danger!": "Panganib!",
|
"Danger!": "Panganib!",
|
||||||
"Database Location": "Lokasyon ng Database",
|
"Database Location": "Lokasyon ng Database",
|
||||||
|
"Debug": "Debug",
|
||||||
"Debugging Facilities": "Mga Facility ng Pag-debug",
|
"Debugging Facilities": "Mga Facility ng Pag-debug",
|
||||||
"Default": "Default",
|
"Default": "Default",
|
||||||
"Default Configuration": "Default na Configuration",
|
"Default Configuration": "Default na Configuration",
|
||||||
@@ -154,7 +157,7 @@
|
|||||||
"Failed Items": "Mga Nabigong Item",
|
"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 bersyon ng file.",
|
||||||
"Failed to load ignore patterns.": "Nabigong i-load ang mga ignore pattern.",
|
"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.",
|
"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 Pull Order": "Order ng Pagkuha ng File",
|
||||||
"File Versioning": "File Versioning",
|
"File Versioning": "File Versioning",
|
||||||
@@ -208,6 +211,7 @@
|
|||||||
"Incoming Rate Limit (KiB/s)": "Rate Limit ng Papasok (KiB/s)",
|
"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 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.",
|
"Incorrect user name or password.": "Maling user name o password.",
|
||||||
|
"Info": "Impormasyon",
|
||||||
"Internally used paths:": "Mga internal na ginamit na path:",
|
"Internally used paths:": "Mga internal na ginamit na path:",
|
||||||
"Introduced By": "Ipinakilala Ni/Ng",
|
"Introduced By": "Ipinakilala Ni/Ng",
|
||||||
"Introducer": "Tagapagpakilala",
|
"Introducer": "Tagapagpakilala",
|
||||||
@@ -225,6 +229,7 @@
|
|||||||
"Learn more": "Matuto pa",
|
"Learn more": "Matuto pa",
|
||||||
"Learn more at {%url%}": "Matuto pa sa {{url}}",
|
"Learn more at {%url%}": "Matuto pa sa {{url}}",
|
||||||
"Limit": "Limitasyon",
|
"Limit": "Limitasyon",
|
||||||
|
"Limit Bandwidth in LAN": "Limitahan ang Bandwidth sa LAN",
|
||||||
"Listener Failures": "Mga Pagbibigo ng Listener",
|
"Listener Failures": "Mga Pagbibigo ng Listener",
|
||||||
"Listener Status": "Status ng Listener",
|
"Listener Status": "Status ng Listener",
|
||||||
"Listeners": "Mga Listener",
|
"Listeners": "Mga Listener",
|
||||||
@@ -311,7 +316,7 @@
|
|||||||
"Receive Encrypted": "Makatanggap Naka-Encrypt",
|
"Receive Encrypted": "Makatanggap Naka-Encrypt",
|
||||||
"Receive Only": "Makatanggap Lamang",
|
"Receive Only": "Makatanggap Lamang",
|
||||||
"Received data is already encrypted": "Naka-encrypt na ang natanggap na data",
|
"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",
|
"Reduced by ignore patterns": "Binabawasan ng mga ignore pattern",
|
||||||
"Relay LAN": "Relay na LAN",
|
"Relay LAN": "Relay na LAN",
|
||||||
"Relay WAN": "Relay na WAN",
|
"Relay WAN": "Relay na WAN",
|
||||||
@@ -368,7 +373,7 @@
|
|||||||
"Show diff with previous version": "Ipakita ang diff sa nakaraang bersyon",
|
"Show diff with previous version": "Ipakita ang diff sa nakaraang bersyon",
|
||||||
"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 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.",
|
"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",
|
"Shutdown Complete": "Tapos na ang Shutdown",
|
||||||
"Simple": "Simple",
|
"Simple": "Simple",
|
||||||
"Simple File Versioning": "Simpleng File Versioning",
|
"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 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 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 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 restarting.": "Nagre-restart ang Syncthing.",
|
||||||
"Syncthing is saving changes.": "Nagse-save ng mga pagbabago ang Syncthing.",
|
"Syncthing is saving changes.": "Nagse-save ng mga pagbabago ang Syncthing.",
|
||||||
"Syncthing is upgrading.": "Naga-upgrade ang Syncthing.",
|
"Syncthing is upgrading.": "Naga-upgrade ang Syncthing.",
|
||||||
@@ -417,17 +422,17 @@
|
|||||||
"The Syncthing Authors": "Ang Mga Awtor ng Syncthing",
|
"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 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 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 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 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 Aksyon > Ipakita ang ID\" na dialog sa isa pang device. Opsyonal ang mga puwang at gitling (hindi pinapansin).",
|
"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 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 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 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 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 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 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 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 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 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 following items could not be synchronized.": "Hindi ma-synchronize ang mga sumusunod na item.",
|
"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 items were changed locally.": "Binago ng lokal ang mga sumusunod na item.",
|
||||||
@@ -436,14 +441,14 @@
|
|||||||
"The following unexpected items were found.": "Nahanap ang mga sumusunod na hindi inaasahang item.",
|
"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 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 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 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 bersyon (bilang araw, itakda sa 0 para panatilihin ang mga bersyon magpakailanman).",
|
"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 number of connections must be a non-negative number.": "Dapat hindi negatibong numero ang bilang ng mga koneksyon.",
|
"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 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 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 number of versions must be a number and cannot be blank.": "Dapat numero ang bilang ng mga bersyon at hindi maaaring walang laman.",
|
||||||
"The path cannot be blank.": "Hindi maaring walang laman ang path.",
|
"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 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 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.",
|
"The remote device has not accepted sharing this folder.": "Hindi tinanggap ng remote device ang pagbahagi ng folder na ito.",
|
||||||
|
|||||||
@@ -160,7 +160,7 @@
|
|||||||
"Show QR": "Afficher l'image QR",
|
"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 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.",
|
"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é !",
|
"Shutdown Complete": "Arrêté !",
|
||||||
"Simple File Versioning": "Suivi simplifié des versions",
|
"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)",
|
"Single level wildcard (matches within a directory only)": "Joker à un seul niveau (correspond uniquement à l’intérieur du répertoire)",
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
"Allowed Networks": "Réseaux autorisés",
|
"Allowed Networks": "Réseaux autorisés",
|
||||||
"Alphabetic": "Alphabétique",
|
"Alphabetic": "Alphabétique",
|
||||||
"Altered by ignoring deletes.": "Protégé par \"Ignore Delete\".",
|
"Altered by ignoring deletes.": "Protégé par \"Ignore Delete\".",
|
||||||
|
"Always turned on when the folder type is \"{%foldertype%}\".": "Toujours activé pour le type de partage \"{{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.": "Une commande externe gère les versions de fichiers. Il lui incombe de supprimer les fichiers du répertoire partagé. Si le chemin contient des espaces, il doit être spécifié entre guillemets.",
|
"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.": "Une commande externe gère les versions de fichiers. Il lui incombe de supprimer les fichiers du répertoire partagé. Si le chemin contient des espaces, il doit être spécifié entre guillemets.",
|
||||||
"Anonymous Usage Reporting": "Rapport anonyme de statistiques d'utilisation",
|
"Anonymous Usage Reporting": "Rapport anonyme de statistiques d'utilisation",
|
||||||
"Anonymous usage report format has changed. Would you like to move to the new format?": "Le format du rapport anonyme d'utilisation a changé. Voulez-vous passer au nouveau format ?",
|
"Anonymous usage report format has changed. Would you like to move to the new format?": "Le format du rapport anonyme d'utilisation a changé. Voulez-vous passer au nouveau format ?",
|
||||||
@@ -52,6 +53,7 @@
|
|||||||
"Body:": "Corps du message :",
|
"Body:": "Corps du message :",
|
||||||
"Bugs": "Bogues",
|
"Bugs": "Bogues",
|
||||||
"Cancel": "Annuler",
|
"Cancel": "Annuler",
|
||||||
|
"Cannot be enabled when the folder type is \"{%foldertype%}\".": "Ne peut être activé pour le type de partage \"{{foldertype}}\".",
|
||||||
"Changelog": "Historique des versions",
|
"Changelog": "Historique des versions",
|
||||||
"Clean out after": "Conserver pendant",
|
"Clean out after": "Conserver pendant",
|
||||||
"Cleaning Versions": "Purge des versions",
|
"Cleaning Versions": "Purge des versions",
|
||||||
@@ -80,6 +82,7 @@
|
|||||||
"Custom Range": "Plage personnalisée",
|
"Custom Range": "Plage personnalisée",
|
||||||
"Danger!": "Attention !",
|
"Danger!": "Attention !",
|
||||||
"Database Location": "Emplacement de la base de données",
|
"Database Location": "Emplacement de la base de données",
|
||||||
|
"Debug": "Débogage",
|
||||||
"Debugging Facilities": "Outils de débogage",
|
"Debugging Facilities": "Outils de débogage",
|
||||||
"Default": "Par défaut",
|
"Default": "Par défaut",
|
||||||
"Default Configuration": "Préférences pour les créations (non rétroactif)",
|
"Default Configuration": "Préférences pour les créations (non rétroactif)",
|
||||||
@@ -154,7 +157,7 @@
|
|||||||
"Failed Items": "Éléments en échec",
|
"Failed Items": "Éléments en échec",
|
||||||
"Failed to load file versions.": "Échec de chargement des versions de fichiers.",
|
"Failed to load file versions.": "Échec de chargement des versions de fichiers.",
|
||||||
"Failed to load ignore patterns.": "Échec du chargement des masques d'exclusions.",
|
"Failed to load ignore patterns.": "Échec du chargement des masques d'exclusions.",
|
||||||
"Failed to setup, retrying": "Échec, nouvel essai",
|
"Failed to set up, retrying": "Échec, nouvel essai",
|
||||||
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "La connexion aux serveurs en IPv6 va échouer s'il n'y a pas de connectivité IPv6.",
|
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "La connexion aux serveurs en IPv6 va échouer s'il n'y a pas de connectivité IPv6.",
|
||||||
"File Pull Order": "Ordre de récupération des fichiers",
|
"File Pull Order": "Ordre de récupération des fichiers",
|
||||||
"File Versioning": "Préservation des fichiers",
|
"File Versioning": "Préservation des fichiers",
|
||||||
@@ -208,6 +211,7 @@
|
|||||||
"Incoming Rate Limit (KiB/s)": "Limite du débit de réception (Kio/s)",
|
"Incoming Rate Limit (KiB/s)": "Limite du débit de réception (Kio/s)",
|
||||||
"Incorrect configuration may damage your folder contents and render Syncthing inoperable.": "Une configuration incorrecte peut créer des dommages dans vos répertoires et mettre Syncthing hors-service.",
|
"Incorrect configuration may damage your folder contents and render Syncthing inoperable.": "Une configuration incorrecte peut créer des dommages dans vos répertoires et mettre Syncthing hors-service.",
|
||||||
"Incorrect user name or password.": "Nom d'utilisateur ou mot de passe incorrect.",
|
"Incorrect user name or password.": "Nom d'utilisateur ou mot de passe incorrect.",
|
||||||
|
"Info": "Informations",
|
||||||
"Internally used paths:": "Chemins utilisés en interne :",
|
"Internally used paths:": "Chemins utilisés en interne :",
|
||||||
"Introduced By": "Introduit par",
|
"Introduced By": "Introduit par",
|
||||||
"Introducer": "Appareil introducteur",
|
"Introducer": "Appareil introducteur",
|
||||||
@@ -225,6 +229,7 @@
|
|||||||
"Learn more": "En savoir plus",
|
"Learn more": "En savoir plus",
|
||||||
"Learn more at {%url%}": "Si vous souhaitez en savoir plus, c'est ici (en anglais) : {{url}}",
|
"Learn more at {%url%}": "Si vous souhaitez en savoir plus, c'est ici (en anglais) : {{url}}",
|
||||||
"Limit": "Limite",
|
"Limit": "Limite",
|
||||||
|
"Limit Bandwidth in LAN": "Limiter la bande passante locale (LAN)",
|
||||||
"Listener Failures": "Échecs de l'écouteur",
|
"Listener Failures": "Échecs de l'écouteur",
|
||||||
"Listener Status": "État de l'écouteur",
|
"Listener Status": "État de l'écouteur",
|
||||||
"Listeners": "Écoute",
|
"Listeners": "Écoute",
|
||||||
@@ -319,7 +324,7 @@
|
|||||||
"Release candidates contain the latest features and fixes. They are similar to the traditional bi-weekly Syncthing releases.": "Les versions préliminaires contiennent les dernières fonctionnalités et derniers correctifs. Elles sont identiques aux traditionnelles mises à jour bimensuelles.",
|
"Release candidates contain the latest features and fixes. They are similar to the traditional bi-weekly Syncthing releases.": "Les versions préliminaires contiennent les dernières fonctionnalités et derniers correctifs. Elles sont identiques aux traditionnelles mises à jour bimensuelles.",
|
||||||
"Remote Devices": "Autres appareils",
|
"Remote Devices": "Autres appareils",
|
||||||
"Remote GUI": "IHM distant",
|
"Remote GUI": "IHM distant",
|
||||||
"Remove": "Supprimer",
|
"Remove": "Enlever",
|
||||||
"Remove Device": "Supprimer l'appareil",
|
"Remove Device": "Supprimer l'appareil",
|
||||||
"Remove Folder": "Supprimer le partage",
|
"Remove Folder": "Supprimer le partage",
|
||||||
"Required identifier for the folder. Must be the same on all cluster devices.": "Identifiant du partage. Doit être le même sur tous les appareils concernés (généré aléatoirement, mais modifiable à la création, par exemple pour faire entrer un appareil dans un partage pré-existant actuellement non connecté mais dont on connaît déjà l'ID, ou s'il n'y a personne à l'autre bout pour vous inviter à y participer).",
|
"Required identifier for the folder. Must be the same on all cluster devices.": "Identifiant du partage. Doit être le même sur tous les appareils concernés (généré aléatoirement, mais modifiable à la création, par exemple pour faire entrer un appareil dans un partage pré-existant actuellement non connecté mais dont on connaît déjà l'ID, ou s'il n'y a personne à l'autre bout pour vous inviter à y participer).",
|
||||||
@@ -368,7 +373,7 @@
|
|||||||
"Show diff with previous version": "Afficher les différences avec la version précédente",
|
"Show diff with previous version": "Afficher les différences avec la version précédente",
|
||||||
"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 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.": "Nom convivial local affiché à la place de l'ID de l'appareil dans la plupart des écrans. Si laissé vide, c'est le nom convivial local de l'appareil distant qui sera utilisé. (Modifiable ultérieurement).",
|
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Nom convivial local affiché à la place de l'ID de l'appareil dans la plupart des écrans. Si laissé vide, c'est le nom convivial local de l'appareil distant qui sera utilisé. (Modifiable ultérieurement).",
|
||||||
"Shutdown": "Arrêter",
|
"Shut Down": "Arrêter",
|
||||||
"Shutdown Complete": "Arrêt complet",
|
"Shutdown Complete": "Arrêt complet",
|
||||||
"Simple": "Suivi simplifié",
|
"Simple": "Suivi simplifié",
|
||||||
"Simple File Versioning": "Suivi simplifié des versions",
|
"Simple File Versioning": "Suivi simplifié des versions",
|
||||||
|
|||||||
Loaded 100 of 751 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user