Compare commits

...

98 Commits

Author SHA1 Message Date
Jakob Borg
755daaa7b7 build: set netgo & osusergo tags for Linux build (#10261)
Avoid:

/_/GOROOT/src/os/user/cgo_lookup_cgo.go:45:(.text+0x54): warning: Using
'getgrgid_r' in statically linked applications requires at runtime the
shared libraries from the glibc version used for linking

and

/tmp/go-build/cgo-gcc-prolog:60:(.text+0x40): warning: Using
'getaddrinfo' in statically linked applications requires at runtime the
shared libraries from the glibc version used for linking
2025-08-16 06:33:01 +02:00
Jakob Borg
33b5c3c62e build: bump required language level to 1.24, compiler to 1.25 (#10248)
(After 2.0.1)
2025-08-16 06:02:58 +02:00
Jakob Borg
ffb30392e8 build: remove netgo and osusergo build tags (fixes #10251) (#10256)
I added these tags as part of the big database PR, but I forget why. I
think it came from an attempt at a static binary using the Go-based
SQLite packages, but that's not the primary build anymore anyway. We can
remove this and go back to the standard resolvers, which gives better
support for primarily Windows and macOS special resolution methods...
2025-08-14 21:32:06 +02:00
Jakob Borg
7a76685d7e fix: increase default delete retention to 15 months (#10252)
365 + 90 days = 10920h.

Also, actually enforce the minimum interval of 24h.
2025-08-14 08:15:53 +02:00
Jakob Borg
370bbb8f26 fix(db): handle path names that include URL special chars (fixes #10245) (#10247)
😬
2025-08-13 13:01:16 +02:00
Jakob Borg
9ea6c9c3c3 fix(etc): correct incantation to launch browser in Linux desktop file (#10246) 2025-08-13 09:56:58 +02:00
Jakob Borg
8f117a4417 build(deps): update (most) dependencies (#10243)
- Except jackpal/gateway which would bump Go
- Except quic-go which had some API change 
- While rebasing the patch on go-sqlite3
2025-08-12 22:34:40 +02:00
Ross Smith II
bbf48ae334 fix(all): various typos (#10242) 2025-08-12 20:05:10 +02:00
Jakob Borg
fcf4916086 fix: allow upgrade without config dir (fixes #10240) (#10241) 2025-08-12 18:44:57 +02:00
Jakob Borg
5d8033343f chore: repo mirror job 2025-08-11 22:16:10 +02:00
Jakob Borg
c74d2a9872 chore: update man pages for 2.0 2025-08-11 19:33:32 +02:00
Jakob Borg
3da84804b6 build: just special case stable-v2 for Debian for now 2025-08-11 19:17:33 +02:00
Jakob Borg
5b75c6ddcb build: split apt archive into major version generations 2025-08-11 18:57:18 +02:00
Syncthing Release Automation
ae03854575 chore(gui, man, authors): update docs, translations, and contributors 2025-08-11 04:06:30 +00:00
tomasz1986
ad196173d0 chore(gui): remove redundant "authenticated" conditions from Actions menu (#10235) (#10237)
chore(gui): remove redundant "authenticated" conditions from Actions
menu (#10235)

Due to previous code changes, the whole Actions menu is only available
when the user is logged in. As such, there is no reason to have the same
ng-if="authenticated" condition repeated in other items belonging to it.

Signed-off-by: Tomasz Wilczyński <twilczynski@naver.com>

Signed-off-by: Tomasz Wilczyński <twilczynski@naver.com>
2025-08-10 22:13:53 +02:00
Jakob Borg
d682220305 chore: remove GUI "debugging" toggle, debug HTTP metrics (#10235)
This removes the `debugging` bool under GUI configuration, and two no
longer relevant development endpoints: `httpmetrics` (which I can't
imagine anyone using for anything -- if we need such metrics today, the
right place is the Prometheus exported metrics) and the `peerCompletion`
endpoint (previously used by integration tests).

The debugging bool initially enabled just those two endpoints, which are
not for end users. Then we added profiling and support bundles, which
are very useful indeed for end users to access, and they were hidden
behind the same debug flag. I don't see any reason for keeping that flag
now that these methods are more generally useful.

https://github.com/syncthing/docs/pull/949
2025-08-10 21:14:25 +02:00
Jakob Borg
29e10e00d2 chore(slogutil): ensure quoting of empty and confusing log values (#10236)
Clearer parsing (also for humans)
2025-08-10 08:23:23 +00:00
Jakob Borg
34f61ce464 fix: correct logging of our ID after startup & generate (#10234)
This is one place we actually want the full string.
2025-08-10 06:25:13 +00:00
Ross Smith II
adcbd31e62 fix(test): remove lib/logger from testmocks target (#10231) 2025-08-09 09:56:37 +02:00
Jakob Borg
431da839cf fix(slogutil): quote values with parentheses in them (#10229)
Avoids an obvious parsing ambiguity in log lines.
2025-08-07 09:47:50 +00:00
Jakob Borg
836045ee87 feat: switch logging framework (#10220)
This updates our logging framework from legacy freetext strings using
the `log` package to structured log entries using `log/slog`. I have
updated all INFO or higher level entries, but not yet DEBUG (😓)... So,
at a high level:

There is a slight change in log levels, effectively adding a new warning
level:

- DEBUG is still debug (ideally not for users but developers, though
this is something we need to work on)
- INFO is still info, though I've added more data here, effectively
making Syncthing more verbose by default (more on this below)
- WARNING is a new log level that is different from the _old_ WARNING
(more below)
- ERROR is what was WARNING before -- problems that must be dealt with,
and also bubbled as a popup in the GUI.

A new feature is that the logging level can be set per package to
something other than just debug or info, and hence I feel that we can
add a bit more things into INFO while moving some (in fact, most)
current INFO level warnings into WARNING. For example, I think it's
justified to get a log of synced files in INFO and sync failures in
WARNING. These are things that have historically been tricky to debug
properly, and having more information by default will be useful to many,
while still making it possible get close to told level of inscrutability
by setting the log level to WARNING. I'd like to get to a stage where
DEBUG is never necessary to just figure out what's going on, as opposed
to trying to narrow down a likely bug.

Code wise:

- Our logging object, generally known as `l` in each package, is now a
new adapter object that provides the old API on top of the newer one.
(This should go away once all old log entries are migrated.) This is
only for `l.Debugln` and `l.Debugf`.
- There is a new level tracker that keeps the log level for each
package.
- There is a nested setup of handlers, since the structure mandated by
`log/slog` is slightly convoluted (imho). We do this because we need to
do formatting at a "medium" level internally so we can buffer log lines
in text format but with separate timestamp and log level for the API/GUI
to consume.
- The `debug` API call becomes a `loglevels` API call, which can set the
log level to `DEBUG`, `INFO`, `WARNING` or `ERROR` per package. The GUI
is updated to handle this.
- Our custom `sync` package provided some debugging of mutexes quite
strongly integrated into the old logging framework, only turned on when
`STTRACE` was set to certain values at startup, etc. It's been a long
time since this has been useful; I removed it.
- The `STTRACE` env var remains and can be used the same way as before,
while additionally permitting specific log levels to be specified,
`STTRACE=model:WARN,scanner:DEBUG`.
- There is a new command line option `--log-level=INFO` to set the
default log level.
- The command line options `--log-flags` and `--verbose` go away, but
are currently retained as hidden & ignored options since we set them by
default in some of our startup examples and Syncthing would otherwise
fail to start.

Sample format messages:

```
2009-02-13 23:31:30 INF A basic info line (attr1="val with spaces" attr2=2 attr3="val\"quote" a=a log.pkg=slogutil)
2009-02-13 23:31:30 INF An info line with grouped values (attr1=val1 foo.attr2=2 foo.bar.attr3=3 a=a log.pkg=slogutil)
2009-02-13 23:31:30 INF An info line with grouped values via logger (foo.attr1=val1 foo.attr2=2 a=a log.pkg=slogutil)
2009-02-13 23:31:30 INF An info line with nested grouped values via logger (bar.foo.attr1=val1 bar.foo.attr2=2 a=a log.pkg=slogutil)
2009-02-13 23:31:30 WRN A warning entry (a=a log.pkg=slogutil)
2009-02-13 23:31:30 ERR An error (a=a log.pkg=slogutil)
```

---------

Co-authored-by: Ross Smith II <ross@smithii.com>
2025-08-07 11:19:36 +02:00
Ross Smith II
49462448d0 feat(ignore): add .stignore escaping on Windows (#10205)
Based on the discussion in
https://forum.syncthing.net/t/towards-syncthing-2-0/24072/35 This PR
adds the ability for Windows users to use the pipe character (|) to
escape the metacharacters *, ?, [, and { in .stignore files.

Additionally, this PR adds the ability for the user to set the escape
character to backslash, or any character they want, by adding a line in
the form:

  #escape=X

(where X is any single rune), to the top of an .stignore file.

This would allow users to use the same .stignore file across platforms,
by simply adding

  #escape=\

to the top of the file.

### Testing

All tests pass in CI.

### Documentation

See https://github.com/syncthing/docs/pull/919

Fixes #10057: Support escaping in .stignore files on Windows
Fixes #7547: Ignore pattern with \[ and \] does not work
2025-08-05 09:55:39 +00:00
Jakob Borg
e3424ad503 fix(model): properly set folder state "syncing" when copying data (#10227)
Prior to this fix, the folder would only get marked as "syncing" once we
started downloading data from the network. However in some cases there
will be a lot of data that can be reused locally and we spend
significant time copying blocks before downloading anything; in that
case, the folder would appear as "preparing to sync" while it was in
fact moving lots of data.

This fixes that, making it "syncing" as soon as it begins either copying
or downloading data.
2025-08-05 09:47:44 +00:00
Syncthing Release Automation
5703423c00 chore(gui, man, authors): update docs, translations, and contributors 2025-08-04 04:11:16 +00:00
Alex Ionescu
356ec26c87 fix(gui): fix identicon generation (#10228)
### Purpose

Identicon generation is supposed to consider the first 15 characters of
a device ID, e.g. if the ID is `ABCDEFG-HIJKLMN-...`, the identicon
should be based on `ABCDEFGHIJKLMN`.

However, the current implementation only strips the first dash,
resulting in `ABCDEFGHIJKLM-`, so the last character is essentially
fixed for all IDs. This corresponds to the lower-middle pixel in
identicons always being "off" (see screenshots below).

The fix is simple: Just add the `g` flag to the regex used to strip
dashes.

### Screenshots

Old vs. new, light theme:

<img width="130" height="55" alt="light-old"
src="https://github.com/user-attachments/assets/7aaf5a2d-bc8e-4fd9-af94-2e8d723e5369"
/>

<img width="130" height="55" alt="light-new"
src="https://github.com/user-attachments/assets/b91a789b-5dbc-4d46-99e0-84d89f634e1f"
/>

Old vs. new, dark theme:

<img width="130" height="55" alt="dark-old"
src="https://github.com/user-attachments/assets/c3de5d4b-83d6-41fa-98da-18dd720462d3"
/>

<img width="130" height="55" alt="dark-new"
src="https://github.com/user-attachments/assets/4d49f563-0ee6-40df-bae4-89149abf9e7e"
/>

Old vs. new, black theme:

<img width="130" height="55" alt="black-old"
src="https://github.com/user-attachments/assets/4c2e75a9-e658-4bdf-b9cc-e3dbc375cde3"
/>

<img width="130" height="55" alt="black-new"
src="https://github.com/user-attachments/assets/c321c3d7-7bbb-433f-b750-2a688956ea40"
/>

Only ~50% of identicons will be affected, since it's based on the parity
of the 15th character.
2025-08-03 15:48:57 +02:00
Daniil Gentili
d37cb02e40 refactor(scanner): use recommended pattern for slice pool (#10225)
### Purpose

Uses recommended pattern for slice pools to avoid copying the slice
struct, suggested by the linter and actually used in the go stdlib, for
example in `net/http/h2_bundle.go`.
2025-08-01 11:27:53 +02:00
Daniil Gentili
953944e54e chore(fs): slightly reduce memory usage of IsParent (#10223)
### Purpose

Small optimizations for IsParent and IsInternal, to avoid needless
allocations.
2025-07-31 14:48:04 +00:00
Daniil Gentili
6e26fab3a0 chore(scanner): reduce memory pressure by using pools inside hasher (#10222) 2025-07-30 19:09:00 +02:00
Syncthing Release Automation
532e30eb6b chore(gui, man, authors): update docs, translations, and contributors 2025-07-28 04:07:18 +00:00
Marcus B Spencer
54bb987fae chore(config): remove fallback STUN servers that are CNAMEs to stun.counterpath.com (#10219)
ref
https://forum.syncthing.net/t/my-local-dns-server-technitium-is-getting-spammed-with-stun-lookups-that-are-failing/24627/2?u=marbens

### Purpose

Reduces unnecessary load on CounterPath's server(s).

### Testing

STUN is still functional, and appears to get the correct external ports,
if enabled.
2025-07-24 13:55:42 +02:00
Syncthing Release Automation
74367d2f66 chore(gui, man, authors): update docs, translations, and contributors 2025-07-21 04:06:40 +00:00
Syncthing Release Automation
0f6750c8f5 chore(gui, man, authors): update docs, translations, and contributors 2025-07-14 04:05:46 +00:00
tomasz1986
c8c38f735f fix(gui): show revert buttons only when folder is idle (fixes #10191) (#10212)
Currently, the Revert Local Changes button for Receive Only folders, and
the Delete Unexpected Items button for Receive Encrypted folders buttons
are shown even when the folder is already performing other operations.
Because of the above, pressing the button seems to have no effect, as
its operation can only proceed after the previous operations have
completed. This confuses the user, who then may keep trying to press the
buttons again and again with no visible result.

Therefore, show the two buttons only when the folders are actually idle,
without performing other operations at the same time. This change makes
them behave similarly to the Override Changes button, which is also only
displayed for Send Only folders when they are idle.

Signed-off-by: Tomasz Wilczyński twilczynski@naver.com
2025-07-12 14:08:11 +00:00
tomasz1986
fa4bd5c057 chore(gui): update fancytree from 2.38.0 to 2.38.5 (ref #10051, ref #10155) (#10214)
Update the jQuery Fancytree Plugin to the newest version. Apart from
keeping it up-to-date out of principle, this may also help with further
investigation of issue #10155, which is related to the plugin.

Signed-off-by: Tomasz Wilczyński twilczynski@naver.com
2025-07-12 13:02:50 +00:00
tomasz1986
36fb5425a5 chore(gui): fix "Shut Down" spelling in Actions (#10213)
chore(gui): Fix "Shut Down" spelling in Actions

Currently, the word is written as "Shutdown". However, similarly to
"Log Out", it is used as a verb here, thus it should be written as two
separate words, i.e. "Shut Down".

Signed-off-by: Tomasz Wilczyński twilczynski@naver.com
2025-07-12 14:52:51 +02:00
Marcus B Spencer
32a913c0ff refactor(beacon, osutil, upnp, netutil): only use anet on Android (#10211)
Add a wrapper that uses anet on Android, but net on other platforms.

### Purpose

Fixes
https://forum.syncthing.net/t/workaround-for-android-local-discovery/20403/12

### Testing

Run two Syncthing instances with Global Discovery disabled. Pair them
with each other, don't hardcode their addresses, and verify they
connect.
2025-07-08 08:18:51 +02:00
Jakob Borg
e8cfc8acfb build: improve next version calculation for bumped prereleases 2025-07-06 20:56:56 +02:00
Jakob Borg
7c07610ab2 fix: allow deleted files to win conflict resolution (#10207)
We've always, since the introduction of conflicts, had the policy that
deletes lose against any other change, for safety's sake. This is a
problem, however, because it means the sort order of versions is not a
total order.

That is, given two versions `A` and `B` that are currently in conflict,
we will sort them in a given order (let's say `A, B`, so `A < B` for
ordering purposes: we say "A wins over B" or "A is newer than B") and
consider the first in the list the winner. The loser (who has `B` on
disk) will process the conflict at some point and move the file to a
conflict copy and announce `A'` as the resolved conflict. The winner
(with `A` on disk) doesn't do anything.

However, if `A` is deleted the ordering changes. We still have `A < B`
and, of course, `Adel < A` (this is not even a conflict, just linear
order). In most sane systems this would imply the ordering `Adel < A <
B`, however in our case we in fact have `B < Adel` because any version
wins over a deleted one, so there is no logical ordering at all of the
files at this point. `Adel < A < B < Adel ???` In practice the deleted
version may end up at the head or the tail of the list, depending on the
order we do the compares.

Hence, at this point, "whatever" happens and it's not guaranteed to make
any sense. 😬

I propose that we resolve this my simply letting deletes be versions
like anything else and maintain a total ordering based on just version
vectors with the existing tie breakers like always. That means a delete
can win in a conflict situation, and the result should be that the file
is moved to a conflict copy on the losing device. I think this retains
the data safety to almost the same degree as previously, while removing
probably an entire class of strange out of sync bugs...

---

(A potential wrinkle here is that, ideally, we wouldn't even create the
conflict copy when the delete and the losing version represent the same
data -- same as when we handle normal modification conflicts. However,
the deleted FileInfo doesn't carry any information on what the contents
were, so we can't do that right now. A possible future extension would
be to carry the block list hash of the deleted data in the deleted
FileInfo and use that for this purpose, but I don't want to complicate
this PR with that. The block list hash itself also isn't a
protocol-defined thing at the moment, it's something implementation
dependent that we just use locally.)
2025-07-06 15:22:03 +02:00
Jakob Borg
ff88430efb feat: add debug commands for folder counts and files (#10206)
This adds two debugging commands that print information directly from
the database; one for folder counts, and one for file metadata for files
matching a pattern in a folder. E.g.,

```
% syncthing debug database-counts p3jms-73gps
DEVICE   TYPE       FLAGS    DELETED  COUNT  SIZE
-local-  FILE       -------  ---      0      0
-local-  FILE       --G----  ---      2473   70094796496
-local-  DIRECTORY  -------  ---      0      0
-local-  DIRECTORY  --G----  ---      19     2432
PSEUDOP  FILE       -------  ---      2473   70094796496
PSEUDOP  FILE       -nG----  ---      0      0
PSEUDOP  DIRECTORY  -------  ---      19     2432
PSEUDOP  DIRECTORY  -nG----  ---      0      0
```

```
% syncthing debug database-file p3jms-73gps 20240929-DSCF1387
DSCF1387
DEVICE   TYPE  NAME                          SEQUENCE  DELETED  MODIFIED                        SIZE      FLAGS    VERSION             BLOCKLIST
-local-  FILE  Austin/20240929-DSCF1387.raf  1204      ---      2024-09-29T01:10:54Z            48911888  --G----  HX2ELNU:1744213700  fsQdMvUL
PSEUDOP  FILE  Austin/20240929-DSCF1387.raf  22279     ---      2024-09-29T01:10:54Z            48911888  -------  HX2ELNU:1744213700  fsQdMvUL
-local-  FILE  Austin/20240929-DSCF1387.xmp  1196      ---      2024-10-16T08:08:35.137501751Z  5579      --G----  HX2ELNU:1744213700  xDGMnepi
PSEUDOP  FILE  Austin/20240929-DSCF1387.xmp  19910     ---      2024-10-16T08:08:35.137501751Z  5579      -------  HX2ELNU:1744213700  xDGMnepi
```

The local flag bits get a string representation for the bitmask,

```
	FlagLocalUnsupported:   "u",
	FlagLocalIgnored:       "i",
	FlagLocalMustRescan:    "r",
	FlagLocalReceiveOnly:   "e",
	FlagLocalGlobal:        "G",
	FlagLocalNeeded:        "n",
	FlagLocalRemoteInvalid: "v",
```
2025-07-04 15:46:24 +02:00
Catfriend1
06dd8ee6d7 fix(pmp, netutil): workaround native code denied to discover gateway ipv4 addr on Android 14+ (#10204)
### Purpose

As discussed on the forum:
https://forum.syncthing.net/t/reviving-nat-pmp-in-v2-x-on-android-14/24554

TL;DR
Android 14+ only lets java code get the gateway IPv4 address which is
used in SyncthingNative’s NAT-PMP feature.

So I’ve added the java code to the wrapper, got the router IP address
and feeded it to SyncthingNative by setting the env var
“FALLBACK_NET_GATEWAY_IPV4”.

This revives the NAT feature:

> [Z36WU] INFO: Detected 1 NAT service

### Testing

Local build and test via Android emulator (AVD 15).
2025-07-02 20:40:38 +02:00
Jakob Borg
c0aa7b436c chore: disable golangci-lint wsl_v5 check
Apparently they renamed a check so it snuck in despite being disabled
2025-06-30 23:29:45 +02:00
Syncthing Release Automation
b80fa9dcd2 chore(gui, man, authors): update docs, translations, and contributors 2025-06-30 04:01:54 +00:00
Jakob Borg
95187bcc64 chore(protocol): minor cleanup of ClusterConfig messages; remove DisableTempIndexes option (#10202)
This makes a couple of backwards compatible changes to the
ClusterConfig:

- Remove the `ignore_permissions` and `ignore_delete` booleans which
we've never read or used for anything
- Remove the `disable_temp_indexes` boolean and option entirely. We did
use this one, and about 1% of users have set the option. The only thing
it does is inhibits sending of periodical DownloadProgress messages
while downloading data, which is a minuscule bandwidth optimisation
given that we're already sending data at the time.
- Change the `read_only` boolean (which indicated send-only folders) to
an enum `FolderType`, where the values zero and one match the existing
usage. Again, we don't actually use this value, but I can see that we
might want to and then it makes more sense for it to be more
comprehensive.
- Change the `paused` boolean to an enum `StopReason`, where zero
indicates not stopped and one indicates paused, exactly the same wire
representation as previously but leaves space for additional stop
reasons (errors etc).
2025-06-29 10:18:51 +02:00
Catfriend1
f2a5b62733 build: unset build ID in generated binaries (#10203) 2025-06-28 12:32:07 +00:00
ardevd
385ca6772c chore(gui): added spacing between folder name and error message (#10201)
### Purpose
Filesystem watcher errors didnt have any whitespace between the share
name and the error message, making it hard to read. A simple colon and
whitespace solves this issue
2025-06-28 09:06:45 +00:00
Simon Frei
88c307b65b chore(config): increase max concurrent writes default (#10200)
I lately wanted some photos on my phone, and watched them sync
excrutiatingly slowly. I am used to android being slow, but not that
slow. This restriction caught my eye and I increased it beyond the
limit (didn't spot it at first), and I did see a clear improvement. Of
course as always with such a one-off test, I might also have
hallucinated it, but it seems plausible with the slow thing in android
being some layer between the actual filesystem and apps.

Also increase the max limit, mostly just because I don't see any reason
to restrict it that low - not that I have a particular reason to want
more.

I also changed the xml default to 0: The `prepare` code will change it
to the actual default - no need to change that anymore if we change the
default in the future.
2025-06-28 08:59:50 +00:00
Catfriend1
9d425b0588 fix(beacon, osutil, upnp): fix local discovery send and intf detection on Android (#10196)
Before:
- Local discovery on Android 10+ is broken. The phone receives local
discovery packets from other devices running Syncthing on the same
network, e.g. a computer. But it doesn't send its own local discovery
packets.
- Startup of the beacon/broadcast.go and beacon/multicast.go "services"
subsequently fail, see the log entries of "service.go" with "2 of 2
failures, backing off".

Root cause:
- Android 10+ restricts determining the network interfaces for privacy
reasons. The interfaces and IP addresses cannot be determined.
- There's a bug in the go "net" library. I can actually get the
interfaces, but the fix was not implemented by the go team.

Workaround:
- The "community" found a workaround by creating a light wrapper around
"net" called "anet" library.
- "anet" adjusts the behaviour on Android 10+ and gets the interfaces
plus their IP addresses, as required by Syncthing.

After:
- By using the "anet" lib, Syncthing is able to get the interface ip
addresses and put them into the "AllAddresses" string array.
- The "AllAddresses" string array is then announced on the local
discovery multicast and broadcast packets, if enabled in Syncthing's
config.
- By correctly getting the interfaces and IP addresses using "anet" in
"beacon/broadcast.go" and "beacon/multicast.go", the services start up
fine again.

Verification: 
- I've built "libSyncthingNative.so" with this PR applied for Android
and put it into Syncthing-Fork v1.29.7.5 for testing. My two phones,
Android 10 and Android 15 (arm64-v8a) immediately discovered each other
using local discovery.
- I can see the "sent XX bytes" and "recv XX bytes" on both phones in
the log filtering for "SyncthingNativeCode" :-).

Personal note:
- Please go light on me, and, if it's not demanded too much of your
time, please help me on this. I am no go programmer. Most things you
think are easy or common sense aren't part of my knowledge set. I'd just
like to help and hope we somehow can drive this home together to fix the
problem.

----

ref: https://github.com/Catfriend1/syncthing-android/pull/1501
ref: https://github.com/Catfriend1/syncthing-android/issues/1500
ref: https://github.com/wlynxg/anet/blob/main/interface.go &
https://github.com/wlynxg/anet/blob/main/interface_android.go

With that fix, I can see the broadcast/multicast lines again and my
phone can be discovered by other phones running the Syncthing app which
wasn't possible before on Android 10+.

```
[ET76H] .346892 broadcast.go:107: DEBUG: sent 185 bytes to 192.168.x.255:21027
[ET76H] .347114 multicast.go:86: DEBUG: sent 185 bytes to [ff12::8384]:21027 on wlan0
```

---------

Co-authored-by: Marcus B Spencer <marcus@marcusspencer.us>
2025-06-25 18:18:12 +00:00
Syncthing Release Automation
cf84a260ca chore(gui, man, authors): update docs, translations, and contributors 2025-06-23 04:02:01 +00:00
Jakob Borg
c4e024c7e3 build: fix detection of next rc version 2025-06-20 11:17:48 +02:00
Jakob Borg
c5a29b5b26 fix(model): don't clobber local flags when receiving index (#10190) 2025-06-20 07:08:06 +00:00
Marcus B Spencer
4c64843d60 feat(connections, nat): add UDP portmapping/pinhole for QUIC (fixes #7403) (#10171)
Fixes #7403.

Tested by enabling UPnP on the router, and checking on the router page
that the external ports of the UDP mappings match what is shown in the
logs and the internal ports matching the QUIC listening port.
2025-06-20 04:24:45 +00:00
Jakob Borg
b4ff96d754 chore(model): log folder removal
Relevant to #10189, #8416
2025-06-18 19:33:41 +02:00
Jakob Borg
21c5ac2161 chore: remove bad comment in authors script
Closes #10185
2025-06-17 12:27:12 +02:00
tomasz1986
6fc0b41f97 feat(gui): add option to limit bandwidth in LAN to Settings (ref #10046) (#10182)
gui: Add option to limit bandwidth in LAN to Settings (ref #10046)

Currently, the option to limit bandwidth in LAN is available only via
the Advanced Configuration, which makes it difficult to discover by new
users, who are confused why their bandwidth limits are not working.

For this reason, make the option easily discoverable by adding it
directly to Connections in the normal Settings in the Web GUI.

Ref:
https://github.com/syncthing/syncthing/issues/10046
https://github.com/syncthing/syncthing/issues/2046
https://github.com/syncthing/syncthing/issues/2569
https://forum.syncthing.net/t/upload-rate-not-work/19025

https://forum.syncthing.net/t/rate-limits-dont-work-over-lan-in-v1-20-1/18521

Signed-off-by: Tomasz Wilczyński <twilczynski@naver.com>

### Screenshots


![image](https://github.com/user-attachments/assets/bd38b3ab-a053-4992-ba86-7fb8b3ba09d9)

Signed-off-by: Tomasz Wilczyński <twilczynski@naver.com>
2025-06-17 09:46:38 +02:00
yparitcher
0b0b2143ed fix(protocol): slightly loosen/correct ownership comparison criteria (fixes #9879) (#10176)
Only Require either matching UID & GID OR matching Names.

If the 2 devices have a different Name => UID mapping, they can never be
totaly equal. Therefore when syncing we try matching the Name and fall
back to the UID. However when scanning for changes we currently require
both the Name & UID to match. This leads to forever having out of sync
files back and forth, or local additions when receive only.

This patch does not change the sending behavoir. It only change what we
decide is equal for exisiting files with mismapped Name => UID,

The added testcases show the change: Test 1,5,6 are the same as current.
Test 2,3 Are what change with this patch (from false to true). Test 4 is
a subset of test 2 they is currently special cased as true, which does
not chnage.

Co-authored-by: Jakob Borg <jakob@kastelo.net>
2025-06-16 15:12:33 +00:00
Jakob Borg
af64140c61 fix(model): avoid flashing "Sync Waiting" unnecessarily (#10181) 2025-06-16 12:53:02 +02:00
Syncthing Release Automation
1c68062231 chore(gui, man, authors): update docs, translations, and contributors 2025-06-16 04:00:36 +00:00
Jakob Borg
4d92855d76 build: release job needs full checkout with tags 2025-06-15 10:37:37 +02:00
Jakob Borg
1c6f542cb7 build: use proper ref for build action 2025-06-15 10:21:03 +02:00
Jakob Borg
b28066c85d build: use access token to trigger release builds 2025-06-15 10:14:13 +02:00
Simon Frei
71c8a2c36f fix(db): remove invalid member from FileMetadata (#10180) 2025-06-15 09:12:25 +02:00
Simon Frei
e4ab7b4ff3 fix(watchaggregator): properly handle sub-second watch durations (fixes #9927) (#10179)
I'll let Audrius words from the ticket explain this :)

> I'm a bit lost, time.Duration is an int64, yet watcher delay is float,
> anything sub 1s gets rounded down to 0, so you just end up going into
an
> infinite loop.


https://github.com/syncthing/syncthing/issues/9927#issuecomment-2967736106
2025-06-14 00:16:22 +02:00
Simon Frei
8b978d4712 chore: add migration for remote invalid local flag (#10174)
Follow to resp. migration for the change in this commit:

7b319111d3

---------

Co-authored-by: Jakob Borg <jakob@kastelo.net>
2025-06-13 21:28:07 +00:00
Simon Frei
7b319111d3 fix: track invalid files in LocalFlags to fix global count (#10170)
Move the "invalid" bit to a local flag, making it easier to track in counts etc.
2025-06-13 07:33:31 +02:00
Simon Frei
cb7cea93a2 chore(model): remove redundant removal of internal fields in indexsender (#10173)
While it doesn't hurt, it's unnecessary since the big protobuf
modernisation, that also introduced types separate from the generated
ones for internal use. Those fields are already dropped when converting
to the wire in protocol.
2025-06-12 22:08:21 +00:00
Jakob Borg
20257faf54 build: compat entry for Go 1.25 2025-06-11 22:40:20 +02:00
ardevd
c14abebd68 refactor(syncthing): use named constant for SIGHUP (#10168) 2025-06-09 20:31:01 +00:00
ardevd
b1a1a90045 chore(syncthing): ensure response body is closed in upgrade request (#10169) 2025-06-09 22:10:17 +02:00
Jakob Borg
8afc9855f2 feat: use Ed25519 keys for sync connections (#10162)
This updates our key generation to use Ed25519 keys/certificates for
sync connections. Certificates for browser use remain ECDSA for wider
compatibility.

Ed25519 is more modern and has fewer concerns for the future than the
ECDSA curves we used previously. It is supported from Go 1.13 and
forwards, which is Syncthing 1.3.0 (October 2019).
2025-06-09 05:48:01 +00:00
Marcus B Spencer
4215058911 fix(gui): don't show dial errors for paused devices (fixes #10166) (#10167)
### Purpose

Fixes the problem where paused devices show errors from dials from
previous disconnections (#10166).

### Testing

Tested manually by reproducing using the steps in the mentioned issue.

### Screenshots

Before:

![dial
errors](https://github.com/user-attachments/assets/c12a5566-beb2-4b27-9fcb-5a6d1397028d)

After:

![no dial
errors](https://github.com/user-attachments/assets/7a31e1e6-243a-4b15-b3a1-cf226b58abe0)

Co-authored-by: Jakob Borg <jakob@kastelo.net>
2025-06-09 05:38:07 +00:00
Jakob Borg
9fb1a18dbf fix(strelaypoolsrv): trivial error handling fix 2025-06-09 07:25:31 +02:00
Ross Smith II
064213ceb8 fix(fs): check for unsupported error on modern Windows (fixes #10164) (#10165)
### Purpose

Locally, on Windows 11, and on the windows-2025 GitHub runner (go 1.23
and 1.24), the `TestCopyRange` test is failing with `The request is not
supported.`

On windows-2022 and windows-2019:
```go
err == syscall.ENOTSUP
```
worked, but on Windows 11 and windows-2025, we need:
```go
errors.Is(err, errors.ErrUnsupported)
```

### Testing

Tested on Windows 11, windows-2019, windows-2022, and
[windows-2025](https://github.com/rasa/syncthing/actions/runs/15525123437/job/43703630634#step:7:2811).
2025-06-09 07:20:46 +02:00
Syncthing Release Automation
0211251b34 chore(gui, man, authors): update docs, translations, and contributors 2025-06-09 04:00:17 +00:00
Jakob Borg
1903da569b build: explicitly trigger build after pushing release tag (#10160)
Because just pushing the tag with a non-Actions token doesn't suffice,
apparently
2025-06-07 14:22:39 +00:00
Jakob Borg
b6a7beca1f build: build both Debian armel and armhf (though they are the same for us) (#10159)
Current "arm" is wrong
2025-06-07 16:04:28 +02:00
Jakob Borg
7b83e7403e build: limit golangci-lint to pull requests
Since all the existing code isn't clean
2025-06-07 15:04:30 +02:00
Jakob Borg
1915a470e9 chore: update AUTHORS 2025-06-07 14:31:58 +02:00
Jakob Borg
0b100296e1 Merge branch 'v2'
* v2: (62 commits)
  build: add dependency for next-version script
  docs: add release note mention of platforms no longer built
  build: streamline gathering of facts, checkouts (#10158)
  build: more resilient pushes to releases
  chore(etc): add option dash to upstart config
  chore(fs): linter complaints
  chore(model): the easier linter complaints
  chore(internal): linter complaints
  chore(sqlite): linter complaints
  build: allow v2 into APT candidate channel
  docs: link to Docker image, APT, in release notes
  build: refactor builds for forks/PRs
  build: use same GitHub token for releases as for translation etc pushes
  refactor(sqlite): move deleted flag into logical order in schema
  feat(config): enable multiple connections by default (#10151)
  docs: mention subcommands in release notes, use for all 2.0 releases
  docs: adjust release notes for v2.0.0
  docs: add relnotes for v2.0.0
  build: upgrade setup-zig action (#10134)
  fix(versioner): correct fs creation in test
  ...
2025-06-07 14:18:48 +02:00
Jakob Borg
e6ed3acf5f build: add dependency for next-version script 2025-06-07 14:18:12 +02:00
Jakob Borg
9f95bf3573 docs: add release note mention of platforms no longer built 2025-06-07 13:35:21 +02:00
Jakob Borg
b05ece0681 build: more resilient pushes to releases 2025-06-07 13:18:58 +02:00
Jakob Borg
5381178c46 build: streamline gathering of facts, checkouts (#10158) 2025-06-07 13:11:52 +02:00
Jakob Borg
e7f4f8306c Merge branch 'main' into v2
* main:
  build: more resilient pushes to releases
2025-06-07 09:35:00 +02:00
Jakob Borg
9922a3abd9 build: more resilient pushes to releases 2025-06-07 09:34:49 +02:00
Jakob Borg
40ab668a73 chore(etc): add option dash to upstart config 2025-06-07 08:20:48 +02:00
Jakob Borg
10d20c4800 chore(fs): linter complaints 2025-06-06 13:45:44 +02:00
Jakob Borg
700bb75016 chore(model): the easier linter complaints 2025-06-06 13:45:44 +02:00
Jakob Borg
e25de22705 chore(internal): linter complaints 2025-06-06 13:45:44 +02:00
Jakob Borg
ef6d561c66 chore(sqlite): linter complaints 2025-06-06 13:45:44 +02:00
Jakob Borg
3c92999406 build: allow v2 into APT candidate channel 2025-06-06 11:00:55 +02:00
Jakob Borg
e61b8a1ae8 docs: link to Docker image, APT, in release notes 2025-06-05 19:21:01 +02:00
Jakob Borg
706409d2f3 Merge branch 'main' into v2
* main:
  docs: link to Docker image, APT, in release notes
  build: also create relaysrv and discosrv releases
  fix(stupgrades): return latest stable & pre for each major
  fix(syncthing): avoid writing panic log to nil fd (#10154)
2025-06-05 19:19:34 +02:00
Jakob Borg
e9133ef82b docs: link to Docker image, APT, in release notes 2025-06-05 19:19:05 +02:00
Jakob Borg
67ba20d777 build: also create relaysrv and discosrv releases 2025-06-05 19:19:05 +02:00
Jakob Borg
21da0d7890 fix(stupgrades): return latest stable & pre for each major 2025-06-05 19:19:05 +02:00
ardevd
ebbe57d0ab fix(syncthing): avoid writing panic log to nil fd (#10154)
### Purpose

This change fixes a logical bug in the panic log writing where we could
end up writing to a uninitialized file descriptor.

On the very first iteration, `panicFd` is nil. We enter the if `panicFd
== nil { … }` block, check for “panic:” or “fatal error:”, and if
neither matches, we skip instantiating `panicFd` altogether. However,
immediately after, still within `if panicFd == nil { … }`, we call
`panicFd.WriteString("Panic at ...")`. But `panicFd` would in this case
be `nil`, which will cause a run‐time panic.

It's not clear to me why panicFd is only initialized if the lines start
with "panic:" or "fatal error:" so I've left that logic untouched. With
this change we at least avoid the risk of writing to a nil
filedescriptor.
## Authorship

Your name and email will be added automatically to the AUTHORS file
based on the commit metadata.

---------

Co-authored-by: Jakob Borg <jakob@kastelo.net>
2025-06-03 07:39:21 +02:00
Jakob Borg
9cf8fca03f Merge branch 'main' into v2
* main:
  chore: copyright in next-version script
2025-06-02 20:42:16 +02:00
Jakob Borg
f4abc71dcc chore: copyright in next-version script 2025-06-02 20:41:42 +02:00
301 changed files with 6606 additions and 6200 deletions

View File

@@ -7,7 +7,7 @@ on:
- infra-*
env:
GO_VERSION: "~1.24.0"
GO_VERSION: "~1.25.0"
CGO_ENABLED: "0"
BUILD_USER: docker
BUILD_HOST: github.syncthing.net

View File

@@ -13,9 +13,9 @@ env:
# The go version to use for builds. We set check-latest to true when
# installing, so we get the latest patch version that matches the
# expression.
GO_VERSION: "~1.24.0"
GO_VERSION: "~1.25.0"
# Optimize compatibility on the slow archictures.
# Optimize compatibility on the slow architectures.
GOMIPS: softfloat
GOARM: "6"
@@ -26,7 +26,8 @@ env:
BUILD_USER: builder
BUILD_HOST: github.syncthing.net
TAGS: "netgo osusergo sqlite_omit_load_extension sqlite_dbstat"
TAGS: "sqlite_omit_load_extension sqlite_dbstat"
TAGS_LINUX: "sqlite_omit_load_extension sqlite_dbstat netgo osusergo"
# A note on actions and third party code... The actions under actions/ (like
# `uses: actions/checkout`) are maintained by GitHub, and we need to trust
@@ -37,6 +38,58 @@ env:
jobs:
#
# Source
#
facts:
name: Gather common facts
runs-on: ubuntu-latest
outputs:
version: ${{ steps.get-version.outputs.version }}
release-kind: ${{ steps.get-version.outputs.release-kind }}
release-generation: ${{ steps.get-version.outputs.release-generation }}
go-version: ${{ steps.get-go.outputs.go-version }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
ref: ${{ github.ref }} # https://github.com/actions/checkout/issues/882
- uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
cache: false
check-latest: true
- name: Get Syncthing version
id: get-version
run: |
version=$(go run build.go version)
echo "version=$version" >> "$GITHUB_OUTPUT"
echo "Version: $version"
kind=stable
if [[ $version == *-rc.[0-9] || $version == *-rc.[0-9][0-9] ]] ; then
kind=candidate
elif [[ $version == *-* ]] ; then
kind=nightly
fi
echo "release-kind=$kind" >> "$GITHUB_OUTPUT"
echo "Release kind: $kind"
generation=v1
if [[ $version == v2.* ]] ; then
generation=v2
fi
echo "release-generation=$generation" >> "$GITHUB_OUTPUT"
echo "Release generation: $generation"
- name: Get Go version
id: get-go
run: |
go version
echo "go-version=$(go version | sed 's#^.*go##;s# .*##')" >> $GITHUB_OUTPUT
#
# Tests for all platforms. Runs a matrix build on Windows, Linux and Mac,
# with the list of expected supported Go versions (current, previous).
@@ -50,7 +103,7 @@ jobs:
runner: ["windows-latest", "ubuntu-latest", "macos-latest"]
# The oldest version in this list should match what we have in our go.mod.
# Variables don't seem to be supported here, or we could have done something nice.
go: ["~1.23.0", "~1.24.0"]
go: ["~1.24.0", "~1.25.0"]
runs-on: ${{ matrix.runner }}
steps:
- name: Set git to use LF
@@ -123,17 +176,18 @@ jobs:
package-windows:
name: Package for Windows
runs-on: ubuntu-latest
needs:
- facts
env:
VERSION: ${{ needs.facts.outputs.version }}
RELEASE_KIND: ${{ needs.facts.outputs.release-kind }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
ref: ${{ github.ref }} # https://github.com/actions/checkout/issues/882
- uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
go-version: ${{ needs.facts.outputs.go-version }}
cache: false
check-latest: true
- uses: mlugg/setup-zig@v2
@@ -142,7 +196,7 @@ jobs:
path: |
~/.cache/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ env.GO_VERSION }}-package-windows-${{ hashFiles('**/go.sum') }}
key: ${{ runner.os }}-go-${{ needs.facts.outputs.go-version }}-package-windows-${{ hashFiles('**/go.sum') }}
- name: Install dependencies
run: |
@@ -154,7 +208,7 @@ jobs:
go run build.go -tags "${{env.TAGS}}" -goos windows -goarch amd64 -cc "zig cc -target x86_64-windows" zip $tgt
go run build.go -tags "${{env.TAGS}}" -goos windows -goarch 386 -cc "zig cc -target x86-windows" zip $tgt
go run build.go -tags "${{env.TAGS}}" -goos windows -goarch arm64 -cc "zig cc -target aarch64-windows" zip $tgt
# go run build.go -tags "${{env.TAGS}}" -goos windows -goarch arm -cc "zig cc -target thumb-windows" zip $tgt # failes with linker errors
# go run build.go -tags "${{env.TAGS}}" -goos windows -goarch arm -cc "zig cc -target thumb-windows" zip $tgt # fails with linker errors
done
env:
CGO_ENABLED: "1"
@@ -231,22 +285,18 @@ jobs:
package-linux:
name: Package for Linux
runs-on: ubuntu-latest
needs:
- facts
env:
VERSION: ${{ needs.facts.outputs.version }}
RELEASE_KIND: ${{ needs.facts.outputs.release-kind }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
ref: ${{ github.ref }} # https://github.com/actions/checkout/issues/882
- uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
go-version: ${{ needs.facts.outputs.go-version }}
cache: false
check-latest: true
- name: Get actual Go version
run: |
go version
echo "GO_VERSION=$(go version | sed 's#^.*go##;s# .*##')" >> $GITHUB_ENV
- uses: mlugg/setup-zig@v2
@@ -255,25 +305,25 @@ jobs:
path: |
~/.cache/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ env.GO_VERSION }}-package-${{ hashFiles('**/go.sum') }}
key: ${{ runner.os }}-go-${{ needs.facts.outputs.go-version }}-package-${{ hashFiles('**/go.sum') }}
- name: Create packages
run: |
sudo apt-get install -y gcc-mips64-linux-gnuabi64 gcc-mips64el-linux-gnuabi64
for tgt in syncthing stdiscosrv strelaysrv ; do
go run build.go -tags "${{env.TAGS}}" -goos linux -goarch amd64 -cc "zig cc -target x86_64-linux-musl" tar "$tgt"
go run build.go -tags "${{env.TAGS}}" -goos linux -goarch 386 -cc "zig cc -target x86-linux-musl" tar "$tgt"
go run build.go -tags "${{env.TAGS}}" -goos linux -goarch arm -cc "zig cc -target arm-linux-musleabi -mcpu=arm1136j_s" tar "$tgt"
go run build.go -tags "${{env.TAGS}}" -goos linux -goarch arm64 -cc "zig cc -target aarch64-linux-musl" tar "$tgt"
go run build.go -tags "${{env.TAGS}}" -goos linux -goarch mips -cc "zig cc -target mips-linux-musleabi" tar "$tgt"
go run build.go -tags "${{env.TAGS}}" -goos linux -goarch mipsle -cc "zig cc -target mipsel-linux-musleabi" tar "$tgt"
go run build.go -tags "${{env.TAGS}}" -goos linux -goarch mips64 -cc mips64-linux-gnuabi64-gcc tar "$tgt"
go run build.go -tags "${{env.TAGS}}" -goos linux -goarch mips64le -cc mips64el-linux-gnuabi64-gcc tar "$tgt"
go run build.go -tags "${{env.TAGS}}" -goos linux -goarch riscv64 -cc "zig cc -target riscv64-linux-musl" tar "$tgt"
go run build.go -tags "${{env.TAGS}}" -goos linux -goarch s390x -cc "zig cc -target s390x-linux-musl" tar "$tgt"
go run build.go -tags "${{env.TAGS}}" -goos linux -goarch loong64 -cc "zig cc -target loongarch64-linux-musl" tar "$tgt"
# go run build.go -tags "${{env.TAGS}}" -goos linux -goarch ppc64 -cc "zig cc -target powerpc64-linux-musl" tar "$tgt" # fails with linkmode not supported
go run build.go -tags "${{env.TAGS}}" -goos linux -goarch ppc64le -cc "zig cc -target powerpc64le-linux-musl" tar "$tgt"
go run build.go -tags "${{env.TAGS_LINUX}}" -goos linux -goarch amd64 -cc "zig cc -target x86_64-linux-musl" tar "$tgt"
go run build.go -tags "${{env.TAGS_LINUX}}" -goos linux -goarch 386 -cc "zig cc -target x86-linux-musl" tar "$tgt"
go run build.go -tags "${{env.TAGS_LINUX}}" -goos linux -goarch arm -cc "zig cc -target arm-linux-musleabi -mcpu=arm1136j_s" tar "$tgt"
go run build.go -tags "${{env.TAGS_LINUX}}" -goos linux -goarch arm64 -cc "zig cc -target aarch64-linux-musl" tar "$tgt"
go run build.go -tags "${{env.TAGS_LINUX}}" -goos linux -goarch mips -cc "zig cc -target mips-linux-musleabi" tar "$tgt"
go run build.go -tags "${{env.TAGS_LINUX}}" -goos linux -goarch mipsle -cc "zig cc -target mipsel-linux-musleabi" tar "$tgt"
go run build.go -tags "${{env.TAGS_LINUX}}" -goos linux -goarch mips64 -cc mips64-linux-gnuabi64-gcc tar "$tgt"
go run build.go -tags "${{env.TAGS_LINUX}}" -goos linux -goarch mips64le -cc mips64el-linux-gnuabi64-gcc tar "$tgt"
go run build.go -tags "${{env.TAGS_LINUX}}" -goos linux -goarch riscv64 -cc "zig cc -target riscv64-linux-musl" tar "$tgt"
go run build.go -tags "${{env.TAGS_LINUX}}" -goos linux -goarch s390x -cc "zig cc -target s390x-linux-musl" tar "$tgt"
go run build.go -tags "${{env.TAGS_LINUX}}" -goos linux -goarch loong64 -cc "zig cc -target loongarch64-linux-musl" tar "$tgt"
# go run build.go -tags "${{env.TAGS_LINUX}}" -goos linux -goarch ppc64 -cc "zig cc -target powerpc64-linux-musl" tar "$tgt" # fails with linkmode not supported
go run build.go -tags "${{env.TAGS_LINUX}}" -goos linux -goarch ppc64le -cc "zig cc -target powerpc64le-linux-musl" tar "$tgt"
done
env:
CGO_ENABLED: "1"
@@ -297,32 +347,27 @@ jobs:
name: Package for macOS
if: github.repository_owner == 'syncthing' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && (github.ref == 'refs/heads/release-nightly' || startsWith(github.ref, 'refs/tags/v'))
environment: release
runs-on: macos-latest
needs:
- facts
env:
CODESIGN_IDENTITY: ${{ secrets.CODESIGN_IDENTITY }}
runs-on: macos-latest
VERSION: ${{ needs.facts.outputs.version }}
RELEASE_KIND: ${{ needs.facts.outputs.release-kind }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
ref: ${{ github.ref }} # https://github.com/actions/checkout/issues/882
- uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
go-version: ${{ needs.facts.outputs.go-version }}
cache: false
check-latest: true
- name: Get actual Go version
run: |
go version
echo "GO_VERSION=$(go version | sed 's#^.*go##;s# .*##')" >> $GITHUB_ENV
- uses: actions/cache@v4
with:
path: |
~/.cache/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ env.GO_VERSION }}-package-${{ hashFiles('**/go.sum') }}
key: ${{ runner.os }}-go-${{ needs.facts.outputs.go-version }}-package-${{ hashFiles('**/go.sum') }}
- name: Import signing certificate
if: env.CODESIGN_IDENTITY != ''
@@ -432,29 +477,25 @@ jobs:
package-cross:
name: Package cross compiled
runs-on: ubuntu-latest
needs:
- facts
env:
VERSION: ${{ needs.facts.outputs.version }}
RELEASE_KIND: ${{ needs.facts.outputs.release-kind }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
ref: ${{ github.ref }} # https://github.com/actions/checkout/issues/882
- uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
go-version: ${{ needs.facts.outputs.go-version }}
cache: false
check-latest: true
- name: Get actual Go version
run: |
go version
echo "GO_VERSION=$(go version | sed 's#^.*go##;s# .*##')" >> $GITHUB_ENV
- uses: actions/cache@v4
with:
path: |
~/.cache/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ env.GO_VERSION }}-cross-${{ hashFiles('**/go.sum') }}
key: ${{ runner.os }}-go-${{ needs.facts.outputs.go-version }}-cross-${{ hashFiles('**/go.sum') }}
- name: Create packages
run: |
@@ -502,33 +543,33 @@ jobs:
package-source:
name: Package source code
runs-on: ubuntu-latest
needs:
- facts
env:
VERSION: ${{ needs.facts.outputs.version }}
RELEASE_KIND: ${{ needs.facts.outputs.release-kind }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
ref: ${{ github.ref }} # https://github.com/actions/checkout/issues/882
- uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
go-version: ${{ needs.facts.outputs.go-version }}
cache: false
check-latest: true
- name: Package source
run: |
version=$(go run build.go version)
echo "$version" > RELEASE
echo "$VERSION" > RELEASE
go mod vendor
go run build.go assets
cd ..
tar c -z -f "syncthing-source-$version.tar.gz" \
tar c -z -f "syncthing-source-$VERSION.tar.gz" \
--exclude .git \
syncthing
mv "syncthing-source-$version.tar.gz" syncthing
mv "syncthing-source-$VERSION.tar.gz" syncthing
- name: Archive artifacts
uses: actions/upload-artifact@v4
@@ -550,27 +591,26 @@ jobs:
- package-macos
- package-cross
- package-source
- facts
env:
VERSION: ${{ needs.facts.outputs.version }}
RELEASE_KIND: ${{ needs.facts.outputs.release-kind }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
ref: ${{ github.ref }} # https://github.com/actions/checkout/issues/882
- uses: actions/checkout@v4
with:
repository: syncthing/release-tools
path: tools
fetch-depth: 0
- name: Download artifacts
uses: actions/download-artifact@v4
- uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
go-version: ${{ needs.facts.outputs.go-version }}
cache: false
check-latest: true
- name: Install signing tool
run: |
@@ -597,9 +637,6 @@ jobs:
sha256sum "${files[@]}" > sha256sum.txt
popd
version=$(go run build.go version)
echo "VERSION=$version" >> $GITHUB_ENV
- name: Sign shasum files
uses: docker://ghcr.io/kastelo/ezapt:latest
with:
@@ -635,22 +672,18 @@ jobs:
package-debian:
name: Package for Debian
runs-on: ubuntu-latest
needs:
- facts
env:
VERSION: ${{ needs.facts.outputs.version }}
RELEASE_KIND: ${{ needs.facts.outputs.release-kind }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
ref: ${{ github.ref }} # https://github.com/actions/checkout/issues/882
- uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
go-version: ${{ needs.facts.outputs.go-version }}
cache: false
check-latest: true
- name: Get actual Go version
run: |
go version
echo "GO_VERSION=$(go version | sed 's#^.*go##;s# .*##')" >> $GITHUB_ENV
- uses: ruby/setup-ruby@v1
with:
@@ -667,14 +700,15 @@ jobs:
path: |
~/.cache/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ env.GO_VERSION }}-debian-${{ hashFiles('**/go.sum') }}
key: ${{ runner.os }}-go-${{ needs.facts.outputs.go-version }}-debian-${{ hashFiles('**/go.sum') }}
- name: Package for Debian (CGO)
run: |
for tgt in syncthing stdiscosrv strelaysrv ; do
go run build.go -no-upgrade -installsuffix=no-upgrade -tags "${{env.TAGS}}" -goos linux -goarch amd64 -cc "zig cc -target x86_64-linux-musl" deb "$tgt"
go run build.go -no-upgrade -installsuffix=no-upgrade -tags "${{env.TAGS}}" -goos linux -goarch arm -cc "zig cc -target arm-linux-musleabi -mcpu=arm1136j_s" deb "$tgt"
go run build.go -no-upgrade -installsuffix=no-upgrade -tags "${{env.TAGS}}" -goos linux -goarch arm64 -cc "zig cc -target aarch64-linux-musl" deb "$tgt"
go run build.go -no-upgrade -installsuffix=no-upgrade -tags "${{env.TAGS_LINUX}}" -goos linux -goarch amd64 -cc "zig cc -target x86_64-linux-musl" deb "$tgt"
go run build.go -no-upgrade -installsuffix=no-upgrade -tags "${{env.TAGS_LINUX}}" -goos linux -goarch armel -cc "zig cc -target arm-linux-musleabi -mcpu=arm1136j_s" deb "$tgt"
go run build.go -no-upgrade -installsuffix=no-upgrade -tags "${{env.TAGS_LINUX}}" -goos linux -goarch armhf -cc "zig cc -target arm-linux-musleabi -mcpu=arm1136j_s" deb "$tgt"
go run build.go -no-upgrade -installsuffix=no-upgrade -tags "${{env.TAGS_LINUX}}" -goos linux -goarch arm64 -cc "zig cc -target aarch64-linux-musl" deb "$tgt"
done
env:
BUILD_USER: debian
@@ -697,13 +731,13 @@ jobs:
environment: release
needs:
- sign-for-upgrade
- facts
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
repository: syncthing/release-tools
path: tools
fetch-depth: 0
- name: Download artifacts
uses: actions/download-artifact@v4
@@ -713,9 +747,8 @@ jobs:
- uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
go-version: ${{ needs.facts.outputs.go-version }}
cache: false
check-latest: true
- name: Create release json
run: |
@@ -735,7 +768,7 @@ jobs:
RCLONE_CONFIG_OBJSTORE_REGION: ${{ secrets.S3_REGION }}
RCLONE_CONFIG_OBJSTORE_ACL: public-read
with:
args: sync -v packages objstore:nightly
args: sync -v --no-update-modtime packages objstore:nightly
#
# Push release artifacts to Spaces
@@ -750,6 +783,10 @@ jobs:
needs:
- sign-for-upgrade
- package-debian
- facts
env:
VERSION: ${{ needs.facts.outputs.version }}
RELEASE_KIND: ${{ needs.facts.outputs.release-kind }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
@@ -771,14 +808,8 @@ jobs:
- uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
go-version: ${{ needs.facts.outputs.go-version }}
cache: false
check-latest: true
- name: Set version
run: |
version=$(go run build.go version)
echo "VERSION=$version" >> $GITHUB_ENV
- name: Push to object store (${{ env.VERSION }})
uses: docker://docker.io/rclone/rclone:latest
@@ -791,7 +822,7 @@ jobs:
RCLONE_CONFIG_OBJSTORE_REGION: ${{ secrets.S3_REGION }}
RCLONE_CONFIG_OBJSTORE_ACL: public-read
with:
args: sync -v packages objstore:release/${{ env.VERSION }}
args: sync -v --no-update-modtime packages objstore:release/${{ env.VERSION }}
- name: Push to object store (latest)
uses: docker://docker.io/rclone/rclone:latest
@@ -804,9 +835,9 @@ jobs:
RCLONE_CONFIG_OBJSTORE_REGION: ${{ secrets.S3_REGION }}
RCLONE_CONFIG_OBJSTORE_ACL: public-read
with:
args: sync -v objstore:release/${{ env.VERSION }} objstore:release/latest
args: sync -v --no-update-modtime objstore:release/${{ env.VERSION }} objstore:release/latest
- name: Create GitHub release and push binaries
- name: Create GitHub releases and push binaries
run: |
maybePrerelease=""
if [[ $VERSION == *-* ]]; then
@@ -814,19 +845,33 @@ jobs:
fi
export GH_PROMPT_DISABLED=1
if ! gh release view --json name "$VERSION" >/dev/null 2>&1 ; then
gh release create \
"$VERSION" \
gh release create "$VERSION" \
$maybePrerelease \
--title "$VERSION" \
--notes-from-tag
fi
gh release upload "$VERSION" \
gh release upload --clobber "$VERSION" \
packages/*.asc packages/*.json \
packages/syncthing-*.tar.gz \
packages/syncthing-*.zip \
packages/syncthing*.deb
packages/syncthing_*.deb
PKGS=$(pwd)/packages
cd /tmp # gh will not release for repo x while inside repo y
for repo in relaysrv discosrv ; do
export GH_REPO="syncthing/$repo"
if ! gh release view --json name "$VERSION" >/dev/null 2>&1 ; then
gh release create "$VERSION" \
$maybePrerelease \
--title "$VERSION" \
--notes "https://github.com/syncthing/syncthing/releases/tag/$VERSION"
fi
gh release upload --clobber "$VERSION" \
$PKGS/*.asc \
$PKGS/*${repo}*
done
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_TOKEN: ${{ secrets.ACTIONS_GITHUB_TOKEN }}
#
# Push Debian/APT archive
@@ -838,12 +883,14 @@ jobs:
environment: release
needs:
- package-debian
- facts
env:
VERSION: ${{ needs.facts.outputs.version }}
RELEASE_KIND: ${{ needs.facts.outputs.release-kind }}
RELEASE_GENERATION: ${{ needs.facts.outputs.release-generation }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
ref: ${{ github.ref }} # https://github.com/actions/checkout/issues/882
- name: Download packages
uses: actions/download-artifact@v4
@@ -851,26 +898,14 @@ jobs:
name: debian-packages
path: packages
- name: Set version
run: |
version=$(go run build.go version)
echo "Version: $version"
echo "VERSION=$version" >> $GITHUB_ENV
# Decide whether packages should go to stable, candidate or nightly
- name: Prepare packages
run: |
kind=stable
if [[ $VERSION == v2* ]] ; then
kind=v2
elif [[ $VERSION == *-rc.[0-9] ]] ; then
kind=candidate
elif [[ $VERSION == *-* ]] ; then
kind=nightly
if [[ $RELEASE_KIND == stable && $RELEASE_GENERATION == v2 ]] ; then
RELEASE_KIND=stable-v2
fi
echo "Kind: $kind"
mkdir -p packages/syncthing/$kind
mv packages/*.deb packages/syncthing/$kind
mkdir -p packages/syncthing/$RELEASE_KIND
mv packages/*.deb packages/syncthing/$RELEASE_KIND
- name: Pull archive
uses: docker://docker.io/rclone/rclone:latest
@@ -906,7 +941,7 @@ jobs:
RCLONE_CONFIG_OBJSTORE_REGION: ${{ secrets.S3_REGION }}
RCLONE_CONFIG_OBJSTORE_ACL: public-read
with:
args: sync -v dists objstore:apt/dists
args: sync -v --no-update-modtime dists objstore:apt/dists
#
# Build and push (except for PRs) to GHCR.
@@ -918,6 +953,11 @@ jobs:
permissions:
contents: read
packages: write
needs:
- facts
env:
VERSION: ${{ needs.facts.outputs.version }}
RELEASE_KIND: ${{ needs.facts.outputs.release-kind }}
strategy:
matrix:
pkg:
@@ -936,20 +976,11 @@ jobs:
image: discosrv
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
ref: ${{ github.ref }} # https://github.com/actions/checkout/issues/882
- uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
go-version: ${{ needs.facts.outputs.go-version }}
cache: false
check-latest: true
- name: Get actual Go version
run: |
go version
echo "GO_VERSION=$(go version | sed 's#^.*go##;s# .*##')" >> $GITHUB_ENV
- uses: mlugg/setup-zig@v2
@@ -958,20 +989,20 @@ jobs:
path: |
~/.cache/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ env.GO_VERSION }}-docker-${{ matrix.pkg }}-${{ hashFiles('**/go.sum') }}
key: ${{ runner.os }}-go-${{ needs.facts.outputs.go-version }}-docker-${{ matrix.pkg }}-${{ hashFiles('**/go.sum') }}
- name: Build binaries (CGO)
run: |
# amd64
go run build.go -goos linux -goarch amd64 -tags "${{env.TAGS}}" -cc "zig cc -target x86_64-linux-musl" -no-upgrade build ${{ matrix.pkg }}
go run build.go -goos linux -goarch amd64 -tags "${{env.TAGS_LINUX}}" -cc "zig cc -target x86_64-linux-musl" -no-upgrade build ${{ matrix.pkg }}
mv ${{ matrix.pkg }} ${{ matrix.pkg }}-linux-amd64
# arm64
go run build.go -goos linux -goarch arm64 -tags "${{env.TAGS}}" -cc "zig cc -target aarch64-linux-musl" -no-upgrade build ${{ matrix.pkg }}
go run build.go -goos linux -goarch arm64 -tags "${{env.TAGS_LINUX}}" -cc "zig cc -target aarch64-linux-musl" -no-upgrade build ${{ matrix.pkg }}
mv ${{ matrix.pkg }} ${{ matrix.pkg }}-linux-arm64
# arm
go run build.go -goos linux -goarch arm -tags "${{env.TAGS}}" -cc "zig cc -target arm-linux-musleabi -mcpu=arm1136j_s" -no-upgrade build ${{ matrix.pkg }}
go run build.go -goos linux -goarch arm -tags "${{env.TAGS_LINUX}}" -cc "zig cc -target arm-linux-musleabi -mcpu=arm1136j_s" -no-upgrade build ${{ matrix.pkg }}
mv ${{ matrix.pkg }} ${{ matrix.pkg }}-linux-arm
env:
CGO_ENABLED: "1"
@@ -990,8 +1021,7 @@ jobs:
- name: Set version tags
run: |
version=$(go run build.go version)
version=${version#v}
version=${VERSION#v}
repo=ghcr.io/${{ github.repository_owner }}/${{ matrix.image }}
ref="${{github.ref_name}}"
ref=${ref//\//-} # slashes to dashes
@@ -1010,9 +1040,7 @@ jobs:
fi
echo Pushing to $tags
echo "DOCKER_TAGS=$tags" >> $GITHUB_ENV
echo "VERSION=$version" >> $GITHUB_ENV
- name: Build and push Docker image
uses: docker/build-push-action@v5
@@ -1057,14 +1085,15 @@ jobs:
govulncheck:
runs-on: ubuntu-latest
name: Run govulncheck
needs:
- facts
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
go-version: ${{ needs.facts.outputs.go-version }}
cache: false
check-latest: true
- name: run govulncheck
run: |
@@ -1079,6 +1108,7 @@ jobs:
golangci:
runs-on: ubuntu-latest
name: Run golangci-lint
if: github.event_name == 'pull_request'
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5

18
.github/workflows/mirrors.yaml vendored Normal file
View File

@@ -0,0 +1,18 @@
name: Mirrors
on: [push, delete]
jobs:
codeberg:
name: Mirror to Codeberg
if: github.repository_owner == 'syncthing'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- uses: yesolutions/mirror-action@master
with:
REMOTE: ssh://git@codeberg.org/${{ github.repository }}.git
GIT_SSH_PRIVATE_KEY: ${{ secrets.CODEBERG_PUSH_KEY }}
GIT_SSH_NO_VERIFY_HOST: "true"

View File

@@ -51,3 +51,10 @@ jobs:
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 }}

View File

@@ -8,12 +8,14 @@ linters:
- exhaustive
- exhaustruct
- forbidigo
- funcorder
- funlen
- gochecknoglobals
- gochecknoinits
- gocognit
- goconst
- gocyclo
- godot
- godox
- gomoddirectives
- inamedparam
@@ -25,6 +27,7 @@ linters:
- musttag
- nestif
- nlreturn
- noinlineerr
- nonamedreturns
- paralleltest
- prealloc
@@ -40,6 +43,7 @@ linters:
- whitespace
- wrapcheck
- wsl
- wsl_v5
exclusions:
generated: lax
presets:
@@ -49,12 +53,23 @@ linters:
- 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
settings:
sloglint:
context: "scope"
static-msg: true
msg-style: capitalized
key-naming-case: camel
formatters:
enable:
- gofumpt

View File

@@ -56,10 +56,12 @@ Anthony Goeckner <agoeckner@users.noreply.github.com>
Antoine Lamielle (0x010C) <antoine.lamielle@0x010c.fr> <gh@0x010c.fr>
Anur <anurnomeru@163.com>
Aranjedeath <Aranjedeath@users.noreply.github.com>
ardevd <ardevd@users.noreply.github.com>
Arkadiusz Tymiński <gevleeog@gmail.com>
Aroun <login@b-vo.fr>
Arthur Axel fREW Schmidt (frioux) <frew@afoolishmanifesto.com> <frioux@gmail.com>
Artur Zubilewicz <AkaZecik@users.noreply.github.com>
Ashish Bhate <bhate.ashish@gmail.com>
Aurélien Rainone <476650+arl@users.noreply.github.com>
BAHADIR YILMAZ <bahadiryilmaz32@gmail.com>
Bart De Vries (mogwa1) <devriesb@gmail.com>
@@ -93,6 +95,7 @@ Daniel Barczyk <46358936+DanielBarczyk@users.noreply.github.com>
Daniel Bergmann (brgmnn) <dan.arne.bergmann@gmail.com> <brgmnn@users.noreply.github.com>
Daniel Martí (mvdan) <mvdan@mvdan.cc>
Daniel Padrta <64928366+danpadcz@users.noreply.github.com>
Daniil Gentili <daniil@daniil.it>
Darshil Chanpura (dtchanpura) <dtchanpura@gmail.com> <dcprime314@gmail.com>
dashangcun <907225865@qq.com>
David Rimmer (dinosore) <dinosore@dbrsoftware.co.uk>
@@ -192,6 +195,7 @@ Luke Hamburg <1992842+luckman212@users.noreply.github.com>
luzpaz <luzpaz@users.noreply.github.com>
Majed Abdulaziz (majedev) <majed.alhajry@gmail.com>
Marc Laporte (marclaporte) <marc@marclaporte.com> <marc@laporte.name>
Marcel Meyer <mm.marcelmeyer@gmail.com>
Marcin Dziadus (marcindziadus) <dziadus.marcin@gmail.com>
Marcus B Spencer <marcus@marcusspencer.xyz> <marcus@marcusspencer.us>
Marcus Legendre <marcus.legendre@gmail.com>
@@ -252,6 +256,7 @@ Philippe Schommers (filoozoom) <philippe@schommers.be>
Phill Luby (pluby) <phill.luby@newredo.com>
Piotr Bejda (piobpl) <piotrb10@gmail.com>
polyfloyd <polyfloyd@users.noreply.github.com>
pullmerge <166967364+pullmerge@users.noreply.github.com>
Quentin Hibon <qh.public@yahoo.com>
Rahmi Pruitt <rjpruitt16@gmail.com>
red_led <red-led@users.noreply.github.com>
@@ -312,5 +317,6 @@ xarx00 <xarx00@users.noreply.github.com>
Xavier O. (damajor) <damajor@gmail.com>
xjtdy888 (xjtdy888) <xjtdy888@163.com> <xjtdy888@gmail.com>
Yannic A. (eipiminus1) <eipiminusone+github@gmail.com> <eipiminus1@users.noreply.github.com>
yparitcher <y@paritcher.com>
佛跳墙 <daoquan@qq.com>
落心 <luoxin.ttt@gmail.com>

View File

@@ -870,7 +870,6 @@ func testmocks() {
"github.com/syncthing/syncthing/lib/connections",
"github.com/syncthing/syncthing/lib/discover",
"github.com/syncthing/syncthing/lib/events",
"github.com/syncthing/syncthing/lib/logger",
"github.com/syncthing/syncthing/lib/model",
"github.com/syncthing/syncthing/lib/protocol",
}
@@ -901,6 +900,7 @@ func weblate() {
func ldflags(tags []string) string {
b := new(strings.Builder)
b.WriteString("-w")
b.WriteString(" -buildid=")
fmt.Fprintf(b, " -X github.com/syncthing/syncthing/lib/build.Version=%s", version)
fmt.Fprintf(b, " -X github.com/syncthing/syncthing/lib/build.Stamp=%d", buildStamp())
fmt.Fprintf(b, " -X github.com/syncthing/syncthing/lib/build.User=%s", buildUser())
@@ -922,6 +922,9 @@ func rmr(paths ...string) {
}
func getReleaseVersion() (string, error) {
if ver := os.Getenv("VERSION"); ver != "" {
return strings.TrimSpace(ver), nil
}
bs, err := os.ReadFile("RELEASE")
if err != nil {
return "", err
@@ -966,7 +969,7 @@ func getGitVersion() (string, error) {
}
func getVersion() string {
// First try for a RELEASE file,
// First try for a RELEASE file or $VERSION env var,
if ver, err := getReleaseVersion(); err == nil {
return ver
}

View File

@@ -44,7 +44,7 @@ type cli struct {
SentryQueue int `help:"Maximum number of reports to queue for sending to Sentry" default:"64" env:"SENTRY_QUEUE"`
DiskQueue int `help:"Maximum number of reports to queue for writing to disk" default:"64" env:"DISK_QUEUE"`
MetricsListen string `help:"HTTP listen address for metrics" default:":8081" env:"METRICS_LISTEN_ADDRESS"`
IngorePatterns string `help:"File containing ignore patterns (regexp)" env:"IGNORE_PATTERNS" type:"existingfile"`
IgnorePatterns string `help:"File containing ignore patterns (regexp)" env:"IGNORE_PATTERNS" type:"existingfile"`
}
func main() {
@@ -68,9 +68,9 @@ func main() {
go ss.Serve(context.Background())
var ip *ignorePatterns
if params.IngorePatterns != "" {
if params.IgnorePatterns != "" {
var err error
ip, err = loadIgnorePatterns(params.IngorePatterns)
ip, err = loadIgnorePatterns(params.IgnorePatterns)
if err != nil {
log.Fatalf("Failed to load ignore patterns: %v", err)
}

View File

@@ -17,6 +17,7 @@ import (
"path/filepath"
"strconv"
"strings"
"sync"
"sync/atomic"
"time"
@@ -31,7 +32,6 @@ import (
"github.com/syncthing/syncthing/lib/protocol"
"github.com/syncthing/syncthing/lib/rand"
"github.com/syncthing/syncthing/lib/relay/client"
"github.com/syncthing/syncthing/lib/sync"
"github.com/syncthing/syncthing/lib/tlsutil"
)
@@ -115,7 +115,7 @@ var (
requests chan request
mut = sync.NewRWMutex()
mut sync.RWMutex
knownRelays = make([]*relay, 0)
permanentRelays = make([]*relay, 0)
evictionTimers = make(map[string]*time.Timer)
@@ -620,7 +620,7 @@ func createTestCertificate() tls.Certificate {
}
certFile, keyFile := filepath.Join(tmpDir, "cert.pem"), filepath.Join(tmpDir, "key.pem")
cert, err := tlsutil.NewCertificate(certFile, keyFile, "relaypoolsrv", 20*365)
cert, err := tlsutil.NewCertificate(certFile, keyFile, "relaypoolsrv", 20*365, false)
if err != nil {
log.Fatalln("Failed to create test X509 key pair:", err)
}

View File

@@ -13,7 +13,6 @@ import (
"net/http/httptest"
"net/url"
"strings"
"sync"
"testing"
)
@@ -28,8 +27,6 @@ func init() {
{URL: "known2"},
{URL: "known3"},
}
mut = new(sync.RWMutex)
}
// Regression test: handleGetRequest should not modify permanentRelays.

View File

@@ -6,10 +6,10 @@ import (
"encoding/json"
"net"
"net/http"
"sync"
"time"
"github.com/prometheus/client_golang/prometheus"
"github.com/syncthing/syncthing/lib/sync"
)
var (
@@ -104,7 +104,7 @@ func refreshStats() {
mut.RUnlock()
now := time.Now()
wg := sync.NewWaitGroup()
var wg sync.WaitGroup
results := make(chan statsFetchResult, len(relays))
for _, rel := range relays {
@@ -173,7 +173,7 @@ func fetchStats(relay *relay) *stats {
var stats stats
if json.NewDecoder(response.Body).Decode(&stats); err != nil {
if err := json.NewDecoder(response.Body).Decode(&stats); err != nil {
return nil
}
return &stats

View File

@@ -24,6 +24,7 @@ import (
"github.com/alecthomas/kong"
"github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/syncthing/syncthing/internal/slogutil"
_ "github.com/syncthing/syncthing/lib/automaxprocs"
"github.com/syncthing/syncthing/lib/httpcache"
"github.com/syncthing/syncthing/lib/upgrade"
@@ -58,10 +59,10 @@ func server(params *cli) error {
if err != nil {
return fmt.Errorf("metrics: %w", err)
}
slog.Info("Metrics listener started", "addr", params.MetricsListen)
slog.Info("Metrics listener started", slogutil.Address(params.MetricsListen))
go func() {
if err := http.Serve(metricsListen, mux); err != nil {
slog.Warn("Metrics server returned", "error", err)
slog.Warn("Metrics server returned", slogutil.Error(err))
}
}()
}
@@ -75,9 +76,9 @@ func server(params *cli) error {
go func() {
for range time.NewTicker(params.CacheTime).C {
slog.Info("Refreshing cached releases", "url", params.URL)
slog.Info("Refreshing cached releases", slogutil.URI(params.URL))
if err := cache.Update(context.Background()); err != nil {
slog.Error("Failed to refresh cached releases", "url", params.URL, "error", err)
slog.Error("Failed to refresh cached releases", slogutil.URI(params.URL), slogutil.Error(err))
}
}
}()
@@ -109,7 +110,7 @@ func server(params *cli) error {
if err != nil {
return fmt.Errorf("listen: %w", err)
}
slog.Info("Main listener started", "addr", params.Listen)
slog.Info("Main listener started", slogutil.Address(params.Listen))
return srv.Serve(srvListener)
}
@@ -137,7 +138,7 @@ func (p *githubReleases) serveReleases(w http.ResponseWriter, req *http.Request)
osv := req.Header.Get("Syncthing-Os-Version")
if ua != "" && osv != "" {
// We should determine the compatibility of the releases.
rels = filterForCompabitility(rels, ua, osv)
rels = filterForCompatibility(rels, ua, osv)
} else {
metricFilterCalls.WithLabelValues("no-ua-or-osversion").Inc()
}
@@ -201,17 +202,21 @@ func (p *proxy) ServeHTTP(w http.ResponseWriter, req *http.Request) {
// looking for a prerelease at all.
func filterForLatest(rels []upgrade.Release) []upgrade.Release {
var filtered []upgrade.Release
var havePre bool
havePre := make(map[string]bool)
haveStable := make(map[string]bool)
for _, rel := range rels {
if !rel.Prerelease {
// We found a stable version, we're good now.
major, _, _ := strings.Cut(rel.Tag, ".")
if !rel.Prerelease && !haveStable[major] {
// Remember the first non-pre for each major
filtered = append(filtered, rel)
break
haveStable[major] = true
continue
}
if rel.Prerelease && !havePre {
// We remember the first prerelease we find.
if rel.Prerelease && !havePre[major] && !haveStable[major] {
// We remember the first prerelease we find, unless we've
// already found a non-pre of the same major.
filtered = append(filtered, rel)
havePre = true
havePre[major] = true
}
}
return filtered
@@ -219,7 +224,7 @@ func filterForLatest(rels []upgrade.Release) []upgrade.Release {
var userAgentOSArchExp = regexp.MustCompile(`^syncthing.*\(.+ (\w+)-(\w+)\)$`)
func filterForCompabitility(rels []upgrade.Release, ua, osv string) []upgrade.Release {
func filterForCompatibility(rels []upgrade.Release, ua, osv string) []upgrade.Release {
osArch := userAgentOSArchExp.FindStringSubmatch(ua)
if len(osArch) != 3 {
metricFilterCalls.WithLabelValues("bad-os-arch").Inc()

View File

@@ -29,6 +29,7 @@ import (
"github.com/syncthing/syncthing/internal/blob"
"github.com/syncthing/syncthing/internal/blob/azureblob"
"github.com/syncthing/syncthing/internal/blob/s3"
"github.com/syncthing/syncthing/internal/slogutil"
"github.com/syncthing/syncthing/lib/build"
"github.com/syncthing/syncthing/lib/geoip"
"github.com/syncthing/syncthing/lib/ur/contract"
@@ -104,23 +105,23 @@ func (cli *CLI) Run() error {
urListener, err := net.Listen("tcp", cli.Listen)
if err != nil {
slog.Error("Failed to listen (usage reports)", "error", err)
slog.Error("Failed to listen (usage reports)", slogutil.Error(err))
return err
}
slog.Info("Listening (usage reports)", "address", urListener.Addr())
slog.Info("Listening (usage reports)", slogutil.Address(urListener.Addr()))
internalListener, err := net.Listen("tcp", cli.ListenInternal)
if err != nil {
slog.Error("Failed to listen (internal)", "error", err)
slog.Error("Failed to listen (internal)", slogutil.Error(err))
return err
}
slog.Info("Listening (internal)", "address", internalListener.Addr())
slog.Info("Listening (internal)", slogutil.Address(internalListener.Addr()))
var geo *geoip.Provider
if cli.GeoIPAccountID != 0 && cli.GeoIPLicenseKey != "" {
geo, err = geoip.NewGeoLite2CityProvider(context.Background(), cli.GeoIPAccountID, cli.GeoIPLicenseKey, os.TempDir())
if err != nil {
slog.Error("Failed to load GeoIP", "error", err)
slog.Error("Failed to load GeoIP", slogutil.Error(err))
return err
}
go geo.Serve(context.TODO())
@@ -132,20 +133,20 @@ func (cli *CLI) Run() error {
if cli.S3Endpoint != "" {
blobs, err = s3.NewSession(cli.S3Endpoint, cli.S3Region, cli.S3Bucket, cli.S3AccessKeyID, cli.S3SecretKey)
if err != nil {
slog.Error("Failed to create S3 session", "error", err)
slog.Error("Failed to create S3 session", slogutil.Error(err))
return err
}
} else if cli.AzureBlobAccount != "" {
blobs, err = azureblob.NewBlobStore(cli.AzureBlobAccount, cli.AzureBlobKey, cli.AzureBlobContainer)
if err != nil {
slog.Error("Failed to create Azure blob store", "error", err)
slog.Error("Failed to create Azure blob store", slogutil.Error(err))
return err
}
}
if _, err := os.Stat(cli.DumpFile); err != nil && blobs != nil {
if err := cli.downloadDumpFile(blobs); err != nil {
slog.Error("Failed to download dump file", "error", err)
slog.Error("Failed to download dump file", slogutil.Error(err))
}
}
@@ -167,7 +168,7 @@ func (cli *CLI) Run() error {
go func() {
for range time.Tick(cli.DumpInterval) {
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))
}
}
}()
@@ -307,7 +308,7 @@ func (s *server) handleNewData(w http.ResponseWriter, r *http.Request) {
lr := &io.LimitedReader{R: r.Body, N: 40 * 1024}
bs, _ := io.ReadAll(lr)
if err := json.Unmarshal(bs, &rep); err != nil {
log.Error("Failed to decode JSON", "error", err)
log.Error("Failed to decode JSON", slogutil.Error(err))
http.Error(w, "JSON Decode Error", http.StatusInternalServerError)
return
}
@@ -317,7 +318,7 @@ func (s *server) handleNewData(w http.ResponseWriter, r *http.Request) {
rep.Address = addr
if err := rep.Validate(); err != nil {
log.Error("Failed to validate report", "error", err)
log.Error("Failed to validate report", slogutil.Error(err))
http.Error(w, "Validation Error", http.StatusInternalServerError)
return
}
@@ -394,7 +395,7 @@ func (s *server) load(r io.Reader) {
if err := dec.Decode(&rep); errors.Is(err, io.EOF) {
break
} else if err != nil {
slog.Error("Failed to load record", "error", err)
slog.Error("Failed to load record", slogutil.Error(err))
break
}
s.addReport(&rep)

View File

@@ -115,7 +115,7 @@ func BenchmarkAPIRequests(b *testing.B) {
srv := httptest.NewServer(http.HandlerFunc(api.handler))
kf := b.TempDir() + "/cert"
crt, err := tlsutil.NewCertificate(kf+".crt", kf+".key", "localhost", 7)
crt, err := tlsutil.NewCertificate(kf+".crt", kf+".key", "localhost", 7, true)
if err != nil {
b.Fatal(err)
}

View File

@@ -107,7 +107,7 @@ func main() {
cert, err = tls.LoadX509KeyPair(cli.Cert, cli.Key)
if os.IsNotExist(err) {
log.Println("Failed to load keypair. Generating one, this might take a while...")
cert, err = tlsutil.NewCertificate(cli.Cert, cli.Key, "stdiscosrv", 20*365)
cert, err = tlsutil.NewCertificate(cli.Cert, cli.Key, "stdiscosrv", 20*365, false)
if err != nil {
log.Fatalln("Failed to generate X509 key pair:", err)
}

View File

@@ -157,7 +157,7 @@ func main() {
cert, err := tls.LoadX509KeyPair(certFile, keyFile)
if err != nil {
log.Println("Failed to load keypair. Generating one, this might take a while...")
cert, err = tlsutil.NewCertificate(certFile, keyFile, "strelaysrv", 20*365)
cert, err = tlsutil.NewCertificate(certFile, keyFile, "strelaysrv", 20*365, false)
if err != nil {
log.Fatalln("Failed to generate X509 key pair:", err)
}

View File

@@ -8,11 +8,14 @@ package main
import (
"fmt"
"log/slog"
"os"
"runtime"
"runtime/pprof"
"syscall"
"time"
"github.com/syncthing/syncthing/internal/slogutil"
)
func startBlockProfiler() {
@@ -20,10 +23,10 @@ func startBlockProfiler() {
if profiler == nil {
panic("Couldn't find block profiler")
}
l.Debugln("Starting block profiling")
slog.Debug("Starting block profiling")
go func() {
err := saveBlockingProfiles(profiler) // Only returns on error
l.Warnln("Block profiler failed:", err)
slog.Error("Block profiler failed", slogutil.Error(err))
panic("Block profiler failed")
}()
}

View File

@@ -131,15 +131,6 @@ func prettyPrintResponse(response *http.Response) error {
return prettyPrintJSON(data)
}
func nulString(bs []byte) string {
for i := range bs {
if bs[i] == 0 {
return string(bs[:i])
}
}
return string(bs)
}
func normalizePath(path string) string {
return filepath.ToSlash(filepath.Clean(path))
}

View File

@@ -11,12 +11,15 @@ import (
"context"
"crypto/sha256"
"fmt"
"log/slog"
"net/http"
"os"
"path/filepath"
"slices"
"strings"
"time"
"github.com/syncthing/syncthing/internal/slogutil"
)
const (
@@ -33,7 +36,7 @@ const (
func uploadPanicLogs(ctx context.Context, urlBase, dir string) {
files, err := filepath.Glob(filepath.Join(dir, "panic-*.log"))
if err != nil {
l.Warnln("Failed to list panic logs:", err)
slog.ErrorContext(ctx, "Failed to list panic logs", slogutil.Error(err))
return
}
@@ -48,7 +51,7 @@ func uploadPanicLogs(ctx context.Context, urlBase, dir string) {
}
if err := uploadPanicLog(ctx, urlBase, file); err != nil {
l.Warnln("Reporting crash:", err)
slog.ErrorContext(ctx, "Reporting crash", slogutil.Error(err))
} else {
// Rename the log so we don't have to try to report it again. This
// succeeds, or it does not. There is no point complaining about it.
@@ -71,7 +74,7 @@ func uploadPanicLog(ctx context.Context, urlBase, file string) error {
data = filterLogLines(data)
hash := fmt.Sprintf("%x", sha256.Sum256(data))
l.Infof("Reporting crash found in %s (report ID %s) ...\n", filepath.Base(file), hash[:8])
slog.InfoContext(ctx, "Reporting crash", slogutil.FilePath(filepath.Base(file)), slog.String("id", hash[:8]))
url := fmt.Sprintf("%s/%s", urlBase, hash)
headReq, err := http.NewRequest(http.MethodHead, url, nil)

View File

@@ -6,8 +6,6 @@
package main
import (
"github.com/syncthing/syncthing/lib/logger"
)
import "github.com/syncthing/syncthing/internal/slogutil"
var l = logger.DefaultLogger.NewFacility("main", "Main package")
func init() { slogutil.RegisterPackage("Main package") }

View File

@@ -12,13 +12,13 @@ import (
"context"
"crypto/tls"
"fmt"
"log/slog"
"os"
"github.com/syncthing/syncthing/lib/config"
"github.com/syncthing/syncthing/lib/events"
"github.com/syncthing/syncthing/lib/fs"
"github.com/syncthing/syncthing/lib/locations"
"github.com/syncthing/syncthing/lib/logger"
"github.com/syncthing/syncthing/lib/protocol"
"github.com/syncthing/syncthing/lib/syncthing"
)
@@ -29,7 +29,7 @@ type CLI struct {
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 {
// Support reading the password from a pipe or similar
if c.GUIPassword == "-" {
reader := bufio.NewReader(os.Stdin)
@@ -40,13 +40,13 @@ func (c *CLI) Run(l logger.Logger) error {
c.GUIPassword = string(password)
}
if err := Generate(l, locations.GetBaseDir(locations.ConfigBaseDir), c.GUIUser, c.GUIPassword, c.NoPortProbing); err != nil {
if err := Generate(locations.GetBaseDir(locations.ConfigBaseDir), c.GUIUser, c.GUIPassword, c.NoPortProbing); err != nil {
return fmt.Errorf("failed to generate config and keys: %w", err)
}
return nil
}
func Generate(l logger.Logger, confDir, guiUser, guiPassword string, skipPortProbing bool) error {
func Generate(confDir, guiUser, guiPassword string, skipPortProbing bool) error {
dir, err := fs.ExpandTilde(confDir)
if err != nil {
return err
@@ -61,15 +61,16 @@ func Generate(l logger.Logger, confDir, guiUser, guiPassword string, skipPortPro
certFile, keyFile := locations.Get(locations.CertFile), locations.Get(locations.KeyFile)
cert, err := tls.LoadX509KeyPair(certFile, keyFile)
if err == nil {
l.Warnln("Key exists; will not overwrite.")
slog.Warn("Key exists; will not overwrite")
} else {
cert, err = syncthing.GenerateCertificate(certFile, keyFile)
if err != nil {
return fmt.Errorf("create certificate: %w", err)
}
}
myID = protocol.NewDeviceID(cert.Certificate[0])
l.Infoln("Device ID:", myID)
slog.Info("Calculated device ID", slog.String("device", myID.String()))
cfgFile := locations.Get(locations.ConfigFile)
cfg, _, err := config.Load(cfgFile, myID, events.NoopLogger)
@@ -87,7 +88,7 @@ func Generate(l logger.Logger, confDir, guiUser, guiPassword string, skipPortPro
var updateErr error
waiter, err := cfg.Modify(func(cfg *config.Configuration) {
updateErr = updateGUIAuthentication(l, &cfg.GUI, guiUser, guiPassword)
updateErr = updateGUIAuthentication(&cfg.GUI, guiUser, guiPassword)
})
if err != nil {
return fmt.Errorf("modify config: %w", err)
@@ -103,17 +104,17 @@ func Generate(l logger.Logger, confDir, guiUser, guiPassword string, skipPortPro
return nil
}
func updateGUIAuthentication(l logger.Logger, guiCfg *config.GUIConfiguration, guiUser, guiPassword string) error {
func updateGUIAuthentication(guiCfg *config.GUIConfiguration, guiUser, guiPassword string) error {
if guiUser != "" && guiCfg.User != guiUser {
guiCfg.User = guiUser
l.Infoln("Updated GUI authentication user name:", guiUser)
slog.Info("Updated GUI authentication user", "name", guiUser)
}
if guiPassword != "" && guiCfg.Password != guiPassword {
if err := guiCfg.SetPassword(guiPassword); err != nil {
return fmt.Errorf("failed to set GUI authentication password: %w", err)
}
l.Infoln("Updated GUI authentication password.")
slog.Info("Updated GUI authentication password")
}
return nil
}

View File

@@ -8,18 +8,21 @@ package main
import (
"fmt"
"log/slog"
"os"
"runtime"
"runtime/pprof"
"syscall"
"time"
"github.com/syncthing/syncthing/internal/slogutil"
)
func startHeapProfiler() {
l.Debugln("Starting heap profiling")
slog.Debug("Starting heap profiling")
go func() {
err := saveHeapProfiles(1) // Only returns on error
l.Warnln("Heap profiler failed:", err)
slog.Error("Heap profiler failed", slogutil.Error(err))
panic("Heap profiler failed")
}()
}

View File

@@ -14,7 +14,8 @@ import (
"errors"
"fmt"
"io"
"log"
"log/slog"
"maps"
"net/http"
_ "net/http/pprof" // Need to import this to support STPROFILER.
"net/url"
@@ -40,6 +41,7 @@ import (
"github.com/syncthing/syncthing/cmd/syncthing/generate"
"github.com/syncthing/syncthing/internal/db"
"github.com/syncthing/syncthing/internal/db/sqlite"
"github.com/syncthing/syncthing/internal/slogutil"
_ "github.com/syncthing/syncthing/lib/automaxprocs"
"github.com/syncthing/syncthing/lib/build"
"github.com/syncthing/syncthing/lib/config"
@@ -47,7 +49,6 @@ import (
"github.com/syncthing/syncthing/lib/events"
"github.com/syncthing/syncthing/lib/fs"
"github.com/syncthing/syncthing/lib/locations"
"github.com/syncthing/syncthing/lib/logger"
"github.com/syncthing/syncthing/lib/osutil"
"github.com/syncthing/syncthing/lib/protocol"
"github.com/syncthing/syncthing/lib/svcutil"
@@ -61,20 +62,8 @@ const (
const (
extraUsage = `
The --logflags value is a sum of the following:
1 Date
2 Time
4 Microsecond time
8 Long filename
16 Short filename
I.e. to prefix each log line with time and filename, set --logflags=18 (2 + 16
from above). The value 0 is used to disable all of the above. The default is
to show date and time (3).
Logging always happens to the command line (stdout) and optionally to the
file at the path specified by --logfile=path. In addition to an path, the special
file at the path specified by --log-file=path. In addition to an path, the special
values "default" and "-" may be used. The former logs to DATADIR/syncthing.log
(see --data), which is the default on Windows, and the latter only to stdout,
no file, which is the default anywhere else.
@@ -87,11 +76,10 @@ The following environment variables modify Syncthing's behavior in ways that
are mostly useful for developers. Use with care. See also the --debug-* options
above.
STTRACE A comma separated string of facilities to trace. The valid
facility strings are listed below.
STLOCKTHRESHOLD Used for debugging internal deadlocks; sets debug
sensitivity. Use only under direction of a developer.
STTRACE A comma separated string of packages to trace or change log
level for. The valid package strings are listed below. A log
level (DEBUG, INFO, WARN or ERROR) can be added after each
package, separated by a colon. Ex: "model:WARN,nat:DEBUG".
STVERSIONEXTRA Add extra information to the version string in logs and the
version line in the GUI. Can be set to the name of a wrapper
@@ -106,8 +94,8 @@ above.
of CPU usage (i.e. performance).
Debugging Facilities
--------------------
Logging Facilities
------------------
The following are valid values for the STTRACE variable:
@@ -167,11 +155,12 @@ type serveCmd struct {
Audit bool `help:"Write events to audit file" env:"STAUDIT"`
AuditFile string `name:"auditfile" help:"Specify audit file (use \"-\" for stdout, \"--\" for stderr)" placeholder:"PATH" env:"STAUDITFILE"`
DBMaintenanceInterval time.Duration `help:"Database maintenance interval" default:"8h" env:"STDBMAINTENANCEINTERVAL"`
DBDeleteRetentionInterval time.Duration `help:"Database deleted item retention interval" default:"4320h" env:"STDBDELETERETENTIONINTERVAL"`
DBDeleteRetentionInterval time.Duration `help:"Database deleted item retention interval" default:"10920h" env:"STDBDELETERETENTIONINTERVAL"`
GUIAddress string `name:"gui-address" help:"Override GUI address (e.g. \"http://192.0.2.42:8443\")" placeholder:"URL" env:"STGUIADDRESS"`
GUIAPIKey string `name:"gui-apikey" help:"Override GUI API key" placeholder:"API-KEY" env:"STGUIAPIKEY"`
LogFile string `name:"logfile" help:"Log file name (see below)" default:"${logFile}" placeholder:"PATH" env:"STLOGFILE"`
LogFlags int `name:"logflags" help:"Select information in log line prefix (see below)" default:"${logFlags}" placeholder:"BITS" env:"STLOGFLAGS"`
LogFile string `name:"log-file" aliases:"logfile" help:"Log file name (see below)" default:"${logFile}" placeholder:"PATH" env:"STLOGFILE"`
LogFlags int `name:"logflags" help:"Deprecated option that does nothing, kept for compatibility" hidden:""`
LogLevel slog.Level `help:"Log level for all packages (DEBUG,INFO,WARN,ERROR)" env:"STLOGLEVEL" default:"INFO"`
LogMaxFiles int `name:"log-max-old-files" help:"Number of old files to keep (zero to keep only current)" default:"${logMaxFiles}" placeholder:"N" env:"STLOGMAXOLDFILES"`
LogMaxSize int `help:"Maximum size of any file (zero to disable log rotation)" default:"${logMaxSize}" placeholder:"BYTES" env:"STLOGMAXSIZE"`
NoBrowser bool `help:"Do not start browser" env:"STNOBROWSER"`
@@ -180,7 +169,6 @@ type serveCmd struct {
NoUpgrade bool `help:"Disable automatic upgrades" env:"STNOUPGRADE"`
Paused bool `help:"Start with all devices and folders paused" env:"STPAUSED"`
Unpaused bool `help:"Start with all devices and folders unpaused" env:"STUNPAUSED"`
Verbose bool `help:"Print verbose log output" env:"STVERBOSE"`
// Debug options below
DebugGUIAssetsDir string `help:"Directory to load GUI assets from" placeholder:"PATH" env:"STGUIASSETS"`
@@ -199,14 +187,9 @@ type serveCmd struct {
func defaultVars() kong.Vars {
vars := kong.Vars{}
vars["logFlags"] = strconv.Itoa(logger.DefaultFlags)
vars["logMaxSize"] = strconv.Itoa(10 << 20) // 10 MiB
vars["logMaxFiles"] = "3" // plus the current one
if os.Getenv("STTRACE") != "" {
vars["logFlags"] = strconv.Itoa(logger.DebugFlags)
}
// On non-Windows, we explicitly default to "-" which means stdout. On
// Windows, the "default" options.logFile will later be replaced with the
// default path, unless the user has manually specified "-" or
@@ -234,13 +217,13 @@ func main() {
defaultVars(),
)
if err != nil {
log.Fatal(err)
slog.Error("Parsing startup", slogutil.Error(err))
os.Exit(svcutil.ExitError.AsInt())
}
kongplete.Complete(parser)
ctx, err := parser.Parse(os.Args[1:])
parser.FatalIfErrorf(err)
ctx.BindTo(l, (*logger.Logger)(nil)) // main logger available to subcommands
err = ctx.Run()
parser.FatalIfErrorf(err)
}
@@ -252,15 +235,13 @@ func helpHandler(options kong.HelpOptions, ctx *kong.Context) error {
if ctx.Command() == "serve" {
// Help was requested for `syncthing serve`, so we add our extra
// usage info afte the normal options output.
fmt.Printf(extraUsage, debugFacilities())
fmt.Printf(extraUsage, logPackages())
}
return nil
}
// serveCmd.Run() is the entrypoint for `syncthing serve`
func (c *serveCmd) Run() error {
l.SetFlags(c.LogFlags)
if c.GUIAddress != "" {
// The config picks this up from the environment.
os.Setenv("STGUIADDRESS", c.GUIAddress)
@@ -274,6 +255,9 @@ func (c *serveCmd) Run() error {
osutil.HideConsole()
}
// The default log level for all packages
slogutil.SetDefaultLevel(c.LogLevel)
// Treat an explicitly empty log file name as no log file
if c.LogFile == "" {
c.LogFile = "-"
@@ -281,7 +265,7 @@ func (c *serveCmd) Run() error {
if c.LogFile != "default" {
// We must set this *after* expandLocations above.
if err := locations.Set(locations.LogFile, c.LogFile); err != nil {
l.Warnln("Setting log file path:", err)
slog.Error("Failed to set log file path", slogutil.Error(err))
os.Exit(svcutil.ExitError.AsInt())
}
}
@@ -290,7 +274,7 @@ func (c *serveCmd) Run() error {
// The asset dir is blank if STGUIASSETS wasn't set, in which case we
// should look for extra assets in the default place.
if err := locations.Set(locations.GUIAssets, c.DebugGUIAssetsDir); err != nil {
l.Warnln("Setting GUI assets path:", err)
slog.Error("Failed to set GUI assets path", slogutil.Error(err))
os.Exit(svcutil.ExitError.AsInt())
}
}
@@ -298,7 +282,7 @@ func (c *serveCmd) Run() error {
// Ensure that our config and data directories exist.
for _, loc := range []locations.BaseDirEnum{locations.ConfigBaseDir, locations.DataBaseDir} {
if err := syncthing.EnsureDir(locations.GetBaseDir(loc), 0o700); err != nil {
l.Warnln("Failed to ensure directory exists:", err)
slog.Error("Failed to ensure directory exists", slogutil.Error(err))
os.Exit(svcutil.ExitError.AsInt())
}
}
@@ -321,29 +305,27 @@ func openGUI() error {
return err
}
} else {
l.Warnln("Browser: GUI is currently disabled")
slog.Error("Browser: GUI is currently disabled")
}
return nil
}
func debugFacilities() string {
facilities := l.Facilities()
func logPackages() string {
packages := slogutil.PackageDescrs()
// Get a sorted list of names
var names []string
names := slices.Sorted(maps.Keys(packages))
maxLen := 0
for name := range facilities {
names = append(names, name)
for _, name := range names {
if len(name) > maxLen {
maxLen = len(name)
}
}
slices.Sort(names)
// Format the choices
b := new(bytes.Buffer)
for _, name := range names {
fmt.Fprintf(b, " %-*s - %s\n", maxLen, name, facilities[name])
fmt.Fprintf(b, " %-*s - %s\n", maxLen, name, packages[name])
}
return b.String()
}
@@ -371,7 +353,7 @@ func checkUpgrade() (upgrade.Release, error) {
return upgrade.Release{}, &errNoUpgrade{build.Version, release.Tag}
}
l.Infof("Upgrade available (current %q < latest %q)", build.Version, release.Tag)
slog.Info("Upgrade available", "current", build.Version, "latest", release.Tag)
return release, nil
}
@@ -403,9 +385,11 @@ func upgradeViaRest() error {
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
bs, err := io.ReadAll(resp.Body)
defer resp.Body.Close()
if err != nil {
return err
}
@@ -426,13 +410,9 @@ func (c *serveCmd) syncthingMain() {
startPerfStats()
}
// Set a log prefix similar to the ID we will have later on, or early log
// lines look ugly.
l.SetPrefix("[start] ")
// Print our version information up front, so any crash that happens
// early etc. will have it available.
l.Infoln(build.LongVersion)
slog.Info(build.LongVersion) //nolint:sloglint
// Ensure that we have a certificate and key.
cert, err := syncthing.LoadOrGenerateCertificate(
@@ -440,7 +420,7 @@ func (c *serveCmd) syncthingMain() {
locations.Get(locations.KeyFile),
)
if err != nil {
l.Warnln("Failed to load/generate certificate:", err)
slog.Error("Failed to load/generate certificate", slogutil.Error(err))
os.Exit(1)
}
@@ -448,10 +428,10 @@ func (c *serveCmd) syncthingMain() {
lf := flock.New(locations.Get(locations.LockFile))
locked, err := lf.TryLock()
if err != nil {
l.Warnln("Failed to acquire lock:", err)
slog.Error("Failed to acquire lock", slogutil.Error(err))
os.Exit(1)
} else if !locked {
l.Warnln("Failed to acquire lock: is another Syncthing instance already running?")
slog.Error("Failed to acquire lock: is another Syncthing instance already running?")
os.Exit(1)
}
@@ -460,7 +440,7 @@ func (c *serveCmd) syncthingMain() {
// earlyService is a supervisor that runs the services needed for or
// before app startup; the event logger, and the config service.
spec := svcutil.SpecWithDebugLogger(l)
spec := svcutil.SpecWithDebugLogger()
earlyService := suture.New("early", spec)
earlyService.ServeBackground(ctx)
@@ -469,7 +449,7 @@ func (c *serveCmd) syncthingMain() {
cfgWrapper, err := syncthing.LoadConfigAtStartup(locations.Get(locations.ConfigFile), cert, evLogger, c.AllowNewerConfig, c.NoPortProbing)
if err != nil {
l.Warnln("Failed to initialize config:", err)
slog.Error("Failed to initialize config", slogutil.Error(err))
os.Exit(svcutil.ExitError.AsInt())
}
earlyService.Add(cfgWrapper)
@@ -481,7 +461,7 @@ func (c *serveCmd) syncthingMain() {
if build.IsCandidate && !upgrade.DisabledByCompilation && !c.NoUpgrade {
cfgWrapper.Modify(func(cfg *config.Configuration) {
l.Infoln("Automatic upgrade is always enabled for candidate releases.")
slog.Info("Automatic upgrade is always enabled for candidate releases")
if cfg.Options.AutoUpgradeIntervalH == 0 || cfg.Options.AutoUpgradeIntervalH > 24 {
cfg.Options.AutoUpgradeIntervalH = 12
// Set the option into the config as well, as the auto upgrade
@@ -493,13 +473,13 @@ func (c *serveCmd) syncthingMain() {
}
if err := syncthing.TryMigrateDatabase(c.DBDeleteRetentionInterval); err != nil {
l.Warnln("Failed to migrate old-style database:", err)
slog.Error("Failed to migrate old-style database", slogutil.Error(err))
os.Exit(1)
}
sdb, err := syncthing.OpenDatabase(locations.Get(locations.Database), c.DBDeleteRetentionInterval)
if err != nil {
l.Warnln("Error opening database:", err)
slog.Error("Error opening database", slogutil.Error(err))
os.Exit(1)
}
@@ -516,12 +496,12 @@ func (c *serveCmd) syncthingMain() {
}
if err != nil {
if _, ok := err.(*errNoUpgrade); ok || err == errTooEarlyUpgradeCheck || err == errTooEarlyUpgrade {
l.Debugln("Initial automatic upgrade:", err)
slog.Debug("Initial automatic upgrade", slogutil.Error(err))
} else {
l.Infoln("Initial automatic upgrade:", err)
slog.Info("Initial automatic upgrade", slogutil.Error(err))
}
} else {
l.Infof("Upgraded to %q, should exit now.", release.Tag)
slog.Info("Upgraded, should exit now", "newVersion", release.Tag)
os.Exit(svcutil.ExitUpgrade.AsInt())
}
}
@@ -536,18 +516,17 @@ func (c *serveCmd) syncthingMain() {
NoUpgrade: c.NoUpgrade,
ProfilerAddr: c.DebugProfilerListen,
ResetDeltaIdxs: c.DebugResetDeltaIdxs,
Verbose: c.Verbose,
DBMaintenanceInterval: c.DBMaintenanceInterval,
}
if c.Audit || cfgWrapper.Options().AuditEnabled {
l.Infoln("Auditing is enabled.")
slog.Info("Auditing is enabled")
auditFile := cfgWrapper.Options().AuditFile
// Ignore config option if command-line option is set
if c.AuditFile != "" {
l.Debugln("Using the audit file from the command-line parameter.")
slog.Debug("Using the audit file from the command-line parameter", slogutil.FilePath(c.AuditFile))
auditFile = c.AuditFile
}
@@ -556,7 +535,7 @@ func (c *serveCmd) syncthingMain() {
app, err := syncthing.New(cfgWrapper, sdb, evLogger, cert, appOpts)
if err != nil {
l.Warnln("Failed to start Syncthing:", err)
slog.Error("Failed to start Syncthing", slogutil.Error(err))
os.Exit(svcutil.ExitError.AsInt())
}
@@ -569,11 +548,11 @@ func (c *serveCmd) syncthingMain() {
if c.DebugProfileCPU {
f, err := os.Create(fmt.Sprintf("cpu-%d.pprof", os.Getpid()))
if err != nil {
l.Warnln("Creating profile:", err)
slog.Error("Failed to create profile", slogutil.Error(err))
os.Exit(svcutil.ExitError.AsInt())
}
if err := pprof.StartCPUProfile(f); err != nil {
l.Warnln("Starting profile:", err)
slog.Error("Failed to start profile", slogutil.Error(err))
os.Exit(svcutil.ExitError.AsInt())
}
}
@@ -593,7 +572,7 @@ func (c *serveCmd) syncthingMain() {
status := app.Wait()
if status == svcutil.ExitError {
l.Warnln("Syncthing stopped with error:", app.Error())
slog.Error("Syncthing stopped with error", slogutil.Error(app.Error()))
}
if c.DebugProfileCPU {
@@ -611,8 +590,7 @@ func setupSignalHandling(app *syncthing.App) {
// Exit cleanly with "restarting" code on SIGHUP.
restartSign := make(chan os.Signal, 1)
sigHup := syscall.Signal(1)
signal.Notify(restartSign, sigHup)
signal.Notify(restartSign, syscall.SIGHUP)
go func() {
<-restartSign
app.Stop(svcutil.ExitRestart)
@@ -662,13 +640,13 @@ func auditWriter(auditFile string) io.Writer {
}
fd, err = os.OpenFile(auditFile, auditFlags, 0o600)
if err != nil {
l.Warnln("Audit:", err)
slog.Error("Failed to open audit file", slogutil.Error(err))
os.Exit(svcutil.ExitError.AsInt())
}
auditDest = auditFile
}
l.Infoln("Audit log in", auditDest)
slog.Info("Writing audit log", slogutil.FilePath(auditDest))
return fd
}
@@ -678,7 +656,7 @@ func (c *serveCmd) autoUpgradePossible() bool {
return false
}
if c.NoUpgrade {
l.Infof("No automatic upgrades; STNOUPGRADE environment variable defined.")
slog.Info("No automatic upgrades; STNOUPGRADE environment variable defined")
return false
}
return true
@@ -695,7 +673,7 @@ func autoUpgrade(cfg config.Wrapper, app *syncthing.App, evLogger events.Logger)
continue
}
if cfg.Options().AutoUpgradeEnabled() {
l.Infof("Connected to device %s with a newer version (current %q < remote %q). Checking for upgrades.", data["id"], build.Version, data["clientVersion"])
slog.Info("Connected to device with a newer version; checking for upgrades", slog.String("device", data["id"]), slog.String("ourVersion", build.Version), slog.String("theirVersion", data["clientVersion"]))
}
case <-timer.C:
}
@@ -715,7 +693,7 @@ func autoUpgrade(cfg config.Wrapper, app *syncthing.App, evLogger events.Logger)
if err != nil {
// Don't complain too loudly here; we might simply not have
// internet connectivity, or the upgrade server might be down.
l.Infoln("Automatic upgrade:", err)
slog.Info("Automatic upgrade", slogutil.Error(err))
timer.Reset(checkInterval)
continue
}
@@ -726,15 +704,15 @@ func autoUpgrade(cfg config.Wrapper, app *syncthing.App, evLogger events.Logger)
continue
}
l.Infof("Automatic upgrade (current %q < latest %q)", build.Version, rel.Tag)
slog.Info("Automatic upgrade", "current", build.Version, "latest", rel.Tag)
err = upgrade.To(rel)
if err != nil {
l.Warnln("Automatic upgrade:", err)
slog.Error("Automatic upgrade failed", slogutil.Error(err))
timer.Reset(checkInterval)
continue
}
sub.Unsubscribe()
l.Warnf("Automatically upgraded to version %q. Restarting in 1 minute.", rel.Tag)
slog.Error("Automatically upgraded, restarting in 1 minute", slog.String("newVersion", rel.Tag))
time.Sleep(time.Minute)
app.Stop(svcutil.ExitUpgrade)
return
@@ -787,22 +765,22 @@ func cleanConfigDirectory() {
fs := fs.NewFilesystem(fs.FilesystemTypeBasic, locations.GetBaseDir(locations.ConfigBaseDir))
files, err := fs.Glob(pat)
if err != nil {
l.Infoln("Cleaning:", err)
slog.Warn("Failed to clean config directory", slogutil.Error(err))
continue
}
for _, file := range files {
info, err := fs.Lstat(file)
if err != nil {
l.Infoln("Cleaning:", err)
slog.Warn("Failed to clean config directory", slogutil.Error(err))
continue
}
if time.Since(info.ModTime()) > dur {
if err = fs.RemoveAll(file); err != nil {
l.Infoln("Cleaning:", err)
slog.Warn("Failed to clean config directory", slogutil.Error(err))
} else {
l.Infoln("Cleaned away old file", filepath.Base(file))
slog.Warn("Cleaned away old file", slogutil.FilePath(filepath.Base(file)))
}
}
}
@@ -819,7 +797,7 @@ func setPauseState(cfgWrapper config.Wrapper, paused bool) {
}
})
if err != nil {
l.Warnln("Cannot adjust paused state:", err)
slog.Error("Cannot adjust paused state", slogutil.Error(err))
os.Exit(svcutil.ExitError.AsInt())
}
}
@@ -846,7 +824,7 @@ func (deviceIDCmd) Run() error {
locations.Get(locations.KeyFile),
)
if err != nil {
l.Warnln("Error reading device ID:", err)
slog.Error("Failed to read device ID", slogutil.Error(err))
os.Exit(svcutil.ExitError.AsInt())
}
@@ -869,7 +847,7 @@ type upgradeCmd struct {
func (u upgradeCmd) Run() error {
if u.CheckOnly {
if _, err := checkUpgrade(); err != nil {
l.Warnln("Checking for upgrade:", err)
slog.Error("Failed to check for upgrade", slogutil.Error(err))
os.Exit(exitCodeForUpgrade(err))
}
return nil
@@ -878,10 +856,10 @@ func (u upgradeCmd) Run() error {
if u.From != "" {
err := upgrade.ToURL(u.From)
if err != nil {
l.Warnln("Error while Upgrading:", err)
slog.Error("Failed to upgrade", slogutil.Error(err))
os.Exit(svcutil.ExitError.AsInt())
}
l.Infoln("Upgraded from", u.From)
slog.Info("Upgraded", "from", u.From)
return nil
}
@@ -890,20 +868,24 @@ func (u upgradeCmd) Run() error {
lf := flock.New(locations.Get(locations.LockFile))
var locked bool
locked, err = lf.TryLock()
if err != nil {
l.Warnln("Upgrade:", err)
// ErrNotExist is a valid error if this is a new/blank installation
// without a config dir, in which case we can proceed with a normal
// non-API upgrade.
switch {
case err != nil && !os.IsNotExist(err):
slog.Error("Failed to lock for upgrade", slogutil.Error(err))
os.Exit(1)
} else if locked {
case locked:
err = upgradeViaRest()
} else {
default:
err = upgrade.To(release)
}
}
if err != nil {
l.Warnln("Upgrade:", err)
slog.Error("Failed to check for upgrade", slogutil.Error(err))
os.Exit(exitCodeForUpgrade(err))
}
l.Infof("Upgraded to %q", release.Tag)
slog.Info("Upgraded", "to", release.Tag)
os.Exit(svcutil.ExitUpgrade.AsInt())
return nil
}
@@ -912,26 +894,28 @@ type browserCmd struct{}
func (browserCmd) Run() error {
if err := openGUI(); err != nil {
l.Warnln("Failed to open web UI:", err)
slog.Error("Failed to open web UI", slogutil.Error(err))
os.Exit(svcutil.ExitError.AsInt())
}
return nil
}
type debugCmd struct {
ResetDatabase resetDatabaseCmd `cmd:"" help:"Reset the database, forcing a full rescan and resync"`
DatabaseStatistics databaseStatsCmd `cmd:"" help:"Display database size statistics"`
ResetDatabase resetDatabaseCmd `cmd:"" help:"Reset the database, forcing a full rescan and resync"`
DatabaseStatistics databaseStatsCmd `cmd:"" help:"Display database size statistics"`
DatabaseCounts databaseCountsCmd `cmd:"" help:"Display database folder counts"`
DatabaseFile databaseFileCmd `cmd:"" help:"Display database file metadata"`
}
type resetDatabaseCmd struct{}
func (resetDatabaseCmd) Run() error {
l.Infoln("Removing database in", locations.Get(locations.Database))
slog.Info("Removing database", slogutil.FilePath(locations.Get(locations.Database)))
if err := os.RemoveAll(locations.Get(locations.Database)); err != nil {
l.Warnln("Resetting database:", err)
slog.Error("Failed to reset database", slogutil.Error(err))
os.Exit(svcutil.ExitError.AsInt())
}
l.Infoln("Successfully reset database - it will be rebuilt after next start.")
slog.Info("Reset database - it will be rebuilt after next start")
return nil
}
@@ -955,6 +939,33 @@ func (c databaseStatsCmd) Run() error {
return tw.Flush()
}
type databaseCountsCmd struct {
Folder string `arg:"" required:""`
}
func (c databaseCountsCmd) Run() error {
db, err := sqlite.Open(locations.Get(locations.Database))
if err != nil {
return err
}
return db.DebugCounts(os.Stdout, c.Folder)
}
type databaseFileCmd struct {
Folder string `arg:"" required:""`
File string `arg:"" required:""`
}
func (c databaseFileCmd) Run() error {
db, err := sqlite.Open(locations.Get(locations.Database))
if err != nil {
return err
}
return db.DebugFilePattern(os.Stdout, c.Folder, c.File)
}
func (c databaseStatsCmd) printStat(w io.Writer, s *sqlite.DatabaseStatistics) {
for _, table := range s.Tables {
fmt.Fprintf(w, "%s\t%s\t%s\t%8d KiB\t%5.01f %%\n", s.Name, cmp.Or(s.FolderID, "-"), table.Name, table.Size/1024, float64(table.Size-table.Unused)*100/float64(table.Size))

View File

@@ -11,26 +11,28 @@ import (
"context"
"fmt"
"io"
"log/slog"
"os"
"os/exec"
"os/signal"
"path/filepath"
"strings"
"sync"
"syscall"
"time"
"github.com/syncthing/syncthing/internal/slogutil"
"github.com/syncthing/syncthing/lib/build"
"github.com/syncthing/syncthing/lib/fs"
"github.com/syncthing/syncthing/lib/locations"
"github.com/syncthing/syncthing/lib/osutil"
"github.com/syncthing/syncthing/lib/svcutil"
"github.com/syncthing/syncthing/lib/sync"
)
var (
stdoutFirstLines []string // The first 10 lines of stdout
stdoutLastLines []string // The last 50 lines of stdout
stdoutMut = sync.NewMutex()
stdoutMut sync.Mutex
)
const (
@@ -44,8 +46,6 @@ const (
)
func (c *serveCmd) monitorMain() {
l.SetPrefix("[monitor] ")
var dst io.Writer = os.Stdout
logFile := locations.Get(locations.LogFile)
@@ -64,7 +64,7 @@ func (c *serveCmd) monitorMain() {
fileDst, err = open(logFile)
}
if err != nil {
l.Warnln("Failed to set up logging to file, proceeding with logging to stdout only:", err)
slog.Error("Failed to set up logging to file, proceeding with logging to stdout only", slogutil.Error(err))
} else {
if build.IsWindows {
// Translate line breaks to Windows standard
@@ -78,14 +78,14 @@ func (c *serveCmd) monitorMain() {
// Log to both stdout and file.
dst = io.MultiWriter(dst, fileDst)
l.Infof(`Log output saved to file "%s"`, logFile)
slog.Info("Saved log output", slogutil.FilePath(logFile))
}
}
args := os.Args
binary, err := getBinary(args[0])
if err != nil {
l.Warnln("Error starting the main Syncthing process:", err)
slog.Error("Failed to start the main Syncthing process", slogutil.Error(err))
panic("Error starting the main Syncthing process")
}
var restarts [restartCounts]time.Time
@@ -102,7 +102,7 @@ func (c *serveCmd) monitorMain() {
maybeReportPanics()
if t := time.Since(restarts[0]); t < restartLoopThreshold {
l.Warnf("%d restarts in %v; not retrying further", restartCounts, t)
slog.Error("Too many restarts; not retrying further", slog.Int("count", restartCounts), slog.Any("interval", t))
os.Exit(svcutil.ExitError.AsInt())
}
@@ -122,10 +122,10 @@ func (c *serveCmd) monitorMain() {
panic(err)
}
l.Debugln("Starting syncthing")
slog.Debug("Starting syncthing")
err = cmd.Start()
if err != nil {
l.Warnln("Error starting the main Syncthing process:", err)
slog.Error("Failed to start the main Syncthing process", slogutil.Error(err))
panic("Error starting the main Syncthing process")
}
@@ -134,7 +134,7 @@ func (c *serveCmd) monitorMain() {
stdoutLastLines = make([]string, 0, 50)
stdoutMut.Unlock()
wg := sync.NewWaitGroup()
var wg sync.WaitGroup
wg.Add(1)
go func() {
@@ -158,13 +158,13 @@ func (c *serveCmd) monitorMain() {
stopped := false
select {
case s := <-stopSign:
l.Infof("Signal %d received; exiting", s)
slog.Info("Received signal; exiting", "signal", s)
cmd.Process.Signal(sigTerm)
err = <-exit
stopped = true
case s := <-restartSign:
l.Infof("Signal %d received; restarting", s)
slog.Info("Received signal; restarting", "signal", s)
cmd.Process.Signal(sigHup)
err = <-exit
@@ -184,9 +184,9 @@ func (c *serveCmd) monitorMain() {
if exitCode == svcutil.ExitUpgrade.AsInt() {
// Restart the monitor process to release the .old
// binary as part of the upgrade process.
l.Infoln("Restarting monitor...")
slog.Info("Restarting monitor...")
if err = restartMonitor(binary, args); err != nil {
l.Warnln("Restart:", err)
slog.Error("Failed to restart monitor", slogutil.Error(err))
}
os.Exit(exitCode)
}
@@ -196,7 +196,7 @@ func (c *serveCmd) monitorMain() {
os.Exit(svcutil.ExitError.AsInt())
}
l.Infoln("Syncthing exited:", err)
slog.Info("Syncthing exited", slogutil.Error(err))
time.Sleep(restartPause)
if first {
@@ -238,47 +238,29 @@ func copyStderr(stderr io.Reader, dst io.Writer) {
return
}
if panicFd == nil {
dst.Write([]byte(line))
dst.Write([]byte(line))
if strings.HasPrefix(line, "panic:") || strings.HasPrefix(line, "fatal error:") {
panicFd, err = os.Create(locations.GetTimestamped(locations.PanicLog))
if err != nil {
l.Warnln("Create panic log:", err)
continue
}
l.Warnf("Panic detected, writing to \"%s\"", panicFd.Name())
if strings.Contains(line, "leveldb") && strings.Contains(line, "corrupt") {
l.Warnln(`
*********************************************************************************
* Crash due to corrupt database. *
* *
* This crash usually occurs due to one of the following reasons: *
* - Syncthing being stopped abruptly (killed/loss of power) *
* - Bad hardware (memory/disk issues) *
* - Software that affects disk writes (SSD caching software and similar) *
* *
* Please see the following URL for instructions on how to recover: *
* https://docs.syncthing.net/users/faq.html#my-syncthing-database-is-corrupt *
*********************************************************************************
`)
} else {
l.Warnln("Please check for existing issues with similar panic message at https://github.com/syncthing/syncthing/issues/")
l.Warnln("If no issue with similar panic message exists, please create a new issue with the panic log attached")
}
stdoutMut.Lock()
for _, line := range stdoutFirstLines {
panicFd.WriteString(line)
}
panicFd.WriteString("...\n")
for _, line := range stdoutLastLines {
panicFd.WriteString(line)
}
stdoutMut.Unlock()
if panicFd == nil && (strings.HasPrefix(line, "panic:") || strings.HasPrefix(line, "fatal error:")) {
panicFd, err = os.Create(locations.GetTimestamped(locations.PanicLog))
if err != nil {
slog.Error("Failed to create panic log", slogutil.Error(err))
continue
}
slog.Error("Panic detected, writing to file", slogutil.FilePath(panicFd.Name()))
slog.Info("Please check for existing issues with similar panic message at https://github.com/syncthing/syncthing/issues/")
slog.Info("If no issue with similar panic message exists, please create a new issue with the panic log attached")
stdoutMut.Lock()
for _, line := range stdoutFirstLines {
panicFd.WriteString(line)
}
panicFd.WriteString("...\n")
for _, line := range stdoutLastLines {
panicFd.WriteString(line)
}
stdoutMut.Unlock()
panicFd.WriteString("Panic at " + time.Now().Format(time.RFC3339) + "\n")
}
@@ -448,7 +430,6 @@ func newAutoclosedFile(name string, closeDelay, maxOpenTime time.Duration) (*aut
name: name,
closeDelay: closeDelay,
maxOpenTime: maxOpenTime,
mut: sync.NewMutex(),
closed: make(chan struct{}),
closeTimer: time.NewTimer(time.Minute),
}
@@ -556,7 +537,7 @@ func maybeReportPanics() {
// Try to get a config to see if/where panics should be reported.
cfg, err := loadOrDefaultConfig()
if err != nil {
l.Warnln("Couldn't load config; not reporting crash")
slog.Error("Couldn't load config; not reporting crash")
return
}
@@ -576,7 +557,7 @@ func maybeReportPanics() {
case <-ctx.Done():
return
case <-time.After(panicUploadNoticeWait):
l.Warnln("Uploading crash reports is taking a while, please wait...")
slog.Warn("Uploading crash reports is taking a while, please wait")
}
}()

View File

@@ -32,3 +32,10 @@
darwin: "20"
linux: "3.2"
windows: "10.0"
- runtime: go1.25
requirements:
# macOS 12 (Monterey) per https://tip.golang.org/doc/go1.25#darwin
darwin: "21"
linux: "3.2"
windows: "10.0"

View File

@@ -2,7 +2,7 @@
Name=Syncthing Web UI
GenericName=File synchronization UI
Comment=Opens Syncthing's Web UI in the default browser (Syncthing must already be started).
Exec=syncthing --browser-only
Exec=syncthing browser
Icon=syncthing
Terminal=false
Type=Application

View File

@@ -7,7 +7,7 @@ StartLimitBurst=4
[Service]
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
RestartSec=1
SuccessExitStatus=3 4

View File

@@ -18,4 +18,4 @@ env STNORESTART=yes
respawn
# the syncthing command Upstart is to execute when it is started up
exec $SYNCTHING_EXE -no-browser
exec $SYNCTHING_EXE --no-browser

72
go.mod
View File

@@ -1,15 +1,16 @@
module github.com/syncthing/syncthing
go 1.23.0
go 1.24.0
require (
github.com/AudriusButkevicius/recli v0.0.7-0.20220911121932-d000ce8fbf0f
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.1
github.com/alecthomas/kong v1.11.0
github.com/aws/aws-sdk-go v1.55.7
github.com/AudriusButkevicius/recli v0.0.7
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.2
github.com/alecthomas/kong v1.12.1
github.com/aws/aws-sdk-go v1.55.8
github.com/calmh/incontainer v1.0.0
github.com/calmh/xdr v1.2.0
github.com/ccding/go-stun v0.1.5
github.com/coreos/go-semver v0.3.1
github.com/d4l3k/messagediff v1.2.1
github.com/getsentry/raven-go v0.2.0
github.com/go-ldap/ldap/v3 v3.4.11
@@ -22,50 +23,50 @@ require (
github.com/julienschmidt/httprouter v1.3.0
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
github.com/maruel/panicparse/v2 v2.5.0
github.com/mattn/go-sqlite3 v1.14.28
github.com/maxbrunsfeld/counterfeiter/v6 v6.11.2
github.com/mattn/go-sqlite3 v1.14.31
github.com/maxbrunsfeld/counterfeiter/v6 v6.11.3
github.com/maxmind/geoipupdate/v6 v6.1.0
github.com/miscreant/miscreant.go v0.0.0-20200214223636-26d376326b75
github.com/oschwald/geoip2-golang v1.11.0
github.com/oschwald/geoip2-golang v1.13.0
github.com/pierrec/lz4/v4 v4.1.22
github.com/prometheus/client_golang v1.22.0
github.com/prometheus/client_golang v1.23.0
github.com/puzpuzpuz/xsync/v3 v3.5.1
github.com/quic-go/quic-go v0.52.0
github.com/rabbitmq/amqp091-go v1.10.0
github.com/rcrowley/go-metrics v0.0.0-20250401214520-65e299d6c5c9
github.com/shirou/gopsutil/v4 v4.25.4
github.com/shirou/gopsutil/v4 v4.25.7
github.com/syncthing/notify v0.0.0-20250528144937-c7027d4f7465
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d
github.com/thejerf/suture/v4 v4.0.6
github.com/urfave/cli v1.22.16
github.com/urfave/cli v1.22.17
github.com/vitrun/qart v0.0.0-20160531060029-bf64b92db6b0
github.com/willabides/kongplete v0.4.0
github.com/wlynxg/anet v0.0.5
go.uber.org/automaxprocs v1.6.0
golang.org/x/crypto v0.38.0
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394
golang.org/x/net v0.40.0
golang.org/x/sys v0.33.0
golang.org/x/text v0.25.0
golang.org/x/time v0.11.0
golang.org/x/tools v0.33.0
google.golang.org/protobuf v1.36.6
modernc.org/sqlite v1.37.0
sigs.k8s.io/yaml v1.4.0
golang.org/x/crypto v0.41.0
golang.org/x/exp v0.0.0-20250811191247-51f88131bc50
golang.org/x/net v0.43.0
golang.org/x/sys v0.35.0
golang.org/x/text v0.28.0
golang.org/x/time v0.12.0
golang.org/x/tools v0.36.0
google.golang.org/protobuf v1.36.7
modernc.org/sqlite v1.38.2
sigs.k8s.io/yaml v1.6.0
)
require (
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.1 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 // indirect
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/certifi/gocertifi v0.0.0-20210507211836-431795d63e8d // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/coreos/go-semver v0.3.1 // 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/dustin/go-humanize v1.0.1 // indirect
github.com/ebitengine/purego v0.8.3 // indirect
github.com/ebitengine/purego v0.8.4 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
@@ -87,28 +88,29 @@ require (
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/posener/complete v1.2.3 // indirect
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.62.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/prometheus/client_model v0.6.2 // indirect
github.com/prometheus/common v0.65.0 // indirect
github.com/prometheus/procfs v0.16.1 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/riywo/loginshell v0.0.0-20200815045211-7d26008be1ab // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/stretchr/objx v0.5.2 // indirect
github.com/stretchr/testify v1.10.0 // indirect
github.com/tklauser/go-sysconf v0.3.14 // indirect
github.com/tklauser/numcpus v0.9.0 // indirect
github.com/tklauser/go-sysconf v0.3.15 // indirect
github.com/tklauser/numcpus v0.10.0 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect
go.uber.org/mock v0.5.2 // indirect
golang.org/x/mod v0.24.0 // indirect
golang.org/x/sync v0.14.0 // indirect
go.yaml.in/yaml/v2 v2.4.2 // indirect
golang.org/x/mod v0.27.0 // indirect
golang.org/x/sync v0.16.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
modernc.org/libc v1.62.1 // indirect
modernc.org/libc v1.66.3 // indirect
modernc.org/mathutil v1.7.1 // indirect
modernc.org/memory v1.9.1 // indirect
modernc.org/memory v1.11.0 // indirect
)
// https://github.com/gobwas/glob/pull/55
replace github.com/gobwas/glob v0.2.3 => github.com/calmh/glob v0.0.0-20220615080505-1d823af5017b
// https://github.com/mattn/go-sqlite3/pull/1338
replace github.com/mattn/go-sqlite3 v1.14.28 => github.com/calmh/go-sqlite3 v1.14.29-0.20250520105817-2e94cda3f7f8
replace github.com/mattn/go-sqlite3 v1.14.31 => github.com/calmh/go-sqlite3 v1.14.32-0.20250812195006-80712c77b76a

168
go.sum
View File

@@ -1,38 +1,38 @@
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/AudriusButkevicius/recli v0.0.7-0.20220911121932-d000ce8fbf0f h1:GmH5lT+moM7PbAJFBq57nH9WJ+wRnBXr/tyaYWbSAx8=
github.com/AudriusButkevicius/recli v0.0.7-0.20220911121932-d000ce8fbf0f/go.mod h1:Nhfib1j/VFnLrXL9cHgA+/n2O6P5THuWelOnbfPNd78=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 h1:Gt0j3wceWMwPmiazCa8MzMA0MfhmPIz0Qp0FJ6qcM0U=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0/go.mod h1:Ot/6aikWnKWi4l9QB7qVSwa8iMphQNqkWALMoNT3rzM=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.9.0 h1:OVoM452qUFBrX+URdH3VpR299ma4kfom0yB0URYky9g=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.9.0/go.mod h1:kUjrAo8bgEwLeZ/CmHqNl3Z/kPm7y6FKfxxK0izYUg4=
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.0 h1:LR0kAX9ykz8G4YgLCaRDVJ3+n43R8MneB5dTy2konZo=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.8.0/go.mod h1:DWAciXemNf++PQJLeXUB4HHH5OpsAh12HZnu2wXE1jA=
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.1 h1:lhZdRq7TIx0GJQvSyX2Si406vrYsov2FXGp/RnSEtcs=
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.1/go.mod h1:8cl44BDmi+effbARHMQjgOKA2AYvcohNm7KEt42mSV8=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.8.1 h1:/Zt+cDPnpC3OVDm/JKLOs7M2DKmLRIIp3XIx9pHHiig=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.8.1/go.mod h1:Ng3urmn6dYe8gnbCMoHHVl5APYz2txho3koEkV2o2HA=
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.2 h1:FwladfywkNirM+FZYLBR2kBz5C8Tg0fw5w5Y7meRXWI=
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.2/go.mod h1:vv5Ad0RrIoT1lJFdWBZwt4mB1+j+V8DUroixmKDTCdk=
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8=
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 h1:oygO0locgZJe7PpYPXT5A29ZkwJaPqcva7BVeemZOZs=
github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0=
github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
github.com/alecthomas/kong v1.11.0 h1:y++1gI7jf8O7G7l4LZo5ASFhrhJvzc+WgF/arranEmM=
github.com/alecthomas/kong v1.11.0/go.mod h1:p2vqieVMeTAnaC83txKtXe8FLke2X07aruPWXyMPQrU=
github.com/alecthomas/kong v1.12.1 h1:iq6aMJDcFYP9uFrLdsiZQ2ZMmcshduyGv4Pek0MQPW0=
github.com/alecthomas/kong v1.12.1/go.mod h1:p2vqieVMeTAnaC83txKtXe8FLke2X07aruPWXyMPQrU=
github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc=
github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa h1:LHTHcTQiSGT7VVbI0o4wBRNQIgn917usHWOd6VAffYI=
github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4=
github.com/aws/aws-sdk-go v1.55.7 h1:UJrkFq7es5CShfBwlWAC8DA077vp8PyVbQd3lqLiztE=
github.com/aws/aws-sdk-go v1.55.7/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
github.com/aws/aws-sdk-go v1.55.8 h1:JRmEUbU52aJQZ2AjX4q4Wu7t4uZjOu71uyNmaWlUkJQ=
github.com/aws/aws-sdk-go v1.55.8/go.mod h1:ZkViS9AqA6otK+JBBNH2++sx1sgxrPKcSzPPvQkUtXk=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/calmh/glob v0.0.0-20220615080505-1d823af5017b h1:Fjm4GuJ+TGMgqfGHN42IQArJb77CfD/mAwLbDUoJe6g=
github.com/calmh/glob v0.0.0-20220615080505-1d823af5017b/go.mod h1:91K7jfEsgJSyfSrX+gmrRfZMtntx6JsHolWubGXDopg=
github.com/calmh/go-sqlite3 v1.14.29-0.20250520105817-2e94cda3f7f8 h1:oNVrBJGXkD334ToEmxJz8G6LhzD1/sMA4twMHsMLzQo=
github.com/calmh/go-sqlite3 v1.14.29-0.20250520105817-2e94cda3f7f8/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/calmh/go-sqlite3 v1.14.32-0.20250812195006-80712c77b76a h1:lTe5qJApKNO+zZCa3/P/7UxM4c58CXWOegv9eODPWvs=
github.com/calmh/go-sqlite3 v1.14.32-0.20250812195006-80712c77b76a/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/calmh/incontainer v1.0.0 h1:g2cTUtZuFGmMGX8GoykPkN1Judj2uw8/3/aEtq4Z/rg=
github.com/calmh/incontainer v1.0.0/go.mod h1:eOhqnw15c9X+4RNBe0W3HlUZFfX16O0EDsCOInTndHY=
github.com/calmh/xdr v1.2.0 h1:GaGSNH4ZDw9kNdYqle6+RcAENiaQ8/611Ok+jQbBEeU=
@@ -50,8 +50,8 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4=
github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec=
github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc=
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/go.mod h1:Oozbb1TVXFac9FtSIxHBMnBCq2qeH/2KkEQxENCrlLo=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -59,8 +59,8 @@ 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/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/ebitengine/purego v0.8.3 h1:K+0AjQp63JEZTEMZiwsI9g0+hAMNohwUOtY0RPGexmc=
github.com/ebitengine/purego v0.8.3/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/ebitengine/purego v0.8.4 h1:CF7LEKg5FFOsASUj0+QwaXf8Ht6TlFxg09+S9wz0omw=
github.com/ebitengine/purego v0.8.4/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
@@ -85,8 +85,8 @@ github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1v
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E=
github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0=
github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang-jwt/jwt/v5 v5.2.3 h1:kkGXqQOBSDDWRhWNXTFpqGSCMyh/PLnqUvMGJPDJDs0=
github.com/golang-jwt/jwt/v5 v5.2.3/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
@@ -102,7 +102,6 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
@@ -168,8 +167,8 @@ github.com/maruel/panicparse/v2 v2.5.0/go.mod h1:DA2fDiBk63bKfBf4CVZP9gb4fuvzdPb
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/maxbrunsfeld/counterfeiter/v6 v6.11.2 h1:yVCLo4+ACVroOEr4iFU1iH46Ldlzz2rTuu18Ra7M8sU=
github.com/maxbrunsfeld/counterfeiter/v6 v6.11.2/go.mod h1:VzB2VoMh1Y32/QqDfg9ZJYHj99oM4LiGtqPZydTiQSQ=
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/go.mod h1:cZYCDzfMzTY4v6dKRdV7KTB6SStxtn3yFkiJ1btTGGc=
github.com/miscreant/miscreant.go v0.0.0-20200214223636-26d376326b75 h1:cUVxyR+UfmdEAZGJ8IiKld1O0dbGotEnkMolG5hfMSY=
@@ -194,10 +193,10 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro=
github.com/onsi/gomega v1.36.3 h1:hID7cr8t3Wp26+cYnfcjR6HpJ00fdogN6dqZ1t6IylU=
github.com/onsi/gomega v1.36.3/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0=
github.com/oschwald/geoip2-golang v1.11.0 h1:hNENhCn1Uyzhf9PTmquXENiWS6AlxAEnBII6r8krA3w=
github.com/oschwald/geoip2-golang v1.11.0/go.mod h1:P9zG+54KPEFOliZ29i7SeYZ/GM6tfEL+rgSn03hYuUo=
github.com/onsi/gomega v1.37.0 h1:CdEG8g0S133B4OswTDC/5XPSzE1OeP29QOioj2PID2Y=
github.com/onsi/gomega v1.37.0/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0=
github.com/oschwald/geoip2-golang v1.13.0 h1:Q44/Ldc703pasJeP5V9+aFSZFmBN7DKHbNsSFzQATJI=
github.com/oschwald/geoip2-golang v1.13.0/go.mod h1:P9zG+54KPEFOliZ29i7SeYZ/GM6tfEL+rgSn03hYuUo=
github.com/oschwald/maxminddb-golang v1.13.1 h1:G3wwjdN9JmIK2o/ermkHM+98oX5fS+k5MbwsmL4MRQE=
github.com/oschwald/maxminddb-golang v1.13.1/go.mod h1:K4pgV9N/GcK694KSTmVSDTODk4IsCNThNdTmnaBZ/F8=
github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU=
@@ -215,14 +214,14 @@ github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/prometheus/client_golang v1.23.0 h1:ust4zpdl9r4trLY/gSjlm07PuiBq2ynaXXlptpfy8Uc=
github.com/prometheus/client_golang v1.23.0/go.mod h1:i/o0R9ByOnHX0McrTMTyhYvKE4haaf2mW08I+jGAjEE=
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE=
github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8=
github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
github.com/puzpuzpuz/xsync/v3 v3.5.1 h1:GJYJZwO6IdxN/IKbneznS6yPkVC+c3zyY/j19c++5Fg=
github.com/puzpuzpuz/xsync/v3 v3.5.1/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA=
github.com/quic-go/quic-go v0.52.0 h1:/SlHrCRElyaU6MaEPKqKr9z83sBg2v4FLLvWM+Z47pA=
@@ -241,8 +240,8 @@ github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sclevine/spec v1.4.0 h1:z/Q9idDcay5m5irkZ28M7PtQM4aOISzOpj4bUPkDee8=
github.com/sclevine/spec v1.4.0/go.mod h1:LvpgJaFyvQzRvc1kaDs0bulYwzC70PbiYjC4QnFHkOM=
github.com/shirou/gopsutil/v4 v4.25.4 h1:cdtFO363VEOOFrUCjZRh4XVJkb548lyF0q0uTeMqYPw=
github.com/shirou/gopsutil/v4 v4.25.4/go.mod h1:xbuxyoZj+UsgnZrENu3lQivsngRR5BdjbJwf2fv4szA=
github.com/shirou/gopsutil/v4 v4.25.7 h1:bNb2JuqKuAu3tRlPv5piSmBZyMfecwQ+t/ILq+1JqVM=
github.com/shirou/gopsutil/v4 v4.25.7/go.mod h1:XV/egmwJtd3ZQjBpJVY5kndsiOO4IRqy9TQnmm6VP7U=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
@@ -254,7 +253,6 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
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.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/syncthing/notify v0.0.0-20250528144937-c7027d4f7465 h1:yhxdTGmFkAM2TFA65c3NgGwpnIkUM8oVqPX2e9S7IVg=
@@ -263,17 +261,19 @@ github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDd
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48=
github.com/thejerf/suture/v4 v4.0.6 h1:QsuCEsCqb03xF9tPAsWAj8QOAJBgQI1c0VqJNaingg8=
github.com/thejerf/suture/v4 v4.0.6/go.mod h1:gu9Y4dXNUWFrByqRt30Rm9/UZ0wzRSt9AJS6xu/ZGxU=
github.com/tklauser/go-sysconf v0.3.14 h1:g5vzr9iPFFz24v2KZXs/pvpvh8/V9Fw6vQK5ZZb78yU=
github.com/tklauser/go-sysconf v0.3.14/go.mod h1:1ym4lWMLUOhuBOPGtRcJm7tEGX4SCYNEEEtghGG/8uY=
github.com/tklauser/numcpus v0.9.0 h1:lmyCHtANi8aRUgkckBgoDk1nHCux3n2cgkJLXdQGPDo=
github.com/tklauser/numcpus v0.9.0/go.mod h1:SN6Nq1O3VychhC1npsWostA+oW+VOQTxZrS604NSRyI=
github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4=
github.com/tklauser/go-sysconf v0.3.15/go.mod h1:Dmjwr6tYFIseJw7a3dRLJfsHAMXZ3nEnL/aZY+0IuI4=
github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfjso=
github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli v1.22.16 h1:MH0k6uJxdwdeWQTwhSO42Pwr4YLrNLwBtg1MRgTqPdQ=
github.com/urfave/cli v1.22.16/go.mod h1:EeJR6BKodywf4zciqrdw6hpCPk68JO9z5LazXZMn5Po=
github.com/urfave/cli v1.22.17 h1:SYzXoiPfQjHBbkYxbew5prZHS1TOLT3ierW8SYLqtVQ=
github.com/urfave/cli v1.22.17/go.mod h1:b0ht0aqgH/6pBYzzxURyrM4xXNgsoT/n2ZzwQiEhNVo=
github.com/vitrun/qart v0.0.0-20160531060029-bf64b92db6b0 h1:okhMind4q9H1OxF44gNegWkiP4H/gsTFLalHFa4OOUI=
github.com/vitrun/qart v0.0.0-20160531060029-bf64b92db6b0/go.mod h1:TTbGUfE+cXXceWtbTHq6lqcTvYPBKLNejBEbnUsQJtU=
github.com/willabides/kongplete v0.4.0 h1:eivXxkp5ud5+4+NVN9e4goxC5mSh3n1RHov+gsblM2g=
github.com/willabides/kongplete v0.4.0/go.mod h1:0P0jtWD9aTsqPSUAl4de35DLghrr57XcayPyvqSi2X8=
github.com/wlynxg/anet v0.0.5 h1:J3VJGi1gvo0JwZ/P1/Yc/8p63SoW98B5dHkYDmpgvvU=
github.com/wlynxg/anet v0.0.5/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
@@ -283,16 +283,20 @@ go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko=
go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o=
go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
go.yaml.in/yaml/v3 v3.0.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-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 h1:nDVHiLt8aIbd/VzvPWN6kSOPE7+F/fNFDSXLVYkE/Iw=
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394/go.mod h1:sIifuuw/Yco/y6yb6+bDNfyeQ/MdPUy/hKEMYQV17cM=
golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
golang.org/x/exp v0.0.0-20250811191247-51f88131bc50 h1:3yiSh9fhy5/RhCSntf4Sy0Tnx50DmMpQ4MQdKKk4yg4=
golang.org/x/exp v0.0.0-20250811191247-51f88131bc50/go.mod h1:rT6SFzZ7oxADUDx58pcaKFTcZ+inxAa9fTrYx/uVYwg=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ=
golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -301,13 +305,13 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180926160741-c2ed4eda69e7/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -330,23 +334,23 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
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.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
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/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg=
golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -360,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.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
google.golang.org/protobuf v1.36.7 h1:IgrO7UwFQGJdRNXH/sQux4R1Dj1WAKcLElzeeRaXV2A=
google.golang.org/protobuf v1.36.7/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
@@ -377,29 +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.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
modernc.org/cc/v4 v4.25.2 h1:T2oH7sZdGvTaie0BRNFbIYsabzCxUQg8nLqCdQ2i0ic=
modernc.org/cc/v4 v4.25.2/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
modernc.org/ccgo/v4 v4.25.1 h1:TFSzPrAGmDsdnhT9X2UrcPMI3N/mJ9/X9ykKXwLhDsU=
modernc.org/ccgo/v4 v4.25.1/go.mod h1:njjuAYiPflywOOrm3B7kCB444ONP5pAVr8PIEoE0uDw=
modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE=
modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ=
modernc.org/cc/v4 v4.26.2 h1:991HMkLjJzYBIfha6ECZdjrIYz2/1ayr+FL8GN+CNzM=
modernc.org/cc/v4 v4.26.2/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
modernc.org/ccgo/v4 v4.28.0 h1:rjznn6WWehKq7dG4JtLRKxb52Ecv8OUGah8+Z/SfpNU=
modernc.org/ccgo/v4 v4.28.0/go.mod h1:JygV3+9AV6SmPhDasu4JgquwU81XAKLd3OKTUDNOiKE=
modernc.org/fileutil v1.3.8 h1:qtzNm7ED75pd1C7WgAGcK4edm4fvhtBsEiI/0NQ54YM=
modernc.org/fileutil v1.3.8/go.mod h1:HxmghZSZVAz/LXcMNwZPA/DRrQZEVP9VX0V4LQGQFOc=
modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
modernc.org/libc v1.62.1 h1:s0+fv5E3FymN8eJVmnk0llBe6rOxCu/DEU+XygRbS8s=
modernc.org/libc v1.62.1/go.mod h1:iXhATfJQLjG3NWy56a6WVU73lWOcdYVxsvwCgoPljuo=
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.9.1 h1:V/Z1solwAVmMW1yttq3nDdZPJqV1rM05Ccq6KMSZ34g=
modernc.org/memory v1.9.1/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
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.37.0 h1:s1TMe7T3Q3ovQiK2Ouz4Jwh7dw4ZDqbebSDTlSJdfjI=
modernc.org/sqlite v1.37.0/go.mod h1:5YiWv+YviqGMuGw4V+PNplcyaJ5v+vQd7TQOgkACoJM=
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.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=

View File

@@ -370,7 +370,7 @@
"Show diff with previous version": "أظهر الفرق مقارنةً بالنسخة السابقة",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "يُعرَض بدلا من المُعرِّف ضمن العناقيد. سيُروَّج للأجهزة الأخرى على أنه اسم أساسي محتمل.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "يُعرَض بدلا من المُعرِّف ضمن العناقيد. إذا تُرك فارغا، سيُحدَّث إلى الاسم المختار من قِبَل الجهاز.",
"Shutdown": "إغلاق",
"Shut Down": "إغلاق",
"Shutdown Complete": "أُغلِق",
"Simple": "بسيط",
"Simple File Versioning": "التقسيم البسيط لإصدارات الملفات",

View File

@@ -152,7 +152,7 @@
"Show ID": "Паказаць ID",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.",
"Shutdown": "Выключыць",
"Shut Down": "Выключыць",
"Shutdown Complete": "Выключэньне завершанае",
"Simple File Versioning": "Простае захоўваньне вэрсій",
"Single level wildcard (matches within a directory only)": "Single level wildcard (matches within a directory only)",

View File

@@ -227,6 +227,7 @@
"Learn more": "Научете повече",
"Learn more at {%url%}": "Научете повече на {{url}}",
"Limit": "Ограничение",
"Limit Bandwidth in LAN": "Огранич. на скоростта в местната мрежа",
"Listener Failures": "Грешки при очакване на връзка",
"Listener Status": "Очакване на връзка",
"Listeners": "Очакване на връзка",
@@ -370,7 +371,7 @@
"Show diff with previous version": "Показване на разликите с предходната версия",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "В списъка на устройствата се показва вместо идентификатор. Ще бъде предложено на другите устройства като име по подразбиране.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "В списъка на устройствата се показва вместо идентификатор. Ако бъде оставено празно ще бъде променено на името, което носи устройството.",
"Shutdown": "Изключване",
"Shut Down": "Изключване",
"Shutdown Complete": "Спирането завършено",
"Simple": "Обикновени",
"Simple File Versioning": "Обикновени версии",

View File

@@ -27,6 +27,7 @@
"Allowed Networks": "Xarxes permeses",
"Alphabetic": "Alfabètic",
"Altered by ignoring deletes.": "S'ha alterat ignorant les supressions.",
"Always turned on when the folder type is \"{%foldertype%}\".": "Sempre activat quan el tipus de carpeta és \"{{foldertype}}\".",
"An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.": "Una ordre externa gestiona la versió. Ha d'eliminar el fitxer de la carpeta compartida. Si el camí a l'aplicació conté espais, s'ha de citar.",
"Anonymous Usage Reporting": "Informe anònim d'ús",
"Anonymous usage report format has changed. Would you like to move to the new format?": "El format de l'informe d'ús anònim ha canviat. Voleu canviar a aquest nou format?",
@@ -52,6 +53,7 @@
"Body:": "Cos de text:",
"Bugs": "Errors (Bugs)",
"Cancel": "Cancel·la",
"Cannot be enabled when the folder type is \"{%foldertype%}\".": "No es pot habilitar quan el tipus de carpeta és \"{{foldertype}}\".",
"Changelog": "Historial de canvis",
"Clean out after": "Netejar després",
"Cleaning Versions": "Netejant versions",
@@ -368,7 +370,7 @@
"Show diff with previous version": "Mostra la diferència amb la versió anterior",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Mostrat en comptes del ID del Node en l'estat del cluster. Serà advertit als altres dispositius com un nom opcional per defecte.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Mostrat en comptes del ID del Node en l'estat del cluster. S'actualitzarà al nom del dispositiu si es deixa buit.",
"Shutdown": "Apaga",
"Shut Down": "Apaga",
"Shutdown Complete": "Apagat complet",
"Simple": "Simple",
"Simple File Versioning": "Versionat de Fitxers Senzill",

View File

@@ -353,7 +353,7 @@
"Show diff with previous version": "Mostrar les diferències amb la versió prèvia",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Mostrat en lloc de l'ID del dispositiu en l'estat del grup (cluster). S'anunciarà als altres dispositius com el nom opcional per defecte.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Mostrat en lloc de l'ID del dispositiu en l'estat del grup (cluster). S'actualitzarà al nom que el dispositiu anuncia si es deixa buit.",
"Shutdown": "Apagar",
"Shut Down": "Apagar",
"Shutdown Complete": "Apagar completament",
"Simple": "Senzill",
"Simple File Versioning": "Versionat de fitxers senzill",

View File

@@ -360,7 +360,7 @@
"Show diff with previous version": "Ukázat rozdíl oproti předchozí verzi",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Zobrazeno místo identifikátoru zařízení na náhledu stavu clusteru. Bude odesíláno ostatním zařízením jako výchozí název zařízení.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Zobrazeno místo identifikátoru zařízení na náhledu stavu clusteru. Pokud nebude vyplněno, bude nastaveno na název, který zařízení odesílá.",
"Shutdown": "Vypnout",
"Shut Down": "Vypnout",
"Shutdown Complete": "Vypnutí dokončeno",
"Simple": "Jednoduché",
"Simple File Versioning": "Jednoduchá správa verzí souborů",

View File

@@ -368,7 +368,7 @@
"Show diff with previous version": "Vis forskelle fra tidligere version",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Vises i stedet for enheds-ID i klyngestatus. Vil blive sendt til andre enheder som valgfrit standardnavn.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Vises i stedet for enheds-ID i klyngestatus. Vil blive opdateret til det navn, som enheden sender, hvis det ikke er udfyldt.",
"Shutdown": "Luk ned",
"Shut Down": "Luk ned",
"Shutdown Complete": "Nedlukning fuldført",
"Simple": "Enkel",
"Simple File Versioning": "Simpel filversionering",

View File

@@ -227,6 +227,7 @@
"Learn more": "Mehr erfahren",
"Learn more at {%url%}": "Erfahren Sie mehr unter {{url}}",
"Limit": "Limit",
"Limit Bandwidth in LAN": "Bandbreite im LAN begrenzen",
"Listener Failures": "Fehler bei Listener",
"Listener Status": "Status der Listener",
"Listeners": "Zuhörer",
@@ -370,7 +371,7 @@
"Show diff with previous version": "Unterschied zur vorherigen Version anzeigen",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Wird anstelle der Gerätekennung im Verbundstatus angezeigt. Wird anderen Geräten als optionaler Standardname bekannt gegeben.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Wird anstelle der Gerätekennung im Verbundstatus angezeigt. Wird auf den Namen aktualisiert, den das Gerät anzeigt, wenn er leer bleibt.",
"Shutdown": "Herunterfahren",
"Shut Down": "Herunterfahren",
"Shutdown Complete": "Vollständig heruntergefahren",
"Simple": "Einfach",
"Simple File Versioning": "Einfache Dateiversionierung",

View File

@@ -368,7 +368,7 @@
"Show diff with previous version": "Εμφάνιση διαφορών με προηγούμενη έκδοση",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Θα φαίνεται αντί για την ταυτότητα της συσκευής στην προβολή της κατάστασης ολόκληρης της συστάδας. Θα γνωστοποιείται σαν το προαιρετικό όνομα της συσκευής.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Θα φαίνεται αντί για την ταυτότητα της συσκευής στην προβολή της κατάστασης ολόκληρης της συστάδας. Θα ενημερώνεται αυτόματα αν αλλάξει το όνομα της συσκευής.",
"Shutdown": "Απενεργοποίηση",
"Shut Down": "Απενεργοποίηση",
"Shutdown Complete": "Πλήρης απενεργοποίηση",
"Simple": "Απλό",
"Simple File Versioning": "Απλή τήρηση εκδόσεων",

View File

@@ -353,7 +353,7 @@
"Show diff with previous version": "Show diff with previous version",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.",
"Shutdown": "Shutdown",
"Shut Down": "Shut Down",
"Shutdown Complete": "Shutdown Complete",
"Simple": "Simple",
"Simple File Versioning": "Simple File Versioning",

View File

@@ -227,6 +227,7 @@
"Learn more": "Learn more",
"Learn more at {%url%}": "Learn more at {{url}}",
"Limit": "Limit",
"Limit Bandwidth in LAN": "Limit Bandwidth in LAN",
"Listener Failures": "Listener Failures",
"Listener Status": "Listener Status",
"Listeners": "Listeners",
@@ -370,7 +371,7 @@
"Show diff with previous version": "Show diff with previous version",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.",
"Shutdown": "Shutdown",
"Shut Down": "Shut Down",
"Shutdown Complete": "Shutdown Complete",
"Simple": "Simple",
"Simple File Versioning": "Simple File Versioning",

View File

@@ -82,6 +82,7 @@
"Custom Range": "Custom Range",
"Danger!": "Danger!",
"Database Location": "Database Location",
"Debug": "Debug",
"Debugging Facilities": "Debugging Facilities",
"Default": "Default",
"Default Configuration": "Default Configuration",
@@ -210,6 +211,7 @@
"Incoming Rate Limit (KiB/s)": "Incoming Rate Limit (KiB/s)",
"Incorrect configuration may damage your folder contents and render Syncthing inoperable.": "Incorrect configuration may damage your folder contents and render Syncthing inoperable.",
"Incorrect user name or password.": "Incorrect user name or password.",
"Info": "Info",
"Internally used paths:": "Internally used paths:",
"Introduced By": "Introduced By",
"Introducer": "Introducer",
@@ -227,6 +229,7 @@
"Learn more": "Learn more",
"Learn more at {%url%}": "Learn more at {{url}}",
"Limit": "Limit",
"Limit Bandwidth in LAN": "Limit Bandwidth in LAN",
"Listener Failures": "Listener Failures",
"Listener Status": "Listener Status",
"Listeners": "Listeners",
@@ -370,7 +373,7 @@
"Show diff with previous version": "Show diff with previous version",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.",
"Shutdown": "Shutdown",
"Shut Down": "Shut Down",
"Shutdown Complete": "Shutdown Complete",
"Simple": "Simple",
"Simple File Versioning": "Simple File Versioning",

View File

@@ -258,7 +258,7 @@
"Show diff with previous version": "Montri diferenco kun antaŭa versio",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Montrita anstataŭ ID de Aparato en la statuso de la grupo. Estos anoncita al aliaj aparatoj kiel laŭvola defaŭlta nomo.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Montri anstataŭ ID de Aparato en la statuso de la grupo. Estos ĝisdatigita al la nomo de la aparato sciigante se ĝi estas lasita malplena.",
"Shutdown": "Sistemfermo",
"Shut Down": "Sistemfermo",
"Shutdown Complete": "Sistemfermo Tuta",
"Simple File Versioning": "Simpla Versionado de Dosieroj",
"Single level wildcard (matches within a directory only)": "Ununivela ĵokero (egalas nur ene de dosierujo)",

View File

@@ -368,7 +368,7 @@
"Show diff with previous version": "Mostrar la diferencia con la versión anterior",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Se muestra en lugar del ID del dispositivo en el estado del grupo (cluster). Se notificará a los otros dispositivos como nombre opcional por defecto.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Se muestra en lugar del ID del dispositivo en el estado del grupo (cluster). Se actualizará al nombre que el dispositivo anuncia si se deja vacío.",
"Shutdown": "Apagar",
"Shut Down": "Apagar",
"Shutdown Complete": "Apagar completamente",
"Simple": "Sencillo",
"Simple File Versioning": "Versionado simple de fichero",

View File

@@ -1,19 +1,22 @@
{
"A device with that ID is already added.": "Sellise seadme ID'ga seade on juba lisatud.",
"A device with that ID is already added.": "Sellise tunnusega seade on juba lisatud.",
"A negative number of days doesn't make sense.": "Negatiivne päevade arv ei ole loogiline.",
"API Key": "API Võti",
"API Key": "API võti",
"About": "Rakenduse teave",
"Action": "Tegevus",
"Actions": "Tegevused",
"Add": "Lisa",
"Add Device": "Lisa seade",
"Add Folder": "Lisa kaust",
"Add new folder?": "Lisa uus kaust?",
"Add Remote Device": "Lisa kaugseade",
"Add new folder?": "Kas lisad uue kausta?",
"Address": "Aadress",
"Addresses": "Aadressid",
"All Data": "Kõik andmed",
"All Time": "Kõik ajad",
"Allowed Networks": "Lubatud võrgud",
"Alphabetic": "Tähestikuline",
"Apply": "Rakenda",
"Automatic upgrades": "Automaatsed uuendused",
"Be careful!": "Ettevaatust!",
"Cancel": "Loobu",
@@ -47,7 +50,7 @@
"Folder Label": "Kausta Silt",
"Folder Type": "Kausta Tüüp",
"Folders": "Kaustad",
"GUI": "GUI",
"GUI": "Kasutajaliides",
"GUI Authentication Password": "GUI Autentimise Salasõna",
"GUI Authentication User": "GUI Autentimise Kasutajatunnus",
"GUI Theme": "GUI Teema",
@@ -75,7 +78,7 @@
"New Folder": "Uus Kaust",
"Newest First": "Uusimad Ennem",
"No": "Ei",
"OK": "OK",
"OK": "Sobib",
"Oldest First": "Vanimad Ennem",
"Options": "Valikud",
"Outgoing Rate Limit (KiB/s)": "Väljuva Kiiruse Piirang (KiB/s)",
@@ -116,7 +119,7 @@
"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 must be unique.": "Kausta ID peab olema unikaalne.",
"The folder path cannot be blank.": "Kausta asukoht ei tohi olla tühi!",
"The folder path cannot be blank.": "Kausta asukoht ei tohi olla tühi.",
"The following items could not be synchronized.": "Järgnevaid üksusi ei õnnestunud sünkroniseerida.",
"The maximum age must be a number and cannot be blank.": "Maksimaalne vanus peab olema arv ning ei tohi olla tühi.",
"Unknown": "Teadmata",

View File

@@ -309,7 +309,7 @@
"Show diff with previous version": "Erakutsi aurreko bertsioarekiko aldeak",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Tresnaren ID-aren ordez erakutsia, taldearen egoeran. Beste tresneri erakutsia izanen da, izen erabilgarri bat bezala",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Tresnaren ID-aren ordez erakutsia, taldearen egoeran. Hutsa utzia balin bada, urrun den tresnak proposatu izenarekin aktualizatua izanen da",
"Shutdown": "Geldi",
"Shut Down": "Geldi",
"Shutdown Complete": "Gelditua!",
"Simple File Versioning": "Bertsioen segitze sinplifikatua",
"Single level wildcard (matches within a directory only)": "Hein bakar bateko jokerra (karpetaren barnean bakarrik dagokiona)",

View File

@@ -288,7 +288,7 @@
"Show diff with previous version": "Näytä muutokset edelliseen versioon",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Näytetään ryhmän tiedoissa laitteen ID:n sijaan. Ilmoitetaan muille laitteille vaihtoehtoisena oletusnimenä.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Näytetään ryhmän tiedoissa laitteen ID:n sijaan. Tyhjä nimi päivitetään laitteen ilmoittamaksi nimeksi.",
"Shutdown": "Sammuta",
"Shut Down": "Sammuta",
"Shutdown Complete": "Sammutus valmis",
"Simple File Versioning": "Yksinkertainen tiedostoversiointi",
"Single level wildcard (matches within a directory only)": "Yksitasoinen jokerimerkki (vaikuttaa vain kyseisen kansion sisällä)",

View File

@@ -370,7 +370,7 @@
"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 updated to the name the device advertises if left empty.": "Ipinapakita sa halip na Device ID sa status ng cluster. Ia-update sa pangalan na ina-advertise ng device kung iiwanang walang laman.",
"Shutdown": "I-shutdown",
"Shut Down": "I-shutdown",
"Shutdown Complete": "Tapos na ang Shutdown",
"Simple": "Simple",
"Simple File Versioning": "Simpleng File Versioning",

View File

@@ -160,7 +160,7 @@
"Show QR": "Afficher l'image QR",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Affiché à la place de l'ID de l'appareil dans l'état du groupe. Sera diffusé aux autres appareils comme nom convivial optionnel par défaut.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Affiché à la place de l'ID de l'appareil dans l'état du groupe. Si laissé vide, il sera renseigné par le nom convivial proposé par l'appareil distant.",
"Shutdown": "Arrêter",
"Shut Down": "Arrêter",
"Shutdown Complete": "Arrêté !",
"Simple File Versioning": "Suivi simplifié des versions",
"Single level wildcard (matches within a directory only)": "Joker à un seul niveau (correspond uniquement à lintérieur du répertoire)",

View File

@@ -227,6 +227,7 @@
"Learn more": "En savoir plus",
"Learn more at {%url%}": "Si vous souhaitez en savoir plus, c'est ici (en anglais) : {{url}}",
"Limit": "Limite",
"Limit Bandwidth in LAN": "Limiter la bande passante locale (LAN)",
"Listener Failures": "Échecs de l'écouteur",
"Listener Status": "État de l'écouteur",
"Listeners": "Écoute",
@@ -370,7 +371,7 @@
"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 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",
"Simple": "Suivi simplifié",
"Simple File Versioning": "Suivi simplifié des versions",

View File

@@ -312,7 +312,7 @@
"Show diff with previous version": "Ferskil (diff) mei de foarige ferzje sjen litte",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Wurd ynstee fan apparaat-ID sjen litten by de bondeltastân. Wurd nei oare apparaten advertearre as in mooglike standertnamme.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Wurd yn de bondeltastân sjen litten ynstee fan apparaat-ID. Wannear't leech litten wurd, wurd it fernijt nei de namme die it apparaat útstjoert.",
"Shutdown": "Ofslute",
"Shut Down": "Ofslute",
"Shutdown Complete": "Ofsluten klear",
"Simple File Versioning": "Ienfâldich triemferzjebehear",
"Single level wildcard (matches within a directory only)": "Inkel-nivo jokerteken (wildcard) (fergeliket allinnich binnen in map)",

View File

@@ -227,6 +227,7 @@
"Learn more": "Faigh tuilleadh eolais",
"Learn more at {%url%}": "Tuilleadh eolais ag {{url}}",
"Limit": "Teorainn",
"Limit Bandwidth in LAN": "Teorainn a chur ar an bandaleithead i LAN",
"Listener Failures": "Teipeanna an Éisteora",
"Listener Status": "Stádas an éisteora",
"Listeners": "Éisteoirí",
@@ -370,7 +371,7 @@
"Show diff with previous version": "Taispeáin diff leis an leagan roimhe seo",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Taispeántar é in ionad ID gléis i stádas na braisle. Fógrófar é do ghléasanna eile mar ainm réamhshocraithe roghnach.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Taispeántar é in ionad ID gléis i stádas na braisle. Déanfar é a nuashonrú go dtí an t-ainm a fhógraíonn an gléas má fhágtar folamh é.",
"Shutdown": "Múchadh",
"Shut Down": "Múchadh",
"Shutdown Complete": "Múchadh Críochnaithe",
"Simple": "Simplí",
"Simple File Versioning": "Leagan Simplí Comhad",

View File

@@ -323,7 +323,7 @@
"Show diff with previous version": "Mostrar a diferencia coa versión anterior",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Mostrado en lugar do ID de Dispositivo no estado do clúster. Anunciarase a outros dispositivos como nome por defecto opcional.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Mostrado en lugar do ID de Dispositivo no estado do clúster. Actualizarase ao nome que anuncia o dispositivo de se deixar baleiro.",
"Shutdown": "Apagar",
"Shut Down": "Apagar",
"Shutdown Complete": "Apagado Completado",
"Simple": "Simple",
"Simple File Versioning": "Versionado de Ficheiros Sinxelo",

View File

@@ -27,6 +27,7 @@
"Allowed Networks": "רשתות מורשות",
"Alphabetic": "אלפביתי",
"Altered by ignoring deletes.": "השתנה על ידי התעלמות ממחיקות.",
"Always turned on when the folder type is \"{%foldertype%}\".": "תמיד מופעל כאשר סוג התיקייה הוא \"{{foldertype}}\".",
"An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.": "פקודה חיצונית מטפלת בניהול הגרסאות. היא חייבת להסיר את הקובץ מהתיקייה המשותפת. אם הנתיב ליישום מכיל רווחים, יש לצטט אותו.",
"Anonymous Usage Reporting": "דיווח שימוש אנונימי",
"Anonymous usage report format has changed. Would you like to move to the new format?": "פורמט דוח שימוש אנונימי השתנה. האם ברצונך לעבור לפורמט החדש?",
@@ -52,6 +53,7 @@
"Body:": "גוף:",
"Bugs": "באגים",
"Cancel": "ביטול",
"Cannot be enabled when the folder type is \"{%foldertype%}\".": "לא ניתן לאפשור כאשר סוג התיקייה הוא \"{{foldertype}}\".",
"Changelog": "יומן שינויים",
"Clean out after": "נקה לאחר",
"Cleaning Versions": "מנקה גרסאות",
@@ -225,6 +227,7 @@
"Learn more": "למד עוד",
"Learn more at {%url%}": "למד עוד באתר {{url}}",
"Limit": "מגבלה",
"Limit Bandwidth in LAN": "הגבלת רוחב פס ברשת תקשורת מקומית (LAN)",
"Listener Failures": "כשלי מאזין",
"Listener Status": "מצב מאזין",
"Listeners": "מאזינים",
@@ -368,7 +371,7 @@
"Show diff with previous version": "הצג הבדל עם הגרסה הקודמת",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "מוצג במקום מזהה ההתקן במצב האשכול. יפורסם להתקנים אחרים כשם ברירת מחדל אופציונלי.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "מוצג במקום מזהה ההתקן במצב האשכול. יעודכן לשם שההתקן מפרסם אם נותר ריק.",
"Shutdown": "כיבוי",
"Shut Down": "כיבוי",
"Shutdown Complete": "הכיבוי הושלם",
"Simple": "פשוט",
"Simple File Versioning": "ניהול גרסאות קבצים פשוט",

View File

@@ -368,7 +368,7 @@
"Show diff with previous version": "पिछले संस्करण के साथ अंतर दिखाएं",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "समूह स्थिति में उपकरण ID के बजाय दिखाया गया। वैकल्पिक तयशुदा नाम के रूप में अन्य उपकरणों पर विज्ञापित किया जाएगा।",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "समूह स्थिति में उपकरण ID के बजाय दिखाया गया। खाली छोड़े जाने पर उपकरण द्वारा विज्ञापित नाम में अद्यतित कर दिया जाएगा।",
"Shutdown": "शटडाउन",
"Shut Down": "शटडाउन",
"Shutdown Complete": "शटडाउन पूर्ण",
"Simple": "सरल",
"Simple File Versioning": "सरल फाइल संस्करण",

View File

@@ -345,7 +345,7 @@
"Show diff with previous version": "Előző verzió eltérésének megjelenítése",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Az eszközazonosító helyett jelenik meg. A többi eszközön alapértelmezett névként használható.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Az eszközazonosító helyett jelenik meg. Üresen hagyva az eszköz saját neve lesz alkalmazva.",
"Shutdown": "Leállítás",
"Shut Down": "Leállítás",
"Shutdown Complete": "Leállítás kész",
"Simple": "Egyszerű",
"Simple File Versioning": "Egyszerű fájlverzió-követés",

View File

@@ -322,7 +322,7 @@
"Show diff with previous version": "Tampilkan perbedaan dengan versi sebelumnya",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Ditampilkan sebagai ganti ID Perangkat dalam status gugus. Akan diumumkan ke perangkat lain sebagai nama bawaan opsional.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Ditampilkan sebagai ganti ID Perangkat dalam status gugus. Akan diubah menjadi nama yang perangkat umumkan jika tidak diisi.",
"Shutdown": "Matikan",
"Shut Down": "Matikan",
"Shutdown Complete": "Pematian Selesai",
"Simple File Versioning": "Pemversian Berkas Sederhana",
"Single level wildcard (matches within a directory only)": "Wildcard tingkat tunggal (cocok hanya dalam satu direktori saja)",

View File

@@ -370,7 +370,7 @@
"Show diff with previous version": "Mostra le differenze con la versione precedente",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Visibile al posto dell'ID Dispositivo nello stato del cluster. Negli altri dispositivi verrà presentato come nome predefinito opzionale.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Visibile al posto dell'ID Dispositivo nello stato del cluster. Se viene lasciato vuoto, verrà utilizzato il nome proposto dal dispositivo.",
"Shutdown": "Arresta",
"Shut Down": "Arresta",
"Shutdown Complete": "Arresto Eseguito",
"Simple": "Semplice",
"Simple File Versioning": "Controllo Versione Semplice",

View File

@@ -311,7 +311,7 @@
"Show diff with previous version": "前バージョンとの差分を表示",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "ステータス画面でデバイスIDの代わりに表示されます。他のデバイスに対してもデフォルトの名前として通知されます。",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "ステータス画面でデバイスIDの代わりに表示されます。空欄にすると相手側デバイスが通知してきた名前で更新されます。",
"Shutdown": "シャットダウン",
"Shut Down": "シャットダウン",
"Shutdown Complete": "シャットダウン完了",
"Simple File Versioning": "単純バージョン管理",
"Single level wildcard (matches within a directory only)": "ワイルドカード (単一のディレクトリ内だけでマッチします)",

View File

@@ -227,6 +227,7 @@
"Learn more": "더 알아보기",
"Learn more at {%url%}": "자세한 내용은 {{url}}을 참조하십시오.",
"Limit": "제한",
"Limit Bandwidth in LAN": "근거리 통신망(LAN)에 속도 제한 적용",
"Listener Failures": "대기자 실패",
"Listener Status": "대기자 현황",
"Listeners": "대기자",
@@ -370,7 +371,7 @@
"Show diff with previous version": "이전 버전과의 diff 보기",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "기기 아이디를 대신해 기기 목록에서 나타납니다. 다른 기기에 선택적 기본값 이름으로 통보됩니다.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "기기 아이디를 대신해 기기 목록에서 나타납니다. 비워 둘 경우 다른 기기에서 통보받은 이름으로 갱신됩니다.",
"Shutdown": "종료",
"Shut Down": "종료",
"Shutdown Complete": "종료 완료",
"Simple": "간단",
"Simple File Versioning": "간단한 파일 버전 관리",

View File

@@ -337,7 +337,7 @@
"Show diff with previous version": "Rodyti skirtumus su ankstesne versija",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Grupės būsenoje rodomas vietoje įrenginio vardo. Kiti įrenginiai matys kaip pasirinktinį vardą.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Grupės būsenoje rodomas vietoje įrenginio vardo. Bus atnaujintas į įrenginio vardą jei nieko neįrašysite.",
"Shutdown": "Išjungti",
"Shut Down": "Išjungti",
"Shutdown Complete": "Sėkmingai išjungta",
"Simple File Versioning": "Supaprastintas versijų valdymas",
"Single level wildcard (matches within a directory only)": "Vieno lygio pakaitos simbolis (atitinka tik vieną katalogo lygį)",

View File

@@ -339,7 +339,7 @@
"Show diff with previous version": "Vis diff med forrige version",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Vis i stedet for enhets-ID i gruppestatus. Vil bli kringkastet til andre enheter som et valgfritt forvalgsnavn.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Vist i stedet for mappe-ID i gruppestatus. Vil bli oppdatert til navnet enheten kringkaster dersom tomt.",
"Shutdown": "Avslutt",
"Shut Down": "Avslutt",
"Shutdown Complete": "Avslutning fullført",
"Simple": "Enkel",
"Simple File Versioning": "Enkel versjonskontroll",

View File

@@ -27,6 +27,7 @@
"Allowed Networks": "Toegestane netwerken",
"Alphabetic": "Alfabetisch",
"Altered by ignoring deletes.": "Veranderd door het negeren van verwijderingen.",
"Always turned on when the folder type is \"{%foldertype%}\".": "Altijd aanzetten als het map-type \"{{foldertype}}\" is.",
"An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.": "Een externe opdracht regelt het versiebeheer. Hij moet het bestand verwijderen uit de gedeelde map. Als het pad naar de toepassing spaties bevat, moet dit tussen aanhalingstekens geplaatst worden.",
"Anonymous Usage Reporting": "Anonieme gebruikersstatistieken",
"Anonymous usage report format has changed. Would you like to move to the new format?": "Het formaat voor anonieme gebruikersrapporten is gewijzigd. Wil je naar het nieuwe formaat overschakelen?",
@@ -52,6 +53,7 @@
"Body:": "Inhoud:",
"Bugs": "Bugs",
"Cancel": "Annuleren",
"Cannot be enabled when the folder type is \"{%foldertype%}\".": "Kan niet aangezet worden als het map-type \"{{foldertype}}\" is.",
"Changelog": "Wijzigingenlogboek",
"Clean out after": "Opruimen na",
"Cleaning Versions": "Versies opruimen",
@@ -225,8 +227,9 @@
"Learn more": "Lees meer",
"Learn more at {%url%}": "Meer informatie op {{url}}",
"Limit": "Begrenzing",
"Listener Failures": "Luisteraarfouten",
"Listener Status": "Luisteraarstatus",
"Limit Bandwidth in LAN": "Beperk de bandbreedte op het lokale netwerk",
"Listener Failures": "Luisteraarsfouten",
"Listener Status": "Luisteraarsstatus",
"Listeners": "Luisteraars",
"Loading data...": "Gegevens laden...",
"Loading...": "Laden...",
@@ -368,7 +371,7 @@
"Show diff with previous version": "Verschil met vorige versie weergeven",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Weergegeven in plaats van de apparaat-ID in de cluster-status. Zal aan andere apparaten voorgesteld worden als een optionele standaardnaam.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Weergegeven in plaats van de apparaat-ID in de clusterstatus. Zal bijgewerkt worden met de naam die het apparaat voorstelt wanneer leeg gelaten.",
"Shutdown": "Afsluiten",
"Shut Down": "Afsluiten",
"Shutdown Complete": "Afsluiten voltooid",
"Simple": "Eenvoudig",
"Simple File Versioning": "Eenvoudig versiebeheer",
@@ -386,6 +389,7 @@
"Staggered File Versioning": "Gespreid versiebeheer",
"Start Browser": "Browser starten",
"Statistics": "Statistieken",
"Stay logged in": "Blijf aangemeld",
"Stopped": "Gestopt",
"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.": "Bewaart en synchroniseert alleen versleutelde gegevens. Mappen op alle verbonden apparaten moeten met hetzelfde wachtwoord ingesteld worden of ook van het type \"{{receiveEncrypted}}\" zijn.",
"Subject:": "Onderwerp:",

View File

@@ -187,7 +187,7 @@
"Show QR": "Vis QR",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Vist i staden for einings-ID-en i klyngjestatusen. Vil verta kringkasta til dei andre einingane som eit valfritt standardnamn.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Vist i staden for mappe-ID-en i klyngjestatuses. Vil verta oppdatert til namnet eininga kringkastar dersom tomt.",
"Shutdown": "Slå Av",
"Shut Down": "Slå Av",
"Shutdown Complete": "Slått av",
"Simple File Versioning": "Enkel filutgåvehandtering",
"Single level wildcard (matches within a directory only)": "Enkeltnivå-jokerteikn (søkjer berre i éi mappe)",

View File

@@ -227,6 +227,7 @@
"Learn more": "Dowiedz się więcej",
"Learn more at {%url%}": "Dowiedz się więcej na {{url}}",
"Limit": "Ograniczenie",
"Limit Bandwidth in LAN": "Ogranicz przepustowość w sieci lokalnej LAN",
"Listener Failures": "Błędy nasłuchujących",
"Listener Status": "Stan nasłuchujących",
"Listeners": "Nasłuchujący",
@@ -370,7 +371,7 @@
"Show diff with previous version": "Pokaż diff z poprzednią wersją",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Widoczna na liście urządzeń zamiast identyfikatora urządzenia. Będzie anonsowana do innych urządzeń jako opcjonalna nazwa domyślna.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Widoczna na liście urządzeń zamiast identyfikatora urządzenia. Zostanie zaktualizowana do nazwy anonsowanej przez urządzenie, jeżeli pozostanie pusta.",
"Shutdown": "Wyłącz",
"Shut Down": "Wyłącz",
"Shutdown Complete": "Wyłączanie ukończone",
"Simple": "Proste",
"Simple File Versioning": "Proste wersjonowanie plików",

View File

@@ -370,7 +370,7 @@
"Show diff with previous version": "Mostrar diferenças da versão anterior",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Mostrado no lugar do ID do dispositivo no indicador de estado do grupo. Será divulgado aos outros dispositivos como um nome opcional e pré-definido.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Mostrado no lugar do ID do dispositivo no indicador de estado do grupo. Será atualizado para o nome que o dispositivo divulga, caso seja deixado em branco.",
"Shutdown": "Desligar",
"Shut Down": "Desligar",
"Shutdown Complete": "Desligamento completado",
"Simple": "Simples",
"Simple File Versioning": "Simples",

View File

@@ -370,7 +370,7 @@
"Show diff with previous version": "Mostrar diferenças em relação à versão anterior",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Apresentado ao invés do ID do dispositivo no indicador de estado do grupo. Será divulgado aos outros dispositivos como um nome predefinido opcional.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Apresentado ao invés do ID do dispositivo no indicador de estado do grupo. Será actualizado para o nome que o dispositivo divulga, se for deixado em branco.",
"Shutdown": "Desligar",
"Shut Down": "Desligar",
"Shutdown Complete": "Encerramento completado",
"Simple": "Simples",
"Simple File Versioning": "Simples",

View File

@@ -312,7 +312,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.": "Vizibil în locul ID-ului dispozitivului într-un grup. Va fi sugerat celorlalte dispozitive ca nume opţional. ",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Vizibil în locul ID-ului dispozitivului într-un grup. Va fi înlocuit de numele sugerat de dispozitiv daca nu este completat. ",
"Shutdown": "Opreşte",
"Shut Down": "Opreşte",
"Shutdown Complete": "Oprește Complet",
"Simple File Versioning": "Versiuni simple ale documentelor",
"Single level wildcard (matches within a directory only)": "Asterisc de nivel simplu (corespunde doar unui fişier)",

View File

@@ -227,6 +227,7 @@
"Learn more": "Узнать больше",
"Learn more at {%url%}": "Узнать больше на сайте {{url}}",
"Limit": "Ограничение",
"Limit Bandwidth in LAN": "Ограничить пропускную способность в локальной сети",
"Listener Failures": "Ошибки прослушивателя",
"Listener Status": "Статус прослушивателя",
"Listeners": "Прослушиватель",
@@ -370,7 +371,7 @@
"Show diff with previous version": "Показать различия с предыдущей версией",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Отображается вместо ID устройства в статусе группы. Будет разослан другим устройствам в качестве имени по умолчанию.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Отображается вместо ID устройства в статусе группы. Если поле не заполнено, то будет установлено имя, передаваемое этим устройством.",
"Shutdown": "Выключить",
"Shut Down": "Выключить",
"Shutdown Complete": "Выключение",
"Simple": "Просто",
"Simple File Versioning": "Простое управление версиями файлов",

View File

@@ -340,7 +340,7 @@
"Show diff with previous version": "පෙර අනුවාදය සමඟ වෙනස පෙන්වන්න",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "පොකුරු තත්ත්‍වයේ උපාංග හැඳුනුම්පත වෙනුවට පෙන්වා ඇත. විකල්ප පෙරනිමි නාමයක් ලෙස වෙනත් උපාංග වෙත ප්‍රචාරණය කරනු ඇත.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "පොකුරු තත්ත්‍වයේ උපාංග හැඳුනුම්පත වෙනුවට පෙන්වා ඇත. හිස්ව තැබුවහොත් උපාංගය ප්‍රචාරණය කරන නමට යාවත්කාලීන වේ.",
"Shutdown": "වසා දමන්න",
"Shut Down": "වසා දමන්න",
"Shutdown Complete": "වසා දැමීම සම්පූර්ණයි",
"Simple": "සරල",
"Simple File Versioning": "සරල ගොනු අනුවාදය",

View File

@@ -366,7 +366,7 @@
"Show diff with previous version": "Ukázať rozdiely s predchádzajúcou verziou",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Zobrazované namiesto ID zariadenia v štatúte klastra. Toto pomenovanie bude oznamovať ostatným zariadeniam ako voliteľné predvolené pomenovanie.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Zobrazované namiesto ID zariadenia v klastri. Ak je ponechané prázdne, bude nahradené pomenovaním, ktoré oznamuje zariadenie.",
"Shutdown": "Vypnutie",
"Shut Down": "Vypnutie",
"Shutdown Complete": "Vypnutie ukončené",
"Simple": "Jednoduché",
"Simple File Versioning": "Jednoduché verzie súborov",

View File

@@ -315,7 +315,7 @@
"Show diff with previous version": "Pokaži razliko s prejšnjo različico",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Prikazano namesto ID-ja naprave v stanju gruče. Oglašuje se drugim napravam kot neobvezno privzeto ime.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Prikazano namesto ID-ja naprave v stanju gruče. Če ostane prazno, bo posodobljeno na ime, ki ga oglašuje naprava.",
"Shutdown": "Izklopi",
"Shut Down": "Izklopi",
"Shutdown Complete": "Izklop končan",
"Simple File Versioning": "Enostvno beleženje različic datotek",
"Single level wildcard (matches within a directory only)": "Enostopenjski nadomestni znak (ujema se samo v imeniku)",

View File

@@ -227,6 +227,7 @@
"Learn more": "Läs mer",
"Learn more at {%url%}": "Läs mer på {{url}}",
"Limit": "Gräns",
"Limit Bandwidth in LAN": "Begränsa bandbredd i LAN",
"Listener Failures": "Lyssnarfel",
"Listener Status": "Lyssnarstatus",
"Listeners": "Lyssnare",
@@ -315,8 +316,8 @@
"Received data is already encrypted": "Mottagna data är redan krypterade",
"Recent Changes": "Senaste ändringar",
"Reduced by ignore patterns": "Minskas med ignoreringsmönster",
"Relay LAN": "Relä LAN",
"Relay WAN": "Relä WAN",
"Relay LAN": "LAN-relä",
"Relay WAN": "WAN-relä",
"Release Notes": "Versionsanteckningar",
"Release candidates contain the latest features and fixes. They are similar to the traditional bi-weekly Syncthing releases.": "Utgåvskandidater innehåller de senaste funktionerna och korrigeringarna. De liknar de traditionella Syncthing-utgåvorna som kommer ut varannan vecka.",
"Remote Devices": "Fjärrenheter",
@@ -370,7 +371,7 @@
"Show diff with previous version": "Visa skillnad med tidigare version",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Visas istället för enhets-ID i klusterstatus. Kommer att annonseras på andra enheter som ett valfritt standardnamn.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Visas istället för enhets-ID i klusterstatusen. Kommer att uppdateras till det namn som enheten annonserar om det lämnas tomt.",
"Shutdown": "Stäng av",
"Shut Down": "Stäng av",
"Shutdown Complete": "Avstängning slutförd",
"Simple": "Enkel",
"Simple File Versioning": "Enkel filversionshantering",

View File

@@ -227,6 +227,7 @@
"Learn more": "Daha fazla bilgi edinin",
"Learn more at {%url%}": "{{url}} adresinde daha fazla bilgi edinin",
"Limit": "Sınır",
"Limit Bandwidth in LAN": "LAN'da Bant Genişliğini Sınırla",
"Listener Failures": "Dinleyici Hataları",
"Listener Status": "Dinleyici Durumu",
"Listeners": "Dinleyiciler",
@@ -370,7 +371,7 @@
"Show diff with previous version": "Önceki sürüm ile farklılıkları göster",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Küme durumunda Cihaz Kimliği yerine gösterilir. İsteğe bağlı varsayılan ad olarak diğer cihazlara duyurulacaktır.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Küme durumunda Cihaz Kimliği yerine gösterilir. Boş bırakılırsa duyurulan cihaz adına güncellenecektir.",
"Shutdown": "Kapat",
"Shut Down": "Kapat",
"Shutdown Complete": "Kapatma İşlemi Tamamlandı",
"Simple": "Basit",
"Simple File Versioning": "Basit Dosya Sürümlendirme",

View File

@@ -227,6 +227,7 @@
"Learn more": "Дізнатися більше",
"Learn more at {%url%}": "Дізнайтесь більше за посиланням {{url}}",
"Limit": "Ліміт",
"Limit Bandwidth in LAN": "Обмеження пропускної здатності в локальній мережі",
"Listener Failures": "Помилки приймачів",
"Listener Status": "Стан приймача",
"Listeners": "Приймачі",
@@ -370,7 +371,7 @@
"Show diff with previous version": "Показати відмінності від попередньої версії",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Показується замість ID пристрою в статусі кластера. Буде розголошено іншим вузлам як опціональне типове ім’я.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Показується замість ID пристрою в статусі кластера. Буде оновлено ім’ям, яке розголошене пристроєм, якщо залишити порожнім.",
"Shutdown": "Вимкнути",
"Shut Down": "Вимкнути",
"Shutdown Complete": "Вимикання завершене",
"Simple": "Просте",
"Simple File Versioning": "Просте версіювання",

View File

@@ -161,7 +161,7 @@
"Show QR": "Hiển thị QR",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Hiển thị thay cho ID th.bị trong trạng thái cụm. Sẽ được giới thiệu đến các th.bị khác như tên mặc định tuỳ chọn.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Hiển thị thay cho ID thiết bị trong trạng thái cụm. Nếu để trống sẽ được cập nhật thành tên mà thiết bị giới thiệu.",
"Shutdown": "Tắt",
"Shut Down": "Tắt",
"Shutdown Complete": "Tắt hoàn tất",
"Simple File Versioning": "Kiểu đơn giản",
"Single level wildcard (matches within a directory only)": "Ký tự thay thế đơn cấp (phù hợp với chỉ một thư mục)",

View File

@@ -227,6 +227,7 @@
"Learn more": "了解更多",
"Learn more at {%url%}": "了解更多请访问 {{url}}",
"Limit": "限制",
"Limit Bandwidth in LAN": "在局域网内限制带宽",
"Listener Failures": "监听程序失败",
"Listener Status": "监听程序状态",
"Listeners": "监听程序",
@@ -370,7 +371,7 @@
"Show diff with previous version": "显示与以前版本的差异",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "在集群状态中显示该名称,而不是设备 ID。将作为可选的默认名称向其他设备通告。",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "在集群状态中显示该名称,而不是设备 ID。如果留空将更新为设备通告的名称。",
"Shutdown": "关闭",
"Shut Down": "关闭",
"Shutdown Complete": "关闭完成",
"Simple": "简单",
"Simple File Versioning": "简单文件版本控制",

View File

@@ -340,7 +340,7 @@
"Show diff with previous version": "顯示與先前版本的差異",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "在集群狀態中顯示該名稱,而不是設備 ID。將會作為當前設備的可選的默認名稱報告給所有其他設備。",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "在集群狀態中顯示該名稱,而不是設備 ID。如果設置為空則會使用目標設備自報的默認名稱。",
"Shutdown": "關閉",
"Shut Down": "關閉",
"Shutdown Complete": "關閉完成",
"Simple": "簡易",
"Simple File Versioning": "簡易版本控制",

View File

@@ -368,7 +368,7 @@
"Show diff with previous version": "顯示與前一個版本的差異",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "代替裝置識別碼顯示在叢集狀態中。這段文字將會廣播到其他的裝置作為一個可選的預設名稱。",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "代替裝置識別碼顯示在叢集狀態中。本欄若未填寫則將被更新為此裝置所廣播的名稱。",
"Shutdown": "關閉",
"Shut Down": "關閉",
"Shutdown Complete": "關閉完成",
"Simple": "簡單",
"Simple File Versioning": "簡單檔案版本控制",

View File

@@ -108,27 +108,23 @@
<li><a href="" ng-click="about.show()"><span class="fa fa-fw fa-heart"></span>&nbsp;<span translate>About</span></a></li>
</ul>
</li>
<li ng-if="authenticated || config.gui.debugging" class="dropdown action-menu">
<li ng-if="authenticated" class="dropdown action-menu">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" aria-expanded="false">
<span class="fa fa-cog"></span>
<span class="hidden-xs" translate>Actions</span>
<span class="caret"></span>
</a>
<ul class="dropdown-menu">
<li ng-if="authenticated"><a href="" ng-click="showSettings()"><span class="fa fa-fw fa-cog"></span>&nbsp;<span translate>Settings</span></a></li>
<li ng-if="authenticated"><a href="" ng-click="showDeviceIdentification(thisDevice())"><span class="fa fa-fw fa-qrcode"></span>&nbsp;<span translate>Show ID</span></a></li>
<li ng-if="authenticated" class="divider" aria-hidden="true"></li>
<li ng-if="authenticated"><a href="" ng-click="advanced()"><span class="fa fa-fw fa-cogs"></span>&nbsp;<span translate>Advanced</span></a></li>
<li ng-if="authenticated"><a href="" ng-click="logging.show()"><span class="fa fa-fw fa-wrench"></span>&nbsp;<span translate>Logs</span></a></li>
<li class="divider" aria-hidden="true" ng-if="config.gui.debugging"></li>
<li><a href="/rest/debug/support" target="_blank" ng-if="config.gui.debugging"><span class="fa fa-fw fa-user-md"></span>&nbsp;<span translate>Support Bundle</span></a></li>
<li ng-if="authenticated" class="divider" aria-hidden="true"></li>
<li ng-if="authenticated && isAuthEnabled()"><a href="" ng-click="logout()"><span class="far fa-fw fa-sign-out"></span>&nbsp;<span translate>Log Out</span></a></li>
<li ng-if="authenticated"><a href="" ng-click="restart()"><span class="fa fa-fw fa-refresh"></span>&nbsp;<span translate>Restart</span></a></li>
<li ng-if="authenticated"><a href="" ng-click="shutdown()"><span class="fa fa-fw fa-power-off"></span>&nbsp;<span translate>Shutdown</span></a></li>
<li><a href="" ng-click="showSettings()"><span class="fa fa-fw fa-cog"></span>&nbsp;<span translate>Settings</span></a></li>
<li><a href="" ng-click="advanced()"><span class="fa fa-fw fa-cogs"></span>&nbsp;<span translate>Advanced</span></a></li>
<li class="divider" aria-hidden="true"></li>
<li><a href="" ng-click="showDeviceIdentification(thisDevice())"><span class="fa fa-fw fa-qrcode"></span>&nbsp;<span translate>Show ID</span></a></li>
<li><a href="" ng-click="logging.show()"><span class="fa fa-fw fa-wrench"></span>&nbsp;<span translate>Logs</span></a></li>
<li><a href="/rest/debug/support" target="_blank"><span class="fa fa-fw fa-user-md"></span>&nbsp;<span translate>Support Bundle</span></a></li>
<li class="divider" aria-hidden="true"></li>
<li ng-if="isAuthEnabled()"><a href="" ng-click="logout()"><span class="far fa-fw fa-sign-out"></span>&nbsp;<span translate>Log Out</span></a></li>
<li><a href="" ng-click="restart()"><span class="fa fa-fw fa-refresh"></span>&nbsp;<span translate>Restart</span></a></li>
<li><a href="" ng-click="shutdown()"><span class="fa fa-fw fa-power-off"></span>&nbsp;<span translate>Shut Down</span></a></li>
</ul>
</li>
</ul>
@@ -333,7 +329,7 @@
</p>
<table>
<tr ng-repeat="(id, err) in fsWatcherErrorMap()">
<td>{{folderLabel(id)}}</td><td>{{err}}</td>
<td>{{folderLabel(id)}}: </td><td>{{err}}</td>
</tr>
</table>
</div>
@@ -619,10 +615,10 @@
<button type="button" class="btn btn-sm btn-danger pull-left" ng-click="revertOverrideConfirmationModal('override', folder.id)" ng-if="folderStatus(folder) == 'outofsync' && folder.type == 'sendonly'">
<span class="fas fa-arrow-circle-up"></span>&nbsp;<span translate>Override Changes</span>
</button>
<button type="button" class="btn btn-sm btn-danger pull-left" ng-click="revertOverrideConfirmationModal('revert', folder.id)" ng-if="hasReceiveOnlyChanged(folder)">
<button type="button" class="btn btn-sm btn-danger pull-left" ng-click="revertOverrideConfirmationModal('revert', folder.id)" ng-if="hasReceiveOnlyChanged(folder) && ['outofsync', 'faileditems', 'localadditions'].indexOf(folderStatus(folder)) >= 0">
<span class="fa fa-arrow-circle-down"></span>&nbsp;<span translate>Revert Local Changes</span>
</button>
<button type="button" class="btn btn-sm btn-danger pull-left" ng-click="revertOverrideConfirmationModal('deleteEnc', folder.id)" ng-if="hasReceiveEncryptedItems(folder)">
<button type="button" class="btn btn-sm btn-danger pull-left" ng-click="revertOverrideConfirmationModal('deleteEnc', folder.id)" ng-if="hasReceiveEncryptedItems(folder) && ['outofsync', 'faileditems', 'localunencrypted'].indexOf(folderStatus(folder)) >= 0">
<span class="fa fa-minus-circle"></span>&nbsp;<span translate>Delete Unexpected Items</span>
</button>
<span class="pull-right">
@@ -874,11 +870,11 @@
<td ng-if="!connections[deviceCfg.deviceID].connected" class="text-right">
<span ng-repeat="addr in deviceCfg.addresses">
<span tooltip data-original-title="{{'Configured' | translate}}">{{addr}}</span><br>
<small ng-if="system.lastDialStatus[addr].error" tooltip data-original-title="{{system.lastDialStatus[addr].error}}" class="text-danger">{{abbreviatedError(addr)}}<br></small>
<small ng-if="system.lastDialStatus[addr].error && !deviceCfg.paused" tooltip data-original-title="{{system.lastDialStatus[addr].error}}" class="text-danger">{{abbreviatedError(addr)}}<br></small>
</span>
<span ng-repeat="addr in discoveryCache[deviceCfg.deviceID].addresses">
<span tooltip data-original-title="{{'Discovered' | translate}}">{{addr}}</span><br>
<small ng-if="system.lastDialStatus[addr].error" tooltip data-original-title="{{system.lastDialStatus[addr].error}}" class="text-danger">{{abbreviatedError(addr)}}<br></small>
<small ng-if="system.lastDialStatus[addr].error && !deviceCfg.paused" tooltip data-original-title="{{system.lastDialStatus[addr].error}}" class="text-danger">{{abbreviatedError(addr)}}<br></small>
</span>
</td>
</tr>

View File

@@ -30,7 +30,7 @@
<h4 class="text-center" translate>The Syncthing Authors</h4>
<div class="row">
<div class="col-md-12" id="contributor-list">
Jakob Borg, Audrius Butkevicius, Simon Frei, Tomasz Wilczyński, Alexander Graf, Alexandre Viau, Anderson Mesquita, André Colomb, Antony Male, Ben Schulz, bt90, Caleb Callaway, Daniel Harte, Emil Lundberg, Eric P, Evgeny Kuznetsov, greatroar, Lars K.W. Gohlke, Lode Hoste, Michael Ploujnikov, Ross Smith II, Stefan Tatschner, Wulf Weich, Adam Piggott, Adel Qalieh, Aleksey Vasenev, Alessandro G., Alex Ionescu, Alex Lindeman, Alex Xu, Alexander Seiler, Alexandre Alves, Aman Gupta, Andreas Sommer, andresvia, Andrew Rabert, Andrey D, andyleap, Anjan Momi, Anthony Goeckner, Antoine Lamielle, Anur, Aranjedeath, Arkadiusz Tymiński, Aroun, Arthur Axel fREW Schmidt, Artur Zubilewicz, Aurélien Rainone, BAHADIR YILMAZ, Bart De Vries, Beat Reichenbach, Ben Shepherd, Ben Sidhom, Benedikt Heine, Benno Fünfstück, Benny Ng, boomsquared, Boqin Qin, Boris Rybalkin, Brendan Long, Catfriend1, Cathryne Linenweaver, Cedric Staniewski, Chih-Hsuan Yen, Choongkyu, Chris Howie, Chris Joel, Christian Kujau, Christian Prescott, chucic, cjc7373, Colin Kennedy, Cromefire_, Cyprien Devillez, d-volution, Dan, Daniel Barczyk, Daniel Bergmann, Daniel Martí, Daniel Padrta, Darshil Chanpura, dashangcun, David Rimmer, DeflateAwning, Denis A., Dennis Wilson, derekriemer, DerRockWolf, desbma, Devon G. Redekopp, digital, Dimitri Papadopoulos Orfanos, Dmitry Saveliev, domain, Domenic Horner, Dominik Heidler, Elias Jarlebring, Elliot Huffman, Emil Hessman, Eng Zer Jun, entity0xfe, Eric Lesiuta, Erik Meitner, Evan Spensley, Federico Castagnini, Felix, Felix Ableitner, Felix Lampe, Felix Unterpaintner, Francois-Xavier Gsell, Frank Isemann, Gahl Saraf, georgespatton, ghjklw, Gilli Sigurdsson, Gleb Sinyavskiy, Graham Miln, Greg, guangwu, gudvinr, Gusted, Han Boetes, HansK-p, Harrison Jones, Hazem Krimi, Heiko Zuerker, Hireworks, Hugo Locurcio, Iain Barnett, Ian Johnson, ignacy123, Iskander Sharipov, Jaakko Hannikainen, Jack Croft, Jacob, Jake Peterson, James O'Beirne, James Patterson, Jaroslav Lichtblau, Jaroslav Malec, Jaspitta, Jaya Chithra, Jaya Kumar, Jeffery To, jelle van der Waa, Jens Diemer, Jochen Voss, Johan Vromans, John Rinehart, Jonas Thelemann, Jonathan, Jose Manuel Delicado, jtagcat, Julian Lehrhuber, Jörg Thalheim, Jędrzej Kula, Kapil Sareen, Karol Różycki, Kebin Liu, Keith Harrison, Kelong Cong, Ken'ichi Kamada, Kevin Allen, Kevin Bushiri, Kevin White, Jr., klemens, Kurt Fitzner, kylosus, Lars Lehtonen, Laurent Etiemble, Leo Arias, Liu Siyuan, Lord Landon Agahnim, LSmithx2, Lukas Lihotzki, Luke Hamburg, luzpaz, Majed Abdulaziz, Marc Laporte, Marcin Dziadus, Marcus B Spencer, Marcus Legendre, Mario Majila, Mark Pulford, Martchus, Mateusz Naściszewski, Mateusz Ż, mathias4833, Matic Potočnik, Matt Burke, Matt Robenolt, Matteo Ruina, Maurizio Tomasi, Max, Max Schulze, MaximAL, Maximilian, Michael Jephcote, Michael Rienstra, MichaIng, Migelo, Mike Boone, MikeLund, MikolajTwarog, Mingxuan Lin, mv1005, Nate Morrison, nf, Nicholas Rishel, Nick Busey, Nico Stapelbroek, Nicolas Braud-Santoni, Nicolas Perraut, Niels Peter Roest, Nils Jakobi, NinoM4ster, Nitroretro, NoLooseEnds, Oliver Freyermuth, orangekame3, otbutz, overkill, Oyebanji Jacob Mayowa, Pablo, Pascal Jungblut, Paul Brit, Paul Donald, Pawel Palenica, perewa, Peter Badida, Peter Dave Hello, Peter Hoeg, Peter Marquardt, Phani Rithvij, Phil Davis, Philippe Schommers, Phill Luby, Piotr Bejda, polyfloyd, Quentin Hibon, Rahmi Pruitt, red_led, Robert Carosi, Roberto Santalla, Robin Schoonover, Roman Zaynetdinov, rubenbe, Ruslan Yevdokymov, Ryan Qian, Ryan Sullivan, Sacheendra Talluri, Scott Klupfel, sec65, Sergey Mishin, Sertonix, Severin von Wnuck-Lipinski, Shaarad Dalvi, Simon Mwepu, Simon Pickup, Sly_tom_cat, Sonu Kumar Saw, Stefan Kuntz, Steven Eckhoff, Suhas Gundimeda, Sven Bachmann, Sébastien WENSKE, Taylor Khan, Terrance, TheCreeper, Thomas, Thomas Hipp, Tim Abell, Tim Howes, Tobias Frölich, Tobias Klauser, Tobias Nygren, Tobias Tom, Tom Jakubowski, Tommy van der Vorst, Tully Robinson, Tyler Brazier, Tyler Kropp, Unrud, vapatel2, Veeti Paananen, Victor Buinsky, Vik, Vil Brekin, villekalliomaki, Vladimir Rusinov, wangguoliang, WangXi, Will Rouesnel, William A. Kennington III, wouter bolsterlee, xarx00, Xavier O., xjtdy888, Yannic A., 佛跳墙, 落心
Jakob Borg, Audrius Butkevicius, Simon Frei, Tomasz Wilczyński, Alexander Graf, Alexandre Viau, Anderson Mesquita, André Colomb, Antony Male, Ben Schulz, bt90, Caleb Callaway, Daniel Harte, Emil Lundberg, Eric P, Evgeny Kuznetsov, greatroar, Lars K.W. Gohlke, Lode Hoste, Michael Ploujnikov, Ross Smith II, Stefan Tatschner, Wulf Weich, Adam Piggott, Adel Qalieh, Aleksey Vasenev, Alessandro G., Alex Ionescu, Alex Lindeman, Alex Xu, Alexander Seiler, Alexandre Alves, Aman Gupta, Andreas Sommer, andresvia, Andrew Rabert, Andrey D, andyleap, Anjan Momi, Anthony Goeckner, Antoine Lamielle, Anur, Aranjedeath, ardevd, Arkadiusz Tymiński, Aroun, Arthur Axel fREW Schmidt, Artur Zubilewicz, Ashish Bhate, Aurélien Rainone, BAHADIR YILMAZ, Bart De Vries, Beat Reichenbach, Ben Shepherd, Ben Sidhom, Benedikt Heine, Benno Fünfstück, Benny Ng, boomsquared, Boqin Qin, Boris Rybalkin, Brendan Long, Catfriend1, Cathryne Linenweaver, Cedric Staniewski, Chih-Hsuan Yen, Choongkyu, Chris Howie, Chris Joel, Christian Kujau, Christian Prescott, chucic, cjc7373, Colin Kennedy, Cromefire_, Cyprien Devillez, d-volution, Dan, Daniel Barczyk, Daniel Bergmann, Daniel Martí, Daniel Padrta, Daniil Gentili, Darshil Chanpura, dashangcun, David Rimmer, DeflateAwning, Denis A., Dennis Wilson, derekriemer, DerRockWolf, desbma, Devon G. Redekopp, digital, Dimitri Papadopoulos Orfanos, Dmitry Saveliev, domain, Domenic Horner, Dominik Heidler, Elias Jarlebring, Elliot Huffman, Emil Hessman, Eng Zer Jun, entity0xfe, Eric Lesiuta, Erik Meitner, Evan Spensley, Federico Castagnini, Felix, Felix Ableitner, Felix Lampe, Felix Unterpaintner, Francois-Xavier Gsell, Frank Isemann, Gahl Saraf, georgespatton, ghjklw, Gilli Sigurdsson, Gleb Sinyavskiy, Graham Miln, Greg, guangwu, gudvinr, Gusted, Han Boetes, HansK-p, Harrison Jones, Hazem Krimi, Heiko Zuerker, Hireworks, Hugo Locurcio, Iain Barnett, Ian Johnson, ignacy123, Iskander Sharipov, Jaakko Hannikainen, Jack Croft, Jacob, Jake Peterson, James O'Beirne, James Patterson, Jaroslav Lichtblau, Jaroslav Malec, Jaspitta, Jaya Chithra, Jaya Kumar, Jeffery To, jelle van der Waa, Jens Diemer, Jochen Voss, Johan Vromans, John Rinehart, Jonas Thelemann, Jonathan, Jose Manuel Delicado, jtagcat, Julian Lehrhuber, Jörg Thalheim, Jędrzej Kula, Kapil Sareen, Karol Różycki, Kebin Liu, Keith Harrison, Kelong Cong, Ken'ichi Kamada, Kevin Allen, Kevin Bushiri, Kevin White, Jr., klemens, Kurt Fitzner, kylosus, Lars Lehtonen, Laurent Etiemble, Leo Arias, Liu Siyuan, Lord Landon Agahnim, LSmithx2, Lukas Lihotzki, Luke Hamburg, luzpaz, Majed Abdulaziz, Marc Laporte, Marcel Meyer, Marcin Dziadus, Marcus B Spencer, Marcus Legendre, Mario Majila, Mark Pulford, Martchus, Mateusz Naściszewski, Mateusz Ż, mathias4833, Matic Potočnik, Matt Burke, Matt Robenolt, Matteo Ruina, Maurizio Tomasi, Max, Max Schulze, MaximAL, Maximilian, Michael Jephcote, Michael Rienstra, MichaIng, Migelo, Mike Boone, MikeLund, MikolajTwarog, Mingxuan Lin, mv1005, Nate Morrison, nf, Nicholas Rishel, Nick Busey, Nico Stapelbroek, Nicolas Braud-Santoni, Nicolas Perraut, Niels Peter Roest, Nils Jakobi, NinoM4ster, Nitroretro, NoLooseEnds, Oliver Freyermuth, orangekame3, otbutz, overkill, Oyebanji Jacob Mayowa, Pablo, Pascal Jungblut, Paul Brit, Paul Donald, Pawel Palenica, perewa, Peter Badida, Peter Dave Hello, Peter Hoeg, Peter Marquardt, Phani Rithvij, Phil Davis, Philippe Schommers, Phill Luby, Piotr Bejda, polyfloyd, pullmerge, Quentin Hibon, Rahmi Pruitt, red_led, Robert Carosi, Roberto Santalla, Robin Schoonover, Roman Zaynetdinov, rubenbe, Ruslan Yevdokymov, Ryan Qian, Ryan Sullivan, Sacheendra Talluri, Scott Klupfel, sec65, Sergey Mishin, Sertonix, Severin von Wnuck-Lipinski, Shaarad Dalvi, Simon Mwepu, Simon Pickup, Sly_tom_cat, Sonu Kumar Saw, Stefan Kuntz, Steven Eckhoff, Suhas Gundimeda, Sven Bachmann, Sébastien WENSKE, Taylor Khan, Terrance, TheCreeper, Thomas, Thomas Hipp, Tim Abell, Tim Howes, Tobias Frölich, Tobias Klauser, Tobias Nygren, Tobias Tom, Tom Jakubowski, Tommy van der Vorst, Tully Robinson, Tyler Brazier, Tyler Kropp, Unrud, vapatel2, Veeti Paananen, Victor Buinsky, Vik, Vil Brekin, villekalliomaki, Vladimir Rusinov, wangguoliang, WangXi, Will Rouesnel, William A. Kennington III, wouter bolsterlee, xarx00, Xavier O., xjtdy888, Yannic A., yparitcher, 佛跳墙, 落心
</div>
</div>
</div>
@@ -58,7 +58,6 @@ Jakob Borg, Audrius Butkevicius, Simon Frei, Tomasz Wilczyński, Alexander Graf,
<li><a href="https://github.com/calmh/xdr">calmh/xdr</a>, Copyright &copy; 2014 Jakob Borg.</li>
<li><a href="https://github.com/ccding/go-stun">ccding/go-stun</a>, Copyright &copy; 2016 Cong Ding.</li>
<li><a href="https://github.com/cespare/xxhash/v2">cespare/xxhash/v2</a>, Copyright &copy; 2016 Caleb Spare.</li>
<li><a href="https://github.com/chmduquesne/rollinghash">chmduquesne/rollinghash</a>, Copyright &copy; 2015 Christophe-Marie Duquesne.</li>
<li><a href="https://github.com/cpuguy83/go-md2man/v2">cpuguy83/go-md2man/v2</a>, Copyright &copy; 2014 Brian Goff.</li>
<li><a href="https://github.com/davecgh/go-spew">davecgh/go-spew</a>, Copyright &copy; 2012-2016 Dave Collins.</li>
<li><a href="https://github.com/go-asn1-ber/asn1-ber">go-asn1-ber/asn1-ber</a>, Copyright &copy; 2011-2015 Michael Mitton (mmitton@gmail.com).</li>
@@ -70,14 +69,15 @@ Jakob Borg, Audrius Butkevicius, Simon Frei, Tomasz Wilczyński, Alexander Graf,
<li><a href="https://github.com/protocolbuffers/protobuf-go">google.golang.org/protobuf</a>, Copyright &copy; 2018 The Go Authors.</li>
<li><a href="https://github.com/google/uuid">google/uuid</a>, Copyright &copy; 2009,2014 Google Inc.</li>
<li><a href="https://gopkg.in/yaml.v3">gopkg.in/yaml.v3</a>, Copyright &copy; 2025, the gopkg.in/yaml.v3 authors.</li>
<li><a href="https://github.com/greatroar/blobloom">greatroar/blobloom</a>, Copyright &copy; 2020-2024 the Blobloom authors.</li>
<li><a href="https://github.com/hashicorp/errwrap">hashicorp/errwrap</a>, Copyright &copy; 2014 HashiCorp, Inc.</li>
<li><a href="https://github.com/hashicorp/go-multierror">hashicorp/go-multierror</a>, Copyright &copy; 2014 HashiCorp, Inc.</li>
<li><a href="https://github.com/hashicorp/golang-lru">hashicorp/golang-lru</a>, Copyright &copy; 2014 HashiCorp, Inc.</li>
<li><a href="https://github.com/jackpal/gateway">jackpal/gateway</a>, Copyright &copy; 2010 Jack Palevich.</li>
<li><a href="https://github.com/jackpal/go-nat-pmp">jackpal/go-nat-pmp</a>, Copyright 2013 John Howard Palevich.</li>
<li><a href="https://github.com/jmoiron/sqlx">jmoiron/sqlx</a>, Copyright &copy; 2013, Jason Moiron.</li>
<li><a href="https://github.com/julienschmidt/httprouter">julienschmidt/httprouter</a>, Copyright &copy; 2013, Julien Schmidt.</li>
<li><a href="https://github.com/kballard/go-shellquote">kballard/go-shellquote</a>, Copyright &copy; 2014 Kevin Ballard.</li>
<li><a href="https://github.com/mattn/go-sqlite3">mattn/go-sqlite3</a>, Copyright &copy; 2014 Yasuhiro Matsumoto.</li>
<li><a href="https://github.com/miscreant/miscreant.go">miscreant/miscreant.go</a>, Copyright &copy; 2017-2019 The Miscreant Developers.</li>
<li><a href="https://github.com/munnerz/goautoneg">munnerz/goautoneg</a>, Copyright &copy; 2011, Open Knowledge Foundation Ltd.</li>
<li><a href="https://github.com/pierrec/lz4">pierrec/lz4</a>, Copyright &copy; 2015 Pierre Curto.</li>

View File

@@ -34,7 +34,7 @@ angular.module('syncthing.core')
middleCol = Math.ceil(size / 2) - 1;
if (value) {
value = value.toString().replace(/[\W_]/i, '');
value = value.toString().replace(/[\W_]/g, '');
for (row = 0; row < size; ++row) {
for (col = middleCol; col > -1; --col) {

View File

@@ -16,11 +16,16 @@
<label translate>Available debug logging facilities:</label>
<table class="table table-condensed table-striped">
<tbody>
<tr ng-repeat="(name, data) in logging.facilities">
<td>
<input type="checkbox" ng-model="data.enabled" ng-change="logging.onFacilityChange(name)" ng-disabled="data.enabled == null"> <span>{{ name }}</span>
<tr ng-repeat="(key, level) in logging.facilities.levels">
<td>{{ logging.facilities.packages[key] }} (<code>{{ key }}</code>)</td>
<td class="form-group">
<select class="form-control" ng-model="logging.facilities.levels[key]" ng-change="logging.onFacilityChange()" ng-disabled="logging.facilities.updating">
<option value="DEBUG" translate>Debug</option>
<option value="INFO" translate>Info</option>
<option value="WARN" translate>Warning</option>
<option value="ERROR" translate>Error</option>
</select>
</td>
<td>{{ data.description }}</td>
</tr>
</tbody>
</table>

View File

@@ -27,7 +27,7 @@ angular.module('syncthing.core')
// before modal show animation
$(element).on('show.bs.modal', function () {
// cycle through open modals, acertain modal with highest z-index
// cycle through open modals, ascertain modal with highest z-index
var largestZ = 1040;
$('.modal:visible').each(function (i) {
var thisZ = parseInt($(this).css('zIndex'));

View File

@@ -1568,16 +1568,8 @@ angular.module('syncthing.core')
$scope.logging = {
facilities: {},
refreshFacilities: function () {
$http.get(urlbase + '/system/debug').success(function (data) {
var facilities = {};
data.enabled = data.enabled || [];
$.each(data.facilities, function (key, value) {
facilities[key] = {
description: value,
enabled: data.enabled.indexOf(key) > -1
}
})
$scope.logging.facilities = facilities;
$http.get(urlbase + '/system/loglevels').success(function (data) {
$scope.logging.facilities = data;
}).error($scope.emitHTTPError);
},
show: function () {
@@ -1597,13 +1589,10 @@ angular.module('syncthing.core')
});
showModal('#logViewer');
},
onFacilityChange: function (facility) {
var enabled = $scope.logging.facilities[facility].enabled;
// Disable checkboxes while we're in flight.
$.each($scope.logging.facilities, function (key) {
$scope.logging.facilities[key].enabled = null;
})
$http.post(urlbase + '/system/debug?' + (enabled ? 'enable=' : 'disable=') + facility)
onFacilityChange: function () {
// Disable editing while we're in flight.
$scope.logging.facilities.updating = true;
$http.post(urlbase + '/system/loglevels', $scope.logging.facilities.levels)
.success($scope.logging.refreshFacilities)
.error($scope.emitHTTPError);
},
@@ -1626,7 +1615,7 @@ angular.module('syncthing.core')
content: function () {
var content = "";
$.each($scope.logging.entries, function (idx, entry) {
content += entry.when.split('.')[0].replace('T', ' ') + ' ' + entry.message + "\n";
content += entry.when.split('.')[0].replace('T', ' ') + ' ' + entry.level + ' ' + entry.message + "\n";
});
return content;
},

View File

@@ -210,6 +210,17 @@
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group">
<div class="checkbox">
<label>
<input id="LimitBandwidthInLan" type="checkbox" ng-model="tmpOptions.limitBandwidthInLan" /> <span translate>Limit Bandwidth in LAN</span>
</label>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group">

View File

@@ -1,4 +1,4 @@
Copyright 2008-2021 Martin Wendt,
Copyright 2008-2023 Martin Wendt,
https://wwWendt.de/
Permission is hereby granted, free of charge, to any person obtaining

View File

File diff suppressed because it is too large Load Diff

View File

@@ -19,9 +19,9 @@ type Counts struct {
Symlinks int
Deleted int
Bytes int64
Sequence int64 // zero for the global state
DeviceID protocol.DeviceID // device ID for remote devices, or special values for local/global
LocalFlags uint32 // the local flag for this count bucket
Sequence int64 // zero for the global state
DeviceID protocol.DeviceID // device ID for remote devices, or special values for local/global
LocalFlags protocol.FlagLocal // the local flag for this count bucket
}
func (c Counts) Add(other Counts) Counts {

View File

@@ -100,10 +100,9 @@ type FileMetadata struct {
Sequence int64
ModNanos int64
Size int64
LocalFlags int64
LocalFlags protocol.FlagLocal
Type protocol.FileInfoType
Deleted bool
Invalid bool
}
func (f *FileMetadata) ModTime() time.Time {
@@ -121,3 +120,7 @@ func (f *FileMetadata) IsDirectory() bool {
func (f *FileMetadata) ShouldConflict() bool {
return f.LocalFlags&protocol.LocalConflictFlags != 0
}
func (f *FileMetadata) IsInvalid() bool {
return f.LocalFlags.IsInvalid()
}

View File

@@ -21,6 +21,7 @@ var (
Namespace: "syncthing",
Subsystem: "db",
Name: "operations_current",
Help: "Number of database operations currently ongoing, per folder and operation",
}, []string{"folder", "operation"})
metricTotalOperationSeconds = promauto.NewCounterVec(prometheus.CounterOpts{
Namespace: "syncthing",

View File

@@ -9,9 +9,9 @@ package olddb
import (
"encoding/binary"
"slices"
"sync"
"github.com/syncthing/syncthing/internal/db/olddb/backend"
"github.com/syncthing/syncthing/lib/sync"
)
// A smallIndex is an in memory bidirectional []byte to uint32 map. It gives
@@ -32,7 +32,6 @@ func newSmallIndex(db backend.Backend, prefix []byte) *smallIndex {
prefix: prefix,
id2val: make(map[uint32]string),
val2id: make(map[string]uint32),
mut: sync.NewMutex(),
}
idx.load()
return idx

View File

@@ -10,6 +10,7 @@ import (
"database/sql"
"embed"
"io/fs"
"net/url"
"path/filepath"
"strconv"
"strings"
@@ -22,7 +23,7 @@ import (
"github.com/syncthing/syncthing/lib/protocol"
)
const currentSchemaVersion = 1
const currentSchemaVersion = 3
//go:embed sql/**
var embedded embed.FS
@@ -44,7 +45,12 @@ type baseDB struct {
func openBase(path string, maxConns int, pragmas, schemaScripts, migrationScripts []string) (*baseDB, error) {
// Open the database with options to enable foreign keys and recursive
// triggers (needed for the delete+insert triggers on row replace).
sqlDB, err := sqlx.Open(dbDriver, "file:"+path+"?"+commonOptions)
pathURL := url.URL{
Scheme: "file",
Path: fileToUriPath(path),
RawQuery: commonOptions,
}
sqlDB, err := sqlx.Open(dbDriver, pathURL.String())
if err != nil {
return nil, wrap(err)
}
@@ -62,6 +68,17 @@ func openBase(path string, maxConns int, pragmas, schemaScripts, migrationScript
baseName: filepath.Base(path),
sql: sqlDB,
statements: make(map[string]*sqlx.Stmt),
tplInput: map[string]any{
"FlagLocalUnsupported": protocol.FlagLocalUnsupported,
"FlagLocalIgnored": protocol.FlagLocalIgnored,
"FlagLocalMustRescan": protocol.FlagLocalMustRescan,
"FlagLocalReceiveOnly": protocol.FlagLocalReceiveOnly,
"FlagLocalGlobal": protocol.FlagLocalGlobal,
"FlagLocalNeeded": protocol.FlagLocalNeeded,
"FlagLocalRemoteInvalid": protocol.FlagLocalRemoteInvalid,
"LocalInvalidFlags": protocol.LocalInvalidFlags,
"SyncthingVersion": build.LongVersion,
},
}
for _, script := range schemaScripts {
@@ -96,19 +113,19 @@ func openBase(path string, maxConns int, pragmas, schemaScripts, migrationScript
return nil, wrap(err)
}
db.tplInput = map[string]any{
"FlagLocalUnsupported": protocol.FlagLocalUnsupported,
"FlagLocalIgnored": protocol.FlagLocalIgnored,
"FlagLocalMustRescan": protocol.FlagLocalMustRescan,
"FlagLocalReceiveOnly": protocol.FlagLocalReceiveOnly,
"FlagLocalGlobal": protocol.FlagLocalGlobal,
"FlagLocalNeeded": protocol.FlagLocalNeeded,
"SyncthingVersion": build.LongVersion,
}
return db, nil
}
func fileToUriPath(path string) string {
path = filepath.ToSlash(path)
if (build.IsWindows && len(path) >= 2 && path[1] == ':') ||
(strings.HasPrefix(path, "//") && !strings.HasPrefix(path, "///")) {
// Add an extra leading slash for Windows drive letter or UNC path
path = "/" + path
}
return path
}
func (s *baseDB) Close() error {
s.updateLock.Lock()
s.statementsMut.Lock()
@@ -151,15 +168,8 @@ func (s *baseDB) stmt(tpl string) stmt {
return stmt
}
// Apply template expansions
var sb strings.Builder
compTpl := template.Must(template.New("tpl").Funcs(tplFuncs).Parse(tpl))
if err := compTpl.Execute(&sb, s.tplInput); err != nil {
panic("bug: bad template: " + err.Error())
}
// Prepare and cache
stmt, err := s.sql.Preparex(sb.String())
stmt, err := s.sql.Preparex(s.expandTemplateVars(tpl))
if err != nil {
return failedStmt{err}
}
@@ -167,6 +177,17 @@ func (s *baseDB) stmt(tpl string) stmt {
return stmt
}
// expandTemplateVars just applies template expansions to the template
// string, or dies trying
func (s *baseDB) expandTemplateVars(tpl string) string {
var sb strings.Builder
compTpl := template.Must(template.New("tpl").Funcs(tplFuncs).Parse(tpl))
if err := compTpl.Execute(&sb, s.tplInput); err != nil {
panic("bug: bad template: " + err.Error())
}
return sb.String()
}
type stmt interface {
Exec(args ...any) (sql.Result, error)
Get(dest any, args ...any) error
@@ -211,7 +232,7 @@ nextScript:
// separately. We require it on a separate line because there are
// also statement-internal semicolons in the triggers.
for _, stmt := range strings.Split(string(bs), "\n;") {
if _, err := tx.Exec(stmt); err != nil {
if _, err := tx.Exec(s.expandTemplateVars(stmt)); err != nil {
return wrap(err, stmt)
}
}

View File

@@ -10,7 +10,9 @@ import (
"database/sql"
"errors"
"fmt"
"io"
"iter"
"log/slog"
"path/filepath"
"strings"
"time"
@@ -77,7 +79,7 @@ func (s *DB) getFolderDB(folder string, create bool) (*folderDB, error) {
}
}
l.Debugf("Folder %s in database %s", folder, dbName)
slog.Debug("Folder database opened", "folder", folder, "db", dbName)
path := dbName
if !filepath.IsAbs(path) {
path = filepath.Join(s.pathBase, dbName)
@@ -255,7 +257,7 @@ func (s *DB) ListDevicesForFolder(folder string) ([]protocol.DeviceID, error) {
func (s *DB) RemoteSequences(folder string) (map[protocol.DeviceID]int64, error) {
fdb, err := s.getFolderDB(folder, false)
if errors.Is(err, errNoSuchFolder) {
return nil, nil
return nil, nil //nolint:nilnil
}
if err != nil {
return nil, err
@@ -376,6 +378,22 @@ func (s *DB) DropDevice(device protocol.DeviceID) error {
})
}
func (s *DB) DebugCounts(out io.Writer, folder string) error {
fdb, err := s.getFolderDB(folder, false)
if err != nil {
return err
}
return fdb.DebugCounts(out)
}
func (s *DB) DebugFilePattern(out io.Writer, folder, name string) error {
fdb, err := s.getFolderDB(folder, false)
if err != nil {
return err
}
return fdb.DebugFilePattern(out, name)
}
// forEachFolder runs the function for each currently open folderDB,
// returning the first error that was encountered.
func (s *DB) forEachFolder(fn func(fdb *folderDB) error) error {

View File

@@ -268,6 +268,57 @@ func TestDontNeedIgnored(t *testing.T) {
}
}
func TestDontNeedRemoteInvalid(t *testing.T) {
t.Parallel()
db, err := OpenTemp()
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() {
if err := db.Close(); err != nil {
t.Fatal(err)
}
})
// A remote file with the invalid bit set
files := []protocol.FileInfo{
genFile("test1", 1, 103),
}
files[0].LocalFlags = protocol.FlagLocalRemoteInvalid
err = db.Update(folderID, protocol.DeviceID{42}, files)
if err != nil {
t.Fatal(err)
}
// It's not part of the global size
s, err := db.CountGlobal(folderID)
if err != nil {
t.Fatal(err)
}
if s.Bytes != 0 || s.Files != 0 {
t.Log(s)
t.Error("bad global")
}
// We don't need it
s, err = db.CountNeed(folderID, protocol.LocalDeviceID)
if err != nil {
t.Fatal(err)
}
if s.Bytes != 0 || s.Files != 0 {
t.Log(s)
t.Error("bad need")
}
// It shouldn't show up in the need list
names := mustCollect[protocol.FileInfo](t)(db.AllNeededGlobalFiles(folderID, protocol.LocalDeviceID, config.PullOrderAlphabetic, 0, 0))
if len(names) != 0 {
t.Log(names)
t.Error("need no files")
}
}
func TestRemoteDontNeedLocalIgnored(t *testing.T) {
t.Parallel()
@@ -517,3 +568,59 @@ func TestNeedPagination(t *testing.T) {
t.Error("bad need")
}
}
func TestDeletedAfterConflict(t *testing.T) {
t.Parallel()
// A delete that comes after a conflict should be applied, not lose the
// conflict and suddenly cause an old conflict version to become
// promoted.
// D:\syncthing-windows-amd64-v2.0.0-rc.22.dev.11.gff88430e>syncthing --home=c:\PortableApp\SyncTrayzorPortable-x64\data\syncthing debug database-file tnhbr-gxtuf TreeSizeFreeSetup.exe
// DEVICE TYPE NAME SEQUENCE DELETED MODIFIED SIZE FLAGS VERSION BLOCKLIST
// -local- FILE TreeSizeFreeSetup.exe 499 del 2025-07-04T11:52:36.2804841Z 0 ------- HZJYWFM:1751507473,OMKHRPB:1751629956 -nil-
// J5WNYJ6 FILE TreeSizeFreeSetup.exe 500 del 2025-07-04T11:52:36.2804841Z 0 ------- HZJYWFM:1751507473,OMKHRPB:1751629956 -nil-
// 23NHXGS FILE TreeSizeFreeSetup.exe 445 --- 2025-06-23T03:16:10.2804841Z 13832808 -nG---- HZJYWFM:1751507473 7B4kLitF
// JKX6ZDN FILE TreeSizeFreeSetup.exe 320 --- 2025-06-23T03:16:10.2804841Z 13832808 ------- JKX6ZDN:1750992570 7B4kLitF
db, err := OpenTemp()
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() {
if err := db.Close(); err != nil {
t.Fatal(err)
}
})
// A file, updated by some remote device. This file is an old, conflicted copy.
file := genFile("test1", 1, 101)
file.ModifiedS = 1750992570
file.Version = protocol.Vector{Counters: []protocol.Counter{{ID: 5 << 60, Value: 1750992570}}}
if err := db.Update(folderID, protocol.DeviceID{5}, []protocol.FileInfo{file}); err != nil {
t.Fatal(err)
}
// The file, updated by a newer remote device. This file is the newer, conflict-winning copy.
file.ModifiedS = 1751507473
file.Version = protocol.Vector{Counters: []protocol.Counter{{ID: 2 << 60, Value: 1751507473}}}
if err := db.Update(folderID, protocol.DeviceID{2}, []protocol.FileInfo{file}); err != nil {
t.Fatal(err)
}
// The file, deleted locally after syncing the file from the remote above..
file.SetDeleted(4)
if err := db.Update(folderID, protocol.LocalDeviceID, []protocol.FileInfo{file}); err != nil {
t.Fatal(err)
}
// The delete should be the global version
f, _, err := db.GetGlobalFile(folderID, "test1")
if err != nil {
t.Fatal(err)
}
if !f.IsDeleted() {
t.Log(f)
t.Error("should be deleted")
}
}

View File

@@ -7,22 +7,27 @@
package sqlite
import (
"log/slog"
"os"
"path/filepath"
"sync"
"time"
"github.com/syncthing/syncthing/internal/db"
"github.com/syncthing/syncthing/internal/slogutil"
)
const maxDBConns = 16
const (
maxDBConns = 16
minDeleteRetention = 24 * time.Hour
)
type DB struct {
*baseDB
pathBase string
deleteRetention time.Duration
*baseDB
folderDBsMut sync.RWMutex
folderDBs map[string]*folderDB
folderDBOpener func(folder, path string, deleteRetention time.Duration) (*folderDB, error)
@@ -34,7 +39,11 @@ type Option func(*DB)
func WithDeleteRetention(d time.Duration) Option {
return func(s *DB) {
s.deleteRetention = d
if d <= 0 {
s.deleteRetention = 0
} else {
s.deleteRetention = max(d, minDeleteRetention)
}
}
}
@@ -50,10 +59,14 @@ func Open(path string, opts ...Option) (*DB, error) {
"sql/schema/common/*",
"sql/schema/main/*",
}
migrations := []string{
"sql/migrations/common/*",
"sql/migrations/main/*",
}
os.MkdirAll(path, 0o700)
_ = os.MkdirAll(path, 0o700)
mainPath := filepath.Join(path, "main.db")
mainBase, err := openBase(mainPath, maxDBConns, pragmas, schemas, nil)
mainBase, err := openBase(mainPath, maxDBConns, pragmas, schemas, migrations)
if err != nil {
return nil, err
}
@@ -88,10 +101,14 @@ func OpenForMigration(path string) (*DB, error) {
"sql/schema/common/*",
"sql/schema/main/*",
}
migrations := []string{
"sql/migrations/common/*",
"sql/migrations/main/*",
}
os.MkdirAll(path, 0o700)
_ = os.MkdirAll(path, 0o700)
mainPath := filepath.Join(path, "main.db")
mainBase, err := openBase(mainPath, 1, pragmas, schemas, nil)
mainBase, err := openBase(mainPath, 1, pragmas, schemas, migrations)
if err != nil {
return nil, err
}
@@ -120,7 +137,7 @@ func OpenTemp() (*DB, error) {
return nil, wrap(err)
}
path := filepath.Join(dir, "db")
l.Debugln("Test DB in", path)
slog.Debug("Test DB", slogutil.FilePath(path))
return Open(path)
}

View File

@@ -9,18 +9,18 @@ package sqlite
import (
"context"
"fmt"
"log/slog"
"time"
"github.com/jmoiron/sqlx"
"github.com/syncthing/syncthing/internal/db"
"github.com/syncthing/syncthing/internal/slogutil"
"github.com/thejerf/suture/v4"
)
const (
internalMetaPrefix = "dbsvc"
lastMaintKey = "lastMaint"
defaultDeleteRetention = 180 * 24 * time.Hour
minDeleteRetention = 24 * time.Hour
internalMetaPrefix = "dbsvc"
lastMaintKey = "lastMaint"
)
func (s *DB) Service(maintenanceInterval time.Duration) suture.Service {
@@ -56,7 +56,7 @@ func (s *Service) Serve(ctx context.Context) error {
if wait < 0 {
wait = time.Minute
}
l.Debugln("Next periodic run in", wait)
slog.DebugContext(ctx, "Next periodic run due", "after", wait)
timer := time.NewTimer(wait)
for {
@@ -71,34 +71,36 @@ func (s *Service) Serve(ctx context.Context) error {
}
timer.Reset(s.maintenanceInterval)
l.Debugln("Next periodic run in", s.maintenanceInterval)
slog.DebugContext(ctx, "Next periodic run due", "after", s.maintenanceInterval)
_ = s.internalMeta.PutTime(lastMaintKey, time.Now())
}
}
func (s *Service) periodic(ctx context.Context) error {
t0 := time.Now()
l.Debugln("Periodic start")
slog.DebugContext(ctx, "Periodic start")
t1 := time.Now()
defer func() { l.Debugln("Periodic done in", time.Since(t1), "+", t1.Sub(t0)) }()
defer func() { slog.DebugContext(ctx, "Periodic done in", "t1", time.Since(t1), "t0t1", t1.Sub(t0)) }()
s.sdb.updateLock.Lock()
tidy(ctx, s.sdb.sql)
err := tidy(ctx, s.sdb.sql)
s.sdb.updateLock.Unlock()
if err != nil {
return err
}
return wrap(s.sdb.forEachFolder(func(fdb *folderDB) error {
fdb.updateLock.Lock()
defer fdb.updateLock.Unlock()
if err := garbageCollectOldDeletedLocked(fdb); err != nil {
if err := garbageCollectOldDeletedLocked(ctx, fdb); err != nil {
return wrap(err)
}
if err := garbageCollectBlocklistsAndBlocksLocked(ctx, fdb); err != nil {
return wrap(err)
}
tidy(ctx, fdb.sql)
return nil
return tidy(ctx, fdb.sql)
}))
}
@@ -116,15 +118,16 @@ func tidy(ctx context.Context, db *sqlx.DB) error {
return nil
}
func garbageCollectOldDeletedLocked(fdb *folderDB) error {
func garbageCollectOldDeletedLocked(ctx context.Context, fdb *folderDB) error {
l := slog.With("fdb", fdb.baseDB)
if fdb.deleteRetention <= 0 {
l.Debugln(fdb.baseName, "delete retention is infinite, skipping cleanup")
slog.DebugContext(ctx, "Delete retention is infinite, skipping cleanup")
return nil
}
// Remove deleted files that are marked as not needed (we have processed
// them) and they were deleted more than MaxDeletedFileAge ago.
l.Debugln(fdb.baseName, "forgetting deleted files older than", fdb.deleteRetention)
l.DebugContext(ctx, "Forgetting deleted files", "retention", fdb.deleteRetention)
res, err := fdb.stmt(`
DELETE FROM files
WHERE deleted AND modified < ? AND local_flags & {{.FlagLocalNeeded}} == 0
@@ -133,7 +136,7 @@ func garbageCollectOldDeletedLocked(fdb *folderDB) error {
return wrap(err)
}
if aff, err := res.RowsAffected(); err == nil {
l.Debugln(fdb.baseName, "removed old deleted file records:", aff)
l.DebugContext(ctx, "Removed old deleted file records", "affected", aff)
}
return nil
}
@@ -174,9 +177,14 @@ func garbageCollectBlocklistsAndBlocksLocked(ctx context.Context, fdb *folderDB)
SELECT 1 FROM files WHERE files.blocklist_hash = blocklists.blocklist_hash
)`); err != nil {
return wrap(err, "delete blocklists")
} else if shouldDebug() {
rows, err := res.RowsAffected()
l.Debugln(fdb.baseName, "blocklist GC:", rows, err)
} else {
slog.DebugContext(ctx, "Blocklist GC", "fdb", fdb.baseName, "result", slogutil.Expensive(func() any {
rows, err := res.RowsAffected()
if err != nil {
return slogutil.Error(err)
}
return slog.Int64("rows", rows)
}))
}
if res, err := tx.ExecContext(ctx, `
@@ -185,9 +193,14 @@ func garbageCollectBlocklistsAndBlocksLocked(ctx context.Context, fdb *folderDB)
SELECT 1 FROM blocklists WHERE blocklists.blocklist_hash = blocks.blocklist_hash
)`); err != nil {
return wrap(err, "delete blocks")
} else if shouldDebug() {
rows, err := res.RowsAffected()
l.Debugln(fdb.baseName, "blocks GC:", rows, err)
} else {
slog.DebugContext(ctx, "Blocks GC", "fdb", fdb.baseName, "result", slogutil.Expensive(func() any {
rows, err := res.RowsAffected()
if err != nil {
return slogutil.Error(err)
}
return slog.Int64("rows", rows)
}))
}
return wrap(tx.Commit())

View File

@@ -12,6 +12,8 @@ import (
"encoding/binary"
"errors"
"iter"
"os"
"path"
"path/filepath"
"sync"
"testing"
@@ -20,6 +22,7 @@ import (
"github.com/syncthing/syncthing/internal/db"
"github.com/syncthing/syncthing/internal/itererr"
"github.com/syncthing/syncthing/internal/timeutil"
"github.com/syncthing/syncthing/lib/build"
"github.com/syncthing/syncthing/lib/config"
"github.com/syncthing/syncthing/lib/protocol"
)
@@ -1157,6 +1160,47 @@ func TestStrangeDeletedGlobalBug(t *testing.T) {
}
}
func TestOpenSpecialName(t *testing.T) {
dir := t.TempDir()
// Create a "base" dir that is in the way if the path becomes
// incorrectly truncated in the next steps.
base := path.Join(dir, "test")
if err := os.Mkdir(base, 0o755); err != nil {
t.Fatal(err)
}
// Should be able to open a path with a hash sign in it.
p1 := base + "#foo"
db, err := Open(p1)
if err != nil {
t.Fatal(err)
}
t.Log(db.path)
db.Close()
if !build.IsWindows {
// Should be able to open a path with something that looks like
// query params.
p2 := base + "?foo=bar"
db, err = Open(p2)
if err != nil {
t.Fatal(err)
}
t.Log(db.path)
db.Close()
}
// Better not a have problem with a single ampersand either.
p2 := base + "&foo"
db, err = Open(p2)
if err != nil {
t.Fatal(err)
}
t.Log(db.path)
db.Close()
}
func mustCollect[T any](t *testing.T) func(it iter.Seq[T], errFn func() error) []T {
t.Helper()
return func(it iter.Seq[T], errFn func() error) []T {

View File

@@ -6,10 +6,6 @@
package sqlite
import (
"github.com/syncthing/syncthing/lib/logger"
)
import "github.com/syncthing/syncthing/internal/slogutil"
var l = logger.DefaultLogger.NewFacility("sqlite", "SQLite database")
func shouldDebug() bool { return l.ShouldDebug("sqlite") }
func init() { slogutil.RegisterPackage("SQLite database") }

View File

@@ -16,7 +16,7 @@ type countsRow struct {
Count int
Size int64
Deleted bool
LocalFlags int64 `db:"local_flags"`
LocalFlags protocol.FlagLocal `db:"local_flags"`
}
func (s *folderDB) CountLocal(device protocol.DeviceID) (db.Counts, error) {
@@ -39,13 +39,10 @@ func (s *folderDB) CountNeed(device protocol.DeviceID) (db.Counts, error) {
}
func (s *folderDB) CountGlobal() (db.Counts, error) {
// Exclude ignored and receive-only changed files from the global count
// (legacy expectation? it's a bit weird since those files can in fact
// be global and you can get them with GetGlobal etc.)
var res []countsRow
err := s.stmt(`
SELECT s.type, s.count, s.size, s.local_flags, s.deleted FROM counts s
WHERE s.local_flags & {{.FlagLocalGlobal}} != 0 AND s.local_flags & {{or .FlagLocalReceiveOnly .FlagLocalIgnored}} = 0
WHERE s.local_flags & {{.FlagLocalGlobal}} != 0 AND s.local_flags & {{.LocalInvalidFlags}} = 0
`).Select(&res)
if err != nil {
return db.Counts{}, wrap(err)
@@ -84,7 +81,7 @@ func (s *folderDB) needSizeRemote(device protocol.DeviceID) (db.Counts, error) {
// See neededGlobalFilesRemote for commentary as that is the same query without summing
if err := s.stmt(`
SELECT g.type, count(*) as count, sum(g.size) as size, g.local_flags, g.deleted FROM files g
WHERE g.local_flags & {{.FlagLocalGlobal}} != 0 AND NOT g.deleted AND NOT g.invalid AND NOT EXISTS (
WHERE g.local_flags & {{.FlagLocalGlobal}} != 0 AND NOT g.deleted AND g.local_flags & {{.LocalInvalidFlags}} = 0 AND NOT EXISTS (
SELECT 1 FROM FILES f
INNER JOIN devices d ON d.idx = f.device_idx
WHERE f.name = g.name AND f.version = g.version AND d.device_id = ?
@@ -94,10 +91,10 @@ func (s *folderDB) needSizeRemote(device protocol.DeviceID) (db.Counts, error) {
UNION ALL
SELECT g.type, count(*) as count, sum(g.size) as size, g.local_flags, g.deleted FROM files g
WHERE g.local_flags & {{.FlagLocalGlobal}} != 0 AND g.deleted AND NOT g.invalid AND EXISTS (
WHERE g.local_flags & {{.FlagLocalGlobal}} != 0 AND g.deleted AND g.local_flags & {{.LocalInvalidFlags}} = 0 AND EXISTS (
SELECT 1 FROM FILES f
INNER JOIN devices d ON d.idx = f.device_idx
WHERE f.name = g.name AND d.device_id = ? AND NOT f.deleted AND NOT f.invalid
WHERE f.name = g.name AND d.device_id = ? AND NOT f.deleted AND f.local_flags & {{.LocalInvalidFlags}} = 0
)
GROUP BY g.type, g.local_flags, g.deleted
`).Select(&res, device.String(),

Some files were not shown because too many files have changed in this diff Show More