Compare commits

..

219 Commits

Author SHA1 Message Date
Frédéric Guillot 6bf89f4b32 Update Changelog 2022-05-27 14:29:57 -07:00
nemunaire 5a07fd8932 Add new rewrite rule to decode base64 content 2022-05-25 20:44:04 -07:00
Frédéric Guillot 9fa086e471 Fix flaky test 2022-05-25 20:34:37 -07:00
kencx 1658db7f10 Add Linkding integration 2022-05-24 20:14:07 -07:00
dependabot[bot] 780c9e3696 Bump github.com/tdewolff/minify/v2 from 2.11.2 to 2.11.5
Bumps [github.com/tdewolff/minify/v2](https://github.com/tdewolff/minify) from 2.11.2 to 2.11.5.
- [Release notes](https://github.com/tdewolff/minify/releases)
- [Commits](https://github.com/tdewolff/minify/compare/v2.11.2...v2.11.5)

---
updated-dependencies:
- dependency-name: github.com/tdewolff/minify/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-05-23 19:46:07 -07:00
luginbash 74fb430697 Adding comment button to telegram message if there's any 2022-05-21 11:45:39 -07:00
Pascal Noisette d118aa8649 Add API endpoint to fetch unread and read counters 2022-05-21 11:44:56 -07:00
dependabot[bot] 953c1742e0 Bump github.com/prometheus/client_golang from 1.12.1 to 1.12.2
Bumps [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) from 1.12.1 to 1.12.2.
- [Release notes](https://github.com/prometheus/client_golang/releases)
- [Changelog](https://github.com/prometheus/client_golang/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prometheus/client_golang/compare/v1.12.1...v1.12.2)

---
updated-dependencies:
- dependency-name: github.com/prometheus/client_golang
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-05-16 17:37:55 -07:00
dependabot[bot] a88996fb3f Bump github.com/lib/pq from 1.10.5 to 1.10.6
Bumps [github.com/lib/pq](https://github.com/lib/pq) from 1.10.5 to 1.10.6.
- [Release notes](https://github.com/lib/pq/releases)
- [Commits](https://github.com/lib/pq/compare/v1.10.5...v1.10.6)

---
updated-dependencies:
- dependency-name: github.com/lib/pq
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-05-16 17:37:02 -07:00
Lars Windolf 0014aac201 Fixes logic bug in sanity check 2022-05-11 22:32:48 -07:00
Lars Windolf 8e6babe5d4 Fix typo 2022-05-11 22:00:33 -07:00
Pascal Noisette cdeb2a8fc0 Reduce number of preflight check to save network brandwidth 2022-05-07 10:38:55 -07:00
dependabot[bot] a6e9f88e7a Bump github.com/tdewolff/minify/v2 from 2.11.1 to 2.11.2
Bumps [github.com/tdewolff/minify/v2](https://github.com/tdewolff/minify) from 2.11.1 to 2.11.2.
- [Release notes](https://github.com/tdewolff/minify/releases)
- [Commits](https://github.com/tdewolff/minify/compare/v2.11.1...v2.11.2)

---
updated-dependencies:
- dependency-name: github.com/tdewolff/minify/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-05-02 20:31:58 -07:00
Berk Özkütük ab3fdf509f Add Espial integration 2022-04-20 19:44:47 -07:00
knrdl fb585d0086 Allow API search for entries which are not starred 2022-04-13 21:53:06 -07:00
lf94 fa8431c5c6 Try to use outermost element text when title is empty 2022-04-13 21:51:54 -07:00
dzaikos ec2b911881 Make swipe gestures feel more natural.
Removes opacity transition when swiping an article read/unread.

Adds "resistance" to the swiped entry when the 75px threshold is
reached.

Fixes an issue in which a swiped article couldn't be moved <15px.
2022-04-13 21:17:53 -07:00
dependabot[bot] 0f2b29741e Bump github.com/lib/pq from 1.10.4 to 1.10.5
Bumps [github.com/lib/pq](https://github.com/lib/pq) from 1.10.4 to 1.10.5.
- [Release notes](https://github.com/lib/pq/releases)
- [Commits](https://github.com/lib/pq/compare/v1.10.4...v1.10.5)

---
updated-dependencies:
- dependency-name: github.com/lib/pq
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-04-11 19:12:40 -07:00
dependabot[bot] 2f26b581f4 Bump github.com/tdewolff/minify/v2 from 2.10.0 to 2.11.1
Bumps [github.com/tdewolff/minify/v2](https://github.com/tdewolff/minify) from 2.10.0 to 2.11.1.
- [Release notes](https://github.com/tdewolff/minify/releases)
- [Commits](https://github.com/tdewolff/minify/compare/v2.10.0...v2.11.1)

---
updated-dependencies:
- dependency-name: github.com/tdewolff/minify/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-04-11 19:12:20 -07:00
Dave Marquard ad266e45ca add support for feed streams to Reader API IDs API 2022-04-01 19:39:27 -07:00
Frédéric Guillot f6825c1c60 Fix invalid parsing of data URL
Fetching icons crashes with "slice bounds out of range" error if no encoding is specified.
2022-03-25 22:30:20 -07:00
austinsuyoyo 40f958c5ef Add Traditional Chinese translation 2022-03-24 21:56:12 -07:00
Frédéric Guillot 02e975d3f3 Add distroless Docker image variant 2022-03-15 22:55:31 -07:00
Frédéric Guillot 40b54b458d Add Go 1.18 to GitHub Actions 2022-03-15 21:01:00 -07:00
Frédéric Guillot ca60f3cff5 Update ChangeLog 2022-03-08 21:12:23 -08:00
Thiago Perrotta c1ece47532 Gray out pagination buttons when they are not applicable
Whenever the "prev" and "next" buttons have no hyperlink, decrease their
opacity to signal that they lead to nowhere.

This signal is stronger and more obvious than the current one which
merely removes the underline decoration from the text.

This patch is an improvement on top of
https://github.com/miniflux/v2/pull/1107
2022-03-07 14:59:02 -08:00
dependabot[bot] b2e5eda89e Bump mvdan.cc/xurls/v2 from 2.3.0 to 2.4.0
Bumps [mvdan.cc/xurls/v2](https://github.com/mvdan/xurls) from 2.3.0 to 2.4.0.
- [Release notes](https://github.com/mvdan/xurls/releases)
- [Commits](https://github.com/mvdan/xurls/compare/v2.3.0...v2.4.0)

---
updated-dependencies:
- dependency-name: mvdan.cc/xurls/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-07 14:55:25 -08:00
Frédéric Guillot 1eb01b39e7 Use truncated entry description as title if unavailable 2022-03-04 17:10:32 -08:00
Frédéric Guillot c9e0f0b3e4 Do not fallback to InnerXML if XHTML title is empty 2022-03-04 14:28:56 -08:00
Thiago Perrotta 1e357d3ced Add '+' shortcut for new subscription page 2022-03-02 21:39:23 -08:00
Thiago Perrotta 4831acba39 Add (+) action next to Feeds to quickly add new feeds 2022-03-02 20:38:06 -08:00
月杪 658b8591b6 Unstar not working via Google Reader API/Reeder 2022-02-22 20:12:46 -08:00
Frédéric Guillot 950b001675 Remove circles in front of page header list items 2022-02-13 15:06:22 -08:00
Frédéric Guillot 0b5b41acd9 Fix CSS hover style for links styled as buttons 2022-02-13 14:45:52 -08:00
Frédéric Guillot 6b4b9e9cff Add missing page header icons 2022-02-13 14:34:45 -08:00
Frédéric Guillot da89831116 Avoid showing "undefined" when clicking on read/unread 2022-02-01 21:15:57 -08:00
Thiago Perrotta 3243d88c9b refactor handleEntryStatus / goToNextListItem / goToPrevListItem 2022-02-01 20:42:14 -08:00
Thiago Perrotta 824fc310a9 Add new keyboard shortcut: 'M' - toggle read/unread, go to prev item
Currently there is "Toggle read/unread = m", which toggles and
then goes to the next item.

Having the opposite operation available is handy, especially when adding
new feeds and going through them from oldest to newest posts.

It seems natural to map 'M' (= shift + 'm') for this action.

Closes https://github.com/miniflux/v2/issues/1352
2022-02-01 20:42:14 -08:00
Thiago Perrotta c891ab2588 Add several icons to menus according to their roles
- refresh: https://tabler-icons.io/i/refresh
- edit: https://tabler-icons.io/i/edit
- delete: https://tabler-icons.io/i/delete
- mark page as read: https://tabler-icons.io/i/check
- mark all as read: https://tabler-icons.io/i/checks
- show all entries: https://tabler-icons.io/i/eye
- show only unread entries: https://tabler-icons.io/i/eye-off
- create category: https://tabler-icons.io/i/folder-plus
- add subscription: https://tabler-icons.io/i/plus
- import: https://tabler-icons.io/i/file-import
- export: https://tabler-icons.io/i/file-export
- categories: https://tabler-icons.io/i/folders
2022-02-01 20:39:05 -08:00
Frédéric Guillot 33e9b26fe9 Add missing event argument to onClick() function call 2022-01-31 15:37:54 -08:00
Thiago Perrotta 6e402f60dc Add links to scraper/rewrite/filtering docs when editing feeds 2022-01-31 14:53:58 -08:00
dependabot[bot] 4b56c84a8c Bump github.com/prometheus/client_golang from 1.12.0 to 1.12.1
Bumps [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) from 1.12.0 to 1.12.1.
- [Release notes](https://github.com/prometheus/client_golang/releases)
- [Changelog](https://github.com/prometheus/client_golang/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prometheus/client_golang/compare/v1.12.0...v1.12.1)

---
updated-dependencies:
- dependency-name: github.com/prometheus/client_golang
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-31 14:53:49 -08:00
dependabot[bot] cee70e04db Bump github.com/tdewolff/minify/v2 from 2.9.29 to 2.10.0
Bumps [github.com/tdewolff/minify/v2](https://github.com/tdewolff/minify) from 2.9.29 to 2.10.0.
- [Release notes](https://github.com/tdewolff/minify/releases)
- [Commits](https://github.com/tdewolff/minify/compare/v2.9.29...v2.10.0)

---
updated-dependencies:
- dependency-name: github.com/tdewolff/minify/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-31 14:44:36 -08:00
Romain de Laage 808635e314 Add a rewrite rule for castopod episodes 2022-01-30 16:33:17 -08:00
Jacob Duba 7a1429bcc1 Fix regression: reset touch-item if not in /unread 2022-01-27 19:39:35 -08:00
Pascal Noisette 21bbaf2691 Add API endpoint to fetch original article 2022-01-26 20:29:37 -08:00
Orpheus Lummis 50c5850f0d Show the category first in feed settings 2022-01-24 21:03:32 -08:00
Thiago Perrotta d22c6c535e Add pagination on top of all entries. Closes #1305.
Enable users to move to prev/next page without having to scroll all the
way to the bottom of the page.

Furthermore, ensure consistency with entry.html which has top and bottom
pagination.
2022-01-24 20:55:27 -08:00
dependabot[bot] 6a41eb382f Bump github.com/prometheus/client_golang from 1.11.0 to 1.12.0
Bumps [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) from 1.11.0 to 1.12.0.
- [Release notes](https://github.com/prometheus/client_golang/releases)
- [Changelog](https://github.com/prometheus/client_golang/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prometheus/client_golang/compare/v1.11.0...v1.12.0)

---
updated-dependencies:
- dependency-name: github.com/prometheus/client_golang
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-24 19:56:36 -08:00
dependabot[bot] aa70ebcdca Bump github.com/tdewolff/minify/v2 from 2.9.28 to 2.9.29
Bumps [github.com/tdewolff/minify/v2](https://github.com/tdewolff/minify) from 2.9.28 to 2.9.29.
- [Release notes](https://github.com/tdewolff/minify/releases)
- [Commits](https://github.com/tdewolff/minify/compare/v2.9.28...v2.9.29)

---
updated-dependencies:
- dependency-name: github.com/tdewolff/minify/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-24 19:56:08 -08:00
Alex Cohn 7111b3749d Display Go version on /about
Translations are copy-paste best effort from the equivalent Postgres
string, and might not account for grammatical gender, etc.
2022-01-22 11:00:12 -08:00
Frédéric Guillot 93277f4307 Update ChangeLog 2022-01-21 16:45:37 -08:00
Frédéric Guillot 9e3ce3f3a4 Set read-all permission to GITHUB_TOKEN for CI jobs 2022-01-19 21:44:23 -08:00
Frédéric Guillot a98e479e71 Pin jshint version in linter job 2022-01-19 21:37:38 -08:00
Frédéric Guillot 897d8644c5 Fix incorrect conversion between integer types 2022-01-19 21:23:56 -08:00
Frédéric Guillot 2f7ad3ac73 Create scorecards-analysis.yml 2022-01-19 20:47:57 -08:00
Frédéric Guillot 20318e3a79 Create CodeQL workflow 2022-01-19 20:37:48 -08:00
Adrian Smith cc3e65dd3c Handle atom feed with space around CDATA
Trim space around CDATA elements before extracting the CharData.

This problem was discovered when reading https://www.sethvargo.com/feed.xml.
Title and Summary fields have newlines and space between the <title>
element and the CDATA element. e.g.

  <title>
    <![CDATA[Entry title here]]>
  </title>

This meant the title of the feed was coming into MiniFlux as,
  <![CDATA[Entry title here]]>
2022-01-17 15:25:22 -08:00
dependabot[bot] 7b0a4a7803 Bump github.com/tdewolff/minify/v2 from 2.9.27 to 2.9.28
Bumps [github.com/tdewolff/minify/v2](https://github.com/tdewolff/minify) from 2.9.27 to 2.9.28.
- [Release notes](https://github.com/tdewolff/minify/releases)
- [Commits](https://github.com/tdewolff/minify/compare/v2.9.27...v2.9.28)

---
updated-dependencies:
- dependency-name: github.com/tdewolff/minify/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-17 15:15:25 -08:00
Thiago Perrotta bab7734582 packaging: add Documentation to systemd service
c.f. https://www.freedesktop.org/software/systemd/man/systemd.directives.html#Documentation=
2022-01-17 13:23:19 -08:00
Jacob Duba 3dda84bf9a Do not reset touch-item if successfully swiped 2022-01-15 10:47:25 -08:00
Frédéric Guillot f18ded6117 Add support for multiple authors in Atom feeds 2022-01-14 20:20:55 -08:00
dependabot[bot] 41d47244af Bump github.com/tdewolff/minify/v2 from 2.9.26 to 2.9.27
Bumps [github.com/tdewolff/minify/v2](https://github.com/tdewolff/minify) from 2.9.26 to 2.9.27.
- [Release notes](https://github.com/tdewolff/minify/releases)
- [Commits](https://github.com/tdewolff/minify/compare/v2.9.26...v2.9.27)

---
updated-dependencies:
- dependency-name: github.com/tdewolff/minify/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-10 17:47:54 -08:00
Frédéric Guillot b113af8472 Omit User-Agent header in image proxy to avoid being blocked 2022-01-09 20:23:35 -08:00
Frédéric Guillot 2309b27458 Use custom feed user agent to fetch website icon 2022-01-08 15:20:18 -08:00
Romain de Laage 8329e9b46c Make Invidious instance configurable 2022-01-05 20:43:03 -08:00
dependabot[bot] 5879404fd2 Bump github.com/tdewolff/minify/v2 from 2.9.24 to 2.9.26
Bumps [github.com/tdewolff/minify/v2](https://github.com/tdewolff/minify) from 2.9.24 to 2.9.26.
- [Release notes](https://github.com/tdewolff/minify/releases)
- [Commits](https://github.com/tdewolff/minify/compare/v2.9.24...v2.9.26)

---
updated-dependencies:
- dependency-name: github.com/tdewolff/minify/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-03 16:21:58 -08:00
Jouni K. Seppänen bb0d2bf675 Add Youtube videos in Quanta articles
Some articles (especially the recent year-in-review ones) include a Youtube
video. The server-side rendered articles do not include the Youtube iframe,
but they do have a script that looks like

    <script type="text/javascript" data-reactid="6">
      window.__APOLLO_STATE__ = {
        ...
          youtube_id: "9uASADiYe_8",

We add a reformatting function that tries to detect obvious JavaScript code
that has a field or variable called youtube_id that has an 11-character
double-quoted value, and adds the referenced Youtube videos in the beginning of
the article. This is slightly more general than needed for Quanta, in the hope
that it could be useful for similar sites.
2022-01-03 10:10:13 -08:00
Jouni K. Seppänen dcf87bd642 Add scrape and rewrite rules for quantamagazine
This is a somewhat complex React site so the rules could be a little fragile.
Text content seems to be always inside .outer--content, and most h6 elements
are fluff like "read later" or pointers to other articles. However, h6.byline
and h6.post__title__kicker are relevant to the current article.

Figure captions are sometimes inside both figure and div.outer--content
elements, sometimes only inside figure, so take both and remove the
intersection.

The figure elements sometimes contain multiple copies of images or
videos, and we just take them all. Math articles seem to use Mathjax,
which we don't add.
2022-01-03 10:10:13 -08:00
Alexandros Kosiaris 4445cdd053 Expose entry unshare in the entry and list views
Why:

A user might want to unshare a specific entry. Navigating to the shared
entries page requires a mental context switch, whereas having the
ability right in the entry page makes it easier.

What:

Add an extra <li> element to display the unshare icon and link in the
entry view as well as the item_meta template. The latter is shared for
multiple pages listing entries, e.g. bookmarks, feed entries, search,
history etc.

The functionality already exists for the shared entries page, we are
just expose it in a couple more places

Signed-off-by: Alexandros Kosiaris <akosiaris@gmail.com>
2022-01-03 10:04:48 -08:00
Frédéric Guillot 0b3c3a1f28 Google Reader: Cosmetic improvements 2022-01-02 20:28:12 -08:00
Gergan Penkov 4b6e46d9ab Add Google Reader API implementation (experimental)
Co-authored-by: Sebastian Kempken <sebastian@kempken.io>
Co-authored-by: Gergan Penkov <gergan@gmail.com>
Co-authored-by: Dave Marquard <dave@marquard.org>
Co-authored-by: Moritz Fago <4459068+MoritzFago@users.noreply.github.com>
2022-01-02 19:45:12 -08:00
Frédéric Guillot 2935aaef45 Add Content-Security-Policy header to feed icon url
- SVG images could contains Javascript. This CSP blocks inline script.
- Feed icons are served using <img> tag and Javascript is not interpreted.

See https://developer.mozilla.org/en-US/docs/Web/SVG/SVG_as_an_Image#restrictions
2022-01-02 17:38:53 -08:00
Nikolay Korotkiy 33fd0a617e Add Finnish translation 2021-12-29 21:25:09 -08:00
Jouni K. Seppänen 2fedd8f234 Add scraper rule for ikiwiki.iki.fi
Feed: https://ikiwiki.iki.fi/feed.php?linkto=current&ns=uutiset%3Ablog&num=5

Example page: https://ikiwiki.iki.fi/uutiset/blog/20210923100421viiveita

(To clarify, I'm not a representative of iki.fi although I have an email address in the domain. This is a nonprofit association that offers email forwarding addresses, and the rss feed in question contains news for their members.)
2021-12-27 20:51:37 -08:00
Ilya Mateyko 527c5f49cb packaging: remove SystemCallFilter from miniflux.service
`SystemCallFilter=@system-service` is not supported on older systemd versions and causes crashes on them.

Fixes #1297.
2021-12-23 17:52:13 -08:00
Éric Gaspar 10d2c88e88 Fix minor typo in French translation 2021-12-21 20:28:57 -08:00
dependabot[bot] fd8e95c878 Bump github.com/tdewolff/minify/v2 from 2.9.22 to 2.9.24
Bumps [github.com/tdewolff/minify/v2](https://github.com/tdewolff/minify) from 2.9.22 to 2.9.24.
- [Release notes](https://github.com/tdewolff/minify/releases)
- [Commits](https://github.com/tdewolff/minify/compare/v2.9.22...v2.9.24)

---
updated-dependencies:
- dependency-name: github.com/tdewolff/minify/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-21 20:23:13 -08:00
Frédéric Guillot 686dd92cb9 Update ChangeLog 2021-12-16 17:00:50 -08:00
Thiago Perrotta 28d036434f Add rewrite rule: monkeyuser.com
Comics site, uses alt image text similarly to xkcd.com.
2021-12-16 11:50:26 -08:00
Thiago Perrotta 4b12043cea Sort rewrite rules 2021-12-16 11:50:26 -08:00
Frédéric Guillot 0f6f4c8c60 Add <head> tag to OPML export 2021-12-16 11:49:50 -08:00
Ilya Mateyko 47b47cc32c Tighten systemd sandboxing and update comments in miniflux.service 2021-12-16 10:33:09 -08:00
Arsenović Arsen ec4e8710f2 packaging: add RuntimeDirectory to systemd service 2021-12-15 20:49:02 -08:00
Frédéric Guillot b3dae675d9 Fix regression introduced by PR #1286 2021-12-11 10:42:30 -08:00
Jebbs c1c3624593 Order disabled feeds at the end of the list 2021-12-10 19:56:14 -08:00
Dustin Breuer b21f12015e Add support for theme color based on preferred color scheme of OS 2021-12-10 19:47:43 -08:00
fguillot ca87894ab2 Update issue templates 2021-12-06 17:34:34 -08:00
dependabot[bot] 42dcc52ed1 Bump github.com/lib/pq from 1.10.3 to 1.10.4
Bumps [github.com/lib/pq](https://github.com/lib/pq) from 1.10.3 to 1.10.4.
- [Release notes](https://github.com/lib/pq/releases)
- [Commits](https://github.com/lib/pq/compare/v1.10.3...v1.10.4)

---
updated-dependencies:
- dependency-name: github.com/lib/pq
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-11-08 16:50:24 -08:00
dependabot[bot] 45bfbb8a2f Bump github.com/PuerkitoBio/goquery from 1.7.1 to 1.8.0
Bumps [github.com/PuerkitoBio/goquery](https://github.com/PuerkitoBio/goquery) from 1.7.1 to 1.8.0.
- [Release notes](https://github.com/PuerkitoBio/goquery/releases)
- [Commits](https://github.com/PuerkitoBio/goquery/compare/v1.7.1...v1.8.0)

---
updated-dependencies:
- dependency-name: github.com/PuerkitoBio/goquery
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-11-01 19:44:59 -07:00
nett_hier d6ad9a471b Fix typos in model/icon.go 2021-10-23 21:47:18 -07:00
Artémis b585dab6b4 Add data-srcset support to "add_dynamic_image rewrite" rewrite rule 2021-10-22 18:12:23 -07:00
Emanuele Bernardi d45e1b3bed Fix docker compose example files compatibility to v3 2021-10-22 14:10:41 -07:00
Akash Kakkar 03fd403e38 Added the role="article" to <article> elements for better accessibility with screen readers and chromium based browsers 2021-10-22 13:50:17 -07:00
Frank Steinborn 2dcabc840c Fix minor typo 2021-10-17 16:58:42 -07:00
Frédéric Guillot 87d58987a6 Do not show secrets in plain text on the /about page 2021-10-17 16:56:04 -07:00
Frédéric Guillot 5f9d6fd81b Handle srcset images with no space after comma 2021-10-13 21:31:08 -07:00
Ilya Mateyko c2c9db737b Hide the logout link when using auth proxy
Fixes #1250
2021-10-13 21:30:18 -07:00
Otto Modinos 4565dd406b Fix wrong CSS var 2021-09-28 18:25:23 -07:00
Benjamin 22bbe1ac50 Change -config-dump to use KEY=VALUE format 2021-09-28 18:20:25 -07:00
Frédéric Guillot 72fbbe311a Update ChangeLog 2021-09-25 17:16:03 -07:00
Frédéric Guillot 423e06cbe8 Build RPM and Debian package with PIE mode enabled 2021-09-25 16:16:47 -07:00
Pk11 1f3a9dabc2 Fix templates being visible on old browsers 2021-09-24 16:24:12 -07:00
NobeKanai 937899194d Fix inconsistent navigation in history pages 2021-09-24 16:22:00 -07:00
NobeKanai cf1939f063 Fix inconsistent navigation 2021-09-24 16:22:00 -07:00
dependabot[bot] edee11931d Bump github.com/tdewolff/minify/v2 from 2.9.21 to 2.9.22
Bumps [github.com/tdewolff/minify/v2](https://github.com/tdewolff/minify) from 2.9.21 to 2.9.22.
- [Release notes](https://github.com/tdewolff/minify/releases)
- [Commits](https://github.com/tdewolff/minify/compare/v2.9.21...v2.9.22)

---
updated-dependencies:
- dependency-name: github.com/tdewolff/minify/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-09-13 16:28:37 -07:00
Frédéric Guillot 612f9cdbc8 Remove RequestURI() hack
I can't remember why this change was done.

Let's use only the standard lib.

But it seems to break URL like this one: https://www.deimeke.net/dirk/blog/index.php?/feeds/index.rss2
2021-09-11 11:08:15 -07:00
NobeKanai fcd18f0b9c Fix translation in zh_CN 2021-09-10 17:34:38 -07:00
James Loh 78f6bbe93d Add ability to change entry sort order in the UI 2021-09-09 19:59:12 -07:00
dependabot[bot] 27d170cbec Bump github.com/lib/pq from 1.10.2 to 1.10.3
Bumps [github.com/lib/pq](https://github.com/lib/pq) from 1.10.2 to 1.10.3.
- [Release notes](https://github.com/lib/pq/releases)
- [Commits](https://github.com/lib/pq/compare/v1.10.2...v1.10.3)

---
updated-dependencies:
- dependency-name: github.com/lib/pq
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-09-07 20:53:54 -07:00
Frédéric Guillot 49119eff00 Minor improvements in integration package 2021-09-07 20:34:47 -07:00
三三 34dd358eb0 Add Telegram integration 2021-09-07 20:04:22 -07:00
Lukas Dietrich 93596c1218 Add rewrite rule to remove dom elements 2021-09-06 09:47:05 -07:00
coxde 9fbcfc213b Update translation in zh_CN
Fix the translation to make it more localized.
2021-09-03 20:20:50 -07:00
hulb 01f678c3b1 add proxy arg in scraper.Fetch 2021-08-28 21:57:11 -07:00
James Loh 2f6895e118 Fix finding JSON feeds with new MIME type
The 1.1 version (https://jsonfeed.org/version/1.1) for JSON feeds defines that feeds should have a MIME type of `application/feed+json` which Miniflux wasn't searching for
2021-08-21 13:01:08 -07:00
Frédéric Guillot b7c229f30f Update scraper rule for theregister.com 2021-08-16 20:04:02 -07:00
Frédéric Guillot b88d46ee7f Add Go 1.17 to GitHub Actions 2021-08-16 19:46:32 -07:00
Jan-Lukas Else b58a46455b Display option to hide feed only when category is not already hidden 2021-08-16 19:36:05 -07:00
Jan-Lukas Else ce6fa4c0fc Fix #1208 2021-08-16 19:36:05 -07:00
Jan-Lukas Else 9965abccfb Add option to hide feeds from the global Unread list 2021-08-16 19:36:05 -07:00
Frédéric Guillot 8bc06a5d65 Update ChangeLog 2021-08-14 19:48:20 -07:00
dependabot[bot] 7f1c17e396 Bump github.com/tdewolff/minify/v2 from 2.9.19 to 2.9.21
Bumps [github.com/tdewolff/minify/v2](https://github.com/tdewolff/minify) from 2.9.19 to 2.9.21.
- [Release notes](https://github.com/tdewolff/minify/releases)
- [Commits](https://github.com/tdewolff/minify/compare/v2.9.19...v2.9.21)

---
updated-dependencies:
- dependency-name: github.com/tdewolff/minify/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-08-02 19:01:30 -07:00
dependabot[bot] c5594fdae0 Bump mvdan.cc/xurls/v2 from 2.2.0 to 2.3.0
Bumps [mvdan.cc/xurls/v2](https://github.com/mvdan/xurls) from 2.2.0 to 2.3.0.
- [Release notes](https://github.com/mvdan/xurls/releases)
- [Commits](https://github.com/mvdan/xurls/compare/v2.2.0...v2.3.0)

---
updated-dependencies:
- dependency-name: mvdan.cc/xurls/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-07-26 16:08:50 -07:00
Alexandros Kosiaris b8b16c3bdf Add /rss/ in finder's wellKnownUrls
ATCOM netvolution WCM, probably alongside others, a CMS powering several
high profile and high traffic Greek news sites, among other sites,
publishes the RSS feed under /rss/. Add it to the list. It's generic
enough to allow us to assume other software might do it to

On a select set of 627 Greek news media sites (the infamous Petsas list),
adding this rule increased discoverability of RSS feeds by a factor of
2.61% (from 498 to 511).
2021-07-22 19:46:40 -07:00
Dave Marquard fc766de02d use authors entry for json 1.1 feeds 2021-07-21 21:28:37 -07:00
Alexandros Kosiaris 79810413bd Add Greek translation
Why:
Cause, why not?

What:
Add the JSON file and amend locale.go to use it
2021-07-18 13:46:41 -07:00
dependabot[bot] dc21991599 Bump github.com/tdewolff/minify/v2 from 2.9.18 to 2.9.19
Bumps [github.com/tdewolff/minify/v2](https://github.com/tdewolff/minify) from 2.9.18 to 2.9.19.
- [Release notes](https://github.com/tdewolff/minify/releases)
- [Commits](https://github.com/tdewolff/minify/compare/v2.9.18...v2.9.19)

---
updated-dependencies:
- dependency-name: github.com/tdewolff/minify/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-07-12 18:04:42 -07:00
dependabot[bot] cc41065d9b Bump github.com/PuerkitoBio/goquery from 1.7.0 to 1.7.1
Bumps [github.com/PuerkitoBio/goquery](https://github.com/PuerkitoBio/goquery) from 1.7.0 to 1.7.1.
- [Release notes](https://github.com/PuerkitoBio/goquery/releases)
- [Commits](https://github.com/PuerkitoBio/goquery/compare/v1.7.0...v1.7.1)

---
updated-dependencies:
- dependency-name: github.com/PuerkitoBio/goquery
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-07-12 18:04:15 -07:00
Alexandros Kosiaris e877800779 ui: Expose markCategoryAsRead
Why:
It is nice to have the ability to mark an entire category as read in the
UI. The API already exposes that functionality anyway, so for
consistency reasons, expose it in the UI as well

What:
Add a new handler in the UI to markCategoryAsRead() and amend views and
router to expose the functionality in the UI
2021-07-05 14:13:18 -07:00
Alexandros Kosiaris d5efd776b7 Add "in" in "logged in" for en_US tooltip.logged_user
Fix a typo, by adding the "in" part of the phrasal verb as the intent is
to notify the user that they are "logged" in in the system as user X and
not to notify them that they are "recorded"
2021-07-05 14:08:59 -07:00
dependabot[bot] 164bcc2f4a Bump github.com/tdewolff/minify/v2 from 2.9.17 to 2.9.18
Bumps [github.com/tdewolff/minify/v2](https://github.com/tdewolff/minify) from 2.9.17 to 2.9.18.
- [Release notes](https://github.com/tdewolff/minify/releases)
- [Commits](https://github.com/tdewolff/minify/compare/v2.9.17...v2.9.18)

---
updated-dependencies:
- dependency-name: github.com/tdewolff/minify/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-06-28 20:22:45 -07:00
dependabot[bot] 21ffd79c63 Bump github.com/PuerkitoBio/goquery from 1.6.1 to 1.7.0
Bumps [github.com/PuerkitoBio/goquery](https://github.com/PuerkitoBio/goquery) from 1.6.1 to 1.7.0.
- [Release notes](https://github.com/PuerkitoBio/goquery/releases)
- [Commits](https://github.com/PuerkitoBio/goquery/compare/v1.6.1...v1.7.0)

---
updated-dependencies:
- dependency-name: github.com/PuerkitoBio/goquery
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-06-22 20:59:29 -07:00
Alexandros Kosiaris 638643cda7 client: Try to parse response Body on InternalServerError
Try to parse the response body from the server when an HTTP 500 is
returned (i.e. http.StatusInternalServerError) as it might contain
useful information. If successful, create a new error and append that
information to the returned error message. Otherwise just maintain the
same behavior
2021-06-16 20:48:12 -07:00
Alexandros Kosiaris 6703e03ce6 contrib: Add support for a $MINIFLUX_IMAGE env var in docker-compose
Allowing to override the image used in docker-compose files can allow
for richer and more easy local development/debugging sessions. The
docker image building process is already using the latest tag anyway,
making it really easy to build an image with a (set of) specific
commits. Using the above built image with the provided docker-compose
files isn't doable without modifications though. Add that support via
environmental variables.
2021-06-16 20:34:25 -07:00
Alexandros Kosiaris 56c3f1193f contrib: Bump docker-compose version to 3.4
'start_period' for 'healthchecks' was added in Compose file version 3.4,

https://docs.docker.com/compose/compose-file/compose-versioning/#version-34

Bump docker-compose files' versions from 3.3 to 3.4

Without this, docker-compose (1.25 at least) returns

ERROR: The Compose file './contrib/docker-compose/caddy.yml' is invalid because:
services.db.healthcheck value Additional properties are not allowed ('start_period' was unexpected)
2021-06-16 20:24:13 -07:00
dependabot[bot] d189df95db Bump github.com/prometheus/client_golang from 1.10.0 to 1.11.0
Bumps [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) from 1.10.0 to 1.11.0.
- [Release notes](https://github.com/prometheus/client_golang/releases)
- [Changelog](https://github.com/prometheus/client_golang/blob/master/CHANGELOG.md)
- [Commits](https://github.com/prometheus/client_golang/compare/v1.10.0...v1.11.0)

---
updated-dependencies:
- dependency-name: github.com/prometheus/client_golang
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-06-14 18:40:44 -07:00
pennae 0bcfc81b1f add option to hide categories from the global unread list 2021-06-12 13:06:30 -07:00
pennae 571d7bf17c Show "saving" labels for entry status button 2021-06-12 13:00:05 -07:00
Frédéric Guillot 93fd98f8d4 Update ChangeLog 2021-06-06 10:03:44 -07:00
Ilya Mateyko 238a3ee9be client: expose comments_url entry field 2021-06-04 22:18:39 -07:00
Frédéric Guillot 897cd7cb34 Use unique file names for cache busting instead of query string 2021-06-02 19:05:32 -07:00
Frédéric Guillot 409d05eec8 Feeds with errors should appear before unread ones 2021-06-02 18:57:57 -07:00
pennae 4b2a25eed4 Highlight and sort feeds with unread entries in feeds list 2021-06-02 14:01:21 -07:00
Frédéric Guillot 1fc95a83b6 Include 'self' in CSP when using custom styles 2021-06-01 15:07:09 -07:00
pennae cc888e2a55 set items to read on click/middle-click of external link 2021-05-31 20:26:18 -07:00
Frédéric Guillot d9f91fd919 Firefox on Windows does not show the active link as bold 2021-05-31 17:04:02 -07:00
Frédéric Guillot dd3f496d06 Avoid extra HTTP request for fetching custom stylesheet
Use inline CSS with a nonce and move CSP headers to a meta tag.
2021-05-31 14:29:33 -07:00
Frédéric Guillot 09be3d2bac Remove invalid CSRF HTML meta tag 2021-05-31 13:54:47 -07:00
Tai 1fd4c4ef13 Add lang attribute to root HTML tag
Allow hyphens css property to work correctly and improve screen readers.
2021-05-31 13:19:37 -07:00
Jan-Lukas Else 20cd023c07 Use runes instead of bytes to truncate JSON feed titles
This fix avoid breaking Unicode string. 

It solves this error:

pq: invalid byte sequence for encoding "UTF8": 0xf0 0x9f 0x9a 0x2e
2021-05-31 11:42:59 -07:00
dependabot[bot] 1655ca235d Bump github.com/tdewolff/minify/v2 from 2.9.16 to 2.9.17
Bumps [github.com/tdewolff/minify/v2](https://github.com/tdewolff/minify) from 2.9.16 to 2.9.17.
- [Release notes](https://github.com/tdewolff/minify/releases)
- [Commits](https://github.com/tdewolff/minify/compare/v2.9.16...v2.9.17)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-30 23:43:57 -07:00
James Loh 77dc6aaf27 Expose changed_at time through the API
No issue

This is already stored and updated in the DB to render the History page but is currently not exposed through the API
2021-05-27 20:13:01 -07:00
Loïc Doubinine b4cdef6875 Fix typo in SECURITY.md 2021-05-24 15:23:20 -07:00
Frédéric Guillot 36868e648c Add new config option CLEANUP_ARCHIVE_BATCH_SIZE 2021-05-23 20:52:13 -07:00
Frédéric Guillot c119a2c011 Add new option DATABASE_CONNECTION_LIFETIME 2021-05-23 19:40:33 -07:00
Frédéric Guillot e0557d8961 Add option to disable watchdog and set default timeout to DB healthcheck 2021-05-23 18:30:20 -07:00
Frédéric Guillot 36d3b1e9fb Add database stats to Prometheus exporter 2021-05-22 20:31:49 -07:00
Frédéric Guillot 012eb61c52 Tweak watchdog 2021-05-22 20:25:38 -07:00
Frédéric Guillot c4a56105ca Add Systemd watchdog 2021-05-22 18:46:15 -07:00
Frédéric Guillot 1005fb973e Avoid custom stylesheet to be cached by third-party CDN
If the application is hosted behind a CDN like Cloudflare,
then all custom stylesheets is be the same for all users.

The random query string prevent the CDN to cache this.
2021-05-21 14:03:45 -07:00
NobeKanai 75ac58abdf Update a shared entry label translation in zh_CN 2021-05-20 07:29:44 -07:00
dependabot[bot] e46b438b06 Bump github.com/lib/pq from 1.10.1 to 1.10.2
Bumps [github.com/lib/pq](https://github.com/lib/pq) from 1.10.1 to 1.10.2.
- [Release notes](https://github.com/lib/pq/releases)
- [Commits](https://github.com/lib/pq/compare/v1.10.1...v1.10.2)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-19 22:47:40 -07:00
Edward Betts 264f4db567 Correct spelling 2021-05-13 18:22:23 -07:00
Frédéric Guillot 0c56b4d580 Update ChangeLog 2021-05-07 16:35:08 -07:00
Frédéric Guillot 32439ca2f0 Security fix: any user can delete any feed
Regression introduced in commit 51fb949.
2021-05-07 16:25:44 -07:00
jacekk fa49bcaf8b Fix password reset via CLI 2021-05-02 20:34:33 -07:00
Frédéric Guillot 89e8e94bf1 Increase default batch size value 2021-04-30 23:02:45 -07:00
Frédéric Guillot 5b8eb4735c Handle RSS feed title with encoded Unicode entities 2021-04-30 22:57:29 -07:00
pennae 1c9f000576 show #unread per category in category list, not #feeds
the number of feeds in the category is currently displayed twice, and a lot less
useful than the number of unread items in the category.
2021-04-26 18:49:32 -07:00
dependabot[bot] de53d8762f Bump github.com/lib/pq from 1.10.0 to 1.10.1
Bumps [github.com/lib/pq](https://github.com/lib/pq) from 1.10.0 to 1.10.1.
- [Release notes](https://github.com/lib/pq/releases)
- [Commits](https://github.com/lib/pq/compare/v1.10.0...v1.10.1)

Signed-off-by: dependabot[bot] <support@github.com>
2021-04-25 23:15:20 -07:00
野辺かない daa56efe27 Fix filtering doesn't work when selecting from multiple found feeds 2021-04-25 21:06:39 -07:00
dependabot[bot] f1db010a68 Bump github.com/tdewolff/minify/v2 from 2.9.15 to 2.9.16
Bumps [github.com/tdewolff/minify/v2](https://github.com/tdewolff/minify) from 2.9.15 to 2.9.16.
- [Release notes](https://github.com/tdewolff/minify/releases)
- [Commits](https://github.com/tdewolff/minify/compare/v2.9.15...v2.9.16)

Signed-off-by: dependabot[bot] <support@github.com>
2021-04-19 20:26:01 -07:00
Vincent Bernat dbb5906767 Use an appropriate color for visited links on dark theme
The contrast between background and purple for visited links was too
low (1.69). Use a brighter purple for the dark theme (contrast 6.87).
2021-04-14 21:28:31 -07:00
yue 18e414ec45 Fix typo in reader/json/doc.go 2021-04-02 19:00:06 -07:00
fguillot fc00a1b3a8 Create SECURITY.md 2021-03-30 20:51:34 -07:00
Benedikt Hopmann 556b74f4fc Update de_DE.json 2021-03-28 17:22:23 -07:00
Frédéric Guillot 6e2e2d1665 Setup golangci-lint Github Action 2021-03-22 21:34:48 -07:00
Darius 9242350f0e Add per feed cookies option 2021-03-22 20:27:58 -07:00
dependabot[bot] b0c14aa8f9 Bump github.com/prometheus/client_golang from 1.9.0 to 1.10.0
Bumps [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) from 1.9.0 to 1.10.0.
- [Release notes](https://github.com/prometheus/client_golang/releases)
- [Changelog](https://github.com/prometheus/client_golang/blob/master/CHANGELOG.md)
- [Commits](https://github.com/prometheus/client_golang/compare/v1.9.0...v1.10.0)

Signed-off-by: dependabot[bot] <support@github.com>
2021-03-22 17:25:02 -07:00
dependabot[bot] 4b2516532e Bump github.com/tdewolff/minify/v2 from 2.9.13 to 2.9.15
Bumps [github.com/tdewolff/minify/v2](https://github.com/tdewolff/minify) from 2.9.13 to 2.9.15.
- [Release notes](https://github.com/tdewolff/minify/releases)
- [Commits](https://github.com/tdewolff/minify/compare/v2.9.13...v2.9.15)

Signed-off-by: dependabot[bot] <support@github.com>
2021-03-22 17:24:36 -07:00
Frédéric Guillot 1aec8d727c Update ChangeLog 2021-03-21 11:29:24 -07:00
Frédéric Guillot e60e0ba3c4 Add workaround to handle some invalid dates 2021-03-21 10:52:27 -07:00
Frédéric Guillot 5877048749 Improve handling of Atom text content with CDATA 2021-03-20 20:47:35 -07:00
Frédéric Guillot c8c1f05328 Add better support of Atom text constructs
- Note that Miniflux does not render entry title with HTML tags as of now
- Omit XHTML div element because it should not be part of the content
2021-03-19 22:05:00 -07:00
Frédéric Guillot 96f3e888cf Handle RDF feed with HTML encoded entry title
Example: http://rss.slashdot.org/Slashdot/slashdotMain
2021-03-19 18:49:51 -07:00
Frédéric Guillot 14888f1cb8 Fix incorrect parsing of Atom entry content of type HTML 2021-03-18 21:43:59 -07:00
Taylan Tatlı 49171c5e8c Turkish language 2021-03-17 21:17:03 -07:00
Frédéric Guillot 51fb9495ad Improve large feed deletion
First patch to avoid the app hanging when deleting large feeds
2021-03-16 22:22:50 -07:00
Ilya Mateyko 89c1b3b4d8 Systemd readiness notification
This change implements the systemd readiness notification, using
the sd_notify protocol.

See https://www.freedesktop.org/software/systemd/man/sd_notify.html.
2021-03-16 20:14:43 -07:00
Gabriel Augendre 1d80c12e18 Prevent Youtube scraping if entry already exists 2021-03-08 20:10:53 -08:00
dependabot[bot] d092fc8436 Bump github.com/lib/pq from 1.9.0 to 1.10.0
Bumps [github.com/lib/pq](https://github.com/lib/pq) from 1.9.0 to 1.10.0.
- [Release notes](https://github.com/lib/pq/releases)
- [Commits](https://github.com/lib/pq/compare/v1.9.0...v1.10.0)

Signed-off-by: dependabot[bot] <support@github.com>
2021-03-07 23:01:21 -08:00
Frédéric Guillot 8a812cd8ec Add basic PWA offline page
- Remove feed_icons cache because it's causing more problems that it solve.
- Add basic offline mode when using the service worker.
- Starting in Chrome 93, offline mode is going to be a requirement to install the PWA.

https://developer.chrome.com/blog/improved-pwa-offline-detection/#enforcement-starting-chrome-93-august-2021
2021-03-07 15:44:42 -08:00
Frédéric Guillot ae13b4e420 Replace icon for "Add to home screen" button 2021-03-07 12:07:54 -08:00
Frédéric Guillot 548c4d4efe Use SVG icons for toast notifications 2021-03-07 12:03:43 -08:00
Frédéric Guillot f6ed2feab4 Use SVG sprite for icons 2021-03-07 11:18:42 -08:00
1pav b730aa520d Reset scroll position on mark page as read 2021-03-06 18:08:13 -08:00
Alexandros Kosiaris 3d16c3e4e6 Add link to mark all feed entries as read 2021-03-06 17:45:25 -08:00
1pav 0d935a863f Make web app display mode configurable
The change is visible after reinstalling the web app. 

It's not compatible with all browsers.

See https://developer.mozilla.org/en-US/docs/Web/Manifest/display
2021-02-28 13:29:51 -08:00
hykhd 053b1d0f8d Handle RSS feeds with CDATA in author item element 2021-02-28 12:26:52 -08:00
Gabriel Augendre b247f3f089 Add read time on article page 2021-02-24 20:06:11 -08:00
Frédéric Guillot 20ec435d6d Move healthcheck from Dockerfile to docker-compose because it's optional 2021-02-22 21:47:30 -08:00
Frédéric Guillot 53cd369b05 Avoid showing a broken image when there is no feed icon 2021-02-22 21:15:08 -08:00
Frédéric Guillot ec3c604a83 Add option to allow self-signed or invalid certificates 2021-02-21 13:58:52 -08:00
Ilya Mateyko c3f871b49b Use YouTube video duration as read time
This feature works by scraping YouTube website.

To enable it, set the FETCH_YOUTUBE_WATCH_TIME environment variable to
1.

Resolves #972.
2021-02-21 11:13:52 -08:00
Gabriel Augendre e5b2eab727 Send full article content to wallabag 2021-02-21 11:05:29 -08:00
Frédéric Guillot bbf93430b7 Add more extensive healthcheck support
- Add new cli argument: -healthcheck
- Add HEALTHCHECK instruction to Dockerfile
- Update Docker Compose examples
2021-02-20 12:58:04 -08:00
hykhd 3cb04b2c56 update whitelist fix bilibili video 2021-02-20 10:29:42 -08:00
Frédéric Guillot e3c28a6c96 Improve health check endpoint to test database connection 2021-02-19 19:32:13 -08:00
Frédéric Guillot c2571f9f47 Remove completely generated files 2021-02-18 21:50:27 -08:00
Frédéric Guillot 4855fbd13f Use embed package for Javascript bundles instead of generated files 2021-02-18 20:49:06 -08:00
Frédéric Guillot 9569666259 Use embed package for CSS bundles instead of generated files 2021-02-17 22:07:28 -08:00
Frédéric Guillot 42edd357bc Add pull-request template 2021-02-17 20:54:39 -08:00
fguillot b33706006a Update issue templates 2021-02-17 20:46:19 -08:00
Frédéric Guillot 0de80c2ff0 Use embed package for binary assets instead of generated files 2021-02-17 20:15:28 -08:00
Frédéric Guillot 5d65a85bdb Use embed package for translations instead of generated files
Replace "go generate" with the new embed package.
2021-02-16 23:09:01 -08:00
Frédéric Guillot a352aff93b Remove deprecated io/ioutil package
Miniflux now requires at least Go 1.16 and io/util is deprecated.

https://golang.org/doc/go1.16#ioutil
2021-02-16 21:25:21 -08:00
Frédéric Guillot 713d575bad Update CI check for Go 1.16 2021-02-16 21:13:13 -08:00
Rogier Lommers 421bb467d0 Show Postgres version in about page 2021-02-16 07:37:24 -08:00
270 changed files with 9913 additions and 8481 deletions
+10
View File
@@ -0,0 +1,10 @@
---
name: Bug report
about: Create a bug report
title: ''
labels: bug
assignees: ''
---
+1
View File
@@ -0,0 +1 @@
blank_issues_enabled: false
+10
View File
@@ -0,0 +1,10 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: feature request
assignees: ''
---
- [ ] I have read this document: https://miniflux.app/opinionated.html#feature-request
+10
View File
@@ -0,0 +1,10 @@
---
name: Feed Problems
about: Problems with a feed or a website
title: ''
labels: feed problems
assignees: ''
---
+10
View File
@@ -0,0 +1,10 @@
---
name: Improvement
about: Do you have an idea to improve an existing feature?
title: ''
labels: improvements
assignees: ''
---
@@ -0,0 +1,10 @@
---
name: Installation Issue
about: Do you need help to install Miniflux?
title: ''
labels: installation issue
assignees: ''
---
@@ -0,0 +1,10 @@
---
name: Question / Discussion
about: Open discussions
title: ''
labels: question / discussion
assignees: ''
---
+4
View File
@@ -0,0 +1,4 @@
Do you follow the guidelines?
- [ ] I have tested my changes
- [ ] I read this document: https://miniflux.app/faq.html#pull-request
+8 -48
View File
@@ -1,40 +1,12 @@
name: CI Workflow
permissions: read-all
on:
pull_request:
branches:
- master
jobs:
linters:
name: Linter Check
runs-on: ubuntu-latest
steps:
- name: Set up Go
uses: actions/setup-go@v1
with:
go-version: 1.15
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Checkout
uses: actions/checkout@v1
with:
fetch-depth: 3
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Install linters
run: |
cd /tmp && go get -u golang.org/x/lint/golint
sudo npm install -g jshint
env:
GO111MODULE: off
- name: Run golint
run: |
export PATH=/home/runner/go/bin:$PATH
make lint
- name: Run jshint
run: jshint ui/static/js/*.js
unit-tests:
name: Unit Tests
runs-on: ${{ matrix.os }}
@@ -42,20 +14,14 @@ jobs:
max-parallel: 4
matrix:
os: [ubuntu-latest, windows-latest, macOS-latest]
go-version: [1.14, 1.15]
go-version: [1.16, 1.17, 1.18]
steps:
- name: Set up Go
uses: actions/setup-go@v1
uses: actions/setup-go@v2
with:
go-version: ${{ matrix.go-version }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Checkout
uses: actions/checkout@v1
with:
fetch-depth: 3
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
uses: actions/checkout@v2
- name: Run unit tests
run: make test
@@ -74,17 +40,11 @@ jobs:
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
steps:
- name: Set up Go
uses: actions/setup-go@v1
uses: actions/setup-go@v2
with:
go-version: 1.15
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
go-version: 1.16
- name: Checkout
uses: actions/checkout@v1
with:
fetch-depth: 3
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
uses: actions/checkout@v2
- name: Install Postgres client
run: sudo apt-get install -y postgresql-client
- name: Run integration tests
+72
View File
@@ -0,0 +1,72 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
permissions: read-all
on:
push:
branches: [ master ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ master ]
schedule:
- cron: '45 22 * * 3'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'go', 'javascript' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
# Learn more about CodeQL language support at https://git.io/codeql-language-support
steps:
- name: Checkout repository
uses: actions/checkout@v2
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
# ️ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1
+29 -5
View File
@@ -1,4 +1,5 @@
name: Docker
permissions: read-all
on:
schedule:
- cron: '0 1 * * *'
@@ -14,8 +15,8 @@ jobs:
with:
fetch-depth: 0
- name: Generate Docker tag
id: docker_tag
- name: Generate Alpine Docker tag
id: docker_alpine_tag
run: |
DOCKER_IMAGE=miniflux/miniflux
DOCKER_VERSION=dev
@@ -28,6 +29,20 @@ jobs:
fi
echo ::set-output name=tags::${TAGS}
- name: Generate Distroless Docker tag
id: docker_distroless_tag
run: |
DOCKER_IMAGE=miniflux/miniflux
DOCKER_VERSION=dev-distroless
if [ "${{ github.event_name }}" = "schedule" ]; then
DOCKER_VERSION=nightly-distroless
TAGS="${DOCKER_IMAGE}:${DOCKER_VERSION},ghcr.io/${DOCKER_IMAGE}:${DOCKER_VERSION}"
elif [[ $GITHUB_REF == refs/tags/* ]]; then
DOCKER_VERSION=${GITHUB_REF#refs/tags/}-distroless
TAGS="${DOCKER_IMAGE}:${DOCKER_VERSION},ghcr.io/${DOCKER_IMAGE}:${DOCKER_VERSION},${DOCKER_IMAGE}:latest-distroless,ghcr.io/${DOCKER_IMAGE}:latest-distroless"
fi
echo ::set-output name=tags::${TAGS}
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
@@ -47,11 +62,20 @@ jobs:
username: ${{ github.repository_owner }}
password: ${{ secrets.CR_PAT }}
- name: Build and push
- name: Build and Push Alpine images
uses: docker/build-push-action@v2
with:
context: .
file: ./packaging/docker/Dockerfile
file: ./packaging/docker/alpine/Dockerfile
platforms: linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64
push: true
tags: ${{ steps.docker_tag.outputs.tags }}
tags: ${{ steps.docker_alpine_tag.outputs.tags }}
- name: Build and Push Distroless images
uses: docker/build-push-action@v2
with:
context: .
file: ./packaging/docker/distroless/Dockerfile
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.docker_distroless_tag.outputs.tags }}
+32
View File
@@ -0,0 +1,32 @@
name: Linters
permissions: read-all
on:
pull_request:
branches:
- master
jobs:
jshint:
name: Javascript Linter
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install jshint
run: |
sudo npm install -g jshint@2.13.3
- name: Run jshint
run: jshint ui/static/js/*.js
golangci:
name: Golang Linter
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-go@v2
with:
go-version: 1.17
- uses: golangci/golangci-lint-action@v2
with:
args: --skip-dirs tests --disable errcheck --enable sqlclosecheck --enable misspell --enable gofmt --enable goimports --enable whitespace
skip-go-installation: true
+55
View File
@@ -0,0 +1,55 @@
name: Scorecards supply-chain security
on:
# Only the default branch is supported.
branch_protection_rule:
schedule:
- cron: '31 8 * * 6'
push:
branches: [ master ]
# Declare default permissions as read only.
permissions: read-all
jobs:
analysis:
name: Scorecards analysis
runs-on: ubuntu-latest
permissions:
# Needed to upload the results to code-scanning dashboard.
security-events: write
actions: read
contents: read
steps:
- name: "Checkout code"
uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # v2.4.0
with:
persist-credentials: false
- name: "Run analysis"
uses: ossf/scorecard-action@e3e75cf2ffbf9364bbff86cdbdf52b23176fe492 # v1.0.1
with:
results_file: results.sarif
results_format: sarif
# Read-only PAT token. To create it,
# follow the steps in https://github.com/ossf/scorecard-action#pat-token-creation.
repo_token: ${{ secrets.SCORECARD_READ_TOKEN }}
# Publish the results to enable scorecard badges. For more details, see
# https://github.com/ossf/scorecard-action#publishing-results.
# For private repositories, `publish_results` will automatically be set to `false`,
# regardless of the value entered here.
publish_results: true
# Upload the results as artifacts (optional).
- name: "Upload artifact"
uses: actions/upload-artifact@82c141cc518b40d92cc801eee768e7aafc9c2fa2 # v2.3.1
with:
name: SARIF file
path: results.sarif
retention-days: 5
# Upload the results to GitHub's code scanning dashboard.
- name: "Upload to code-scanning"
uses: github/codeql-action/upload-sarif@5f532563584d71fdef14ee64d17bafb34f751ce5 # v1.0.26
with:
sarif_file: results.sarif
+1
View File
@@ -2,3 +2,4 @@ miniflux-*
miniflux
*.rpm
*.deb
.idea
+217 -1
View File
@@ -1,3 +1,219 @@
Version 2.0.37 (May 27, 2022)
-----------------------------
* Add rewrite rule to decode base64 content
* Add Linkding integration
* Add comment button to Telegram message
* Add API endpoint to fetch unread and read counters
* Fixes logic bug in Google Reader API sanity check
* Reduce number of CORS preflight check to save network brandwidth
* Add Espial integration
* Allow API search for entries which are not starred
* Try to use outermost element text when title is empty
* Make swipe gestures feel more natural
- Removes opacity transition when swiping an article read/unread
- Adds "resistance" to the swiped entry when the 75px threshold is
reached
- Fixes an issue in which a swiped article couldn't be moved <15px
* Add support for feed streams to Google Reader API IDs API
* Fix invalid parsing of icon data URL
* Add Traditional Chinese translation
* Add distroless Docker image variant
* Add Go 1.18 to GitHub Action
* Bump `github.com/tdewolff/minify/v2` from `2.10.0` to `2.11`
* Bump `github.com/prometheus/client_golan` from `1.12.1` to `1.12.2`
* Bump `github.com/lib/pq` from `1.10.4` to `1.10.6`
Version 2.0.36 (March 8, 2022)
------------------------------
* Gray out pagination buttons when they are not applicable
* Use truncated entry description as title if unavailable
* Do not fallback to InnerXML if XHTML title is empty
* Add `+` keyboard shortcut for new subscription page
* Add `(+)` action next to Feeds to quickly add new feeds
* Fix unstar not working via Google Reader API
* Remove circles in front of page header list items
* Fix CSS hover style for links styled as buttons
* Avoid showing `undefined` when clicking on read/unread
* Add new keyboard shortcut `M` to toggle read/unread, and go to previous item
* Add several icons to menus according to their roles
* Add missing event argument to `onClick()` function call
* Add links to scraper/rewrite/filtering docs when editing feeds
* Add a rewrite rule for Castopod episodes
* Fix regression: reset touch-item if not in `/unread` page
* Add API endpoint to fetch original article
* Show the category first in feed settings
* Add pagination on top of all entries
* Display Go version in "About" page
* Bump `mvdan.cc/xurls/v2` from 2.3.0 to 2.4.0
* Bump `github.com/prometheus/client_golang` from 1.11.0 to 1.12.1
* Bump `github.com/tdewolff/minify/v2` from 2.9.28 to 2.10.0
Version 2.0.35 (January 21, 2022)
---------------------------------
* Set `read-all` permission to `GITHUB_TOKEN` for GitHub Actions
* Pin `jshint` version in linter job
* Fix incorrect conversion between integer types
* Add new GitHub Actions workflows: CodeQL and Scorecards analysis
* Handle Atom feeds with space around CDATA
* Bump `github.com/tdewolff/minify/v2` from 2.9.22 to 2.9.28
* Add Documentation directive to Systemd service
* Do not reset `touch-item` if successfully swiped
* Add support for multiple authors in Atom feeds
* Omit `User-Agent` header in image proxy to avoid being blocked
* Use custom feed user agent to fetch website icon
* Make default Invidious instance configurable
* Add new rewrite rule `add_youtube_video_from_id` to add Youtube videos in Quanta articles
* Add scrape and rewrite rules for `quantamagazine.org`
* Expose entry unshare link in the entry and list views
* Add Google Reader API implementation (experimental)
* Add `Content-Security-Policy` header to feed icon and image proxy endpoints
- SVG images could contain Javascript. This CSP blocks inline script.
- Feed icons are served using `<img>` tag and Javascript is not interpreted.
* Add Finnish translation
* Add scraper rule for `ikiwiki.iki.fi`
* Remove `SystemCallFilter` from `miniflux.service`
* Fix minor typo in French translation
Version 2.0.34 (December 16, 2021)
----------------------------------
* Add rewrite rule for comics website http://monkeyuser.com
* Add `<head>` tag to OPML export
* Tighten Systemd sandboxing and update comments in `miniflux.service`
* Add `RuntimeDirectory` to Systemd service
* Order disabled feeds at the end of the list
* Add support for theme color based on preferred color scheme of OS
* Bump `github.com/lib/pq` from 1.10.3 to 1.10.4
* Bump `github.com/PuerkitoBio/goquery` from 1.7.1 to 1.8.0
* Fix typos in `model/icon.go`
* Add `data-srcset` support to `add_dynamic_image rewrite` rewrite rule
* Fix Docker Compose example files compatibility to v3
* Added the `role="article"` to `<article>` elements for better accessibility with screen readers
* Redact secrets shown on the about page
* Handle `srcset` images with no space after comma
* Hide the logout link when using auth proxy
* Fix wrong CSS variable
* Change `-config-dump` command to use `KEY=VALUE` format
Version 2.0.33 (September 25, 2021)
-----------------------------------
* Build RPM and Debian package with PIE mode enabled
* Add CSS rule to hide `<template>` tag in old browsers
* Bump `github.com/tdewolff/minify/v2 from 2.9.21 to 2.9.22`
* Bump `github.com/lib/pq from 1.10.2 to 1.10.3`
* Remove `RequestURI()` hack
* Improve `zh_CN` translation
* Add ability to change entry sort order in the UI
* Add minor improvements in integration package
* Add Telegram integration
* Add rewrite rule to remove DOM elements
* Add proxy argument to `scraper.Fetch()`
* Add mime type `application/feed+json` to discover JSON Feed v1.1
* Update scraper rule for `theregister.com`
* Add Go 1.17 to GitHub Actions
* Display option to hide feed only when category is not already hidden
* Add option to hide feeds from the global Unread list
Version 2.0.32 (August 14, 2021)
--------------------------------
* Bump `github.com/tdewolff/minify/v2` from 2.9.17 to 2.9.21
* Bump `mvdan.cc/xurls/v2` from 2.2.0 to 2.3.0
* Bump `github.com/PuerkitoBio/goquery` from 1.6.1 to 1.7.1
* Bump `github.com/prometheus/client_golang` from 1.10.0 to 1.11.0
* Add `/rss/` to the list of well known URLs during feed discovery
* Use `authors` entry for JSON 1.1 feeds
* Added Greek translation
* Added the ability to mark an entire category as read in the web ui
* Added "in" in "logged in" for en_US `tooltip.logged_user`
* Added option to hide categories from the global unread list
* Show "saving" labels for entry status button
* Golang client: Try to parse response body on `InternalServerError` errors
* contrib: Add support for a $MINIFLUX_IMAGE env var in docker-compose
* contrib: Bump docker-compose version to 3.4
Version 2.0.31 (June 6, 2021)
-----------------------------
* Expose comments_url entry field in Golang API client
* Use unique file names for cache busting instead of query string
* Highlight and sort feeds with unread entries in feeds list
* Mark items as read on click/middle click of external links
* Fix: Firefox on Windows does not show the active link as bold
* Avoid extra HTTP request for fetching custom stylesheet
* Remove invalid CSRF HTML meta tag
* Add lang attribute to root HTML tag
* Use runes instead of bytes to truncate JSON feed titles (avoid breaking Unicode strings)
* Expose changed_at time through the API
* Add new config option CLEANUP_ARCHIVE_BATCH_SIZE
* Add new option DATABASE_CONNECTION_LIFETIME
* Add database stats to Prometheus exporter
* Add Systemd watchdog
* Avoid custom stylesheet to be cached by third-party CDN
* Update a shared entry label translation in zh_CN
* Bump github.com/tdewolff/minify/v2 from 2.9.16 to 2.9.17
* Bump github.com/lib/pq from 1.10.1 to 1.10.2
Version 2.0.30 (May 7, 2021)
----------------------------
* Security fix: any user can delete any feed (Regression introduced in commit 51fb949)
* Fix password reset via CLI
* Increase default batch size value
* Handle RSS feed title with encoded Unicode entities
* Show number of unread per category in category list instead of number of feeds
* Bump github.com/lib/pq from 1.10.0 to 1.10.1
* Filtering doesn't work when selecting from multiple found feeds
* Bump github.com/tdewolff/minify/v2 from 2.9.15 to 2.9.16
* Use an appropriate color for visited links on dark theme
* Fix typo in reader/json/doc.go
* Create SECURITY.md
* Setup golangci-lint Github Action
* Add per feed cookies option
* Bump github.com/prometheus/client_golang from 1.9.0 to 1.10.0
* Bump github.com/tdewolff/minify/v2 from 2.9.13 to 2.9.15
Version 2.0.29 (Mar 21, 2021)
-----------------------------
* Miniflux requires at least Go 1.16 now
* Improved support of Atom text constructs
- Improve handling of CDATA in text elements
- Omit XHTML root element because it should not be part of the content
- Fix incorrect parsing of HTML elements
* Handle RDF feed with HTML encoded entry title
* Add Turkish language
* Improve deletion of feeds with lots of entries
* Add support of Systemd readiness notification using the sd_notify protocol
* Remove feed_icons service worker cache because it's causing more problems than it solves (and HTTP cache seems faster)
* Add basic PWA offline page
- Add basic offline mode when using the service worker
- Starting in Chrome 93, offline mode is going to be a requirement to install the PWA
* Replace icon for "Add to home screen" button
* Use SVG icons for "toast" notifications
* Use SVG sprite for icons instead of inline elements
* Reset scroll position on mark page as read
* Add link to mark all feed entries as read
* Make web app display mode configurable (The change is visible after reinstalling the web app)
* Handle RSS feeds with CDATA in author item element
* Add read time on the article page
* Avoid showing a broken image when there is no feed icon
* Add option to allow self-signed or invalid certificates
* Add new config option to scrape YouTube's website to get video duration as read time (disabled by default)
* Send full article content to Wallabag
* Add more extensive health check support
- Improve endpoint to test database connection
- Add new cli argument: -healthcheck
- Update Docker Compose examples
* Update iframe "allow list" to support Bilibili videos
* Remove completely generated files and use embed package to bundle JS/CSS/Images/Translations files into the final binary
* Remove deprecated io/ioutil package
* Show Postgres version in "About" page
Version 2.0.28 (Feb 15, 2021)
-----------------------------
@@ -405,7 +621,7 @@ Version 2.0.13 (Nov 25, 2018)
* Add man page
* Add support for Systemd Socket Activation (experimental)
* Add the possiblity to listen on Unix sockets
* Add the possibility to listen on Unix sockets
* Add config options to disable HTTP and scheduler services
* Archive more read entries in cleanup job
* Change default database connection string (backward compatible)
+30 -25
View File
@@ -10,7 +10,7 @@ DEB_IMG_ARCH := amd64
export PGPASSWORD := postgres
.PHONY: generate \
.PHONY: \
miniflux \
linux-amd64 \
linux-arm64 \
@@ -19,6 +19,7 @@ export PGPASSWORD := postgres
linux-armv5 \
linux-x86 \
darwin-amd64 \
darwin-arm64 \
freebsd-amd64 \
freebsd-x86 \
openbsd-amd64 \
@@ -35,66 +36,67 @@ export PGPASSWORD := postgres
integration-test \
clean-integration-test \
docker-image \
docker-image-distroless \
docker-images \
rpm \
debian \
debian-packages
generate:
@ go generate
miniflux:
@ go build -buildmode=pie -ldflags=$(LD_FLAGS) -o $(APP) main.go
miniflux: generate
@ go build -ldflags=$(LD_FLAGS) -o $(APP) main.go
linux-amd64: generate
linux-amd64:
@ GOOS=linux GOARCH=amd64 go build -ldflags=$(LD_FLAGS) -o $(APP)-linux-amd64 main.go
linux-arm64: generate
linux-arm64:
@ GOOS=linux GOARCH=arm64 go build -ldflags=$(LD_FLAGS) -o $(APP)-linux-arm64 main.go
linux-armv7: generate
linux-armv7:
@ GOOS=linux GOARCH=arm GOARM=7 go build -ldflags=$(LD_FLAGS) -o $(APP)-linux-armv7 main.go
linux-armv6: generate
linux-armv6:
@ GOOS=linux GOARCH=arm GOARM=6 go build -ldflags=$(LD_FLAGS) -o $(APP)-linux-armv6 main.go
linux-armv5: generate
linux-armv5:
@ GOOS=linux GOARCH=arm GOARM=5 go build -ldflags=$(LD_FLAGS) -o $(APP)-linux-armv5 main.go
darwin-amd64: generate
darwin-amd64:
@ GOOS=darwin GOARCH=amd64 go build -ldflags=$(LD_FLAGS) -o $(APP)-darwin-amd64 main.go
freebsd-amd64: generate
darwin-arm64:
@ GOOS=darwin GOARCH=arm64 go build -ldflags=$(LD_FLAGS) -o $(APP)-darwin-arm64 main.go
freebsd-amd64:
@ GOOS=freebsd GOARCH=amd64 go build -ldflags=$(LD_FLAGS) -o $(APP)-freebsd-amd64 main.go
openbsd-amd64: generate
openbsd-amd64:
@ GOOS=openbsd GOARCH=amd64 go build -ldflags=$(LD_FLAGS) -o $(APP)-openbsd-amd64 main.go
windows-amd64: generate
windows-amd64:
@ GOOS=windows GOARCH=amd64 go build -ldflags=$(LD_FLAGS) -o $(APP)-windows-amd64 main.go
build: linux-amd64 linux-arm64 linux-armv7 linux-armv6 linux-armv5 darwin-amd64 freebsd-amd64 openbsd-amd64 windows-amd64
build: linux-amd64 linux-arm64 linux-armv7 linux-armv6 linux-armv5 darwin-amd64 darwin-arm64 freebsd-amd64 openbsd-amd64 windows-amd64
# NOTE: unsupported targets
netbsd-amd64: generate
netbsd-amd64:
@ GOOS=netbsd GOARCH=amd64 go build -ldflags=$(LD_FLAGS) -o $(APP)-netbsd-amd64 main.go
linux-x86: generate
linux-x86:
@ GOOS=linux GOARCH=386 go build -ldflags=$(LD_FLAGS) -o $(APP)-linux-x86 main.go
freebsd-x86: generate
freebsd-x86:
@ GOOS=freebsd GOARCH=386 go build -ldflags=$(LD_FLAGS) -o $(APP)-freebsd-x86 main.go
netbsd-x86: generate
netbsd-x86:
@ GOOS=netbsd GOARCH=386 go build -ldflags=$(LD_FLAGS) -o $(APP)-netbsd-x86 main.go
openbsd-x86: generate
openbsd-x86:
@ GOOS=openbsd GOARCH=386 go build -ldflags=$(LD_FLAGS) -o $(APP)-freebsd-x86 main.go
windows-x86: generate
windows-x86:
@ GOOS=windows GOARCH=386 go build -ldflags=$(LD_FLAGS) -o $(APP)-windows-x86 main.go
run: generate
run:
@ LOG_DATE_TIME=1 go run main.go -debug
clean:
@@ -129,12 +131,15 @@ clean-integration-test:
@ psql -U postgres -c 'drop database if exists miniflux_test;'
docker-image:
docker build -t $(DOCKER_IMAGE):$(VERSION) -f packaging/docker/Dockerfile .
docker build -t $(DOCKER_IMAGE):$(VERSION) -f packaging/docker/alpine/Dockerfile .
docker-image-distroless:
docker build -t $(DOCKER_IMAGE):$(VERSION) -f packaging/docker/distroless/Dockerfile .
docker-images:
docker buildx build \
--platform linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v6 \
--file packaging/docker/Dockerfile \
--file packaging/docker/alpine/Dockerfile \
--tag $(DOCKER_IMAGE):$(VERSION) \
--push .
+9
View File
@@ -0,0 +1,9 @@
# Security Policy
## Supported Versions
Only the latest stable version is supported.
## Reporting a Vulnerability
Send an email to `security AT miniflux DOT net` with all the steps to reproduce the problem.
+2
View File
@@ -47,6 +47,7 @@ func Serve(router *mux.Router, store *storage.Storage, pool *worker.Pool) {
sr.HandleFunc("/discover", handler.discoverSubscriptions).Methods(http.MethodPost)
sr.HandleFunc("/feeds", handler.createFeed).Methods(http.MethodPost)
sr.HandleFunc("/feeds", handler.getFeeds).Methods(http.MethodGet)
sr.HandleFunc("/feeds/counters", handler.fetchCounters).Methods(http.MethodGet)
sr.HandleFunc("/feeds/refresh", handler.refreshAllFeeds).Methods(http.MethodPut)
sr.HandleFunc("/feeds/{feedID}/refresh", handler.refreshFeed).Methods(http.MethodPut)
sr.HandleFunc("/feeds/{feedID}", handler.getFeed).Methods(http.MethodGet)
@@ -62,4 +63,5 @@ func Serve(router *mux.Router, store *storage.Storage, pool *worker.Pool) {
sr.HandleFunc("/entries", handler.setEntryStatus).Methods(http.MethodPut)
sr.HandleFunc("/entries/{entryID}", handler.getEntry).Methods(http.MethodGet)
sr.HandleFunc("/entries/{entryID}/bookmark", handler.toggleBookmark).Methods(http.MethodPut)
sr.HandleFunc("/entries/{entryID}/fetch-content", handler.fetchContent).Methods(http.MethodGet)
}
+46 -1
View File
@@ -8,11 +8,13 @@ import (
json_parser "encoding/json"
"errors"
"net/http"
"strconv"
"time"
"miniflux.app/http/request"
"miniflux.app/http/response/json"
"miniflux.app/model"
"miniflux.app/reader/processor"
"miniflux.app/storage"
"miniflux.app/validator"
)
@@ -172,6 +174,46 @@ func (h *handler) toggleBookmark(w http.ResponseWriter, r *http.Request) {
json.NoContent(w, r)
}
func (h *handler) fetchContent(w http.ResponseWriter, r *http.Request) {
loggedUserID := request.UserID(r)
entryID := request.RouteInt64Param(r, "entryID")
entryBuilder := h.store.NewEntryQueryBuilder(loggedUserID)
entryBuilder.WithEntryID(entryID)
entryBuilder.WithoutStatus(model.EntryStatusRemoved)
entry, err := entryBuilder.GetEntry()
if err != nil {
json.ServerError(w, r, err)
return
}
if entry == nil {
json.NotFound(w, r)
return
}
feedBuilder := storage.NewFeedQueryBuilder(h.store, loggedUserID)
feedBuilder.WithFeedID(entry.FeedID)
feed, err := feedBuilder.GetFeed()
if err != nil {
json.ServerError(w, r, err)
return
}
if feed == nil {
json.NotFound(w, r)
return
}
if err := processor.ProcessEntryWebPage(feed, entry); err != nil {
json.ServerError(w, r, err)
return
}
json.OK(w, r, map[string]string{"content": entry.Content})
}
func configureFilters(builder *storage.EntryQueryBuilder, r *http.Request) {
beforeEntryID := request.QueryInt64Param(r, "before_entry_id", 0)
if beforeEntryID > 0 {
@@ -199,7 +241,10 @@ func configureFilters(builder *storage.EntryQueryBuilder, r *http.Request) {
}
if request.HasQueryParam(r, "starred") {
builder.WithStarred()
starred, err := strconv.ParseBool(r.URL.Query().Get("starred"))
if err == nil {
builder.WithStarred(starred)
}
}
searchQuery := request.QueryStringParam(r, "search", "")
+10
View File
@@ -170,6 +170,16 @@ func (h *handler) getFeeds(w http.ResponseWriter, r *http.Request) {
json.OK(w, r, feeds)
}
func (h *handler) fetchCounters(w http.ResponseWriter, r *http.Request) {
counters, err := h.store.FetchCounters(request.UserID(r))
if err != nil {
json.ServerError(w, r, err)
return
}
json.OK(w, r, counters)
}
func (h *handler) getFeed(w http.ResponseWriter, r *http.Request) {
feedID := request.RouteInt64Param(r, "feedID")
feed, err := h.store.FeedByID(request.UserID(r), feedID)
+1
View File
@@ -27,6 +27,7 @@ func (m *middleware) handleCORS(next http.Handler) http.Handler {
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
w.Header().Set("Access-Control-Allow-Headers", "X-Auth-Token")
if r.Method == http.MethodOptions {
w.Header().Set("Access-Control-Max-Age", "3600")
w.WriteHeader(http.StatusOK)
return
}
+2
View File
@@ -29,9 +29,11 @@ func (h *handler) discoverSubscriptions(w http.ResponseWriter, r *http.Request)
subscriptions, finderErr := subscription.FindSubscriptions(
subscriptionDiscoveryRequest.URL,
subscriptionDiscoveryRequest.UserAgent,
subscriptionDiscoveryRequest.Cookie,
subscriptionDiscoveryRequest.Username,
subscriptionDiscoveryRequest.Password,
subscriptionDiscoveryRequest.FetchViaProxy,
subscriptionDiscoveryRequest.AllowSelfSignedCertificates,
)
if finderErr != nil {
json.ServerError(w, r, finderErr)
+42 -7
View File
@@ -10,8 +10,10 @@ import (
"miniflux.app/config"
"miniflux.app/database"
"miniflux.app/locale"
"miniflux.app/logger"
"miniflux.app/storage"
"miniflux.app/ui/static"
"miniflux.app/version"
)
@@ -26,6 +28,7 @@ const (
flagDebugModeHelp = "Show debug logs"
flagConfigFileHelp = "Load configuration file"
flagConfigDumpHelp = "Print parsed configuration values"
flagHealthCheckHelp = `Perform a health check on the given endpoint (the value "auto" try to guess the health check endpoint).`
)
// Parse parses command line arguments.
@@ -42,6 +45,7 @@ func Parse() {
flagDebugMode bool
flagConfigFile string
flagConfigDump bool
flagHealthCheck string
)
flag.BoolVar(&flagInfo, "info", false, flagInfoHelp)
@@ -57,6 +61,7 @@ func Parse() {
flag.StringVar(&flagConfigFile, "config-file", "", flagConfigFileHelp)
flag.StringVar(&flagConfigFile, "c", "", flagConfigFileHelp)
flag.BoolVar(&flagConfigDump, "config-dump", false, flagConfigDumpHelp)
flag.StringVar(&flagHealthCheck, "healthcheck", "", flagHealthCheckHelp)
flag.Parse()
cfg := config.NewParser()
@@ -86,6 +91,11 @@ func Parse() {
logger.EnableDebug()
}
if flagHealthCheck != "" {
doHealthCheck(flagHealthCheck)
return
}
if flagInfo {
info()
return
@@ -100,22 +110,47 @@ func Parse() {
logger.Info("The default value for DATABASE_URL is used")
}
logger.Debug("Loading translations...")
if err := locale.LoadCatalogMessages(); err != nil {
logger.Fatal("Unable to load translations: %v", err)
}
logger.Debug("Loading static assets...")
if err := static.CalculateBinaryFileChecksums(); err != nil {
logger.Fatal("Unable to calculate binary files checksum: %v", err)
}
if err := static.GenerateStylesheetsBundles(); err != nil {
logger.Fatal("Unable to generate Stylesheet bundles: %v", err)
}
if err := static.GenerateJavascriptBundles(); err != nil {
logger.Fatal("Unable to generate Javascript bundles: %v", err)
}
db, err := database.NewConnectionPool(
config.Opts.DatabaseURL(),
config.Opts.DatabaseMinConns(),
config.Opts.DatabaseMaxConns(),
config.Opts.DatabaseConnectionLifetime(),
)
if err != nil {
logger.Fatal("Unable to connect to the database: %v", err)
logger.Fatal("Unable to initialize database connection pool: %v", err)
}
defer db.Close()
if flagMigrate {
database.Migrate(db)
return
store := storage.NewStorage(db)
if err := store.Ping(); err != nil {
logger.Fatal("Unable to connect to the database: %v", err)
}
store := storage.NewStorage(db)
if flagMigrate {
if err := database.Migrate(db); err != nil {
logger.Fatal(`%v`, err)
}
return
}
if flagResetFeedErrors {
store.ResetFeedErrors()
@@ -137,7 +172,7 @@ func Parse() {
return
}
// Run migrations and start the deamon.
// Run migrations and start the daemon.
if config.Opts.RunMigrations() {
if err := database.Migrate(db); err != nil {
logger.Fatal(`%v`, err)
@@ -148,7 +183,7 @@ func Parse() {
logger.Fatal(`You must run the SQL migrations, %v`, err)
}
// Create admin user and start the deamon.
// Create admin user and start the daemon.
if config.Opts.CreateAdmin() {
createAdmin(store)
}
+32
View File
@@ -18,6 +18,7 @@ import (
"miniflux.app/service/httpd"
"miniflux.app/service/scheduler"
"miniflux.app/storage"
"miniflux.app/systemd"
"miniflux.app/worker"
)
@@ -44,6 +45,37 @@ func startDaemon(store *storage.Storage) {
go collector.GatherStorageMetrics()
}
if systemd.HasNotifySocket() {
logger.Info("Sending readiness notification to Systemd")
if err := systemd.SdNotify(systemd.SdNotifyReady); err != nil {
logger.Error("Unable to send readiness notification to systemd: %v", err)
}
if config.Opts.HasWatchdog() && systemd.HasSystemdWatchdog() {
logger.Info("Activating Systemd watchdog")
go func() {
interval, err := systemd.WatchdogInterval()
if err != nil {
logger.Error("Unable to parse watchdog interval from systemd: %v", err)
return
}
for {
err := store.Ping()
if err != nil {
logger.Error(`Systemd Watchdog: %v`, err)
} else {
systemd.SdNotify(systemd.SdNotifyWatchdog)
}
time.Sleep(interval / 3)
}
}()
}
}
<-stop
logger.Info("Shutting down the process...")
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+34
View File
@@ -0,0 +1,34 @@
// Copyright 2021 Frédéric Guillot. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.
package cli // import "miniflux.app/cli"
import (
"net/http"
"time"
"miniflux.app/config"
"miniflux.app/logger"
)
func doHealthCheck(healthCheckEndpoint string) {
if healthCheckEndpoint == "auto" {
healthCheckEndpoint = "http://" + config.Opts.ListenAddr() + config.Opts.BasePath() + "/healthcheck"
}
logger.Debug(`Executing health check on %s`, healthCheckEndpoint)
client := &http.Client{Timeout: 3 * time.Second}
resp, err := client.Get(healthCheckEndpoint)
if err != nil {
logger.Fatal(`Health check failure: %v`, err)
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
logger.Fatal(`Health check failed with status code %d`, resp.StatusCode)
}
logger.Debug(`Health check is OK`)
}
+1
View File
@@ -34,6 +34,7 @@ func resetPassword(store *storage.Storage) {
os.Exit(1)
}
user.Password = password
if err := store.UpdateUser(user); err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
os.Exit(1)
+2 -2
View File
@@ -20,7 +20,7 @@ package main
import (
"fmt"
"io/ioutil"
"os"
miniflux "miniflux.app/client"
)
@@ -47,7 +47,7 @@ func main() {
return
}
err = ioutil.WriteFile("opml.xml", opml, 0644)
err = os.WriteFile("opml.xml", opml, 0644)
if err != nil {
fmt.Println(err)
return
+22 -6
View File
@@ -8,7 +8,6 @@ import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/url"
"strconv"
)
@@ -223,7 +222,7 @@ func (c *Client) MarkCategoryAsRead(categoryID int64) error {
return err
}
// CategoryFeeds gets feeds of a cateogry.
// CategoryFeeds gets feeds of a category.
func (c *Client) CategoryFeeds(categoryID int64) (Feeds, error) {
body, err := c.request.Get(fmt.Sprintf("/v1/categories/%d/feeds", categoryID))
if err != nil {
@@ -270,7 +269,7 @@ func (c *Client) Export() ([]byte, error) {
}
defer body.Close()
opml, err := ioutil.ReadAll(body)
opml, err := io.ReadAll(body)
if err != nil {
return nil, err
}
@@ -346,7 +345,7 @@ func (c *Client) MarkFeedAsRead(feedID int64) error {
// RefreshAllFeeds refreshes all feeds.
func (c *Client) RefreshAllFeeds() error {
_, err := c.request.Put(fmt.Sprintf("/v1/feeds/refresh"), nil)
_, err := c.request.Put("/v1/feeds/refresh", nil)
return err
}
@@ -503,6 +502,23 @@ func (c *Client) ToggleBookmark(entryID int64) error {
return err
}
// FetchCounters
func (c *Client) FetchCounters() (*FeedCounters, error) {
body, err := c.request.Get("/v1/feeds/counters")
if err != nil {
return nil, err
}
defer body.Close()
var result FeedCounters
decoder := json.NewDecoder(body)
if err := decoder.Decode(&result); err != nil {
return nil, fmt.Errorf("miniflux: response error (%v)", err)
}
return &result, nil
}
func buildFilterQueryString(path string, filter *Filter) string {
if filter != nil {
values := url.Values{}
@@ -543,8 +559,8 @@ func buildFilterQueryString(path string, filter *Filter) string {
values.Set("before_entry_id", strconv.FormatInt(filter.BeforeEntryID, 10))
}
if filter.Starred {
values.Set("starred", "1")
if filter.Starred != "" {
values.Set("starred", filter.Starred)
}
if filter.Search != "" {
+76 -51
View File
@@ -26,6 +26,7 @@ type User struct {
Language string `json:"language"`
Timezone string `json:"timezone"`
EntryDirection string `json:"entry_sorting_direction"`
EntryOrder string `json:"entry_sorting_order"`
Stylesheet string `json:"stylesheet"`
GoogleID string `json:"google_id"`
OpenIDConnectID string `json:"openid_connect_id"`
@@ -34,6 +35,7 @@ type User struct {
ShowReadingTime bool `json:"show_reading_time"`
EntrySwipe bool `json:"entry_swipe"`
LastLoginAt *time.Time `json:"last_login_at"`
DisplayMode string `json:"display_mode"`
}
func (u User) String() string {
@@ -58,6 +60,7 @@ type UserModificationRequest struct {
Language *string `json:"language"`
Timezone *string `json:"timezone"`
EntryDirection *string `json:"entry_sorting_direction"`
EntryOrder *string `json:"entry_sorting_order"`
Stylesheet *string `json:"stylesheet"`
GoogleID *string `json:"google_id"`
OpenIDConnectID *string `json:"openid_connect_id"`
@@ -65,6 +68,7 @@ type UserModificationRequest struct {
KeyboardShortcuts *bool `json:"keyboard_shortcuts"`
ShowReadingTime *bool `json:"show_reading_time"`
EntrySwipe *bool `json:"entry_swipe"`
DisplayMode *string `json:"display_mode"`
}
// Users represents a list of users.
@@ -100,64 +104,73 @@ type Subscriptions []*Subscription
// Feed represents a Miniflux feed.
type Feed struct {
ID int64 `json:"id"`
UserID int64 `json:"user_id"`
FeedURL string `json:"feed_url"`
SiteURL string `json:"site_url"`
Title string `json:"title"`
CheckedAt time.Time `json:"checked_at,omitempty"`
EtagHeader string `json:"etag_header,omitempty"`
LastModifiedHeader string `json:"last_modified_header,omitempty"`
ParsingErrorMsg string `json:"parsing_error_message,omitempty"`
ParsingErrorCount int `json:"parsing_error_count,omitempty"`
Disabled bool `json:"disabled"`
IgnoreHTTPCache bool `json:"ignore_http_cache"`
FetchViaProxy bool `json:"fetch_via_proxy"`
ScraperRules string `json:"scraper_rules"`
RewriteRules string `json:"rewrite_rules"`
BlocklistRules string `json:"blocklist_rules"`
KeeplistRules string `json:"keeplist_rules"`
Crawler bool `json:"crawler"`
UserAgent string `json:"user_agent"`
Username string `json:"username"`
Password string `json:"password"`
Category *Category `json:"category,omitempty"`
ID int64 `json:"id"`
UserID int64 `json:"user_id"`
FeedURL string `json:"feed_url"`
SiteURL string `json:"site_url"`
Title string `json:"title"`
CheckedAt time.Time `json:"checked_at,omitempty"`
EtagHeader string `json:"etag_header,omitempty"`
LastModifiedHeader string `json:"last_modified_header,omitempty"`
ParsingErrorMsg string `json:"parsing_error_message,omitempty"`
ParsingErrorCount int `json:"parsing_error_count,omitempty"`
Disabled bool `json:"disabled"`
IgnoreHTTPCache bool `json:"ignore_http_cache"`
AllowSelfSignedCertificates bool `json:"allow_self_signed_certificates"`
FetchViaProxy bool `json:"fetch_via_proxy"`
ScraperRules string `json:"scraper_rules"`
RewriteRules string `json:"rewrite_rules"`
BlocklistRules string `json:"blocklist_rules"`
KeeplistRules string `json:"keeplist_rules"`
Crawler bool `json:"crawler"`
UserAgent string `json:"user_agent"`
Cookie string `json:"cookie"`
Username string `json:"username"`
Password string `json:"password"`
Category *Category `json:"category,omitempty"`
HideGlobally bool `json:"hide_globally"`
}
// FeedCreationRequest represents the request to create a feed.
type FeedCreationRequest struct {
FeedURL string `json:"feed_url"`
CategoryID int64 `json:"category_id"`
UserAgent string `json:"user_agent"`
Username string `json:"username"`
Password string `json:"password"`
Crawler bool `json:"crawler"`
Disabled bool `json:"disabled"`
IgnoreHTTPCache bool `json:"ignore_http_cache"`
FetchViaProxy bool `json:"fetch_via_proxy"`
ScraperRules string `json:"scraper_rules"`
RewriteRules string `json:"rewrite_rules"`
BlocklistRules string `json:"blocklist_rules"`
KeeplistRules string `json:"keeplist_rules"`
FeedURL string `json:"feed_url"`
CategoryID int64 `json:"category_id"`
UserAgent string `json:"user_agent"`
Cookie string `json:"cookie"`
Username string `json:"username"`
Password string `json:"password"`
Crawler bool `json:"crawler"`
Disabled bool `json:"disabled"`
IgnoreHTTPCache bool `json:"ignore_http_cache"`
AllowSelfSignedCertificates bool `json:"allow_self_signed_certificates"`
FetchViaProxy bool `json:"fetch_via_proxy"`
ScraperRules string `json:"scraper_rules"`
RewriteRules string `json:"rewrite_rules"`
BlocklistRules string `json:"blocklist_rules"`
KeeplistRules string `json:"keeplist_rules"`
HideGlobally bool `json:"hide_globally"`
}
// FeedModificationRequest represents the request to update a feed.
type FeedModificationRequest struct {
FeedURL *string `json:"feed_url"`
SiteURL *string `json:"site_url"`
Title *string `json:"title"`
ScraperRules *string `json:"scraper_rules"`
RewriteRules *string `json:"rewrite_rules"`
BlocklistRules *string `json:"blocklist_rules"`
KeeplistRules *string `json:"keeplist_rules"`
Crawler *bool `json:"crawler"`
UserAgent *string `json:"user_agent"`
Username *string `json:"username"`
Password *string `json:"password"`
CategoryID *int64 `json:"category_id"`
Disabled *bool `json:"disabled"`
IgnoreHTTPCache *bool `json:"ignore_http_cache"`
FetchViaProxy *bool `json:"fetch_via_proxy"`
FeedURL *string `json:"feed_url"`
SiteURL *string `json:"site_url"`
Title *string `json:"title"`
ScraperRules *string `json:"scraper_rules"`
RewriteRules *string `json:"rewrite_rules"`
BlocklistRules *string `json:"blocklist_rules"`
KeeplistRules *string `json:"keeplist_rules"`
Crawler *bool `json:"crawler"`
UserAgent *string `json:"user_agent"`
Cookie *string `json:"cookie"`
Username *string `json:"username"`
Password *string `json:"password"`
CategoryID *int64 `json:"category_id"`
Disabled *bool `json:"disabled"`
IgnoreHTTPCache *bool `json:"ignore_http_cache"`
AllowSelfSignedCertificates *bool `json:"allow_self_signed_certificates"`
FetchViaProxy *bool `json:"fetch_via_proxy"`
HideGlobally *bool `json:"hide_globally"`
}
// FeedIcon represents the feed icon.
@@ -167,6 +180,11 @@ type FeedIcon struct {
Data string `json:"data"`
}
type FeedCounters struct {
ReadCounters map[int64]int `json:"reads"`
UnreadCounters map[int64]int `json:"unreads"`
}
// Feeds represents a list of feeds.
type Feeds []*Feed
@@ -179,8 +197,10 @@ type Entry struct {
Hash string `json:"hash"`
Title string `json:"title"`
URL string `json:"url"`
CommentsURL string `json:"comments_url"`
Date time.Time `json:"published_at"`
CreatedAt time.Time `json:"created_at"`
ChangedAt time.Time `json:"changed_at"`
Content string `json:"content"`
Author string `json:"author"`
ShareCode string `json:"share_code"`
@@ -206,6 +226,11 @@ type Enclosure struct {
// Enclosures represents a list of attachments.
type Enclosures []*Enclosure
const (
FilterNotStarred = "0"
FilterOnlyStarred = "1"
)
// Filter is used to filter entries.
type Filter struct {
Status string
@@ -213,7 +238,7 @@ type Filter struct {
Limit int
Order string
Direction string
Starred bool
Starred string
Before int64
After int64
BeforeEntryID int64
+12 -6
View File
@@ -10,7 +10,6 @@ import (
"errors"
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"net/url"
@@ -83,11 +82,11 @@ func (r *request) execute(method, path string, data interface{}) (io.ReadCloser,
}
if data != nil {
switch data.(type) {
switch data := data.(type) {
case io.ReadCloser:
request.Body = data.(io.ReadCloser)
request.Body = data
default:
request.Body = ioutil.NopCloser(bytes.NewBuffer(r.toJSON(data)))
request.Body = io.NopCloser(bytes.NewBuffer(r.toJSON(data)))
}
}
@@ -105,8 +104,15 @@ func (r *request) execute(method, path string, data interface{}) (io.ReadCloser,
response.Body.Close()
return nil, ErrForbidden
case http.StatusInternalServerError:
response.Body.Close()
return nil, ErrServerError
defer response.Body.Close()
var resp errorResponse
decoder := json.NewDecoder(response.Body)
// If we failed to decode, just return a generic ErrServerError
if err := decoder.Decode(&resp); err != nil {
return nil, ErrServerError
}
return nil, errors.New("miniflux: internal server error: " + resp.ErrorMessage)
case http.StatusNotFound:
response.Body.Close()
return nil, ErrNotFound
+54 -2
View File
@@ -5,7 +5,6 @@
package config // import "miniflux.app/config"
import (
"io/ioutil"
"os"
"testing"
)
@@ -1309,7 +1308,7 @@ DEBUG = yes
Invalid text
`)
tmpfile, err := ioutil.TempFile(".", "miniflux.*.unit_test.conf")
tmpfile, err := os.CreateTemp(".", "miniflux.*.unit_test.conf")
if err != nil {
t.Fatal(err)
}
@@ -1414,3 +1413,56 @@ func TestAuthProxyUserCreationAdmin(t *testing.T) {
t.Fatalf(`Unexpected AUTH_PROXY_USER_CREATION value, got %v instead of %v`, result, expected)
}
}
func TestFetchYouTubeWatchTime(t *testing.T) {
os.Clearenv()
os.Setenv("FETCH_YOUTUBE_WATCH_TIME", "1")
parser := NewParser()
opts, err := parser.ParseEnvironmentVariables()
if err != nil {
t.Fatalf(`Parsing failure: %v`, err)
}
expected := true
result := opts.FetchYouTubeWatchTime()
if result != expected {
t.Fatalf(`Unexpected FETCH_YOUTUBE_WATCH_TIME value, got %v instead of %v`, result, expected)
}
}
func TestParseConfigDumpOutput(t *testing.T) {
os.Clearenv()
wantOpts := NewOptions()
wantOpts.adminUsername = "my-username"
serialized := wantOpts.String()
tmpfile, err := os.CreateTemp(".", "miniflux.*.unit_test.conf")
if err != nil {
t.Fatal(err)
}
if _, err := tmpfile.Write([]byte(serialized)); err != nil {
t.Fatal(err)
}
parser := NewParser()
parsedOpts, err := parser.ParseFile(tmpfile.Name())
if err != nil {
t.Errorf(`Parsing failure: %v`, err)
}
if parsedOpts.AdminUsername() != wantOpts.AdminUsername() {
t.Fatalf(`Unexpected ADMIN_USERNAME value, got %q instead of %q`, parsedOpts.AdminUsername(), wantOpts.AdminUsername())
}
if err := tmpfile.Close(); err != nil {
t.Fatal(err)
}
if err := os.Remove(tmpfile.Name()); err != nil {
t.Fatal(err)
}
}
+66 -10
View File
@@ -8,6 +8,7 @@ import (
"fmt"
"sort"
"strings"
"time"
"miniflux.app/version"
)
@@ -25,7 +26,7 @@ const (
defaultBasePath = ""
defaultWorkerPoolSize = 5
defaultPollingFrequency = 60
defaultBatchSize = 10
defaultBatchSize = 100
defaultPollingScheduler = "round_robin"
defaultSchedulerEntryFrequencyMinInterval = 5
defaultSchedulerEntryFrequencyMaxInterval = 24 * 60
@@ -34,6 +35,7 @@ const (
defaultDatabaseURL = "user=postgres password=postgres dbname=miniflux2 sslmode=disable"
defaultDatabaseMaxConns = 20
defaultDatabaseMinConns = 1
defaultDatabaseConnectionLifetime = 5
defaultListenAddr = "127.0.0.1:8080"
defaultCertFile = ""
defaultKeyFile = ""
@@ -41,8 +43,10 @@ const (
defaultCleanupFrequencyHours = 24
defaultCleanupArchiveReadDays = 60
defaultCleanupArchiveUnreadDays = 180
defaultCleanupArchiveBatchSize = 10000
defaultCleanupRemoveSessionsDays = 30
defaultProxyImages = "http-only"
defaultFetchYouTubeWatchTime = false
defaultCreateAdmin = false
defaultAdminUsername = ""
defaultAdminPassword = ""
@@ -63,6 +67,8 @@ const (
defaultMetricsCollector = false
defaultMetricsRefreshInterval = 60
defaultMetricsAllowedNetworks = "127.0.0.1/8"
defaultWatchdog = true
defaultInvidiousInstance = "yewtu.be"
)
var defaultHTTPClientUserAgent = "Mozilla/5.0 (compatible; Miniflux/" + version.Version + "; +https://miniflux.app)"
@@ -88,6 +94,7 @@ type Options struct {
databaseURL string
databaseMaxConns int
databaseMinConns int
databaseConnectionLifetime int
runMigrations bool
listenAddr string
certFile string
@@ -96,6 +103,7 @@ type Options struct {
cleanupFrequencyHours int
cleanupArchiveReadDays int
cleanupArchiveUnreadDays int
cleanupArchiveBatchSize int
cleanupRemoveSessionsDays int
pollingFrequency int
batchSize int
@@ -108,6 +116,7 @@ type Options struct {
adminUsername string
adminPassword string
proxyImages string
fetchYouTubeWatchTime bool
oauth2UserCreationAllowed bool
oauth2ClientID string
oauth2ClientSecret string
@@ -126,6 +135,8 @@ type Options struct {
metricsCollector bool
metricsRefreshInterval int
metricsAllowedNetworks []string
watchdog bool
invidiousInstance string
}
// NewOptions returns Options with default values.
@@ -144,6 +155,7 @@ func NewOptions() *Options {
databaseURL: defaultDatabaseURL,
databaseMaxConns: defaultDatabaseMaxConns,
databaseMinConns: defaultDatabaseMinConns,
databaseConnectionLifetime: defaultDatabaseConnectionLifetime,
runMigrations: defaultRunMigrations,
listenAddr: defaultListenAddr,
certFile: defaultCertFile,
@@ -152,6 +164,7 @@ func NewOptions() *Options {
cleanupFrequencyHours: defaultCleanupFrequencyHours,
cleanupArchiveReadDays: defaultCleanupArchiveReadDays,
cleanupArchiveUnreadDays: defaultCleanupArchiveUnreadDays,
cleanupArchiveBatchSize: defaultCleanupArchiveBatchSize,
cleanupRemoveSessionsDays: defaultCleanupRemoveSessionsDays,
pollingFrequency: defaultPollingFrequency,
batchSize: defaultBatchSize,
@@ -162,6 +175,7 @@ func NewOptions() *Options {
workerPoolSize: defaultWorkerPoolSize,
createAdmin: defaultCreateAdmin,
proxyImages: defaultProxyImages,
fetchYouTubeWatchTime: defaultFetchYouTubeWatchTime,
oauth2UserCreationAllowed: defaultOAuth2UserCreation,
oauth2ClientID: defaultOAuth2ClientID,
oauth2ClientSecret: defaultOAuth2ClientSecret,
@@ -180,6 +194,8 @@ func NewOptions() *Options {
metricsCollector: defaultMetricsCollector,
metricsRefreshInterval: defaultMetricsRefreshInterval,
metricsAllowedNetworks: []string{defaultMetricsAllowedNetworks},
watchdog: defaultWatchdog,
invidiousInstance: defaultInvidiousInstance,
}
}
@@ -243,6 +259,11 @@ func (o *Options) DatabaseMinConns() int {
return o.databaseMinConns
}
// DatabaseConnectionLifetime returns the maximum amount of time a connection may be reused.
func (o *Options) DatabaseConnectionLifetime() time.Duration {
return time.Duration(o.databaseConnectionLifetime) * time.Minute
}
// ListenAddr returns the listen address for the HTTP server.
func (o *Options) ListenAddr() string {
return o.listenAddr
@@ -278,6 +299,11 @@ func (o *Options) CleanupArchiveUnreadDays() int {
return o.cleanupArchiveUnreadDays
}
// CleanupArchiveBatchSize returns the number of entries to archive for each interval.
func (o *Options) CleanupArchiveBatchSize() int {
return o.cleanupArchiveBatchSize
}
// CleanupRemoveSessionsDays returns the number of days after which to remove sessions.
func (o *Options) CleanupRemoveSessionsDays() int {
return o.cleanupRemoveSessionsDays
@@ -373,6 +399,12 @@ func (o *Options) AdminPassword() string {
return o.adminPassword
}
// FetchYouTubeWatchTime returns true if the YouTube video duration
// should be fetched and used as a reading time.
func (o *Options) FetchYouTubeWatchTime() bool {
return o.fetchYouTubeWatchTime
}
// ProxyImages returns "none" to never proxy, "http-only" to proxy non-HTTPS, "all" to always proxy.
func (o *Options) ProxyImages() string {
return o.proxyImages
@@ -448,10 +480,20 @@ func (o *Options) HTTPClientUserAgent() string {
return o.httpClientUserAgent
}
// HasWatchdog returns true if the systemd watchdog is enabled.
func (o *Options) HasWatchdog() bool {
return o.watchdog
}
// InvidiousInstance returns the invidious instance used by miniflux
func (o *Options) InvidiousInstance() string {
return o.invidiousInstance
}
// SortedOptions returns options as a list of key value pairs, sorted by keys.
func (o *Options) SortedOptions() []*Option {
func (o *Options) SortedOptions(redactSecret bool) []*Option {
var keyValues = map[string]interface{}{
"ADMIN_PASSWORD": o.adminPassword,
"ADMIN_PASSWORD": redactSecretValue(o.adminPassword, redactSecret),
"ADMIN_USERNAME": o.adminUsername,
"AUTH_PROXY_HEADER": o.authProxyHeader,
"AUTH_PROXY_USER_CREATION": o.authProxyUserCreation,
@@ -462,14 +504,19 @@ func (o *Options) SortedOptions() []*Option {
"CERT_FILE": o.certFile,
"CLEANUP_ARCHIVE_READ_DAYS": o.cleanupArchiveReadDays,
"CLEANUP_ARCHIVE_UNREAD_DAYS": o.cleanupArchiveUnreadDays,
"CLEANUP_ARCHIVE_BATCH_SIZE": o.cleanupArchiveBatchSize,
"CLEANUP_FREQUENCY_HOURS": o.cleanupFrequencyHours,
"CLEANUP_REMOVE_SESSIONS_DAYS": o.cleanupRemoveSessionsDays,
"CREATE_ADMIN": o.createAdmin,
"DATABASE_MAX_CONNS": o.databaseMaxConns,
"DATABASE_MIN_CONNS": o.databaseMinConns,
"DATABASE_URL": o.databaseURL,
"DATABASE_CONNECTION_LIFETIME": o.databaseConnectionLifetime,
"DATABASE_URL": redactSecretValue(o.databaseURL, redactSecret),
"DEBUG": o.debug,
"HSTS": o.hsts,
"DISABLE_HSTS": !o.hsts,
"DISABLE_SCHEDULER_SERVICE": !o.schedulerService,
"DISABLE_HTTP_SERVICE": !o.httpService,
"FETCH_YOUTUBE_WATCH_TIME": o.fetchYouTubeWatchTime,
"HTTPS": o.HTTPS,
"HTTP_CLIENT_MAX_BODY_SIZE": o.httpClientMaxBodySize,
"HTTP_CLIENT_PROXY": o.httpClientProxy,
@@ -477,20 +524,21 @@ func (o *Options) SortedOptions() []*Option {
"HTTP_CLIENT_USER_AGENT": o.httpClientUserAgent,
"HTTP_SERVICE": o.httpService,
"KEY_FILE": o.certKeyFile,
"INVIDIOUS_INSTANCE": o.invidiousInstance,
"LISTEN_ADDR": o.listenAddr,
"LOG_DATE_TIME": o.logDateTime,
"MAINTENANCE_MESSAGE": o.maintenanceMessage,
"MAINTENANCE_MODE": o.maintenanceMode,
"METRICS_ALLOWED_NETWORKS": o.metricsAllowedNetworks,
"METRICS_ALLOWED_NETWORKS": strings.Join(o.metricsAllowedNetworks, ","),
"METRICS_COLLECTOR": o.metricsCollector,
"METRICS_REFRESH_INTERVAL": o.metricsRefreshInterval,
"OAUTH2_CLIENT_ID": o.oauth2ClientID,
"OAUTH2_CLIENT_SECRET": o.oauth2ClientSecret,
"OAUTH2_CLIENT_SECRET": redactSecretValue(o.oauth2ClientSecret, redactSecret),
"OAUTH2_OIDC_DISCOVERY_ENDPOINT": o.oauth2OidcDiscoveryEndpoint,
"OAUTH2_PROVIDER": o.oauth2Provider,
"OAUTH2_REDIRECT_URL": o.oauth2RedirectURL,
"OAUTH2_USER_CREATION": o.oauth2UserCreationAllowed,
"POCKET_CONSUMER_KEY": o.pocketConsumerKey,
"POCKET_CONSUMER_KEY": redactSecretValue(o.pocketConsumerKey, redactSecret),
"POLLING_FREQUENCY": o.pollingFrequency,
"POLLING_PARSING_ERROR_LIMIT": o.pollingParsingErrorLimit,
"POLLING_SCHEDULER": o.pollingScheduler,
@@ -502,6 +550,7 @@ func (o *Options) SortedOptions() []*Option {
"SCHEDULER_SERVICE": o.schedulerService,
"SERVER_TIMING_HEADER": o.serverTimingHeader,
"WORKER_POOL_SIZE": o.workerPoolSize,
"WATCHDOG": o.watchdog,
}
keys := make([]string, 0, len(keyValues))
@@ -520,9 +569,16 @@ func (o *Options) SortedOptions() []*Option {
func (o *Options) String() string {
var builder strings.Builder
for _, option := range o.SortedOptions() {
builder.WriteString(fmt.Sprintf("%s: %v\n", option.Key, option.Value))
for _, option := range o.SortedOptions(false) {
fmt.Fprintf(&builder, "%s=%v\n", option.Key, option.Value)
}
return builder.String()
}
func redactSecretValue(value string, redactSecret bool) string {
if redactSecret && value != "" {
return "******"
}
return value
}
+13 -4
View File
@@ -10,7 +10,6 @@ import (
"errors"
"fmt"
"io"
"io/ioutil"
url_parser "net/url"
"os"
"strconv"
@@ -96,6 +95,8 @@ func (p *Parser) parseLines(lines []string) (err error) {
p.opts.databaseMaxConns = parseInt(value, defaultDatabaseMaxConns)
case "DATABASE_MIN_CONNS":
p.opts.databaseMinConns = parseInt(value, defaultDatabaseMinConns)
case "DATABASE_CONNECTION_LIFETIME":
p.opts.databaseConnectionLifetime = parseInt(value, defaultDatabaseConnectionLifetime)
case "RUN_MIGRATIONS":
p.opts.runMigrations = parseBool(value, defaultRunMigrations)
case "DISABLE_HSTS":
@@ -118,6 +119,8 @@ func (p *Parser) parseLines(lines []string) (err error) {
p.opts.cleanupArchiveReadDays = parseInt(value, defaultCleanupArchiveReadDays)
case "CLEANUP_ARCHIVE_UNREAD_DAYS":
p.opts.cleanupArchiveUnreadDays = parseInt(value, defaultCleanupArchiveUnreadDays)
case "CLEANUP_ARCHIVE_BATCH_SIZE":
p.opts.cleanupArchiveBatchSize = parseInt(value, defaultCleanupArchiveBatchSize)
case "CLEANUP_REMOVE_SESSIONS_DAYS":
p.opts.cleanupRemoveSessionsDays = parseInt(value, defaultCleanupRemoveSessionsDays)
case "WORKER_POOL_SIZE":
@@ -188,6 +191,12 @@ func (p *Parser) parseLines(lines []string) (err error) {
p.opts.metricsRefreshInterval = parseInt(value, defaultMetricsRefreshInterval)
case "METRICS_ALLOWED_NETWORKS":
p.opts.metricsAllowedNetworks = parseStringList(value, []string{defaultMetricsAllowedNetworks})
case "FETCH_YOUTUBE_WATCH_TIME":
p.opts.fetchYouTubeWatchTime = parseBool(value, defaultFetchYouTubeWatchTime)
case "WATCHDOG":
p.opts.watchdog = parseBool(value, defaultWatchdog)
case "INVIDIOUS_INSTANCE":
p.opts.invidiousInstance = parseString(value, defaultInvidiousInstance)
}
}
@@ -208,12 +217,12 @@ func parseBaseURL(value string) (string, string, string, error) {
url, err := url_parser.Parse(value)
if err != nil {
return "", "", "", fmt.Errorf("Invalid BASE_URL: %v", err)
return "", "", "", fmt.Errorf("config: invalid BASE_URL: %w", err)
}
scheme := strings.ToLower(url.Scheme)
if scheme != "https" && scheme != "http" {
return "", "", "", errors.New("Invalid BASE_URL: scheme must be http or https")
return "", "", "", errors.New("config: invalid BASE_URL: scheme must be http or https")
}
basePath := url.Path
@@ -269,7 +278,7 @@ func parseStringList(value string, fallback []string) []string {
}
func readSecretFile(filename, fallback string) string {
data, err := ioutil.ReadFile(filename)
data, err := os.ReadFile(filename)
if err != nil {
return fallback
}
+12 -3
View File
@@ -1,8 +1,9 @@
version: '3.3'
version: '3.4'
services:
miniflux:
image: miniflux/miniflux:latest
image: ${MINIFLUX_IMAGE:-miniflux/miniflux:latest}
container_name: miniflux
restart: always
ports:
- "80:8080"
depends_on:
@@ -13,6 +14,10 @@ services:
- CREATE_ADMIN=1
- ADMIN_USERNAME=admin
- ADMIN_PASSWORD=test123
- DEBUG=1
# Optional health check:
# healthcheck:
# test: ["CMD", "/usr/bin/miniflux", "-healthcheck", "auto"]
db:
image: postgres:latest
container_name: postgres
@@ -21,5 +26,9 @@ services:
- POSTGRES_PASSWORD=secret
volumes:
- miniflux-db:/var/lib/postgresql/data
healthcheck:
test: ["CMD", "pg_isready", "-U", "miniflux"]
interval: 10s
start_period: 30s
volumes:
miniflux-db:
miniflux-db:
+6 -2
View File
@@ -1,4 +1,4 @@
version: '3.3'
version: '3.4'
services:
caddy:
image: caddy:2
@@ -13,7 +13,7 @@ services:
- caddy_data:/data
- caddy_config:/config
miniflux:
image: miniflux/miniflux:latest
image: ${MINIFLUX_IMAGE:-miniflux/miniflux:latest}
container_name: miniflux
depends_on:
- db
@@ -32,6 +32,10 @@ services:
- POSTGRES_PASSWORD=secret
volumes:
- miniflux-db:/var/lib/postgresql/data
healthcheck:
test: ["CMD", "pg_isready", "-U", "miniflux"]
interval: 10s
start_period: 30s
volumes:
miniflux-db:
caddy_data:
+6 -2
View File
@@ -1,4 +1,4 @@
version: '3.3'
version: '3.4'
services:
traefik:
image: "traefik:v2.3"
@@ -18,7 +18,7 @@ services:
- "./letsencrypt:/letsencrypt"
- "/var/run/docker.sock:/var/run/docker.sock:ro"
miniflux:
image: miniflux/miniflux:latest
image: ${MINIFLUX_IMAGE:-miniflux/miniflux:latest}
container_name: miniflux
depends_on:
- db
@@ -44,5 +44,9 @@ services:
- POSTGRES_PASSWORD=secret
volumes:
- miniflux-db:/var/lib/postgresql/data
healthcheck:
test: ["CMD", "pg_isready", "-U", "miniflux"]
interval: 10s
start_period: 30s
volumes:
miniflux-db:
+3 -1
View File
@@ -7,13 +7,14 @@ package database // import "miniflux.app/database"
import (
"database/sql"
"fmt"
"time"
// Postgresql driver import
_ "github.com/lib/pq"
)
// NewConnectionPool configures the database connection pool.
func NewConnectionPool(dsn string, minConnections, maxConnections int) (*sql.DB, error) {
func NewConnectionPool(dsn string, minConnections, maxConnections int, connectionLifetime time.Duration) (*sql.DB, error) {
db, err := sql.Open("postgres", dsn)
if err != nil {
return nil, err
@@ -21,6 +22,7 @@ func NewConnectionPool(dsn string, minConnections, maxConnections int) (*sql.DB,
db.SetMaxOpenConns(maxConnections)
db.SetMaxIdleConns(minConnections)
db.SetConnMaxLifetime(connectionLifetime)
return db, nil
}
+89 -12
View File
@@ -10,13 +10,14 @@ import (
var schemaVersion = len(migrations)
// Order is important. Add new migrations at the end of the list.
var migrations = []func(tx *sql.Tx) error{
func(tx *sql.Tx) (err error) {
sql := `
CREATE TABLE schema_version (
version text not null
);
CREATE TABLE users (
id serial not null,
username text not null unique,
@@ -28,7 +29,7 @@ var migrations = []func(tx *sql.Tx) error{
last_login_at timestamp with time zone,
primary key (id)
);
CREATE TABLE sessions (
id serial not null,
user_id int not null,
@@ -40,7 +41,7 @@ var migrations = []func(tx *sql.Tx) error{
unique (user_id, token),
foreign key (user_id) references users(id) on delete cascade
);
CREATE TABLE categories (
id serial not null,
user_id int not null,
@@ -49,7 +50,7 @@ var migrations = []func(tx *sql.Tx) error{
unique (user_id, title),
foreign key (user_id) references users(id) on delete cascade
);
CREATE TABLE feeds (
id bigserial not null,
user_id int not null,
@@ -67,9 +68,9 @@ var migrations = []func(tx *sql.Tx) error{
foreign key (user_id) references users(id) on delete cascade,
foreign key (category_id) references categories(id) on delete cascade
);
CREATE TYPE entry_status as enum('unread', 'read', 'removed');
CREATE TABLE entries (
id bigserial not null,
user_id int not null,
@@ -86,9 +87,9 @@ var migrations = []func(tx *sql.Tx) error{
foreign key (user_id) references users(id) on delete cascade,
foreign key (feed_id) references feeds(id) on delete cascade
);
CREATE INDEX entries_feed_idx on entries using btree(feed_id);
CREATE TABLE enclosures (
id bigserial not null,
user_id int not null,
@@ -100,7 +101,7 @@ var migrations = []func(tx *sql.Tx) error{
foreign key (user_id) references users(id) on delete cascade,
foreign key (entry_id) references entries(id) on delete cascade
);
CREATE TABLE icons (
id bigserial not null,
hash text not null unique,
@@ -108,14 +109,14 @@ var migrations = []func(tx *sql.Tx) error{
content bytea not null,
primary key (id)
);
CREATE TABLE feed_icons (
feed_id bigint not null,
icon_id bigint not null,
primary key(feed_id, icon_id),
foreign key (feed_id) references feeds(id) on delete cascade,
foreign key (icon_id) references icons(id) on delete cascade
);
);
`
_, err = tx.Exec(sql)
return err
@@ -409,7 +410,7 @@ var migrations = []func(tx *sql.Tx) error{
},
func(tx *sql.Tx) (err error) {
sql := `
ALTER TABLE feeds
ALTER TABLE feeds
ADD COLUMN blocklist_rules text not null default '',
ADD COLUMN keeplist_rules text not null default ''
`
@@ -514,4 +515,80 @@ var migrations = []func(tx *sql.Tx) error{
`)
return err
},
func(tx *sql.Tx) (err error) {
_, err = tx.Exec(`
ALTER TABLE feeds ADD COLUMN allow_self_signed_certificates boolean not null default false
`)
return err
},
func(tx *sql.Tx) (err error) {
sql := `
CREATE TYPE webapp_display_mode AS enum('fullscreen', 'standalone', 'minimal-ui', 'browser');
ALTER TABLE users ADD COLUMN display_mode webapp_display_mode default 'standalone';
`
_, err = tx.Exec(sql)
return err
},
func(tx *sql.Tx) (err error) {
sql := `ALTER TABLE feeds ADD COLUMN cookie text default ''`
_, err = tx.Exec(sql)
return err
},
func(tx *sql.Tx) (err error) {
_, err = tx.Exec(`
ALTER TABLE categories ADD COLUMN hide_globally boolean not null default false
`)
return err
},
func(tx *sql.Tx) (err error) {
_, err = tx.Exec(`
ALTER TABLE feeds ADD COLUMN hide_globally boolean not null default false
`)
return err
},
func(tx *sql.Tx) (err error) {
sql := `
ALTER TABLE integrations ADD COLUMN telegram_bot_enabled bool default 'f';
ALTER TABLE integrations ADD COLUMN telegram_bot_token text default '';
ALTER TABLE integrations ADD COLUMN telegram_bot_chat_id text default '';
`
_, err = tx.Exec(sql)
return err
},
func(tx *sql.Tx) (err error) {
sql := `
CREATE TYPE entry_sorting_order AS enum('published_at', 'created_at');
ALTER TABLE users ADD COLUMN entry_order entry_sorting_order default 'published_at';
`
_, err = tx.Exec(sql)
return err
},
func(tx *sql.Tx) (err error) {
sql := `
ALTER TABLE integrations ADD COLUMN googlereader_enabled bool default 'f';
ALTER TABLE integrations ADD COLUMN googlereader_username text default '';
ALTER TABLE integrations ADD COLUMN googlereader_password text default '';
`
_, err = tx.Exec(sql)
return err
},
func(tx *sql.Tx) (err error) {
sql := `
ALTER TABLE integrations ADD COLUMN espial_enabled bool default 'f';
ALTER TABLE integrations ADD COLUMN espial_url text default '';
ALTER TABLE integrations ADD COLUMN espial_api_key text default '';
ALTER TABLE integrations ADD COLUMN espial_tags text default 'miniflux';
`
_, err = tx.Exec(sql)
return err
},
func(tx *sql.Tx) (err error) {
sql := `
ALTER TABLE integrations ADD COLUMN linkding_enabled bool default 'f';
ALTER TABLE integrations ADD COLUMN linkding_url text default '';
ALTER TABLE integrations ADD COLUMN linkding_api_key text default '';
`
_, err = tx.Exec(sql)
return err
},
}
+1 -1
View File
@@ -365,7 +365,7 @@ func (h *handler) handleSavedItems(w http.ResponseWriter, r *http.Request) {
logger.Debug("[Fever] Fetching saved items for user #%d", userID)
builder := h.store.NewEntryQueryBuilder(userID)
builder.WithStarred()
builder.WithStarred(true)
entryIDs, err := builder.GetEntryIDs()
if err != nil {
-221
View File
@@ -1,221 +0,0 @@
// Copyright 2017 Frédéric Guillot. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.
// +build ignore
package main
import (
"crypto/sha256"
"encoding/base64"
"fmt"
"io/ioutil"
"os"
"path"
"path/filepath"
"strings"
"text/template"
"github.com/tdewolff/minify/v2"
"github.com/tdewolff/minify/v2/css"
"github.com/tdewolff/minify/v2/js"
)
const tpl = `// Code generated by go generate; DO NOT EDIT.
package {{ .Package }} // import "miniflux.app/{{ .ImportPath }}"
var {{ .Map }} = map[string]string{
{{ range $constant, $content := .Files }}` + "\t" + `"{{ $constant }}": ` + "`{{ $content }}`" + `,
{{ end }}}
var {{ .Map }}Checksums = map[string]string{
{{ range $constant, $content := .Checksums }}` + "\t" + `"{{ $constant }}": "{{ $content }}",
{{ end }}}
`
var bundleTpl = template.Must(template.New("").Parse(tpl))
type Bundle struct {
Package string
Map string
ImportPath string
Files map[string]string
Checksums map[string]string
}
func (b *Bundle) Write(filename string) {
f, err := os.Create(filename)
if err != nil {
panic(err)
}
defer f.Close()
bundleTpl.Execute(f, b)
}
func NewBundle(pkg, mapName, importPath string) *Bundle {
return &Bundle{
Package: pkg,
Map: mapName,
ImportPath: importPath,
Files: make(map[string]string),
Checksums: make(map[string]string),
}
}
func readFile(filename string) []byte {
data, err := ioutil.ReadFile(filename)
if err != nil {
panic(err)
}
return data
}
func checksum(data []byte) string {
return fmt.Sprintf("%x", sha256.Sum256(data))
}
func basename(filename string) string {
return path.Base(filename)
}
func stripExtension(filename string) string {
filename = strings.TrimSuffix(filename, path.Ext(filename))
return strings.Replace(filename, " ", "_", -1)
}
func glob(pattern string) []string {
// There is no Glob function in path package, so we have to use filepath and replace in case of Windows
files, _ := filepath.Glob(pattern)
for i := range files {
if strings.Contains(files[i], "\\") {
files[i] = strings.Replace(files[i], "\\", "/", -1)
}
}
return files
}
func concat(files []string) string {
var b strings.Builder
for _, file := range files {
b.Write(readFile(file))
}
return b.String()
}
func generateJSBundle(bundleFile string, bundleFiles map[string][]string, prefixes, suffixes map[string]string) {
bundle := NewBundle("static", "Javascripts", "ui/static")
m := minify.New()
m.AddFunc("text/javascript", js.Minify)
for name, srcFiles := range bundleFiles {
var b strings.Builder
if prefix, found := prefixes[name]; found {
b.WriteString(prefix)
}
b.WriteString(concat(srcFiles))
if suffix, found := suffixes[name]; found {
b.WriteString(suffix)
}
minifiedData, err := m.String("text/javascript", b.String())
if err != nil {
panic(err)
}
bundle.Files[name] = minifiedData
bundle.Checksums[name] = checksum([]byte(minifiedData))
}
bundle.Write(bundleFile)
}
func generateCSSBundle(bundleFile string, themes map[string][]string) {
bundle := NewBundle("static", "Stylesheets", "ui/static")
m := minify.New()
m.AddFunc("text/css", css.Minify)
for theme, srcFiles := range themes {
data := concat(srcFiles)
minifiedData, err := m.String("text/css", data)
if err != nil {
panic(err)
}
bundle.Files[theme] = minifiedData
bundle.Checksums[theme] = checksum([]byte(minifiedData))
}
bundle.Write(bundleFile)
}
func generateBinaryBundle(bundleFile string, srcFiles []string) {
bundle := NewBundle("static", "Binaries", "ui/static")
for _, srcFile := range srcFiles {
data := readFile(srcFile)
filename := basename(srcFile)
encodedData := base64.StdEncoding.EncodeToString(data)
bundle.Files[filename] = string(encodedData)
bundle.Checksums[filename] = checksum(data)
}
bundle.Write(bundleFile)
}
func generateBundle(bundleFile, pkg, mapName string, srcFiles []string) {
bundle := NewBundle(pkg, mapName, pkg)
for _, srcFile := range srcFiles {
data := readFile(srcFile)
filename := stripExtension(basename(srcFile))
bundle.Files[filename] = string(data)
bundle.Checksums[filename] = checksum(data)
}
bundle.Write(bundleFile)
}
func main() {
generateJSBundle("ui/static/js.go", map[string][]string{
"app": []string{
"ui/static/js/dom_helper.js",
"ui/static/js/touch_handler.js",
"ui/static/js/keyboard_handler.js",
"ui/static/js/request_builder.js",
"ui/static/js/modal_handler.js",
"ui/static/js/app.js",
"ui/static/js/bootstrap.js",
},
"service-worker": []string{
"ui/static/js/service_worker.js",
},
}, map[string]string{
"app": "(function(){'use strict';",
"sw": "'use strict';",
}, map[string]string{
"app": "})();",
})
generateCSSBundle("ui/static/css.go", map[string][]string{
"light_serif": []string{"ui/static/css/light.css", "ui/static/css/serif.css", "ui/static/css/common.css"},
"light_sans_serif": []string{"ui/static/css/light.css", "ui/static/css/sans_serif.css", "ui/static/css/common.css"},
"dark_serif": []string{"ui/static/css/dark.css", "ui/static/css/serif.css", "ui/static/css/common.css"},
"dark_sans_serif": []string{"ui/static/css/dark.css", "ui/static/css/sans_serif.css", "ui/static/css/common.css"},
"system_serif": []string{"ui/static/css/system.css", "ui/static/css/serif.css", "ui/static/css/common.css"},
"system_sans_serif": []string{"ui/static/css/system.css", "ui/static/css/sans_serif.css", "ui/static/css/common.css"},
})
generateBinaryBundle("ui/static/bin.go", glob("ui/static/bin/*"))
generateBundle("template/views.go", "template", "templateViewsMap", glob("template/html/*.html"))
generateBundle("template/common.go", "template", "templateCommonMap", glob("template/html/common/*.html"))
generateBundle("locale/translations.go", "locale", "translations", glob("locale/translations/*.json"))
}
+11 -11
View File
@@ -1,27 +1,27 @@
module miniflux.app
// +heroku goVersion go1.15
// +heroku goVersion go1.16
require (
github.com/PuerkitoBio/goquery v1.6.1
github.com/PuerkitoBio/goquery v1.8.0
github.com/coreos/go-oidc v2.2.1+incompatible
github.com/felixge/httpsnoop v1.0.1 // indirect
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible
github.com/golang/gddo v0.0.0-20200831202555-721e228c7686 // indirect
github.com/gorilla/mux v1.8.0
github.com/lib/pq v1.9.0
github.com/lib/pq v1.10.6
github.com/mitchellh/go-server-timing v1.0.2-0.20201108055052-feb680ab92c2
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect
github.com/prometheus/client_golang v1.9.0
github.com/prometheus/client_golang v1.12.2
github.com/rylans/getlang v0.0.0-20200505200108-4c3188ff8a2d
github.com/stretchr/testify v1.6.1 // indirect
github.com/tdewolff/minify/v2 v2.9.11 // indirect
github.com/tdewolff/minify/v2 v2.11.5
github.com/technoweenie/multipartstreamer v1.0.1 // indirect
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9
golang.org/x/net v0.0.0-20201029221708-28c70e62bb1d
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
google.golang.org/appengine v1.6.6 // indirect
google.golang.org/protobuf v1.25.0 // indirect
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c
gopkg.in/square/go-jose.v2 v2.5.0 // indirect
mvdan.cc/xurls/v2 v2.2.0
mvdan.cc/xurls/v2 v2.4.0
)
go 1.15
go 1.16
+290 -234
View File
@@ -1,203 +1,199 @@
cloud.google.com/go v0.16.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/PuerkitoBio/goquery v1.6.1 h1:FgjbQZKl5HTmcn4sKBgvx8vv63nhyhIpv7lJpFGCWpk=
github.com/PuerkitoBio/goquery v1.6.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
github.com/PuerkitoBio/goquery v1.8.0 h1:PJTF7AmFCFKk1N6V6jmKfrNH9tV5pNE6lZMkG0gta/U=
github.com/PuerkitoBio/goquery v1.8.0/go.mod h1:ypIiRMtY7COPGk+I/YbZLbxsxn9g5ejnI2HSMtkjZvI=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
github.com/andybalholm/cascadia v1.1.0 h1:BuuO6sSfQNFRu1LppgbD25Hr2vLYW25JvxHs5zzsLTo=
github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c=
github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
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/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bradfitz/gomemcache v0.0.0-20170208213004-1952afaa557d/go.mod h1:PmM6Mmwb0LSuEubjR8N7PtNe1KxZLtOUHtbeikc5h60=
github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cheekybits/is v0.0.0-20150225183255-68e9c0620927/go.mod h1:h/aW8ynjgkuj+NQRlZcDbAbM1ORAbXjXX77sX7T289U=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-oidc v2.2.1+incompatible h1:mh48q/BqXqgjVHpy2ZY7WnWAbenxRjsz9N1i1YxjHAk=
github.com/coreos/go-oidc v2.2.1+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cosiner/argv v0.1.0/go.mod h1:EusR6TucWKX+zFgtdUsKT2Cvg45K5rtpCcWz4hK06d8=
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/djherbis/atime v1.1.0/go.mod h1:28OF6Y8s3NQWwacXc5eZTsEsiMzp7LF8MbXE+XJPdBE=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/felixge/httpsnoop v1.0.0 h1:gh8fMGz0rlOv/1WmRZm7OgncIOTsAj21iNJot48omJQ=
github.com/felixge/httpsnoop v1.0.0/go.mod h1:3+D9sFq0ahK/JeJPhCBUV1xlf4/eIYrUQaxulT0VzX8=
github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ=
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
github.com/fsnotify/fsnotify v1.4.3-0.20170329110642-4da3e2cfbabc/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.5.3/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
github.com/garyburd/redigo v1.1.1-0.20170914051019-70e1b1943d4f/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-delve/delve v1.5.0/go.mod h1:c6b3a1Gry6x8a4LGCe/CWzrocrfaHvkUxCj3k4bvSUQ=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o=
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-stack/stack v1.6.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible h1:2cauKuaELYAEARXRkq2LrJ0yDDv1rW7+wrTEdVL3uaU=
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible/go.mod h1:qf9acutJ8cwBUhm1bqgz6Bei9/C/c93FPDljKWwsOgM=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/golang/gddo v0.0.0-20180823221919-9d8ff1c67be5 h1:yrv1uUvgXH/tEat+wdvJMRJ4g51GlIydtDpU9pFjaaI=
github.com/golang/gddo v0.0.0-20180823221919-9d8ff1c67be5/go.mod h1:xEhNfoBDX1hzLm2Nf80qUvZ2sVwoMZ8d6IE2SrsQfh4=
github.com/golang/gddo v0.0.0-20200831202555-721e228c7686 h1:5vu7C+63KTbsSNnLhrgB98Sqy8MNVSW8FdhkcWA/3Rk=
github.com/golang/gddo v0.0.0-20200831202555-721e228c7686/go.mod h1:sam69Hju0uq+5uvLJUMDlsKlQ21Vrs1Kd/1YFPNYdOU=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/lint v0.0.0-20170918230701-e5d664eb928e/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0 h1:oOuy+ugB+P/kBdUnG5QaMXSIyJ1q38wWSojYCb3z5VQ=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.1.1-0.20171103154506-982329095285/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-dap v0.2.0/go.mod h1:5q8aYQFnHOAZEMP+6vmq25HKYAEwE+LF5yh7JKrrhSQ=
github.com/google/go-dap v0.3.0/go.mod h1:5q8aYQFnHOAZEMP+6vmq25HKYAEwE+LF5yh7JKrrhSQ=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gregjones/httpcache v0.0.0-20170920190843-316c5e0ff04e/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE=
github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/hcl v0.0.0-20170914154624-68e816d1c783/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/inconshreveable/log15 v0.0.0-20170622235902-74a0988b5f80/go.mod h1:cOaXtrgN4ScfRrD9Bre7U1thNq5RtJ8ZoP4iXVGRj6o=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
@@ -205,170 +201,106 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/lib/pq v1.9.0 h1:L8nSXQQzAYByakOFMTwpjRoHsMJklur4Gi59b6VivR8=
github.com/lib/pq v1.9.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
github.com/lib/pq v1.10.6 h1:jbk+ZieJ0D7EVGJYpL9QTz7/YW6UHbmdnZWYyK5cdBs=
github.com/lib/pq v1.10.6/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/magiconair/properties v1.7.4-0.20170902060319-8d7837e64d3c/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/matryer/try v0.0.0-20161228173917-9ac251b645a2/go.mod h1:0KeJpeMD6o+O4hW7qJOT7vyQPKrWmj26uf5wMc/IiIs=
github.com/mattn/go-colorable v0.0.0-20170327083344-ded68f7a9561/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.0.10-0.20170816031813-ad5389df28cd/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.2/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-server-timing v1.0.2-0.20201108055052-feb680ab92c2 h1:9kYRAm87/M5VL+HWegDGIorBWDiErrZrksLKTJBF2IQ=
github.com/mitchellh/go-server-timing v1.0.2-0.20201108055052-feb680ab92c2/go.mod h1:8mKJVJkyMI20ifXXO0zlV3sh3FrtjvltAeBqz38clBE=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v0.0.0-20170523030023-d0303fe80992/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=
github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU=
github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k=
github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w=
github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis=
github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA=
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
github.com/pelletier/go-toml v1.0.1-0.20170904195809-1d6b12b7cb29/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac=
github.com/peterh/liner v0.0.0-20170317030525-88609521dc4b/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc=
github.com/peterh/liner v1.2.0/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0=
github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 h1:J9b7z+QKAmPf4YLrFg6oQUotqHQeUNWwkvo7jZp1GLU=
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og=
github.com/prometheus/client_golang v1.7.1 h1:NTGy1Ja9pByO+xAeH/qiWnLrKtr3hJPNjaVUwnjpdpA=
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
github.com/prometheus/client_golang v1.9.0 h1:Rrch9mh17XcxvEu9D9DEpb4isxjGBtcevQjKvxPRQIU=
github.com/prometheus/client_golang v1.9.0/go.mod h1:FqZLKOZnGdFAhOK4nqGHa7D66IdsO+O441Eve7ptJDU=
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
github.com/prometheus/client_golang v1.12.2 h1:51L9cDoUHVrXx4zWYlcLQIZ+d+VXHgqnYKkIuq4g/34=
github.com/prometheus/client_golang v1.12.2/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA=
github.com/prometheus/common v0.10.0 h1:RyRA7RzGXQZiW+tGMr7sxa85G1z0yOpM1qq5c8lNawc=
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
github.com/prometheus/common v0.15.0 h1:4fgOnadei3EZvgRwxJ7RMpG1k1pOZth5Pc13tyspaKM=
github.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s=
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4=
github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
github.com/prometheus/procfs v0.1.3 h1:F0+tqvhOksq22sc6iCHF5WGlWjdwj92p0udFh1VFBS8=
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.2.0 h1:wH4vA7pcjKuZzjF7lM8awk4fnuJO6idemZXoKnULUx4=
github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU=
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.5.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/rylans/getlang v0.0.0-20200505200108-4c3188ff8a2d h1:4Jn2kzSKcpxxwpJdyWxfNWKCYe4Uki2VibkExYmBLiA=
github.com/rylans/getlang v0.0.0-20200505200108-4c3188ff8a2d/go.mod h1:3vfmZI6aJd5Rb9W2TQ0Nmupl+qem21R05+hmCscI0Bk=
github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v0.0.0-20170901052352-ee1bd8ee15a1/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/cast v1.1.0/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v0.0.0-20170417170307-b6cb39589372/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
github.com/spf13/jwalterweatherman v0.0.0-20170901151539-12bd96e66386/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v0.0.0-20170417173400-9e4c21054fa1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.1-0.20170901120850-7aff26db30c1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.0.0/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM=
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
@@ -376,82 +308,112 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/tdewolff/minify v1.1.0 h1:nxHQi1ML+g3ZbZHffiZ6eC7vMqNvSRfX3KB5Y5y/kfw=
github.com/tdewolff/minify v2.3.6+incompatible h1:2hw5/9ZvxhWLvBUnHE06gElGYz+Jv9R4Eys0XUzItYo=
github.com/tdewolff/minify/v2 v2.9.11 h1:8o6hclGwxm6MNwTPHabvdND5SghhHs0bn+3/+uAf0yQ=
github.com/tdewolff/minify/v2 v2.9.11/go.mod h1:YZk0lGOc6CvQrqvm5f7V3ihaq3QUd9acS4HESdVDOaM=
github.com/tdewolff/parse/v2 v2.5.8 h1:vutkOO9Xi3DehIzCLHqvMM2hFXo54S0iDvIG/hYznnE=
github.com/tdewolff/parse/v2 v2.5.8/go.mod h1:WzaJpRSbwq++EIQHYIRTpbYKNA3gn9it1Ik++q4zyho=
github.com/tdewolff/minify/v2 v2.11.5 h1:wfRJ6JMmu87Azx+Lj7NcCTg8D/L/92SNuYF5fv31fjg=
github.com/tdewolff/minify/v2 v2.11.5/go.mod h1:ZR6VK7tbgF0XXBVwDHXrbqM4tGYchBpdwhsbKeDmAww=
github.com/tdewolff/parse/v2 v2.5.31 h1:PrJN/trWTUYaYxg7jRpKnKXpBK4kBjT9rAFhFAh2Lus=
github.com/tdewolff/parse/v2 v2.5.31/go.mod h1:WzaJpRSbwq++EIQHYIRTpbYKNA3gn9it1Ik++q4zyho=
github.com/tdewolff/test v1.0.6 h1:76mzYJQ83Op284kMT+63iCNCI7NEERsIN8dLM+RiKr4=
github.com/tdewolff/test v1.0.6/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/technoweenie/multipartstreamer v1.0.1 h1:XRztA5MXiR1TIRHxH2uNxXxaIkKQDeX7m2XsSOlQEnM=
github.com/technoweenie/multipartstreamer v1.0.1/go.mod h1:jNVxdtShOxzAsukZwTSw6MDx5eUJoiEBsSvzDU9uzog=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.starlark.net v0.0.0-20190702223751-32f345186213/go.mod h1:c1/X6cHgvdXj6pUlmWKMkuqRnW4K8x2vwt6JAaaircg=
go.starlark.net v0.0.0-20201006213952-227f4aabceb5/go.mod h1:f0znQkUKRrkk36XxWbGjMqQM8wGv/xHBVE2qc3B5oFU=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
golang.org/x/arch v0.0.0-20190927153633-4e8777c89be4/go.mod h1:flIaEI6LNU6xOCD5PaJvn9wGP0agmIOqjrtsKGRguv4=
golang.org/x/arch v0.0.0-20201008161808-52c3e6f60cff/go.mod h1:flIaEI6LNU6xOCD5PaJvn9wGP0agmIOqjrtsKGRguv4=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200625001655-4c5254603344 h1:vGXIOMxbNfDTk/aXCmfdLgkrSV+Z2tcbze+pEc3v5W4=
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201029221708-28c70e62bb1d h1:dOiJ2n2cMwGLce/74I/QHMbnpk5GfY7InR8rczoMqRM=
golang.org/x/net v0.0.0-20201029221708-28c70e62bb1d/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8 h1:/6y1LfuqNuQdHAm0jjtPtgRcxIxjVZgm5OTu8/QhZvk=
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/oauth2 v0.0.0-20170912212905-13449ad91cb2/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c h1:pkQiBZBvdos9qq4wBAHqlzuZHEXo07pqV06ef90u1WI=
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/sync v0.0.0-20170517211232-f52d1811a629/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -459,88 +421,180 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e h1:AyodaIpKjppX+cBfTASF2E1US3H2JFBj920Ot3rtDjs=
golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 h1:XfKQ4OlFl8okEOr5UvAqFRVj8pY/4yfcXrddB8qAbU0=
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/time v0.0.0-20170424234030-8be79e1e0910/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191127201027-ecd32218bd7f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
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 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.0.0-20170921000349-586095a6e407/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc=
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20170918111702-1e559d0a00ee/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/grpc v1.2.1-0.20170921194603-d4b75ebd4f9f/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@@ -549,36 +603,38 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/square/go-jose.v2 v2.5.0 h1:OZ4sdq+Y+SHfYB7vfthi1Ei8b0vkP8ZPQgUfUwdUSqo=
gopkg.in/square/go-jose.v2 v2.5.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
mvdan.cc/xurls/v2 v2.2.0 h1:NSZPykBXJFCetGZykLAxaL6SIpvbVy/UFEniIfHAa8A=
mvdan.cc/xurls/v2 v2.2.0/go.mod h1:EV1RMtya9D6G5DMYPGD8zTQzaHet6Jh8gFlRgGRJeO8=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
mvdan.cc/xurls/v2 v2.4.0 h1:tzxjVAj+wSBmDcF6zBB7/myTy3gX9xvi8Tyr28AuQgc=
mvdan.cc/xurls/v2 v2.4.0/go.mod h1:+GEjq9uNjqs8LQfM9nVnM8rff0OQ5Iash5rzX+N1CSg=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
+10
View File
@@ -0,0 +1,10 @@
// Copyright 2022 Frédéric Guillot. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.
/*
Package googlereader implements Google Reader API endpoints.
*/
package googlereader // import "miniflux.app/googlereader"
File diff suppressed because it is too large Load Diff
+208
View File
@@ -0,0 +1,208 @@
// Copyright 2022 Frédéric Guillot. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.
package googlereader // import "miniflux.app/googlereader"
import (
"context"
"crypto/hmac"
"crypto/sha1"
"encoding/hex"
"net/http"
"strings"
"miniflux.app/http/request"
"miniflux.app/http/response"
"miniflux.app/http/response/json"
"miniflux.app/logger"
"miniflux.app/model"
"miniflux.app/storage"
)
type middleware struct {
store *storage.Storage
}
func newMiddleware(s *storage.Storage) *middleware {
return &middleware{s}
}
func (m *middleware) clientLogin(w http.ResponseWriter, r *http.Request) {
clientIP := request.ClientIP(r)
var username, password, output string
var integration *model.Integration
err := r.ParseForm()
if err != nil {
logger.Error("[GoogleReader][Login] [ClientIP=%s] Could not parse form", clientIP)
json.Unauthorized(w, r)
return
}
username = r.Form.Get("Email")
password = r.Form.Get("Passwd")
output = r.Form.Get("output")
if username == "" || password == "" {
logger.Error("[GoogleReader][Login] [ClientIP=%s] Empty username or password", clientIP)
json.Unauthorized(w, r)
return
}
if err = m.store.GoogleReaderUserCheckPassword(username, password); err != nil {
logger.Error("[GoogleReader][Login] [ClientIP=%s] Invalid username or password: %s", clientIP, username)
json.Unauthorized(w, r)
return
}
logger.Info("[GoogleReader][Login] [ClientIP=%s] User authenticated: %s", clientIP, username)
if integration, err = m.store.GoogleReaderUserGetIntegration(username); err != nil {
logger.Error("[GoogleReader][Login] [ClientIP=%s] Could not load integration: %s", clientIP, username)
json.Unauthorized(w, r)
return
}
m.store.SetLastLogin(integration.UserID)
token := getAuthToken(integration.GoogleReaderUsername, integration.GoogleReaderPassword)
logger.Info("[GoogleReader][Login] [ClientIP=%s] Created token: %s", clientIP, token)
result := login{SID: token, LSID: token, Auth: token}
if output == "json" {
json.OK(w, r, result)
return
}
builder := response.New(w, r)
builder.WithHeader("Content-Type", "text/plain; charset=UTF-8")
builder.WithBody(result.String())
builder.Write()
}
func (m *middleware) token(w http.ResponseWriter, r *http.Request) {
clientIP := request.ClientIP(r)
if !request.IsAuthenticated(r) {
logger.Error("[GoogleReader][Token] [ClientIP=%s] User is not authenticated", clientIP)
json.Unauthorized(w, r)
return
}
token := request.GoolgeReaderToken(r)
if token == "" {
logger.Error("[GoogleReader][Token] [ClientIP=%s] User does not have token: %s", clientIP, request.UserID(r))
json.Unauthorized(w, r)
return
}
logger.Info("[GoogleReader][Token] [ClientIP=%s] token: %s", clientIP, token)
w.Header().Add("Content-Type", "text/plain; charset=UTF-8")
w.WriteHeader(http.StatusOK)
w.Write([]byte(token))
}
func (m *middleware) handleCORS(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
w.Header().Set("Access-Control-Allow-Headers", "Authorization")
if r.Method == http.MethodOptions {
w.WriteHeader(http.StatusOK)
return
}
next.ServeHTTP(w, r)
})
}
func (m *middleware) apiKeyAuth(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
clientIP := request.ClientIP(r)
var token string
if r.Method == http.MethodPost {
err := r.ParseForm()
if err != nil {
logger.Error("[GoogleReader][Login] [ClientIP=%s] Could not parse form", clientIP)
Unauthorized(w, r)
return
}
token = r.Form.Get("T")
if token == "" {
logger.Error("[GoogleReader][Auth] [ClientIP=%s] Post-Form T field is empty", clientIP)
Unauthorized(w, r)
return
}
} else {
authorization := r.Header.Get("Authorization")
if authorization == "" {
logger.Error("[GoogleReader][Auth] [ClientIP=%s] No token provided", clientIP)
Unauthorized(w, r)
return
}
fields := strings.Fields(authorization)
if len(fields) != 2 {
logger.Error("[GoogleReader][Auth] [ClientIP=%s] Authorization header does not have the expected structure GoogleLogin auth=xxxxxx - '%s'", clientIP, authorization)
Unauthorized(w, r)
return
}
if fields[0] != "GoogleLogin" {
logger.Error("[GoogleReader][Auth] [ClientIP=%s] Authorization header does not begin with GoogleLogin - '%s'", clientIP, authorization)
Unauthorized(w, r)
return
}
auths := strings.Split(fields[1], "=")
if len(auths) != 2 {
logger.Error("[GoogleReader][Auth] [ClientIP=%s] Authorization header does not have the expected structure GoogleLogin auth=xxxxxx - '%s'", clientIP, authorization)
Unauthorized(w, r)
return
}
if auths[0] != "auth" {
logger.Error("[GoogleReader][Auth] [ClientIP=%s] Authorization header does not have the expected structure GoogleLogin auth=xxxxxx - '%s'", clientIP, authorization)
Unauthorized(w, r)
return
}
token = auths[1]
}
parts := strings.Split(token, "/")
if len(parts) != 2 {
logger.Error("[GoogleReader][Auth] [ClientIP=%s] Auth token does not have the expected structure username/hash - '%s'", clientIP, token)
Unauthorized(w, r)
return
}
var integration *model.Integration
var user *model.User
var err error
if integration, err = m.store.GoogleReaderUserGetIntegration(parts[0]); err != nil {
logger.Error("[GoogleReader][Auth] [ClientIP=%s] token: %s", clientIP, token)
logger.Error("[GoogleReader][Auth] [ClientIP=%s] No user found with the given google reader username: %s", clientIP, parts[0])
Unauthorized(w, r)
return
}
expectedToken := getAuthToken(integration.GoogleReaderUsername, integration.GoogleReaderPassword)
if expectedToken != token {
logger.Error("[GoogleReader][Auth] [ClientIP=%s] Token does not match: %s", clientIP, token)
Unauthorized(w, r)
return
}
if user, err = m.store.UserByID(integration.UserID); err != nil {
logger.Error("[GoogleReader][Auth] [ClientIP=%s] No user found with the userID: %d", clientIP, integration.UserID)
Unauthorized(w, r)
return
}
m.store.SetLastLogin(integration.UserID)
ctx := r.Context()
ctx = context.WithValue(ctx, request.UserIDContextKey, user.ID)
ctx = context.WithValue(ctx, request.UserTimezoneContextKey, user.Timezone)
ctx = context.WithValue(ctx, request.IsAdminUserContextKey, user.IsAdmin)
ctx = context.WithValue(ctx, request.IsAuthenticatedContextKey, true)
ctx = context.WithValue(ctx, request.GoogleReaderToken, token)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
func getAuthToken(username, password string) string {
token := hex.EncodeToString(hmac.New(sha1.New, []byte(username+password)).Sum(nil))
token = username + "/" + token
return token
}
+144
View File
@@ -0,0 +1,144 @@
// Copyright 2022 Frédéric Guillot. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.
package googlereader // import "miniflux.app/googlereader"
import (
"fmt"
"net/http"
"miniflux.app/http/response"
"miniflux.app/logger"
)
type login struct {
SID string `json:"SID,omitempty"`
LSID string `json:"LSID,omitempty"`
Auth string `json:"Auth,omitempty"`
}
func (l login) String() string {
return fmt.Sprintf("SID=%s\nLSID=%s\nAuth=%s\n", l.SID, l.LSID, l.Auth)
}
type userInfo struct {
UserID string `json:"userId"`
UserName string `json:"userName"`
UserProfileID string `json:"userProfileId"`
UserEmail string `json:"userEmail"`
}
type subscription struct {
ID string `json:"id"`
Title string `json:"title"`
Categories []subscriptionCategory `json:"categories"`
URL string `json:"url"`
HTMLURL string `json:"htmlUrl"`
IconURL string `json:"iconUrl"`
}
type quickAddResponse struct {
NumResults int64 `json:"numResults"`
Query string `json:"query,omitempty"`
StreamID string `json:"streamId,omitempty"`
StreamName string `json:"streamName,omitempty"`
}
type subscriptionCategory struct {
ID string `json:"id"`
Label string `json:"label,omitempty"`
Type string `json:"type,omitempty"`
}
type subscriptionsResponse struct {
Subscriptions []subscription `json:"subscriptions"`
}
type itemRef struct {
ID string `json:"id"`
DirectStreamIDs string `json:"directStreamIds,omitempty"`
TimestampUsec string `json:"timestampUsec,omitempty"`
}
type streamIDResponse struct {
ItemRefs []itemRef `json:"itemRefs"`
}
type tagsResponse struct {
Tags []subscriptionCategory `json:"tags"`
}
type streamContentItems struct {
Direction string `json:"direction"`
ID string `json:"id"`
Title string `json:"title"`
Self []contentHREF `json:"self"`
Alternate []contentHREFType `json:"alternate"`
Updated int64 `json:"updated"`
Items []contentItem `json:"items"`
Author string `json:"author"`
}
type contentItem struct {
ID string `json:"id"`
Categories []string `json:"categories"`
Title string `json:"title"`
CrawlTimeMsec string `json:"crawlTimeMsec"`
TimestampUsec string `json:"timestampUsec"`
Published int64 `json:"published"`
Updated int64 `json:"updated"`
Author string `json:"author"`
Alternate []contentHREFType `json:"alternate"`
Summary contentItemContent `json:"summary"`
Content contentItemContent `json:"content"`
Origin contentItemOrigin `json:"origin"`
Enclosure []contentItemEnclosure `json:"enclosure"`
Canonical []contentHREF `json:"canonical"`
}
type contentHREFType struct {
HREF string `json:"href"`
Type string `json:"type"`
}
type contentHREF struct {
HREF string `json:"href"`
}
type contentItemEnclosure struct {
URL string `json:"url"`
Type string `json:"type"`
}
type contentItemContent struct {
Direction string `json:"direction"`
Content string `json:"content"`
}
type contentItemOrigin struct {
StreamID string `json:"streamId"`
Title string `json:"title"`
HTMLUrl string `json:"htmlUrl"`
}
// Unauthorized sends a not authorized error to the client.
func Unauthorized(w http.ResponseWriter, r *http.Request) {
logger.Error("[HTTP:Unauthorized] %s", r.URL)
builder := response.New(w, r)
builder.WithStatus(http.StatusUnauthorized)
builder.WithHeader("Content-Type", "text/plain")
builder.WithHeader("X-Reader-Google-Bad-Token", "true")
builder.WithBody("Unauthorized")
builder.Write()
}
// OK sends a ok response to the client.
func OK(w http.ResponseWriter, r *http.Request) {
logger.Info("[HTTP:OK] %s", r.URL)
builder := response.New(w, r)
builder.WithStatus(http.StatusOK)
builder.WithHeader("Content-Type", "text/plain")
builder.WithBody("OK")
builder.Write()
}
+26 -11
View File
@@ -6,11 +6,11 @@ package client // import "miniflux.app/http/client"
import (
"bytes"
"crypto/tls"
"crypto/x509"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net"
"net/http"
"net/url"
@@ -21,7 +21,6 @@ import (
"miniflux.app/errors"
"miniflux.app/logger"
"miniflux.app/timer"
url_helper "miniflux.app/url"
)
const (
@@ -40,20 +39,21 @@ var (
type Client struct {
inputURL string
requestURL string
requestEtagHeader string
requestLastModifiedHeader string
requestAuthorizationHeader string
requestUsername string
requestPassword string
requestUserAgent string
requestCookie string
useProxy bool
doNotFollowRedirects bool
ClientTimeout int
ClientMaxBodySize int64
ClientProxyURL string
ClientTimeout int
ClientMaxBodySize int64
ClientProxyURL string
AllowSelfSignedCertificates bool
}
// New initializes a new HTTP client.
@@ -88,13 +88,13 @@ func (c *Client) String() string {
}
return fmt.Sprintf(
`InputURL=%q RequestURL=%q ETag=%s LastModified=%s Auth=%v UserAgent=%q`,
`InputURL=%q ETag=%s LastMod=%s Auth=%v UserAgent=%q Verify=%v`,
c.inputURL,
c.requestURL,
etagHeader,
lastModifiedHeader,
c.requestAuthorizationHeader != "" || (c.requestUsername != "" && c.requestPassword != ""),
c.requestUserAgent,
!c.AllowSelfSignedCertificates,
)
}
@@ -140,6 +140,14 @@ func (c *Client) WithUserAgent(userAgent string) *Client {
return c
}
// WithCookie defines the Cookies to use for HTTP requests.
func (c *Client) WithCookie(cookie string) *Client {
if cookie != "" {
c.requestCookie = cookie
}
return c
}
// Get performs a GET HTTP request.
func (c *Client) Get() (*Response, error) {
request, err := c.buildRequest(http.MethodGet, nil)
@@ -219,7 +227,7 @@ func (c *Client) executeRequest(request *http.Request) (*Response, error) {
return nil, fmt.Errorf("client: response too large (%d bytes)", resp.ContentLength)
}
buf, err := ioutil.ReadAll(resp.Body)
buf, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("client: error while reading body %v", err)
}
@@ -252,8 +260,7 @@ func (c *Client) executeRequest(request *http.Request) (*Response, error) {
}
func (c *Client) buildRequest(method string, body io.Reader) (*http.Request, error) {
c.requestURL = url_helper.RequestURI(c.inputURL)
request, err := http.NewRequest(method, c.requestURL, body)
request, err := http.NewRequest(method, c.inputURL, body)
if err != nil {
return nil, err
}
@@ -289,6 +296,10 @@ func (c *Client) buildClient() http.Client {
IdleConnTimeout: 10 * time.Second,
}
if c.AllowSelfSignedCertificates {
transport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
}
if c.doNotFollowRedirects {
client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
@@ -330,6 +341,10 @@ func (c *Client) buildHeaders() http.Header {
headers.Add("Authorization", c.requestAuthorizationHeader)
}
if c.requestCookie != "" {
headers.Add("Cookie", c.requestCookie)
}
headers.Add("Connection", "close")
return headers
}
+2 -2
View File
@@ -19,7 +19,7 @@ func TestClientWithDelay(t *testing.T) {
func TestClientWithError(t *testing.T) {
clt := New("http://httpbin.org/status/502")
clt.ClientTimeout = 1
clt.ClientTimeout = 5
response, err := clt.Get()
if err != nil {
t.Fatal(err)
@@ -30,7 +30,7 @@ func TestClientWithError(t *testing.T) {
}
if !response.HasServerFailure() {
t.Fatal(`A 500 error is considered as server failure`)
t.Fatal(`A 502 error is considered as server failure`)
}
}
+3 -4
View File
@@ -8,7 +8,6 @@ import (
"bytes"
"fmt"
"io"
"io/ioutil"
"regexp"
"strings"
"unicode/utf8"
@@ -43,7 +42,7 @@ func (r *Response) String() string {
)
}
// IsNotFound returns true if the resource doesn't exists anymore.
// IsNotFound returns true if the resource doesn't exist anymore.
func (r *Response) IsNotFound() bool {
return r.StatusCode == 404 || r.StatusCode == 410
}
@@ -87,7 +86,7 @@ func (r *Response) IsModified(etag, lastModified string) bool {
// - Feeds with encoding specified only in XML document and not in HTTP header
// - Feeds with wrong encoding defined and already in UTF-8
func (r *Response) EnsureUnicodeBody() (err error) {
buffer, err := ioutil.ReadAll(r.Body)
buffer, err := io.ReadAll(r.Body)
if err != nil {
return err
}
@@ -116,6 +115,6 @@ func (r *Response) EnsureUnicodeBody() (err error) {
// BodyAsString returns the response body as string.
func (r *Response) BodyAsString() string {
bytes, _ := ioutil.ReadAll(r.Body)
bytes, _ := io.ReadAll(r.Body)
return string(bytes)
}
+3 -3
View File
@@ -6,7 +6,7 @@ package client // import "miniflux.app/http/client"
import (
"bytes"
"io/ioutil"
"os"
"strings"
"testing"
"unicode/utf8"
@@ -96,7 +96,7 @@ func TestToString(t *testing.T) {
r := &Response{Body: strings.NewReader(input)}
if r.BodyAsString() != input {
t.Error(`Unexpected ouput`)
t.Error(`Unexpected output`)
}
}
@@ -129,7 +129,7 @@ func TestEnsureUnicodeWithHTMLDocuments(t *testing.T) {
}
for _, tc := range unicodeTestCases {
content, err := ioutil.ReadFile("testdata/" + tc.filename)
content, err := os.ReadFile("testdata/" + tc.filename)
if err != nil {
t.Fatalf(`Unable to read file %q: %v`, tc.filename, err)
}
+6
View File
@@ -25,8 +25,14 @@ const (
FlashErrorMessageContextKey
PocketRequestTokenContextKey
ClientIPContextKey
GoogleReaderToken
)
// GoolgeReaderToken returns the google reader token if it exists.
func GoolgeReaderToken(r *http.Request) string {
return getContextStringValue(r, GoogleReaderToken)
}
// IsAdminUser checks if the logged user is administrator.
func IsAdminUser(r *http.Request) bool {
return getContextBoolValue(r, IsAdminUserContextKey)
+15 -1
View File
@@ -72,7 +72,21 @@ func QueryStringParamList(r *http.Request, param string) []string {
// QueryIntParam returns a query string parameter as integer.
func QueryIntParam(r *http.Request, param string, defaultValue int) int {
return int(QueryInt64Param(r, param, int64(defaultValue)))
value := r.URL.Query().Get(param)
if value == "" {
return defaultValue
}
val, err := strconv.ParseInt(value, 10, 0)
if err != nil {
return defaultValue
}
if val < 0 {
return defaultValue
}
return int(val)
}
// QueryInt64Param returns a query string parameter as int64.
-1
View File
@@ -96,7 +96,6 @@ func (b *Builder) writeHeaders() {
b.headers["X-XSS-Protection"] = "1; mode=block"
b.headers["X-Content-Type-Options"] = "nosniff"
b.headers["X-Frame-Options"] = "DENY"
b.headers["Content-Security-Policy"] = "default-src 'self'; img-src * data:; media-src *; frame-src *"
b.headers["Referrer-Policy"] = "no-referrer"
for key, value := range b.headers {
+3 -4
View File
@@ -29,10 +29,9 @@ func TestResponseHasCommonHeaders(t *testing.T) {
resp := w.Result()
headers := map[string]string{
"X-XSS-Protection": "1; mode=block",
"X-Content-Type-Options": "nosniff",
"X-Frame-Options": "DENY",
"Content-Security-Policy": "default-src 'self'; img-src * data:; media-src *; frame-src *",
"X-XSS-Protection": "1; mode=block",
"X-Content-Type-Options": "nosniff",
"X-Frame-Options": "DENY",
}
for header, expected := range headers {
+4 -5
View File
@@ -19,13 +19,12 @@ func Path(router *mux.Router, name string, args ...interface{}) string {
}
var pairs []string
for _, param := range args {
switch param.(type) {
for _, arg := range args {
switch param := arg.(type) {
case string:
pairs = append(pairs, param.(string))
pairs = append(pairs, param)
case int64:
val := param.(int64)
pairs = append(pairs, strconv.FormatInt(val, 10))
pairs = append(pairs, strconv.FormatInt(param, 10))
}
}
-10
View File
@@ -1,10 +0,0 @@
// Copyright 2018 Frédéric Guillot. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.
/*
Package integration implements API clients for third-party services.
*/
package integration // import "miniflux.app/integration"
+69
View File
@@ -0,0 +1,69 @@
package espial // import "miniflux.app/integration/espial"
import (
"fmt"
"net/url"
"path"
"miniflux.app/http/client"
)
// Document structure of an Espial document
type Document struct {
Title string `json:"title,omitempty"`
Url string `json:"url,omitempty"`
ToRead bool `json:"toread,omitempty"`
Tags string `json:"tags,omitempty"`
}
// Client represents an Espial client.
type Client struct {
baseURL string
apiKey string
}
// NewClient returns a new Espial client.
func NewClient(baseURL, apiKey string) *Client {
return &Client{baseURL: baseURL, apiKey: apiKey}
}
// AddEntry sends an entry to Espial.
func (c *Client) AddEntry(link, title, content, tags string) error {
if c.baseURL == "" || c.apiKey == "" {
return fmt.Errorf("espial: missing credentials")
}
doc := &Document{
Title: title,
Url: link,
ToRead: true,
Tags: tags,
}
apiURL, err := getAPIEndpoint(c.baseURL, "/api/add")
if err != nil {
return err
}
clt := client.New(apiURL)
clt.WithAuthorization("ApiKey " + c.apiKey)
response, err := clt.PostJSON(doc)
if err != nil {
return fmt.Errorf("espial: unable to send entry: %v", err)
}
if response.HasServerFailure() {
return fmt.Errorf("espial: unable to send entry, status=%d", response.StatusCode)
}
return nil
}
func getAPIEndpoint(baseURL, pathURL string) (string, error) {
u, err := url.Parse(baseURL)
if err != nil {
return "", fmt.Errorf("espial: invalid API endpoint: %v", err)
}
u.Path = path.Join(u.Path, pathURL)
return u.String(), nil
}
-10
View File
@@ -1,10 +0,0 @@
// Copyright 2018 Frédéric Guillot. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.
/*
Package instapaper provides an integration with Instapaper.
*/
package instapaper // import "miniflux.app/integration/instapaper"
+5 -5
View File
@@ -17,6 +17,11 @@ type Client struct {
password string
}
// NewClient returns a new Instapaper client.
func NewClient(username, password string) *Client {
return &Client{username: username, password: password}
}
// AddURL sends a link to Instapaper.
func (c *Client) AddURL(link, title string) error {
if c.username == "" || c.password == "" {
@@ -41,8 +46,3 @@ func (c *Client) AddURL(link, title string) error {
return nil
}
// NewClient returns a new Instapaper client.
func NewClient(username, password string) *Client {
return &Client{username: username, password: password}
}
+52 -2
View File
@@ -6,18 +6,23 @@ package integration // import "miniflux.app/integration"
import (
"miniflux.app/config"
"miniflux.app/integration/espial"
"miniflux.app/integration/instapaper"
"miniflux.app/integration/linkding"
"miniflux.app/integration/nunuxkeeper"
"miniflux.app/integration/pinboard"
"miniflux.app/integration/pocket"
"miniflux.app/integration/telegrambot"
"miniflux.app/integration/wallabag"
"miniflux.app/logger"
"miniflux.app/model"
)
// SendEntry send the entry to the activated providers.
// SendEntry sends the entry to third-party providers when the user click on "Save".
func SendEntry(entry *model.Entry, integration *model.Integration) {
if integration.PinboardEnabled {
logger.Debug("[Integration] Sending Entry #%d %q for User #%d to Pinboard", entry.ID, entry.URL, integration.UserID)
client := pinboard.NewClient(integration.PinboardToken)
err := client.AddBookmark(
entry.URL,
@@ -32,6 +37,8 @@ func SendEntry(entry *model.Entry, integration *model.Integration) {
}
if integration.InstapaperEnabled {
logger.Debug("[Integration] Sending Entry #%d %q for User #%d to Instapaper", entry.ID, entry.URL, integration.UserID)
client := instapaper.NewClient(integration.InstapaperUsername, integration.InstapaperPassword)
if err := client.AddURL(entry.URL, entry.Title); err != nil {
logger.Error("[Integration] UserID #%d: %v", integration.UserID, err)
@@ -39,6 +46,8 @@ func SendEntry(entry *model.Entry, integration *model.Integration) {
}
if integration.WallabagEnabled {
logger.Debug("[Integration] Sending Entry #%d %q for User #%d to Wallabag", entry.ID, entry.URL, integration.UserID)
client := wallabag.NewClient(
integration.WallabagURL,
integration.WallabagClientID,
@@ -47,12 +56,14 @@ func SendEntry(entry *model.Entry, integration *model.Integration) {
integration.WallabagPassword,
)
if err := client.AddEntry(entry.URL, entry.Title); err != nil {
if err := client.AddEntry(entry.URL, entry.Title, entry.Content); err != nil {
logger.Error("[Integration] UserID #%d: %v", integration.UserID, err)
}
}
if integration.NunuxKeeperEnabled {
logger.Debug("[Integration] Sending Entry #%d %q for User #%d to NunuxKeeper", entry.ID, entry.URL, integration.UserID)
client := nunuxkeeper.NewClient(
integration.NunuxKeeperURL,
integration.NunuxKeeperAPIKey,
@@ -63,10 +74,49 @@ func SendEntry(entry *model.Entry, integration *model.Integration) {
}
}
if integration.EspialEnabled {
logger.Debug("[Integration] Sending Entry #%d %q for User #%d to Espial", entry.ID, entry.URL, integration.UserID)
client := espial.NewClient(
integration.EspialURL,
integration.EspialAPIKey,
)
if err := client.AddEntry(entry.URL, entry.Title, entry.Content, integration.EspialTags); err != nil {
logger.Error("[Integration] UserID #%d: %v", integration.UserID, err)
}
}
if integration.PocketEnabled {
logger.Debug("[Integration] Sending Entry #%d %q for User #%d to Pocket", entry.ID, entry.URL, integration.UserID)
client := pocket.NewClient(config.Opts.PocketConsumerKey(integration.PocketConsumerKey), integration.PocketAccessToken)
if err := client.AddURL(entry.URL, entry.Title); err != nil {
logger.Error("[Integration] UserID #%d: %v", integration.UserID, err)
}
}
if integration.LinkdingEnabled {
logger.Debug("[Integration] Sending Entry #%d %q for User #%d to Linkding", entry.ID, entry.URL, integration.UserID)
client := linkding.NewClient(
integration.LinkdingURL,
integration.LinkdingAPIKey,
)
if err := client.AddEntry(entry.Title, entry.URL); err != nil {
logger.Error("[Integration] UserID #%d: %v", integration.UserID, err)
}
}
}
// PushEntry pushes an entry to third-party providers during feed refreshes.
func PushEntry(entry *model.Entry, integration *model.Integration) {
if integration.TelegramBotEnabled {
logger.Debug("[Integration] Sending Entry %q for User #%d to Telegram", entry.URL, integration.UserID)
err := telegrambot.PushEntry(entry, integration.TelegramBotToken, integration.TelegramBotChatID)
if err != nil {
logger.Error("[Integration] push entry to telegram bot failed: %v", err)
}
}
}
+74
View File
@@ -0,0 +1,74 @@
// Copyright 2017 Frédéric Guillot. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.
package linkding // import "miniflux.app/integration/linkding"
import (
"fmt"
"net/url"
"miniflux.app/http/client"
)
// Document structure of a Linkding document
type Document struct {
Url string `json:"url,omitempty"`
Title string `json:"title,omitempty"`
}
// Client represents an Linkding client.
type Client struct {
baseURL string
apiKey string
}
// NewClient returns a new Linkding client.
func NewClient(baseURL, apiKey string) *Client {
return &Client{baseURL: baseURL, apiKey: apiKey}
}
// AddEntry sends an entry to Linkding.
func (c *Client) AddEntry(title, url string) error {
if c.baseURL == "" || c.apiKey == "" {
return fmt.Errorf("linkding: missing credentials")
}
doc := &Document{
Url: url,
Title: title,
}
apiURL, err := getAPIEndpoint(c.baseURL, "/api/bookmarks/")
if err != nil {
return err
}
clt := client.New(apiURL)
clt.WithAuthorization("Token " + c.apiKey)
response, err := clt.PostJSON(doc)
if err != nil {
return fmt.Errorf("linkding: unable to send entry: %v", err)
}
if response.HasServerFailure() {
return fmt.Errorf("linkding: unable to send entry, status=%d", response.StatusCode)
}
return nil
}
func getAPIEndpoint(baseURL, pathURL string) (string, error) {
u, err := url.Parse(baseURL)
if err != nil {
return "", fmt.Errorf("linkding: invalid API endpoint: %v", err)
}
relative, err := url.Parse(pathURL)
if err != nil {
return "", fmt.Errorf("linkding: invalid API endpoint: %v", err)
}
u = u.ResolveReference(relative)
return u.String(), nil
}
-10
View File
@@ -1,10 +0,0 @@
// Copyright 2018 Frédéric Guillot. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.
/*
Package nunuxkeeper provides an integration with the Nunux Keeper application.
*/
package nunuxkeeper // import "miniflux.app/integration/nunuxkeeper"
+5 -5
View File
@@ -26,6 +26,11 @@ type Client struct {
apiKey string
}
// NewClient returns a new Nunux Keeepr client.
func NewClient(baseURL, apiKey string) *Client {
return &Client{baseURL: baseURL, apiKey: apiKey}
}
// AddEntry sends an entry to Nunux Keeper.
func (c *Client) AddEntry(link, title, content string) error {
if c.baseURL == "" || c.apiKey == "" {
@@ -58,11 +63,6 @@ func (c *Client) AddEntry(link, title, content string) error {
return nil
}
// NewClient returns a new Nunux Keeepr client.
func NewClient(baseURL, apiKey string) *Client {
return &Client{baseURL: baseURL, apiKey: apiKey}
}
func getAPIEndpoint(baseURL, pathURL string) (string, error) {
u, err := url.Parse(baseURL)
if err != nil {
-10
View File
@@ -1,10 +0,0 @@
// Copyright 2018 Frédéric Guillot. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.
/*
Package pinboard provides an integration with Pinboard.
*/
package pinboard // import "miniflux.app/integration/pinboard"
+5 -5
View File
@@ -16,6 +16,11 @@ type Client struct {
authToken string
}
// NewClient returns a new Pinboard client.
func NewClient(authToken string) *Client {
return &Client{authToken: authToken}
}
// AddBookmark sends a link to Pinboard.
func (c *Client) AddBookmark(link, title, tags string, markAsUnread bool) error {
if c.authToken == "" {
@@ -46,8 +51,3 @@ func (c *Client) AddBookmark(link, title, tags string, markAsUnread bool) error
return nil
}
// NewClient returns a new Pinboard client.
func NewClient(authToken string) *Client {
return &Client{authToken: authToken}
}
+8 -8
View File
@@ -7,7 +7,7 @@ package pocket // import "miniflux.app/integration/pocket"
import (
"errors"
"fmt"
"io/ioutil"
"io"
"net/url"
"miniflux.app/http/client"
@@ -18,6 +18,11 @@ type Connector struct {
consumerKey string
}
// NewConnector returns a new Pocket Connector.
func NewConnector(consumerKey string) *Connector {
return &Connector{consumerKey}
}
// RequestToken fetches a new request token from Pocket API.
func (c *Connector) RequestToken(redirectURL string) (string, error) {
type req struct {
@@ -35,7 +40,7 @@ func (c *Connector) RequestToken(redirectURL string) (string, error) {
return "", fmt.Errorf("pocket: unable to fetch request token, status=%d", response.StatusCode)
}
body, err := ioutil.ReadAll(response.Body)
body, err := io.ReadAll(response.Body)
if err != nil {
return "", fmt.Errorf("pocket: unable to read response body: %v", err)
}
@@ -70,7 +75,7 @@ func (c *Connector) AccessToken(requestToken string) (string, error) {
return "", fmt.Errorf("pocket: unable to fetch access token, status=%d", response.StatusCode)
}
body, err := ioutil.ReadAll(response.Body)
body, err := io.ReadAll(response.Body)
if err != nil {
return "", fmt.Errorf("pocket: unable to read response body: %v", err)
}
@@ -96,8 +101,3 @@ func (c *Connector) AuthorizationURL(requestToken, redirectURL string) string {
redirectURL,
)
}
// NewConnector returns a new Pocket Connector.
func NewConnector(consumerKey string) *Connector {
return &Connector{consumerKey}
}
-10
View File
@@ -1,10 +0,0 @@
// Copyright 2018 Frédéric Guillot. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.
/*
Package pocket provides an integration with Pocket.
*/
package pocket // import "miniflux.app/integration/pocket"
+5 -5
View File
@@ -16,6 +16,11 @@ type Client struct {
accessToken string
}
// NewClient returns a new Pocket client.
func NewClient(consumerKey, accessToken string) *Client {
return &Client{consumerKey, accessToken}
}
// AddURL sends a single link to Pocket.
func (c *Client) AddURL(link, title string) error {
if c.consumerKey == "" || c.accessToken == "" {
@@ -48,8 +53,3 @@ func (c *Client) AddURL(link, title string) error {
return nil
}
// NewClient returns a new Pocket client.
func NewClient(consumerKey, accessToken string) *Client {
return &Client{consumerKey, accessToken}
}
+51
View File
@@ -0,0 +1,51 @@
// Copyright 2021 Frédéric Guillot. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.
package telegrambot // import "miniflux.app/integration/telegrambot"
import (
"bytes"
"fmt"
"html/template"
"strconv"
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api"
"miniflux.app/model"
)
// PushEntry pushes entry to telegram chat using integration settings provided
func PushEntry(entry *model.Entry, botToken, chatID string) error {
bot, err := tgbotapi.NewBotAPI(botToken)
if err != nil {
return fmt.Errorf("telegrambot: bot creation failed: %w", err)
}
tpl, err := template.New("message").Parse("{{ .Title }}\n<a href=\"{{ .URL }}\">{{ .URL }}</a>")
if err != nil {
return fmt.Errorf("telegrambot: template parsing failed: %w", err)
}
var result bytes.Buffer
if err := tpl.Execute(&result, entry); err != nil {
return fmt.Errorf("telegrambot: template execution failed: %w", err)
}
chatIDInt, _ := strconv.ParseInt(chatID, 10, 64)
msg := tgbotapi.NewMessage(chatIDInt, result.String())
msg.ParseMode = tgbotapi.ModeHTML
msg.DisableWebPagePreview = false
if entry.CommentsURL != "" {
msg.ReplyMarkup = tgbotapi.NewInlineKeyboardMarkup(
tgbotapi.NewInlineKeyboardRow(
tgbotapi.NewInlineKeyboardButtonURL("Comments", entry.CommentsURL),
))
}
if _, err := bot.Send(msg); err != nil {
return fmt.Errorf("telegrambot: sending message failed: %w", err)
}
return nil
}
-10
View File
@@ -1,10 +0,0 @@
// Copyright 2018 Frédéric Guillot. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.
/*
Package wallabag provides an integration with the Wallabag application.
*/
package wallabag // import "miniflux.app/integration/wallabag"
+10 -9
View File
@@ -22,8 +22,14 @@ type Client struct {
password string
}
// NewClient returns a new Wallabag client.
func NewClient(baseURL, clientID, clientSecret, username, password string) *Client {
return &Client{baseURL, clientID, clientSecret, username, password}
}
// AddEntry sends a link to Wallabag.
func (c *Client) AddEntry(link, title string) error {
// Pass an empty string in `content` to let Wallabag fetch the article content.
func (c *Client) AddEntry(link, title, content string) error {
if c.baseURL == "" || c.clientID == "" || c.clientSecret == "" || c.username == "" || c.password == "" {
return fmt.Errorf("wallabag: missing credentials")
}
@@ -33,10 +39,10 @@ func (c *Client) AddEntry(link, title string) error {
return err
}
return c.createEntry(accessToken, link, title)
return c.createEntry(accessToken, link, title, content)
}
func (c *Client) createEntry(accessToken, link, title string) error {
func (c *Client) createEntry(accessToken, link, title, content string) error {
endpoint, err := getAPIEndpoint(c.baseURL, "/api/entries.json")
if err != nil {
return fmt.Errorf("wallbag: unable to get entries endpoint: %v", err)
@@ -44,7 +50,7 @@ func (c *Client) createEntry(accessToken, link, title string) error {
clt := client.New(endpoint)
clt.WithAuthorization("Bearer " + accessToken)
response, err := clt.PostJSON(map[string]string{"url": link, "title": title})
response, err := clt.PostJSON(map[string]string{"url": link, "title": title, "content": content})
if err != nil {
return fmt.Errorf("wallabag: unable to post entry: %v", err)
}
@@ -87,11 +93,6 @@ func (c *Client) getAccessToken() (string, error) {
return token.AccessToken, nil
}
// NewClient returns a new Wallabag client.
func NewClient(baseURL, clientID, clientSecret, username, password string) *Client {
return &Client{baseURL, clientID, clientSecret, username, password}
}
func getAPIEndpoint(baseURL, path string) (string, error) {
u, err := url.Parse(baseURL)
if err != nil {
+30 -11
View File
@@ -5,6 +5,7 @@
package locale // import "miniflux.app/locale"
import (
"embed"
"encoding/json"
"fmt"
)
@@ -14,23 +15,41 @@ type catalog map[string]translationDict
var defaultCatalog catalog
func init() {
//go:embed translations/*.json
var translationFiles embed.FS
// LoadCatalogMessages loads and parses all translations encoded in JSON.
func LoadCatalogMessages() error {
var err error
defaultCatalog = make(catalog)
for language, data := range translations {
messages, err := parseTranslationDict(data)
for language := range AvailableLanguages() {
defaultCatalog[language], err = loadTranslationFile(language)
if err != nil {
panic(err)
return err
}
defaultCatalog[language] = messages
}
return nil
}
func parseTranslationDict(data string) (translationDict, error) {
var translations translationDict
if err := json.Unmarshal([]byte(data), &translations); err != nil {
return nil, fmt.Errorf("invalid translation file: %v", err)
func loadTranslationFile(language string) (translationDict, error) {
translationFileData, err := translationFiles.ReadFile(fmt.Sprintf("translations/%s.json", language))
if err != nil {
return nil, err
}
return translations, nil
translationMessages, err := parseTranslationMessages(translationFileData)
if err != nil {
return nil, err
}
return translationMessages, nil
}
func parseTranslationMessages(data []byte) (translationDict, error) {
var translationMessages translationDict
if err := json.Unmarshal(data, &translationMessages); err != nil {
return nil, fmt.Errorf(`invalid translation file: %w`, err)
}
return translationMessages, nil
}
+59 -2
View File
@@ -7,14 +7,14 @@ package locale // import "miniflux.app/locale"
import "testing"
func TestParserWithInvalidData(t *testing.T) {
_, err := parseTranslationDict(`{`)
_, err := parseTranslationMessages([]byte(`{`))
if err == nil {
t.Fatal(`An error should be returned when parsing invalid data`)
}
}
func TestParser(t *testing.T) {
translations, err := parseTranslationDict(`{"k": "v"}`)
translations, err := parseTranslationMessages([]byte(`{"k": "v"}`))
if err != nil {
t.Fatalf(`Unexpected parsing error: %v`, err)
}
@@ -32,3 +32,60 @@ func TestParser(t *testing.T) {
t.Fatal(`The translation key should contains the defined value`)
}
}
func TestLoadCatalog(t *testing.T) {
if err := LoadCatalogMessages(); err != nil {
t.Fatal(err)
}
}
func TestAllKeysHaveValue(t *testing.T) {
for language := range AvailableLanguages() {
messages, err := loadTranslationFile(language)
if err != nil {
t.Fatalf(`Unable to load translation messages for language %q`, language)
}
if len(messages) == 0 {
t.Fatalf(`The language %q doesn't have any messages`, language)
}
for k, v := range messages {
switch value := v.(type) {
case string:
if value == "" {
t.Errorf(`The key %q for the language %q have an empty string as value`, k, language)
}
case []string:
if len(value) == 0 {
t.Errorf(`The key %q for the language %q have an empty list as value`, k, language)
}
}
}
}
}
func TestMissingTranslations(t *testing.T) {
refLang := "en_US"
references, err := loadTranslationFile(refLang)
if err != nil {
t.Fatal(`Unable to parse reference language`)
}
for language := range AvailableLanguages() {
if language == refLang {
continue
}
messages, err := loadTranslationFile(language)
if err != nil {
t.Fatalf(`Parsing error for language %q`, language)
}
for key := range references {
if _, found := messages[key]; !found {
t.Fatalf(`Translation key %q not found in language %q`, key, language)
}
}
}
}
+4
View File
@@ -14,9 +14,13 @@ func AvailableLanguages() map[string]string {
"pl_PL": "Polski",
"pt_BR": "Português Brasileiro",
"zh_CN": "简体中文",
"zh_TW": "繁體中文",
"nl_NL": "Nederlands",
"ru_RU": "Русский",
"it_IT": "Italiano",
"ja_JP": "日本語",
"tr_TR": "Türkçe",
"el_EL": "Ελληνικά",
"fi_FI": "Suomi",
}
}
+8 -8
View File
@@ -8,12 +8,12 @@ import "testing"
func TestPluralRules(t *testing.T) {
scenarios := map[string]map[int]int{
"default": map[int]int{
"default": {
1: 0,
2: 1,
5: 1,
},
"ar_AR": map[int]int{
"ar_AR": {
0: 0,
1: 1,
2: 2,
@@ -21,32 +21,32 @@ func TestPluralRules(t *testing.T) {
11: 4,
200: 5,
},
"cs_CZ": map[int]int{
"cs_CZ": {
1: 0,
2: 1,
5: 2,
},
"pl_PL": map[int]int{
"pl_PL": {
1: 0,
2: 1,
5: 2,
},
"pt_BR": map[int]int{
"pt_BR": {
1: 0,
2: 1,
5: 1,
},
"ru_RU": map[int]int{
"ru_RU": {
1: 0,
2: 1,
5: 2,
},
"sr_RS": map[int]int{
"sr_RS": {
1: 0,
2: 1,
5: 2,
},
"zh_CN": map[int]int{
"zh_CN": {
1: 0,
5: 0,
},
File diff suppressed because it is too large Load Diff
+37 -2
View File
@@ -92,6 +92,7 @@
"Es gibt %d Abonnement.",
"Es gibt %d Abonnements."
],
"page.categories.unread_counter": "Anzahl der ungelesenen Artikel",
"page.new_category.title": "Neue Kategorie",
"page.new_user.title": "Neuer Benutzer",
"page.edit_category.title": "Kategorie bearbeiten: %s",
@@ -114,6 +115,8 @@
"page.about.author": "Autor:",
"page.about.license": "Lizenz:",
"page.about.global_config_options": "Globale Konfigurationsoptionen",
"page.about.postgres_version": "Postgres Version:",
"page.about.go_version": "Go Version:",
"page.add_feed.title": "Neues Abonnement",
"page.add_feed.no_category": "Es ist keine Kategorie vorhanden. Wenigstens eine Kategorie muss angelegt sein.",
"page.add_feed.label.url": "URL",
@@ -149,7 +152,8 @@
"page.keyboard_shortcuts.open_original_same_window": "Öffne den Original-Link in der aktuellen Registerkarte",
"page.keyboard_shortcuts.open_comments": "Kommentare öffnen",
"page.keyboard_shortcuts.open_comments_same_window": "Öffne den Kommentare-Link in der aktuellen Registerkarte",
"page.keyboard_shortcuts.toggle_read_status": "Gewählten Artikel als gelesen/ungelesen markieren",
"page.keyboard_shortcuts.toggle_read_status_next": "Gewählten Artikel als gelesen/ungelesen markieren, fokus als nächstes",
"page.keyboard_shortcuts.toggle_read_status_prev": "Gewählten Artikel als gelesen/ungelesen markieren, fokus vorherige",
"page.keyboard_shortcuts.refresh_all_feeds": "Alle Abonnements im Hintergrund aktualisieren",
"page.keyboard_shortcuts.mark_page_as_read": "Aktuelle Seite als gelesen markieren",
"page.keyboard_shortcuts.download_content": "Vollständigen Inhalt herunterladen",
@@ -199,6 +203,9 @@
"page.api_keys.table.actions": "Aktionen",
"page.api_keys.never_used": "Nie benutzt",
"page.new_api_key.title": "Neuer API-Schlüssel",
"page.offline.title": "Offline-Modus",
"page.offline.message": "Du bist offline",
"page.offline.refresh_page": "Versuchen Sie, die Seite zu aktualisieren",
"alert.no_shared_entry": "Es existieren derzeit keine geteilten Artikel.",
"alert.no_bookmark": "Es existiert derzeit kein Lesezeichen.",
"alert.no_category": "Es ist keine Kategorie vorhanden.",
@@ -218,6 +225,7 @@
"error.unlink_account_without_password": "Sie müssen ein Passwort festlegen, sonst können Sie sich nicht erneut anmelden.",
"error.duplicate_linked_account": "Es ist bereits jemand mit diesem Anbieter assoziiert!",
"error.duplicate_fever_username": "Es existiert bereits jemand mit diesem Fever Benutzernamen!",
"error.duplicate_googlereader_username": "Es existiert bereits jemand mit diesem Google Reader Benutzernamen!",
"error.pocket_request_token": "Anfrage-Token konnte nicht von Pocket abgerufen werden!",
"error.pocket_access_token": "Zugriffstoken konnte nicht von Pocket abgerufen werden!",
"error.category_already_exists": "Diese Kategorie existiert bereits.",
@@ -253,6 +261,7 @@
"error.invalid_language": "Ungültige Sprache.",
"error.invalid_timezone": "Ungültige Zeitzone.",
"error.invalid_entry_direction": "Ungültige Sortierreihenfolge.",
"error.invalid_display_mode": "Ungültiger Web-App-Anzeigemodus.",
"form.feed.label.title": "Titel",
"form.feed.label.site_url": "Webseite-URL",
"form.feed.label.feed_url": "Abonnement-URL",
@@ -261,14 +270,18 @@
"form.feed.label.feed_username": "Benutzername des Abonnements",
"form.feed.label.feed_password": "Passwort des Abonnements",
"form.feed.label.user_agent": "Standardbenutzeragenten überschreiben",
"form.feed.label.cookie": "Cookies setzen",
"form.feed.label.scraper_rules": "Extraktionsregeln",
"form.feed.label.rewrite_rules": "Umschreiberegeln",
"form.feed.label.blocklist_rules": "Blockierregeln",
"form.feed.label.keeplist_rules": "Erlaubnisregeln",
"form.feed.label.ignore_http_cache": "Ignoriere HTTP-cache",
"form.feed.label.allow_self_signed_certificates": "Erlaube selbstsignierte oder ungültige Zertifikate",
"form.feed.label.fetch_via_proxy": "Über Proxy abrufen",
"form.feed.label.disabled": "Dieses Abonnement nicht aktualisieren",
"form.feed.label.hide_globally": "Einträge in der globalen Ungelesen-Liste ausblenden",
"form.category.label.title": "Titel",
"form.category.hide_globally": "Einträge in der globalen Ungelesen-Liste ausblenden",
"form.user.label.username": "Benutzername",
"form.user.label.password": "Passwort",
"form.user.label.confirmation": "Passwort Bestätigung",
@@ -278,18 +291,30 @@
"form.prefs.label.theme": "Thema",
"form.prefs.label.entry_sorting": "Sortierung der Artikel",
"form.prefs.label.entries_per_page": "Einträge pro Seite",
"form.prefs.label.display_mode": "Anzeigemodus der Web-App (muss neu installiert werden)",
"form.prefs.select.older_first": "Älteste Artikel zuerst",
"form.prefs.select.recent_first": "Neueste Artikel zuerst",
"form.prefs.select.fullscreen": "Vollbildschirm",
"form.prefs.select.standalone": "Eigenständige",
"form.prefs.select.minimal_ui": "Minimal",
"form.prefs.select.browser": "Browser",
"form.prefs.select.publish_time": "Eintrag veröffentlichte Zeit",
"form.prefs.select.created_time": "Eintrag erstellt Zeit",
"form.prefs.label.keyboard_shortcuts": "Tastaturkürzel aktivieren",
"form.prefs.label.entry_swipe": "Wischgeste für Einträge auf dem Handy aktivieren",
"form.prefs.label.show_reading_time": "Geschätzte Lesezeit für Artikel anzeigen",
"form.prefs.label.custom_css": "Benutzerdefiniertes CSS",
"form.prefs.label.entry_order": "Eintrag Sortierspalte",
"form.import.label.file": "OPML Datei",
"form.import.label.url": "URL",
"form.integration.fever_activate": "Fever API aktivieren",
"form.integration.fever_username": "Fever Benutzername",
"form.integration.fever_password": "Fever Passwort",
"form.integration.fever_endpoint": "Fever API Endpunkt:",
"form.integration.googlereader_activate": "Google Reader API aktivieren",
"form.integration.googlereader_username": "Google Reader Benutzername",
"form.integration.googlereader_password": "Google Reader Passwort",
"form.integration.googlereader_endpoint": "Google Reader API Endpunkt:",
"form.integration.pinboard_activate": "Artikel in Pinboard speichern",
"form.integration.pinboard_token": "Pinboard API Token",
"form.integration.pinboard_tags": "Pinboard Tags",
@@ -310,6 +335,16 @@
"form.integration.nunux_keeper_activate": "Artikel in Nunux Keeper speichern",
"form.integration.nunux_keeper_endpoint": "Nunux Keeper API-Endpunkt",
"form.integration.nunux_keeper_api_key": "Nunux Keeper API-Schlüssel",
"form.integration.espial_activate": "Artikel in Espial speichern",
"form.integration.espial_endpoint": "Espial API-Endpunkt",
"form.integration.espial_api_key": "Espial API-Schlüssel",
"form.integration.espial_tags": "Espial tags",
"form.integration.telegram_bot_activate": "Pushen Sie neue Artikel in den Telegram-Chat",
"form.integration.telegram_bot_token": "Bot token",
"form.integration.telegram_chat_id": "Chat ID",
"form.integration.linkding_activate": "Artikel in Linkding speichern",
"form.integration.linkding_endpoint": "Linkding API-Endpunkt",
"form.integration.linkding_api_key": "Linkding API-Schlüssel",
"form.api_key.label.description": "API-Schlüsselbezeichnung",
"form.submit.loading": "Lade...",
"form.submit.saving": "Speichern...",
@@ -359,5 +394,5 @@
"Website unreachable, the request timed out after %d seconds": "Webseite nicht erreichbar, die Anfrage endete nach %d Sekunden",
"You are not authorized to access this resource (invalid username/password)": "Sie sind nicht berechtigt, auf diese Ressource zuzugreifen (Benutzername/Passwort ungültig)",
"Unable to fetch this resource (Status Code = %d)": "Ressource konnte nicht abgerufen werden (code=%d)",
"Resource not found (404), this feed doesn't exists anymore, check the feed URL": "Ressource nicht gefunden (404), dieses Abonnement existiert nicht mehr, überprüfen Sie die Abonnement-URL"
"Resource not found (404), this feed doesn't exist anymore, check the feed URL": "Ressource nicht gefunden (404), dieses Abonnement existiert nicht mehr, überprüfen Sie die Abonnement-URL"
}
+378
View File
@@ -0,0 +1,378 @@
{
"confirm.question": "Είστε σίγουροι;",
"confirm.yes": "ναι",
"confirm.no": "όχι",
"confirm.loading": "Σε εξέλιξη...",
"action.subscribe": "Εγγραφείτε",
"action.save": "Αποθηκεύσετε",
"action.or": "ή",
"action.cancel": "ακύρωση",
"action.remove": "Κατάργηση",
"action.remove_feed": "Κατάργηση αυτής της ροής",
"action.update": "Ενημέρωση",
"action.edit": "Επεξεργασία",
"action.download": "Λήψη",
"action.import": "Εισαγωγή",
"action.login": "Σύνδεση",
"action.home_screen": "Προσθήκη στην αρχική οθόνη",
"tooltip.keyboard_shortcuts": "Συντόμευση πληκτρολογίου: % s",
"tooltip.logged_user": "Συνδεδεμένος/η ως %s",
"menu.unread": "Μη αναγνωσμένα",
"menu.starred": "Αγαπημένα",
"menu.history": "Ιστορικό",
"menu.feeds": "Ροές",
"menu.categories": "Κατηγορίες",
"menu.settings": "Ρυθμίσεις",
"menu.logout": "Αποσύνδεση",
"menu.preferences": "Προτιμήσεις",
"menu.integrations": "Ενσωμάτωσεις",
"menu.sessions": "Συνδέσεις",
"menu.users": "Χρήστες",
"menu.about": "Περί",
"menu.export": "Εξαγωγή",
"menu.import": "Εισαγωγή",
"menu.create_category": "Δημιουργήστε μια κατηγορία",
"menu.mark_page_as_read": "Σημείωση αυτής της σελίδας ως αναγνωσμένη",
"menu.mark_all_as_read": "Σημείωση όλων ως αναγνωσμένα",
"menu.show_all_entries": "Εμφάνιση όλων των καταχωρήσεων",
"menu.show_only_unread_entries": "Εμφάνιση μόνο μη αναγνωσμένων καταχωρήσεων",
"menu.refresh_feed": "Ανανέωση",
"menu.refresh_all_feeds": "Ανανέωση όλων των ροών στο παρασκήνιο",
"menu.edit_feed": "Επεξεργασία",
"menu.edit_category": "Επεξεργασία",
"menu.add_feed": "Προσθήκη συνδρομής",
"menu.add_user": "Προσθήκη χρήστη",
"menu.flush_history": "Εκκαθάριση ιστορικού",
"menu.feed_entries": "Καταχωρήσεις",
"menu.api_keys": "Κλειδιά API",
"menu.create_api_key": "Δημιουργήστε ένα νέο κλειδί API",
"menu.shared_entries": "Κοινόχρηστες καταχωρήσεις",
"search.label": "Αναζήτηση",
"search.placeholder": "Αναζήτηση...",
"pagination.next": "Επόμενη",
"pagination.previous": "Προηγούμενη",
"entry.status.unread": "Μη αναγνωσμένο",
"entry.status.read": "Αναγνωσμένο",
"entry.status.toast.unread": "Επισήμανση ως μη αναγνωσμένο",
"entry.status.toast.read": "Επισήμανση ως αναγνωσμένο",
"entry.status.title": "Αλλαγή κατάστασης καταχώρησης",
"entry.bookmark.toggle.on": "Αγαπημένο",
"entry.bookmark.toggle.off": "Αναίρεση αγαπημένου",
"entry.bookmark.toast.on": "Αγαπημένα",
"entry.bookmark.toast.off": "Μη αγαπημένα",
"entry.state.saving": "Aποθήκευση...",
"entry.state.loading": "Φόρτωση...",
"entry.save.label": "Αποθηκεύσετε",
"entry.save.title": "Αποθηκεύστε αυτό το άρθρο",
"entry.save.completed": "Έγινε!",
"entry.save.toast.completed": "Το άρθρο αποθηκεύτηκε",
"entry.scraper.label": "Λήψη",
"entry.scraper.title": "Λήψη αρχικού περιεχομένου",
"entry.scraper.completed": "Έγινε!",
"entry.external_link.label": "Εξωτερικός σύνδεσμος",
"entry.comments.label": "Σχόλια",
"entry.comments.title": "Δείτε Σχόλια",
"entry.share.label": "Διαμοιρασμός",
"entry.share.title": "Μοιραστείτε αυτό το άρθρο",
"entry.unshare.label": "Aναίρεση Διαμοιρασμού",
"entry.shared_entry.title": "Ανοίξτε τον δημόσιο σύνδεσμο",
"entry.shared_entry.label": "Διαμοιρασμός",
"entry.estimated_reading_time": [
"%d λεπτό ανάγνωση",
"%d λεπτά ανάγνωση"
],
"page.shared_entries.title": "Κοινόχρηστες Καταχωρήσεις",
"page.unread.title": "Μη αναγνωσμένα",
"page.starred.title": "Αγαπημένo",
"page.categories.title": "Κατηγορίες",
"page.categories.no_feed": "Καμία ροή.",
"page.categories.entries": "Άρθρα",
"page.categories.feeds": "Συνδρομές",
"page.categories.feed_count": [
"Υπάρχει μία %d ροή.",
"Υπάρχουν %d ροές."
],
"page.categories.unread_counter": "Αριθμός μη αναγνωσμένων καταχωρήσεων",
"page.new_category.title": "Νέα Κατηγορία",
"page.new_user.title": "Νέος Χρήστης",
"page.edit_category.title": "Επεξεργασία κατηγορίας: % s",
"page.edit_user.title": "Επεξεργασία χρήστη: % s",
"page.feeds.title": "Ροές",
"page.feeds.last_check": "Τελευταίος έλεγχος:",
"page.feeds.unread_counter": "Αριθμός μη αναγνωσμένων καταχωρήσεων",
"page.feeds.read_counter": "Αριθμός αναγνωσμένων καταχωρήσεων",
"page.feeds.error_count": [
"%d σφάλμα",
"%d σφάλματα"
],
"page.history.title": "Ιστορικό",
"page.import.title": "Εισαγωγή",
"page.search.title": "Αποτελέσματα Αναζήτησης",
"page.about.title": "Περί",
"page.about.credits": "Συνεισφέροντες",
"page.about.version": "Έκδοση:",
"page.about.build_date": "Ημερομηνία Κατασκευής:",
"page.about.author": "Συγγραφέας:",
"page.about.license": "Άδεια:",
"page.about.global_config_options": "Γενικές ρυθμίσεις",
"page.about.postgres_version": "Έκδοση Postgres:",
"page.about.go_version": "Έκδοση Go:",
"page.add_feed.title": "Νέα Συνδρομή",
"page.add_feed.no_category": "Δεν υπάρχει κατηγορία. Πρέπει να έχετε τουλάχιστον μία κατηγορία.",
"page.add_feed.label.url": "URL",
"page.add_feed.submit": "Βρείτε μια συνδρομή",
"page.add_feed.legend.advanced_options": "Προχωρημένες Επιλογές",
"page.add_feed.choose_feed": "Επιλέξτε μια συνδρομή",
"page.edit_feed.title": "Επεξεργασία ροής: % s",
"page.edit_feed.last_check": "Τελευταίος έλεγχος:",
"page.edit_feed.last_modified_header": "LastModified κεφαλίδα:",
"page.edit_feed.etag_header": "Κεφαλίδα ETag:",
"page.edit_feed.no_header": "Καμία",
"page.edit_feed.last_parsing_error": "Τελευταίο Σφάλμα Ανάλυσης",
"page.entry.attachments": "Συνημμένα",
"page.keyboard_shortcuts.title": "Συντομεύσεις Πληκτρολογίου",
"page.keyboard_shortcuts.subtitle.sections": "Πλοήγηση Τμημάτων",
"page.keyboard_shortcuts.subtitle.items": "Πλοήγηση Στοιχείων",
"page.keyboard_shortcuts.subtitle.pages": "Πλοήγηση Σελίδων",
"page.keyboard_shortcuts.subtitle.actions": "Ενέργειες",
"page.keyboard_shortcuts.go_to_unread": "Μεταβείτε στα μη αναγνωσμένα",
"page.keyboard_shortcuts.go_to_starred": "Μεταβείτε στους σελιδοδείκτες",
"page.keyboard_shortcuts.go_to_history": "Μεταβείτε στο ιστορικό",
"page.keyboard_shortcuts.go_to_feeds": "Μεταβείτε στις ροές",
"page.keyboard_shortcuts.go_to_categories": "Μεταβείτε στις κατηγορίες",
"page.keyboard_shortcuts.go_to_settings": "Μεταβείτε στις ρυθμίσεις",
"page.keyboard_shortcuts.show_keyboard_shortcuts": "Εμφάνιση συντομεύσεων πληκτρολογίου",
"page.keyboard_shortcuts.go_to_previous_item": "Μεταβείτε στο προηγούμενο στοιχείο",
"page.keyboard_shortcuts.go_to_next_item": "Μετάβαση στο επόμενο στοιχείο",
"page.keyboard_shortcuts.go_to_feed": "Πηγαίνετε στη ροή",
"page.keyboard_shortcuts.go_to_previous_page": "Μετάβαση στην προηγούμενη σελίδα",
"page.keyboard_shortcuts.go_to_next_page": "Μετάβαση στην επόμενη σελίδα",
"page.keyboard_shortcuts.open_item": "Άνοιγμα επιλεγμένου στοιχείου",
"page.keyboard_shortcuts.open_original": "Άνοιγμα αρχικού συνδέσμου",
"page.keyboard_shortcuts.open_original_same_window": "Άνοιγμα αρχικού συνδέσμου στην τρέχουσα καρτέλα",
"page.keyboard_shortcuts.open_comments": "Άνοιγμα συνδέσμου σχολίων",
"page.keyboard_shortcuts.open_comments_same_window": "Άνοιγμα συνδέσμου σχολίων στην τρέχουσα καρτέλα",
"page.keyboard_shortcuts.toggle_read_status_next": "Εναλλαγή ανάγνωσης / μη αναγνωσμένης, εστίαση στη συνέχεια",
"page.keyboard_shortcuts.toggle_read_status_prev": "Εναλλαγή ανάγνωσης / μη αναγνωσμένης, εστίαση στο προηγούμενο",
"page.keyboard_shortcuts.refresh_all_feeds": "Ανανέωση όλων των ροών στο παρασκήνιο",
"page.keyboard_shortcuts.mark_page_as_read": "Σημείωση της τρέχουσας σελίδας ως αναγνωσμένη",
"page.keyboard_shortcuts.download_content": "Κατεβάστε το αρχικό περιεχόμενο",
"page.keyboard_shortcuts.toggle_bookmark_status": "Εναλλαγή σελιδοδείκτη",
"page.keyboard_shortcuts.save_article": "Αποθήκευση άρθρου",
"page.keyboard_shortcuts.scroll_item_to_top": "Μετακινηση στοιχείου στην κορυφή",
"page.keyboard_shortcuts.remove_feed": "Κατάργηση αυτής της ροής",
"page.keyboard_shortcuts.go_to_search": "Ορίστε εστίαση στη φόρμα αναζήτησης",
"page.keyboard_shortcuts.close_modal": "Κλείσιμο παραθύρου διαλόγου",
"page.users.title": "Χρήστες",
"page.users.username": "Χρήστης",
"page.users.never_logged": "Ποτέ",
"page.users.admin.yes": "Ναι.",
"page.users.admin.no": "Όχι",
"page.users.actions": "Eνέργειες",
"page.users.last_login": "Τελευταία Σύνδεση",
"page.users.is_admin": "Διαχειριστής",
"page.settings.title": "Ρυθμίσεις",
"page.settings.link_google_account": "Σύνδεση του λογαριασμό μου Google",
"page.settings.unlink_google_account": "Αποσύνδεση του λογαριασμού μου Google",
"page.settings.link_oidc_account": "Σύνδεση του λογαριασμού μου OpenID Connect",
"page.settings.unlink_oidc_account": "Αποσύνδεση του λογαριασμού μου OpenID Connect",
"page.login.title": "Είσοδος",
"page.login.google_signin": "Συνδεθείτε με τo Google",
"page.login.oidc_signin": "Συνδεθείτε με το OpenID Connect",
"page.integrations.title": "Ενσωμάτωση",
"page.integration.miniflux_api": "Miniflux API",
"page.integration.miniflux_api_endpoint": "Τελικό σημείο API",
"page.integration.miniflux_api_username": "Χρήστης",
"page.integration.miniflux_api_password": "Κωδικός",
"page.integration.miniflux_api_password_value": "Ο κωδικός πρόσβασης του λογαριασμού σας",
"page.integration.bookmarklet": "Bookmarklet",
"page.integration.bookmarklet.name": "Προσθήκη στο Miniflux",
"page.integration.bookmarklet.instructions": "Σύρετε και αποθέστε αυτόν τον σύνδεσμο στους σελιδοδείκτες σας.",
"page.integration.bookmarklet.help": "Αυτός ο ειδικός σύνδεσμος σάς επιτρέπει να εγγραφείτε απευθείας σε έναν ιστότοπο χρησιμοποιώντας ένα σελιδοδείκτη στο πρόγραμμα περιήγησης ιστού σας.",
"page.sessions.title": "Συνεδρίες",
"page.sessions.table.date": "Ημερομηνία",
"page.sessions.table.ip": "Διεύθυνση IP",
"page.sessions.table.user_agent": "User Agent",
"page.sessions.table.actions": "Eνέργειες",
"page.sessions.table.current_session": "Τρέχουσα Συνεδρία",
"page.api_keys.title": "Κλειδιά API",
"page.api_keys.table.description": "Περιγραφή",
"page.api_keys.table.token": "Token",
"page.api_keys.table.last_used_at": "Τελευταία Χρήση",
"page.api_keys.table.created_at": "Ημερομηνία Δημιουργίας",
"page.api_keys.table.actions": "Eνέργειες",
"page.api_keys.never_used": "Δεν έχει χρησιμοποιηθεί ποτέ",
"page.new_api_key.title": "Νέο κλειδί API",
"page.offline.title": "Λειτουργία Εκτός Σύνδεσης",
"page.offline.message": "Είστε εκτός σύνδεσης",
"page.offline.refresh_page": "Προσπαθήστε να ανανεώσετε τη σελίδα",
"alert.no_shared_entry": "Δεν υπάρχει κοινόχρηστη καταχώρηση.",
"alert.no_bookmark": "Δεν υπάρχει σελιδοδείκτης αυτή τη στιγμή.",
"alert.no_category": "Δεν υπάρχει κατηγορία.",
"alert.no_category_entry": "Δεν υπάρχουν άρθρα σε αυτήν την κατηγορία.",
"alert.no_feed_entry": "Δεν υπάρχουν άρθρα για αυτήν τη ροή.",
"alert.no_feed": "Δεν έχετε συνδρομές.",
"alert.no_feed_in_category": "Δεν υπάρχει συνδρομή για αυτήν την κατηγορία.",
"alert.no_history": "Δεν υπάρχει ιστορικό αυτή τη στιγμή.",
"alert.feed_error": "Υπάρχει πρόβλημα με αυτήν τη ροή",
"alert.no_search_result": "Δεν υπάρχουν αποτελέσματα για αυτήν την αναζήτηση.",
"alert.no_unread_entry": "Δεν υπάρχουν μη αναγνωσμένα άρθρα.",
"alert.no_user": "Είστε ο μόνος χρήστης.",
"alert.account_unlinked": "Ο εξωτερικός σας λογαριασμός είναι πλέον αποσυνδεδεμένος!",
"alert.account_linked": "Ο εξωτερικός σας λογαριασμός είναι πλέον συνδεδεμένος!",
"alert.pocket_linked": "Ο λογαριασμός Pocket είναι τώρα συνδεδεμένος!",
"alert.prefs_saved": "Οι προτιμήσεις αποθηκεύτηκαν!",
"error.unlink_account_without_password": "Πρέπει να ορίσετε έναν κωδικό πρόσβασης διαφορετικά δεν θα μπορείτε να συνδεθείτε ξανά.",
"error.duplicate_linked_account": "Υπάρχει ήδη κάποιος που σχετίζεται με αυτόν τον πάροχο!",
"error.duplicate_fever_username": "Υπάρχει ήδη κάποιος άλλος με το ίδιο όνομα χρήστη Fever!",
"error.duplicate_googlereader_username": "Υπάρχει ήδη κάποιος άλλος με το ίδιο όνομα χρήστη Google Reader!",
"error.pocket_request_token": "Δεν είναι δυνατή η λήψη του request token από το Pocket!",
"error.pocket_access_token": "Δεν είναι δυνατή η λήψη του access token από το Pocket!",
"error.category_already_exists": "Αυτή η κατηγορία υπάρχει ήδη.",
"error.unable_to_create_category": "Δεν είναι δυνατή η δημιουργία αυτής της κατηγορίας.",
"error.unable_to_update_category": "Δεν είναι δυνατή η ενημέρωση αυτής της κατηγορίας.",
"error.user_already_exists": "Αυτός ο χρήστης υπάρχει ήδη.",
"error.unable_to_create_user": "Δεν είναι δυνατή η δημιουργία αυτού του χρήστη.",
"error.unable_to_update_user": "Δεν είναι δυνατή η ενημέρωση αυτού του χρήστη.",
"error.unable_to_update_feed": "Δεν είναι δυνατή η ενημέρωση αυτής της ροής.",
"error.subscription_not_found": "Δεν είναι δυνατή η εύρεση συνδρομής.",
"error.invalid_theme": "Μη έγκυρο θέμα.",
"error.invalid_language": "Μη έγκυρη γλώσσα.",
"error.invalid_timezone": "Μη έγκυρη ζώνη ώρας.",
"error.invalid_entry_direction": "Μη έγκυρη κατεύθυνση ταξινόμησης άρθρων.",
"error.invalid_display_mode": "Μη έγκυρη λειτουργία εμφάνισης εφαρμογών ιστού.",
"error.empty_file": "Αυτό το αρχείο είναι κενό.",
"error.bad_credentials": "Μη έγκυρο όνομα χρήστη ή κωδικό πρόσβασης.",
"error.fields_mandatory": "Όλα τα πεδία είναι υποχρεωτικά.",
"error.title_required": "Ο τίτλος είναι υποχρεωτικός.",
"error.different_passwords": "Οι κωδικοί πρόσβασης δεν είναι οι ίδιοι.",
"error.password_min_length": "Ο κωδικός πρόσβασης πρέπει να έχει τουλάχιστον 6 χαρακτήρες.",
"error.settings_mandatory_fields": "Τα πεδία όνομα χρήστη, θέμα, Γλώσσα και ζώνη ώρας είναι υποχρεωτικά.",
"error.entries_per_page_invalid": "Ο αριθμός των καταχωρήσεων ανά σελίδα δεν είναι έγκυρος.",
"error.feed_mandatory_fields": "Η διεύθυνση URL και η κατηγορία είναι υποχρεωτικά.",
"error.feed_already_exists": "Αυτή η ροή υπάρχει ήδη.",
"error.invalid_feed_url": "Μη έγκυρη διεύθυνση URL ροής.",
"error.invalid_site_url": "Μη έγκυρη διεύθυνση URL ιστότοπου.",
"error.feed_url_not_empty": "Η διεύθυνση URL ροής δεν μπορεί να είναι κενή.",
"error.site_url_not_empty": "Η διεύθυνση URL του ιστότοπου δεν μπορεί να είναι κενή.",
"error.feed_title_not_empty": "Ο τίτλος ροής δεν μπορεί να είναι κενός.",
"error.feed_category_not_found": "Αυτή η κατηγορία δεν υπάρχει ή δεν ανήκει σε αυτόν τον χρήστη.",
"error.feed_invalid_blocklist_rule": "Ο κανόνας λίστας μπλοκ δεν είναι έγκυρος.",
"error.feed_invalid_keeplist_rule": "Ο κανόνας keep list δεν είναι έγκυρος.",
"error.user_mandatory_fields": "Το όνομα χρήστη είναι υποχρεωτικό.",
"error.api_key_already_exists": "Αυτό το κλειδί API υπάρχει ήδη.",
"error.unable_to_create_api_key": "Δεν είναι δυνατή η δημιουργία αυτού του κλειδιού API.",
"form.feed.label.title": "Τίτλος",
"form.feed.label.site_url": "Διεύθυνση URL ιστότοπου",
"form.feed.label.feed_url": "Διεύθυνση URL ροής",
"form.feed.label.category": "Κατηγορία",
"form.feed.label.crawler": "Λήψη αρχικού περιεχομένου",
"form.feed.label.feed_username": "Όνομα Χρήστη ροής",
"form.feed.label.feed_password": "Κωδικός Πρόσβασης ροής",
"form.feed.label.user_agent": "Παράκαμψη Προεπιλεγμένου User Agent Χρήστη",
"form.feed.label.cookie": "Ορισμός Cookies",
"form.feed.label.scraper_rules": "Κανόνες Scraper",
"form.feed.label.rewrite_rules": "Κανόνες Μετατροπής",
"form.feed.label.blocklist_rules": "Κανόνες Αποκλεισμού",
"form.feed.label.keeplist_rules": "Κρατήστε Κανόνες",
"form.feed.label.ignore_http_cache": "Αγνοήστε την προσωρινή μνήμη HTTP",
"form.feed.label.allow_self_signed_certificates": "Να επιτρέπονται αυτο-υπογεγραμμένα ή μη έγκυρα πιστοποιητικά",
"form.feed.label.fetch_via_proxy": "Λήψη μέσω διακομιστή μεσολάβησης",
"form.feed.label.disabled": "Μη ανανέωση αυτής της ροής",
"form.feed.label.hide_globally": "Απόκρυψη καταχωρήσεων σε γενική λίστα μη αναγνωσμένων",
"form.category.label.title": "Τίτλος",
"form.category.hide_globally": "Απόκρυψη καταχωρήσεων σε γενική λίστα μη αναγνωσμένων",
"form.user.label.username": "Χρήστης",
"form.user.label.password": "Κωδικός",
"form.user.label.confirmation": "Επιβεβαίωση Κωδικού Πρόσβασης",
"form.user.label.admin": "Διαχειριστής",
"form.prefs.label.language": "Γλώσσα",
"form.prefs.label.timezone": "Ζώνη Ώρας",
"form.prefs.label.theme": "Θέμα",
"form.prefs.label.entry_sorting": "Ταξινόμηση",
"form.prefs.label.entries_per_page": "Καταχωρήσεις ανά σελίδα",
"form.prefs.label.display_mode": "Λειτουργία προβολής εφαρμογών ιστού (χρειάζεται επανεγκατάσταση)",
"form.prefs.select.older_first": "Παλαιότερες καταχωρήσεις πρώτα",
"form.prefs.select.recent_first": "Πρόσφατες καταχωρήσεις πρώτα",
"form.prefs.select.fullscreen": "Πλήρης οθόνη",
"form.prefs.select.standalone": "Μεμονωμένο",
"form.prefs.select.minimal_ui": "Ελάχιστη",
"form.prefs.select.browser": "Περιηγητής",
"form.prefs.select.publish_time": "Δημοσιευμένος χρόνος εισόδου",
"form.prefs.select.created_time": "Χρόνος δημιουργίας καταχώρησης",
"form.prefs.label.keyboard_shortcuts": "Ενεργοποίηση συντομεύσεων πληκτρολογίου",
"form.prefs.label.entry_swipe": "Ενεργοποιήστε τη χειρονομία σάρωσης στις καταχωρήσεις στο κινητό",
"form.prefs.label.show_reading_time": "Εμφάνιση εκτιμώμενου χρόνου ανάγνωσης για άρθρα",
"form.prefs.label.custom_css": "Προσαρμοσμένο CSS",
"form.prefs.label.entry_order": "Στήλη ταξινόμησης εισόδου",
"form.import.label.file": "Αρχείο OPML",
"form.import.label.url": "URL",
"form.integration.fever_activate": "Ενεργοποιήστε το Fever API",
"form.integration.fever_username": "Όνομα Χρήστη Fever",
"form.integration.fever_password": "Κωδικός Πρόσβασης Fever",
"form.integration.fever_endpoint": "Τελικό σημείο Fever API:",
"form.integration.googlereader_activate": "Ενεργοποιήστε το Google Reader API",
"form.integration.googlereader_username": "Όνομα Χρήστη Google Reader",
"form.integration.googlereader_password": "Κωδικός Πρόσβασης Google Reader",
"form.integration.googlereader_endpoint": "Τελικό σημείο Google Reader API:",
"form.integration.pinboard_activate": "Αποθήκευση άρθρων στο Pinboard",
"form.integration.pinboard_token": "Pinboard API Token",
"form.integration.pinboard_tags": "Ετικέτες Pinboard",
"form.integration.pinboard_bookmark": "Σημείωση του σελιδοδείκτη ως μη αναγνωσμένου",
"form.integration.instapaper_activate": "Αποθήκευση άρθρων στο Instapaper",
"form.integration.instapaper_username": "Όνομα Χρήστη Instapaper",
"form.integration.instapaper_password": "Κωδικός Πρόσβασης Instapaper",
"form.integration.pocket_activate": "Αποθήκευση άρθρων στο Pocket",
"form.integration.pocket_consumer_key": "Pocket Consumer Key",
"form.integration.pocket_access_token": "Pocket Access Token",
"form.integration.pocket_connect_link": "Συνδέστε τον λογαριασμό Pocket σας",
"form.integration.wallabag_activate": "Αποθήκευση άρθρων στο Wallabag",
"form.integration.wallabag_endpoint": "Τελικό σημείο Wallabag API ",
"form.integration.wallabag_client_id": "Ταυτότητα πελάτη Wallabag",
"form.integration.wallabag_client_secret": "Wallabag Μυστικό Πελάτη",
"form.integration.wallabag_username": "Όνομα Χρήστη Wallabag",
"form.integration.wallabag_password": "Wallabag Κωδικός Πρόσβασης",
"form.integration.nunux_keeper_activate": "Αποθήκευση άρθρων στο Nunux Keeper",
"form.integration.nunux_keeper_endpoint": "Τελικό σημείο Nunux Keeper API",
"form.integration.nunux_keeper_api_key": "Κλειδί API Nunux Keeper",
"form.integration.espial_activate": "Αποθήκευση άρθρων στο Espial",
"form.integration.espial_endpoint": "Τελικό σημείο Espial API",
"form.integration.espial_api_key": "Κλειδί API Espial",
"form.integration.espial_tags": "Ετικέτες Espial",
"form.integration.telegram_bot_activate": "Προωθήστε νέα άρθρα στη συνομιλία Telegram",
"form.integration.telegram_bot_token": "Διακριτικό bot",
"form.integration.telegram_chat_id": "Αναγνωριστικό συνομιλίας",
"form.integration.linkding_activate": "Αποθήκευση άρθρων στο Linkding",
"form.integration.linkding_endpoint": "Τελικό σημείο Linkding API",
"form.integration.linkding_api_key": "Κλειδί API Linkding",
"form.api_key.label.description": "Ετικέτα κλειδιού API",
"form.submit.loading": "Φόρτωση...",
"form.submit.saving": "Αποθήκευση...",
"time_elapsed.not_yet": "όχι ακόμα.",
"time_elapsed.yesterday": "χθες",
"time_elapsed.now": "μόλις τώρα",
"time_elapsed.minutes": [
"πριν %d λεπτό",
"πριν %d λεπτά"
],
"time_elapsed.hours": [
"πριν %d ώρα",
"πριν %d ώρες"
],
"time_elapsed.days": [
"πριν %d ημέρα",
"πριν %d ημέρες"
],
"time_elapsed.weeks": [
"πριν %d εβδομάδα",
"πριν %d εβδομάδες"
],
"time_elapsed.months": [
"πριν %d μήνα",
"πριν %d μήνες"
],
"time_elapsed.years": [
"πριν %d έτος",
"πριν %d έτη"
]
}
+37 -2
View File
@@ -16,7 +16,7 @@
"action.login": "Login",
"action.home_screen": "Add to home screen",
"tooltip.keyboard_shortcuts": "Keyboard Shortcut: %s",
"tooltip.logged_user": "Logged as %s",
"tooltip.logged_user": "Logged in as %s",
"menu.unread": "Unread",
"menu.starred": "Starred",
"menu.history": "History",
@@ -92,6 +92,7 @@
"There is %d feed.",
"There are %d feeds."
],
"page.categories.unread_counter": "Number of unread entries",
"page.new_category.title": "New Category",
"page.new_user.title": "New User",
"page.edit_category.title": "Edit Category: %s",
@@ -114,6 +115,8 @@
"page.about.author": "Author:",
"page.about.license": "License:",
"page.about.global_config_options": "Global configuration options",
"page.about.postgres_version": "Postgres version:",
"page.about.go_version": "Go version:",
"page.add_feed.title": "New Subscription",
"page.add_feed.no_category": "There is no category. You must have at least one category.",
"page.add_feed.label.url": "URL",
@@ -149,7 +152,8 @@
"page.keyboard_shortcuts.open_original_same_window": "Open original link in current tab",
"page.keyboard_shortcuts.open_comments": "Open comments link",
"page.keyboard_shortcuts.open_comments_same_window": "Open comments link in current tab",
"page.keyboard_shortcuts.toggle_read_status": "Toggle read/unread",
"page.keyboard_shortcuts.toggle_read_status_next": "Toggle read/unread, focus next",
"page.keyboard_shortcuts.toggle_read_status_prev": "Toggle read/unread, focus previous",
"page.keyboard_shortcuts.refresh_all_feeds": "Refresh all feeds in the background",
"page.keyboard_shortcuts.mark_page_as_read": "Mark current page as read",
"page.keyboard_shortcuts.download_content": "Download original content",
@@ -199,6 +203,9 @@
"page.api_keys.table.actions": "Actions",
"page.api_keys.never_used": "Never Used",
"page.new_api_key.title": "New API Key",
"page.offline.title": "Offline Mode",
"page.offline.message": "You are offline",
"page.offline.refresh_page": "Try to refresh the page",
"alert.no_shared_entry": "There is no shared entry.",
"alert.no_bookmark": "There is no bookmark at the moment.",
"alert.no_category": "There is no category.",
@@ -218,6 +225,7 @@
"error.unlink_account_without_password": "You must define a password otherwise you won't be able to login again.",
"error.duplicate_linked_account": "There is already someone associated with this provider!",
"error.duplicate_fever_username": "There is already someone else with the same Fever username!",
"error.duplicate_googlereader_username": "There is already someone else with the same Google Reader username!",
"error.pocket_request_token": "Unable to fetch request token from Pocket!",
"error.pocket_access_token": "Unable to fetch access token from Pocket!",
"error.category_already_exists": "This category already exists.",
@@ -232,6 +240,7 @@
"error.invalid_language": "Invalid language.",
"error.invalid_timezone": "Invalid timezone.",
"error.invalid_entry_direction": "Invalid entry direction.",
"error.invalid_display_mode": "Invalid web app display mode.",
"error.empty_file": "This file is empty.",
"error.bad_credentials": "Invalid username or password.",
"error.fields_mandatory": "All fields are mandatory.",
@@ -261,14 +270,18 @@
"form.feed.label.feed_username": "Feed Username",
"form.feed.label.feed_password": "Feed Password",
"form.feed.label.user_agent": "Override Default User Agent",
"form.feed.label.cookie": "Set Cookies",
"form.feed.label.scraper_rules": "Scraper Rules",
"form.feed.label.rewrite_rules": "Rewrite Rules",
"form.feed.label.blocklist_rules": "Block Rules",
"form.feed.label.keeplist_rules": "Keep Rules",
"form.feed.label.ignore_http_cache": "Ignore HTTP cache",
"form.feed.label.allow_self_signed_certificates": "Allow self-signed or invalid certificates",
"form.feed.label.fetch_via_proxy": "Fetch via proxy",
"form.feed.label.disabled": "Do not refresh this feed",
"form.feed.label.hide_globally": "Hide entries in global unread list",
"form.category.label.title": "Title",
"form.category.hide_globally": "Hide entries in global unread list",
"form.user.label.username": "Username",
"form.user.label.password": "Password",
"form.user.label.confirmation": "Password Confirmation",
@@ -278,18 +291,30 @@
"form.prefs.label.theme": "Theme",
"form.prefs.label.entry_sorting": "Entry Sorting",
"form.prefs.label.entries_per_page": "Entries per page",
"form.prefs.label.display_mode": "Web app display mode (needs reinstalling)",
"form.prefs.select.older_first": "Older entries first",
"form.prefs.select.recent_first": "Recent entries first",
"form.prefs.select.fullscreen": "Fullscreen",
"form.prefs.select.standalone": "Standalone",
"form.prefs.select.minimal_ui": "Minimal",
"form.prefs.select.browser": "Browser",
"form.prefs.select.publish_time": "Entry published time",
"form.prefs.select.created_time": "Entry created time",
"form.prefs.label.keyboard_shortcuts": "Enable keyboard shortcuts",
"form.prefs.label.entry_swipe": "Enable swipe gesture on entries on mobile",
"form.prefs.label.show_reading_time": "Show estimated reading time for articles",
"form.prefs.label.custom_css": "Custom CSS",
"form.prefs.label.entry_order": "Entry Sorting Column",
"form.import.label.file": "OPML file",
"form.import.label.url": "URL",
"form.integration.fever_activate": "Activate Fever API",
"form.integration.fever_username": "Fever Username",
"form.integration.fever_password": "Fever Password",
"form.integration.fever_endpoint": "Fever API endpoint:",
"form.integration.googlereader_activate": "Activate Google Reader API",
"form.integration.googlereader_username": "Google Reader Username",
"form.integration.googlereader_password": "Google Reader Password",
"form.integration.googlereader_endpoint": "Google Reader API endpoint:",
"form.integration.pinboard_activate": "Save articles to Pinboard",
"form.integration.pinboard_token": "Pinboard API Token",
"form.integration.pinboard_tags": "Pinboard Tags",
@@ -310,6 +335,16 @@
"form.integration.nunux_keeper_activate": "Save articles to Nunux Keeper",
"form.integration.nunux_keeper_endpoint": "Nunux Keeper API Endpoint",
"form.integration.nunux_keeper_api_key": "Nunux Keeper API key",
"form.integration.espial_activate": "Save articles to Espial",
"form.integration.espial_endpoint": "Espial API Endpoint",
"form.integration.espial_api_key": "Espial API key",
"form.integration.espial_tags": "Espial Tags",
"form.integration.telegram_bot_activate": "Push new articles to Telegram chat",
"form.integration.telegram_bot_token": "Bot token",
"form.integration.telegram_chat_id": "Chat ID",
"form.integration.linkding_activate": "Save articles to Linkding",
"form.integration.linkding_endpoint": "Linkding API Endpoint",
"form.integration.linkding_api_key": "Linkding API key",
"form.api_key.label.description": "API Key Label",
"form.submit.loading": "Loading...",
"form.submit.saving": "Saving...",
+36 -1
View File
@@ -92,6 +92,7 @@
"Hay %d fuente.",
"Hay %d fuentes."
],
"page.categories.unread_counter": "Número de entradas no leídas",
"page.new_category.title": "Nueva categoría",
"page.new_user.title": "Nuevo usario",
"page.edit_category.title": "Editar categoría: %s",
@@ -114,6 +115,8 @@
"page.about.author": "Autor:",
"page.about.license": "Licencia:",
"page.about.global_config_options": "Opciones de configuración global",
"page.about.postgres_version": "Postgres versión:",
"page.about.go_version": "Go versión:",
"page.add_feed.title": "Nueva suscripción",
"page.add_feed.no_category": "No hay categoría. Debe tener al menos una categoría.",
"page.add_feed.label.url": "URL",
@@ -149,7 +152,8 @@
"page.keyboard_shortcuts.open_original_same_window": "Abrir enlace original en la pestaña actual",
"page.keyboard_shortcuts.open_comments": "Abrir el enlace de comentarios",
"page.keyboard_shortcuts.open_comments_same_window": "Abrir enlace de comentarios en la pestaña actual",
"page.keyboard_shortcuts.toggle_read_status": "Marcar como leído o no leído",
"page.keyboard_shortcuts.toggle_read_status_next": "Marcar como leído o no leído, enfoque siguiente",
"page.keyboard_shortcuts.toggle_read_status_prev": "Marcar como leído o no leído, foco anterior",
"page.keyboard_shortcuts.refresh_all_feeds": "Refrescar todas las fuentes en el fondo",
"page.keyboard_shortcuts.mark_page_as_read": "Marcar pagína actual como leída",
"page.keyboard_shortcuts.download_content": "Descargar el contento original",
@@ -199,6 +203,9 @@
"page.api_keys.table.actions": "Acciones",
"page.api_keys.never_used": "Nunca usado",
"page.new_api_key.title": "Nueva clave API",
"page.offline.title": "Modo offline",
"page.offline.message": "Estas desconectado",
"page.offline.refresh_page": "Intenta actualizar la página",
"alert.no_shared_entry": "No hay entrada compartida.",
"alert.no_bookmark": "No hay marcador en este momento.",
"alert.no_category": "No hay categoría.",
@@ -218,6 +225,7 @@
"error.unlink_account_without_password": "Debe definir una contraseña, de lo contrario no podrá volver a iniciar sesión.",
"error.duplicate_linked_account": "¡Ya hay alguien asociado a este servicio!",
"error.duplicate_fever_username": "¡Ya hay alguien con el mismo nombre de usuario de Fever!",
"error.duplicate_googlereader_username": "¡Ya hay alguien con el mismo nombre de usuario de Google Reader!",
"error.pocket_request_token": "Incapaz de obtener un token de solicitud de Pocket!",
"error.pocket_access_token": "Incapaz de obtener un token de acceso de Pocket!",
"error.category_already_exists": "Esta categoría ya existe.",
@@ -253,6 +261,7 @@
"error.invalid_language": "Idioma no válido.",
"error.invalid_timezone": "Zona horaria no válida.",
"error.invalid_entry_direction": "Dirección de entrada no válida.",
"error.invalid_display_mode": "Modo de visualización de la aplicación web no válido.",
"form.feed.label.title": "Título",
"form.feed.label.site_url": "URL del sitio",
"form.feed.label.feed_url": "URL de la fuente",
@@ -261,14 +270,18 @@
"form.feed.label.feed_username": "Nombre de usuario de fuente",
"form.feed.label.feed_password": "Contraseña de fuente",
"form.feed.label.user_agent": "Invalidar el agente de usuario predeterminado",
"form.feed.label.cookie": "Configurar las cookies",
"form.feed.label.scraper_rules": "Reglas de raspador",
"form.feed.label.rewrite_rules": "Reglas de reescribir",
"form.feed.label.blocklist_rules": "Reglas de Filtrado(Bloquear)",
"form.feed.label.keeplist_rules": "Reglas de Filtrado(Permitir)",
"form.feed.label.ignore_http_cache": "Ignorar caché HTTP",
"form.feed.label.allow_self_signed_certificates": "Permitir certificados autofirmados o no válidos",
"form.feed.label.fetch_via_proxy": "Buscar a través de proxy",
"form.feed.label.disabled": "No actualice este feed",
"form.feed.label.hide_globally": "Ocultar entradas en la lista global de no leídos",
"form.category.label.title": "Título",
"form.category.hide_globally": "Ocultar entradas en la lista global de no leídos",
"form.user.label.username": "Nombre de usuario",
"form.user.label.password": "Contraseña",
"form.user.label.confirmation": "Confirmación de contraseña",
@@ -278,18 +291,30 @@
"form.prefs.label.theme": "Tema",
"form.prefs.label.entry_sorting": "Clasificación de entradas",
"form.prefs.label.entries_per_page": "Entradas por página",
"form.prefs.label.display_mode": "Modo de visualización de la aplicación web (necesita reinstalación)",
"form.prefs.select.older_first": "Entradas más viejas primero",
"form.prefs.select.recent_first": "Entradas recientes primero",
"form.prefs.select.fullscreen": "Pantalla completa",
"form.prefs.select.standalone": "Ser único",
"form.prefs.select.minimal_ui": "Mínimo",
"form.prefs.select.browser": "Navegador",
"form.prefs.select.publish_time": "Hora de publicación de la entrada",
"form.prefs.select.created_time": "Hora de creación de la entrada",
"form.prefs.label.keyboard_shortcuts": "Habilitar atajos de teclado",
"form.prefs.label.entry_swipe": "Habilitar el gesto de deslizar el dedo en las entradas en el móvil",
"form.prefs.label.show_reading_time": "Mostrar el tiempo estimado de lectura de los artículos",
"form.prefs.label.custom_css": "CSS personalizado",
"form.prefs.label.entry_order": "Columna de clasificación de entradas",
"form.import.label.file": "Archivo OPML",
"form.import.label.url": "URL",
"form.integration.fever_activate": "Activar API de Fever",
"form.integration.fever_username": "Nombre de usuario de Fever",
"form.integration.fever_password": "Contraseña de Fever",
"form.integration.fever_endpoint": "Extremo de API de Fever:",
"form.integration.googlereader_activate": "Activar API de Google Reader",
"form.integration.googlereader_username": "Nombre de usuario de Google Reader",
"form.integration.googlereader_password": "Contraseña de Google Reader",
"form.integration.googlereader_endpoint": "Extremo de API de Google Reader:",
"form.integration.pinboard_activate": "Guardar artículos a Pinboard",
"form.integration.pinboard_token": "Token de API de Pinboard",
"form.integration.pinboard_tags": "Etiquetas de Pinboard",
@@ -310,6 +335,16 @@
"form.integration.nunux_keeper_activate": "Guardar artículos a Nunux Keeper",
"form.integration.nunux_keeper_endpoint": "Extremo de API de Nunux Keeper",
"form.integration.nunux_keeper_api_key": "Clave de API de Nunux Keeper",
"form.integration.espial_activate": "Guardar artículos a Espial",
"form.integration.espial_endpoint": "Extremo de API de Espial",
"form.integration.espial_api_key": "Clave de API de Espial",
"form.integration.espial_tags": "Etiquetas de Espial",
"form.integration.telegram_bot_activate": "Envíe nuevos artículos al chat de Telegram",
"form.integration.telegram_bot_token": "Token de bot",
"form.integration.telegram_chat_id": "ID de chat",
"form.integration.linkding_activate": "Guardar artículos a Linkding",
"form.integration.linkding_endpoint": "Extremo de API de Linkding",
"form.integration.linkding_api_key": "Clave de API de Linkding",
"form.api_key.label.description": "Etiqueta de clave API",
"form.submit.loading": "Cargando...",
"form.submit.saving": "Guardando...",
+378
View File
@@ -0,0 +1,378 @@
{
"confirm.question": "Oletko varma?",
"confirm.yes": "kyllä",
"confirm.no": "ei",
"confirm.loading": "Käynnissä...",
"action.subscribe": "Tilaa",
"action.save": "Tallenna",
"action.or": "tai",
"action.cancel": "peru",
"action.remove": "Poista",
"action.remove_feed": "Poista tämä syöte",
"action.update": "Päivitä",
"action.edit": "Muokkaa",
"action.download": "Lataa",
"action.import": "Tuo",
"action.login": "Kirjaudu sisään",
"action.home_screen": "Lisää aloitusnäytölle",
"tooltip.keyboard_shortcuts": "Pikanäppäin: %s",
"tooltip.logged_user": "Kirjautunut %s-käyttäjänä",
"menu.unread": "Lukemattomat",
"menu.starred": "Suosikit",
"menu.history": "Historia",
"menu.feeds": "Syötteet",
"menu.categories": "Kategoriat",
"menu.settings": "Asetukset",
"menu.logout": "Kirjaudu ulos",
"menu.preferences": "Asetukset",
"menu.integrations": "Integraatiot",
"menu.sessions": "Istunnot",
"menu.users": "Käyttäjät",
"menu.about": "Tietoja",
"menu.export": "Vie",
"menu.import": "Tuo",
"menu.create_category": "Luo kategoria",
"menu.mark_page_as_read": "Merkitse tämä sivu luetuksi",
"menu.mark_all_as_read": "Merkitse kaikki luetuksi",
"menu.show_all_entries": "Näytä kaikki artikkelit",
"menu.show_only_unread_entries": "Näytä vain lukemattomat artikkelit",
"menu.refresh_feed": "Päivitä",
"menu.refresh_all_feeds": "Päivitä kaikki syötteet taustalla",
"menu.edit_feed": "Muokkaa",
"menu.edit_category": "Muokkaa",
"menu.add_feed": "Lisää tilaus",
"menu.add_user": "Lisää käyttäjä",
"menu.flush_history": "Tyhjennä historia",
"menu.feed_entries": "Artikkelit",
"menu.api_keys": "API-avaimet",
"menu.create_api_key": "Luo uusi API-avain",
"menu.shared_entries": "Jaetut artikkelit",
"search.label": "Haku",
"search.placeholder": "Hae...",
"pagination.next": "Seuraava",
"pagination.previous": "Edellinen",
"entry.status.unread": "Lukematon",
"entry.status.read": "Luettu",
"entry.status.toast.unread": "Merkitty lukemattomaksi",
"entry.status.toast.read": "Merkitty luetuksi",
"entry.status.title": "Vaihda artikkelin tilaa",
"entry.bookmark.toggle.on": "Lisää suosikkeihin",
"entry.bookmark.toggle.off": "Poista suosikeista",
"entry.bookmark.toast.on": "Tähdellä merkityt",
"entry.bookmark.toast.off": "Tähdettömät",
"entry.state.saving": "Tallennetaan...",
"entry.state.loading": "Ladataan...",
"entry.save.label": "Tallenna",
"entry.save.title": "Tallenna tämä artikkeli",
"entry.save.completed": "Valmis!",
"entry.save.toast.completed": "Artikkeli tallennettu",
"entry.scraper.label": "Lataa",
"entry.scraper.title": "Nouda alkuperäinen sisältö",
"entry.scraper.completed": "Valmis!",
"entry.external_link.label": "Ulkoinen linkki",
"entry.comments.label": "Kommentit",
"entry.comments.title": "Näytä kommentit",
"entry.share.label": "Jaa",
"entry.share.title": "Jaa tämä artikkeli",
"entry.unshare.label": "Poista jako",
"entry.shared_entry.title": "Avaa julkinen linkki",
"entry.shared_entry.label": "Jaa",
"entry.estimated_reading_time": [
"%d minuutin lukuaika",
"%d minuutin lukuaika"
],
"page.shared_entries.title": "Jaetut artikkelit",
"page.unread.title": "Lukemattomat",
"page.starred.title": "Suosikit",
"page.categories.title": "Kategoriat",
"page.categories.no_feed": "Ei syötettä.",
"page.categories.entries": "Artikkelit",
"page.categories.feeds": "Tilaukset",
"page.categories.feed_count": [
"On %d syöte.",
"On %d syötettä."
],
"page.categories.unread_counter": "Lukemattomien artikkeleiden määrä",
"page.new_category.title": "Uusi kategoria",
"page.new_user.title": "Uusi käyttäjä",
"page.edit_category.title": "Muokkaa kategoria: %s",
"page.edit_user.title": "Muokkaa käyttäjä: %s",
"page.feeds.title": "Syötteet",
"page.feeds.last_check": "Viimeisin tarkistus:",
"page.feeds.unread_counter": "Lukemattomien artikkeleiden määrä",
"page.feeds.read_counter": "Luettujen artikkeleiden määrä",
"page.feeds.error_count": [
"%d virhe",
"%d virhettä"
],
"page.history.title": "Historia",
"page.import.title": "Tuo",
"page.search.title": "Hakutulokset",
"page.about.title": "Tietoja",
"page.about.credits": "Kiitokset",
"page.about.version": "Versio:",
"page.about.build_date": "Valmistuspäivä:",
"page.about.author": "Tekijä:",
"page.about.license": "Lisenssi:",
"page.about.global_config_options": "Yleiset asetukset",
"page.about.postgres_version": "Postgres-versio:",
"page.about.go_version": "Go-versio:",
"page.add_feed.title": "Uusi tilaus",
"page.add_feed.no_category": "Ei ole ketegoriaa. Sinulla on oltava vähintään yksi ketegoria.",
"page.add_feed.label.url": "URL-osoite",
"page.add_feed.submit": "Etsi tilaus",
"page.add_feed.legend.advanced_options": "Edistyneet asetukset",
"page.add_feed.choose_feed": "Valitse tilaus",
"page.edit_feed.title": "Muokkaa syöte: %s",
"page.edit_feed.last_check": "Viimeisin tarkistus:",
"page.edit_feed.last_modified_header": "LastModified-otsikko:",
"page.edit_feed.etag_header": "ETag-otsikko:",
"page.edit_feed.no_header": "Ei mitään",
"page.edit_feed.last_parsing_error": "Viimeisin jäsennysvirhe",
"page.entry.attachments": "Liitteet",
"page.keyboard_shortcuts.title": "Pikanäppäimet",
"page.keyboard_shortcuts.subtitle.sections": "Osion navigointi",
"page.keyboard_shortcuts.subtitle.items": "Kohteiden navigointi",
"page.keyboard_shortcuts.subtitle.pages": "Sivujen navigointi",
"page.keyboard_shortcuts.subtitle.actions": "Toiminnot",
"page.keyboard_shortcuts.go_to_unread": "Siirry lukemattomiin",
"page.keyboard_shortcuts.go_to_starred": "Siirry kirjanmerkkeihin",
"page.keyboard_shortcuts.go_to_history": "Siirry historiaan",
"page.keyboard_shortcuts.go_to_feeds": "Siirry syötteisiin",
"page.keyboard_shortcuts.go_to_categories": "Siirry kategorioihin",
"page.keyboard_shortcuts.go_to_settings": "Siirry asetuksiin",
"page.keyboard_shortcuts.show_keyboard_shortcuts": "Näytä pikanäppäimet",
"page.keyboard_shortcuts.go_to_previous_item": "Siirry edelliseen kohteeseen",
"page.keyboard_shortcuts.go_to_next_item": "Siirry seuraavaan kohteeseen",
"page.keyboard_shortcuts.go_to_feed": "Siirry syötteeseen",
"page.keyboard_shortcuts.go_to_previous_page": "Siirry edelliselle sivulle",
"page.keyboard_shortcuts.go_to_next_page": "Siirry seuraavalle sivulle",
"page.keyboard_shortcuts.open_item": "Avaa valittu kohde",
"page.keyboard_shortcuts.open_original": "Avaa alkuperäinen linkki",
"page.keyboard_shortcuts.open_original_same_window": "Avaa alkuperäinen linkki nykyisessä välilehdessä",
"page.keyboard_shortcuts.open_comments": "Avaa kommenttilinkki",
"page.keyboard_shortcuts.open_comments_same_window": "Avaa kommenttilinkki nykyisessä välilehdessä",
"page.keyboard_shortcuts.toggle_read_status_next": "Vaihda luettu/lukematon, keskity seuraavaksi",
"page.keyboard_shortcuts.toggle_read_status_prev": "Vaihda luettu/lukematon, keskity edelliseen",
"page.keyboard_shortcuts.refresh_all_feeds": "Päivitä kaikki syötteet taustalla",
"page.keyboard_shortcuts.mark_page_as_read": "Merkitse nykyinen sivu luetuksi",
"page.keyboard_shortcuts.download_content": "Lataa alkuperäinen sisältö",
"page.keyboard_shortcuts.toggle_bookmark_status": "Vaihda kirjanmerkki",
"page.keyboard_shortcuts.save_article": "Tallenna artikkeli",
"page.keyboard_shortcuts.scroll_item_to_top": "Vieritä ylös",
"page.keyboard_shortcuts.remove_feed": "Poista tämä syöte",
"page.keyboard_shortcuts.go_to_search": "Aseta painopiste hakukenttään",
"page.keyboard_shortcuts.close_modal": "Sulje modaalinen valintaikkuna",
"page.users.title": "Käyttäjät",
"page.users.username": "Käyttäjätunnus",
"page.users.never_logged": "Ei koskaan",
"page.users.admin.yes": "Kyllä",
"page.users.admin.no": "Ei",
"page.users.actions": "Toiminnot",
"page.users.last_login": "Viimeisin kirjautuminen",
"page.users.is_admin": "Ylläpitäjä",
"page.settings.title": "Asetukset",
"page.settings.link_google_account": "Linkitä Google-tilini",
"page.settings.unlink_google_account": "Poista Google-tilini linkitys",
"page.settings.link_oidc_account": "Linkitä OpenID Connect -tilini",
"page.settings.unlink_oidc_account": "Poista OpenID Connect -tilini linkitys",
"page.login.title": "Kirjaudu sisään",
"page.login.google_signin": "Kirjaudu sisään Googlella",
"page.login.oidc_signin": "Kirjaudu sisään OpenID Connectilla",
"page.integrations.title": "Integraatiot",
"page.integration.miniflux_api": "Miniflux API",
"page.integration.miniflux_api_endpoint": "API-päätepiste",
"page.integration.miniflux_api_username": "Käyttäjätunnus",
"page.integration.miniflux_api_password": "Salasana",
"page.integration.miniflux_api_password_value": "Tilisi salasana",
"page.integration.bookmarklet": "Sovelluskirjanmerkki",
"page.integration.bookmarklet.name": "Lisää Minifluxiin",
"page.integration.bookmarklet.instructions": "Vedä ja pudota tämä linkki kirjanmerkkeihisi.",
"page.integration.bookmarklet.help": "This special link allows you to subscribe to a website directly by using a bookmark in your web browser.",
"page.sessions.title": "Istunnot",
"page.sessions.table.date": "Päivämäärä",
"page.sessions.table.ip": "IP-osoite",
"page.sessions.table.user_agent": "Käyttäjäagentti",
"page.sessions.table.actions": "Toiminnot",
"page.sessions.table.current_session": "Nykyinen istunto",
"page.api_keys.title": "API-avaimet",
"page.api_keys.table.description": "Kuvaus",
"page.api_keys.table.token": "Tunnus",
"page.api_keys.table.last_used_at": "Viimeksi käytetty",
"page.api_keys.table.created_at": "Luomispäivä",
"page.api_keys.table.actions": "Toiminnot",
"page.api_keys.never_used": "Käyttämätön",
"page.new_api_key.title": "Uusi API-avain",
"page.offline.title": "Offline-tila",
"page.offline.message": "Olet offline-tilassa",
"page.offline.refresh_page": "Yritä päivittää sivu",
"alert.no_shared_entry": "Jaettua artikkelia ei ole.",
"alert.no_bookmark": "Tällä hetkellä ei ole kirjanmerkkiä.",
"alert.no_category": "Ei ole kategoriaa.",
"alert.no_category_entry": "Tässä kategoriassa ei ole artikkeleita.",
"alert.no_feed_entry": "Tässä syötteessä ei ole artikkeleita.",
"alert.no_feed": "Sinulla ei ole tilauksia.",
"alert.no_feed_in_category": "Tälle kategorialle ei ole tilausta.",
"alert.no_history": "Tällä hetkellä ei ole historiaa.",
"alert.feed_error": "Tässä syötteessä on ongelma",
"alert.no_search_result": "Ei hakua vastaavia tuloksia.",
"alert.no_unread_entry": "Ei ole lukemattomia artikkeleita.",
"alert.no_user": "Olet ainoa käyttäjä.",
"alert.account_unlinked": "Ulkoinen tilisi on nyt irrotettu!",
"alert.account_linked": "Ulkoinen tilisi on nyt linkitetty!",
"alert.pocket_linked": "Pocket-tilisi on nyt linkitetty!",
"alert.prefs_saved": "Asetukset tallennettu!",
"error.unlink_account_without_password": "Sinun on määritettävä salasana, muuten et voi kirjautua uudelleen.",
"error.duplicate_linked_account": "There is already someone associated with this provider!",
"error.duplicate_fever_username": "There is already someone else with the same Fever username!",
"error.duplicate_googlereader_username": "On jo joku muu, jolla on sama Google-syötteenlukijan käyttäjätunnus!",
"error.pocket_request_token": "Unable to fetch request token from Pocket!",
"error.pocket_access_token": "Unable to fetch access token from Pocket!",
"error.category_already_exists": "Kategoria on jo olemassa. ",
"error.unable_to_create_category": "Kategoriaa ei voi luoda.",
"error.unable_to_update_category": "Kategoriaa ei voi päivittää.",
"error.user_already_exists": "Käyttäjä on jo olemassa.",
"error.unable_to_create_user": "Käyttäjää ei voi luoda.",
"error.unable_to_update_user": "Käyttäjää ei voi päivittää.",
"error.unable_to_update_feed": "Syötettä ei voi päivittää.",
"error.subscription_not_found": "Tilausta ei löydy.",
"error.invalid_theme": "Virheellinen teema.",
"error.invalid_language": "Virheellinen kieli.",
"error.invalid_timezone": "Virheellinen aikavyöhyke.",
"error.invalid_entry_direction": "Invalid entry direction.",
"error.invalid_display_mode": "Virheellinen verkkosovelluksen näyttötila.",
"error.empty_file": "Tiedosto on tyhjä.",
"error.bad_credentials": "Virheellinen käyttäjänimi tai salasana.",
"error.fields_mandatory": "Kaikki kentät ovat pakollisia.",
"error.title_required": "Otsikko on pakollinen.",
"error.different_passwords": "Salasanat eivät ole samat.",
"error.password_min_length": "Salasanassa on oltava vähintään 6 merkkiä.",
"error.settings_mandatory_fields": "Käyttäjätunnus, teema, kieli ja aikavyöhyke ovat pakollisia.",
"error.entries_per_page_invalid": "Artikkelien määrä sivulla ei kelpaa.",
"error.feed_mandatory_fields": "URL-osoite ja kategoria ovat pakollisia.",
"error.feed_already_exists": "Tämä syöte on jo olemassa.",
"error.invalid_feed_url": "Virheellinen syötteen URL-osoite.",
"error.invalid_site_url": "Virheellinen sivuston URL-osoite.",
"error.feed_url_not_empty": "Syötteen URL-osoite ei voi olla tyhjä.",
"error.site_url_not_empty": "Sivuston URL-osoite ei voi olla tyhjä.",
"error.feed_title_not_empty": "Syötteen otsikko ei voi olla tyhjä.",
"error.feed_category_not_found": "Tätä kategoriaa ei ole olemassa tai se ei kuulu tälle käyttäjälle.",
"error.feed_invalid_blocklist_rule": "The block list rule is invalid.",
"error.feed_invalid_keeplist_rule": "The keep list rule is invalid.",
"error.user_mandatory_fields": "Käyttäjätunnus on pakollinen.",
"error.api_key_already_exists": "API-avain on jo olemassa.",
"error.unable_to_create_api_key": "API-avainta ei voi luoda.",
"form.feed.label.title": "Otsikko",
"form.feed.label.site_url": "Sivuston URL-osoite",
"form.feed.label.feed_url": "Syötteen URL-osoite",
"form.feed.label.category": "Kategoria",
"form.feed.label.crawler": "Nouda alkuperäinen sisältö",
"form.feed.label.feed_username": "Syötteen käyttäjätunnus",
"form.feed.label.feed_password": "Syötteen salasana",
"form.feed.label.user_agent": "Ohita oletuskäyttäjäagentti",
"form.feed.label.cookie": "Aseta evästeet",
"form.feed.label.scraper_rules": "Scraper-säännöt",
"form.feed.label.rewrite_rules": "Rewrite-säännöt",
"form.feed.label.blocklist_rules": "Block-säännöt",
"form.feed.label.keeplist_rules": "Keep-säännöt",
"form.feed.label.ignore_http_cache": "Ohita HTTP-välimuisti",
"form.feed.label.allow_self_signed_certificates": "Salli itseallekirjoitetut tai virheelliset varmenteet",
"form.feed.label.fetch_via_proxy": "Nouda välityspalvelimen kautta",
"form.feed.label.disabled": "Älä päivitä tätä syötettä",
"form.feed.label.hide_globally": "Piilota artikkelit lukemattomien listassa",
"form.category.label.title": "Otsikko",
"form.category.hide_globally": "Piilota artikkelit lukemattomien listassa",
"form.user.label.username": "Käyttäjätunnus",
"form.user.label.password": "Salasana",
"form.user.label.confirmation": "Salasanan vahvistus",
"form.user.label.admin": "Ylläpitäjä",
"form.prefs.label.language": "Kieli",
"form.prefs.label.timezone": "Aikavyöhyke",
"form.prefs.label.theme": "Teema",
"form.prefs.label.entry_sorting": "Lajittelu",
"form.prefs.label.entries_per_page": "Artikkelia sivulla",
"form.prefs.label.display_mode": "Verkkosovelluksen näyttötila (vaatii uudelleenasennuksen)",
"form.prefs.select.older_first": "Vanhin ensin",
"form.prefs.select.recent_first": "Uusin ensin",
"form.prefs.select.fullscreen": "Kokoruututila",
"form.prefs.select.standalone": "Itsenäinen tila",
"form.prefs.select.minimal_ui": "Minimaalinen",
"form.prefs.select.browser": "Selain",
"form.prefs.select.publish_time": "Julkaisuaika",
"form.prefs.select.created_time": "Luomisaika",
"form.prefs.label.keyboard_shortcuts": "Ota pikanäppäimet käyttöön",
"form.prefs.label.entry_swipe": "Ota pyyhkäisyele käyttöön mobiililaitteella",
"form.prefs.label.show_reading_time": "Näytä artikkeleiden arvioitu lukuaika",
"form.prefs.label.custom_css": "Mukautettu CSS",
"form.prefs.label.entry_order": "Lajittele sarakkeen mukaan",
"form.import.label.file": "OPML-tiedosto",
"form.import.label.url": "URL",
"form.integration.fever_activate": "Ota Fever API käyttöön",
"form.integration.fever_username": "Fever-käyttäjätunnus",
"form.integration.fever_password": "Fever-salasana",
"form.integration.fever_endpoint": "Fever API -päätepiste:",
"form.integration.googlereader_activate": "Aktivoi Google Reader API",
"form.integration.googlereader_username": "Google-lukijan käyttäjätunnus",
"form.integration.googlereader_password": "Google-lukijan salasana",
"form.integration.googlereader_endpoint": "Google Reader API -päätepiste:",
"form.integration.pinboard_activate": "Tallenna artikkelit Pinboardiin",
"form.integration.pinboard_token": "Pinboard API-tunnus",
"form.integration.pinboard_tags": "Pinboard-tagit",
"form.integration.pinboard_bookmark": "Merkitse kirjanmerkki lukemattomaksi",
"form.integration.instapaper_activate": "Tallenna artikkelit Instapaperiin",
"form.integration.instapaper_username": "Instapaper-käyttäjätunnus",
"form.integration.instapaper_password": "Instapaper-salasana",
"form.integration.pocket_activate": "Tallenna artikkelit Pocketiin",
"form.integration.pocket_consumer_key": "Pocket Consumer Key",
"form.integration.pocket_access_token": "Pocket-käyttöoikeustunnus",
"form.integration.pocket_connect_link": "Yhdistä Pocket-tilisi",
"form.integration.wallabag_activate": "Tallenna artikkelit Wallabagiin",
"form.integration.wallabag_endpoint": "Wallabag API -päätepiste",
"form.integration.wallabag_client_id": "Wallabag Client ID",
"form.integration.wallabag_client_secret": "Wallabag Client Secret",
"form.integration.wallabag_username": "Wallabag-käyttäjätunnus",
"form.integration.wallabag_password": "Wallabag-salasana",
"form.integration.nunux_keeper_activate": "Tallenna artikkelit Nunux Keeperiin",
"form.integration.nunux_keeper_endpoint": "Nunux Keeper API-päätepiste",
"form.integration.nunux_keeper_api_key": "Nunux Keeper API-avain",
"form.integration.espial_activate": "Tallenna artikkelit Espialiin",
"form.integration.espial_endpoint": "Espial API-päätepiste",
"form.integration.espial_api_key": "Espial API-avain",
"form.integration.espial_tags": "Espial-tagit",
"form.integration.telegram_bot_activate": "Lähetä uusia artikkeleita Telegram-chatiin",
"form.integration.telegram_bot_token": "Bot-tunnus",
"form.integration.telegram_chat_id": "Chat ID",
"form.integration.linkding_activate": "Tallenna artikkelit Linkkiin",
"form.integration.linkding_endpoint": "Linkding API-päätepiste",
"form.integration.linkding_api_key": "Linkding API-avain",
"form.api_key.label.description": "API Key Label",
"form.submit.loading": "Ladataan...",
"form.submit.saving": "Tallennetaan...",
"time_elapsed.not_yet": "ei vielä",
"time_elapsed.yesterday": "eilen",
"time_elapsed.now": "juuri nyt",
"time_elapsed.minutes": [
"%d minuutti sitten",
"%d minuuttia sitten"
],
"time_elapsed.hours": [
"%d tunti sitten",
"%d tuntia sitten"
],
"time_elapsed.days": [
"%d päivä sitten",
"%d päivää sitten"
],
"time_elapsed.weeks": [
"%d viikko sitten",
"%d viikkoa sitten"
],
"time_elapsed.months": [
"%d kuukausi sitten",
"%d kuukautta sitten"
],
"time_elapsed.years": [
"%d vuosi sitten",
"%d vuotta sitten"
]
}
+38 -3
View File
@@ -92,6 +92,7 @@
"Il y a %d abonnement.",
"Il y a %d abonnements."
],
"page.categories.unread_counter": "Nombre d'entrées non lues",
"page.new_category.title": "Nouvelle catégorie",
"page.new_user.title": "Nouvel Utilisateur",
"page.edit_category.title": "Modification de la catégorie : %s",
@@ -107,13 +108,15 @@
"page.history.title": "Historique",
"page.import.title": "Importation",
"page.search.title": "Résultats de la recherche",
"page.about.title": "A propos",
"page.about.title": "À propos",
"page.about.credits": "Crédits",
"page.about.version": "Version :",
"page.about.build_date": "Date de la compilation :",
"page.about.author": "Auteur :",
"page.about.license": "Licence :",
"page.about.global_config_options": "Options de configuration globales",
"page.about.postgres_version": "Version de Postgresql :",
"page.about.go_version": "Version de Go :",
"page.add_feed.title": "Nouvel Abonnement",
"page.add_feed.no_category": "Il n'y a aucune catégorie. Vous devez avoir au moins une catégorie.",
"page.add_feed.label.url": "Lien",
@@ -149,7 +152,8 @@
"page.keyboard_shortcuts.open_original_same_window": "Ouvrir le lien original dans l'onglet en cours",
"page.keyboard_shortcuts.open_comments": "Ouvrir le lien des commentaires",
"page.keyboard_shortcuts.open_comments_same_window": "Ouvrir le lien des commentaires dans l'onglet en cours",
"page.keyboard_shortcuts.toggle_read_status": "Basculer entre lu/non lu",
"page.keyboard_shortcuts.toggle_read_status_next": "Basculer entre lu/non lu, et changer le focus sur l'élément suivant",
"page.keyboard_shortcuts.toggle_read_status_prev": "Basculer entre lu/non lu, et changer le focus sur l'élément précédent",
"page.keyboard_shortcuts.refresh_all_feeds": "Actualiser les abonnements en arrière-plan",
"page.keyboard_shortcuts.mark_page_as_read": "Marquer la page actuelle comme lu",
"page.keyboard_shortcuts.download_content": "Télécharger le contenu original",
@@ -199,6 +203,9 @@
"page.api_keys.table.actions": "Actions",
"page.api_keys.never_used": "Jamais utilisé",
"page.new_api_key.title": "Nouvelle clé d'API",
"page.offline.title": "Mode Hors-Ligne",
"page.offline.message": "Vous n'êtes pas connecté",
"page.offline.refresh_page": "Essayez de rafraîchir la page",
"alert.no_shared_entry": "Il n'y a pas d'article partagé.",
"alert.no_bookmark": "Il n'y a aucun favoris pour le moment.",
"alert.no_category": "Il n'y a aucune catégorie.",
@@ -218,6 +225,7 @@
"error.unlink_account_without_password": "Vous devez définir un mot de passe sinon vous ne pourrez plus vous connecter par la suite.",
"error.duplicate_linked_account": "Il y a déjà quelqu'un d'associé avec ce provider !",
"error.duplicate_fever_username": "Il y a déjà quelqu'un d'autre avec le même nom d'utilisateur Fever !",
"error.duplicate_googlereader_username": "Il y a déjà quelqu'un d'autre avec le même nom d'utilisateur Google Reader !",
"error.pocket_request_token": "Impossible de récupérer le jeton d'accès depuis Pocket !",
"error.pocket_access_token": "Impossible de récupérer le jeton d'accès depuis Pocket !",
"error.category_already_exists": "Cette catégorie existe déjà.",
@@ -253,6 +261,7 @@
"error.invalid_language": "Langue non valide.",
"error.invalid_timezone": "Fuseau horaire non valide.",
"error.invalid_entry_direction": "Ordre de trie non valide.",
"error.invalid_display_mode": "Mode d'affichage de l'application web non valide.",
"form.feed.label.title": "Titre",
"form.feed.label.site_url": "URL du site web",
"form.feed.label.feed_url": "URL du flux",
@@ -261,14 +270,18 @@
"form.feed.label.feed_username": "Nom d'utilisateur du flux",
"form.feed.label.feed_password": "Mot de passe du flux",
"form.feed.label.user_agent": "Remplacer l'agent utilisateur par défaut",
"form.feed.label.cookie": "Définir les cookies",
"form.feed.label.scraper_rules": "Règles pour récupérer le contenu original",
"form.feed.label.rewrite_rules": "Règles de réécriture",
"form.feed.label.blocklist_rules": "Règles de blocage",
"form.feed.label.keeplist_rules": "Règles d'autorisation",
"form.feed.label.ignore_http_cache": "Ignorer le cache HTTP",
"form.feed.label.allow_self_signed_certificates": "Autoriser les certificats auto-signés ou non valides",
"form.feed.label.fetch_via_proxy": "Récupérer via proxy",
"form.feed.label.disabled": "Ne pas actualiser ce flux",
"form.feed.label.hide_globally": "Masquer les entrées dans la liste globale non lue",
"form.category.label.title": "Titre",
"form.category.hide_globally": "Masquer les entrées dans la liste globale non lue",
"form.user.label.username": "Nom d'utilisateur",
"form.user.label.password": "Mot de passe",
"form.user.label.confirmation": "Confirmation du mot de passe",
@@ -278,18 +291,30 @@
"form.prefs.label.theme": "Thème",
"form.prefs.label.entry_sorting": "Ordre des éléments",
"form.prefs.label.entries_per_page": "Entrées par page",
"form.prefs.label.display_mode": "Mode d'affichage de l'application web (doit être réinstallé)",
"form.prefs.select.older_first": "Ancien éléments en premier",
"form.prefs.select.recent_first": "Éléments récents en premier",
"form.prefs.select.fullscreen": "Plein écran",
"form.prefs.select.standalone": "Autonome",
"form.prefs.select.minimal_ui": "Minimal",
"form.prefs.select.browser": "Navigateur",
"form.prefs.select.publish_time": "Heure de publication de l'entrée",
"form.prefs.select.created_time": "Heure de création de l'entrée",
"form.prefs.label.keyboard_shortcuts": "Activer les raccourcis clavier",
"form.prefs.label.entry_swipe": "Activer le geste de balayage sur les entrées sur mobile",
"form.prefs.label.show_reading_time": "Afficher le temps de lecture estimé des articles",
"form.prefs.label.custom_css": "CSS personnalisé",
"form.prefs.label.entry_order": "Colonne de tri des entrées",
"form.import.label.file": "Fichier OPML",
"form.import.label.url": "URL",
"form.integration.fever_activate": "Activer l'API de Fever",
"form.integration.fever_username": "Nom d'utilisateur pour l'API de Fever",
"form.integration.fever_password": "Mot de passe pour l'API de Fever",
"form.integration.fever_endpoint": "Point de terminaison de l'API Fever :",
"form.integration.googlereader_activate": "Activer l'API de Google Reader",
"form.integration.googlereader_username": "Nom d'utilisateur pour l'API de Google Reader",
"form.integration.googlereader_password": "Mot de passe pour l'API de Google Reader",
"form.integration.googlereader_endpoint": "Point de terminaison de l'API Google Reader :",
"form.integration.pinboard_activate": "Sauvegarder les articles vers Pinboard",
"form.integration.pinboard_token": "Jeton de sécurité de l'API de Pinboard",
"form.integration.pinboard_tags": "Libellés de Pinboard",
@@ -310,6 +335,16 @@
"form.integration.nunux_keeper_activate": "Sauvegarder les articles vers Nunux Keeper",
"form.integration.nunux_keeper_endpoint": "URL de l'API de Nunux Keeper",
"form.integration.nunux_keeper_api_key": "Clé d'API de Nunux Keeper",
"form.integration.espial_activate": "Sauvegarder les articles vers Espial",
"form.integration.espial_endpoint": "URL de l'API de Espial",
"form.integration.espial_api_key": "Clé d'API de Espial",
"form.integration.espial_tags": "Libellés de Espial",
"form.integration.telegram_bot_activate": "Envoyer les nouveaux articles vers Telegram",
"form.integration.telegram_bot_token": "Jeton de sécurité de l'API du Bot Telegram",
"form.integration.telegram_chat_id": "Identifiant de discussion",
"form.integration.linkding_activate": "Sauvegarder les articles vers Linkding",
"form.integration.linkding_endpoint": "URL de l'API de Linkding",
"form.integration.linkding_api_key": "Clé d'API de Linkding",
"form.api_key.label.description": "Libellé de la clé d'API",
"form.submit.loading": "Chargement...",
"form.submit.saving": "Sauvegarde en cours...",
@@ -359,5 +394,5 @@
"Website unreachable, the request timed out after %d seconds": "Site web injoignable, la requête à échouée après %d secondes",
"You are not authorized to access this resource (invalid username/password)": "Vous n'êtes pas autorisé à accéder à cette ressource (nom d'utilisateur / mot de passe incorrect)",
"Unable to fetch this resource (Status Code = %d)": "Impossible de récupérer cette ressource (code=%d)",
"Resource not found (404), this feed doesn't exists anymore, check the feed URL": "Page introuvable (404), cet abonnement n'existe plus, vérifiez l'adresse du flux"
"Resource not found (404), this feed doesn't exist anymore, check the feed URL": "Page introuvable (404), cet abonnement n'existe plus, vérifiez l'adresse du flux"
}
+36 -1
View File
@@ -92,6 +92,7 @@
"C'è %d feed.",
"Ci sono %d feed."
],
"page.categories.unread_counter": "Numero di voci non lette",
"page.new_category.title": "Nuova categoria",
"page.new_user.title": "Nuovo utente",
"page.edit_category.title": "Modifica categoria: %s",
@@ -114,6 +115,8 @@
"page.about.author": "Autore:",
"page.about.license": "Licenza:",
"page.about.global_config_options": "Opzioni di configurazione globali",
"page.about.postgres_version": "Postgres versione:",
"page.about.go_version": "Go versione:",
"page.add_feed.title": "Nuovo feed",
"page.add_feed.no_category": "Nessuna categoria selezionata. Devi scegliere almeno una categoria.",
"page.add_feed.label.url": "URL",
@@ -149,7 +152,8 @@
"page.keyboard_shortcuts.open_original_same_window": "Apri il link originale nella scheda corrente",
"page.keyboard_shortcuts.open_comments": "Apri la pagina web dei commenti",
"page.keyboard_shortcuts.open_comments_same_window": "Apri il link dei commenti nella scheda corrente",
"page.keyboard_shortcuts.toggle_read_status": "Cambia lo stato di lettura (letto/da leggere)",
"page.keyboard_shortcuts.toggle_read_status_next": "Cambia lo stato di lettura (letto/da leggere), concentrati dopo",
"page.keyboard_shortcuts.toggle_read_status_prev": "Cambia lo stato di lettura (letto/da leggere), focus precedente",
"page.keyboard_shortcuts.refresh_all_feeds": "Aggiorna tutti i feed in background",
"page.keyboard_shortcuts.mark_page_as_read": "Segna la pagina attuale come letta",
"page.keyboard_shortcuts.download_content": "Scarica il contenuto integrale",
@@ -199,6 +203,9 @@
"page.api_keys.table.actions": "Azioni",
"page.api_keys.never_used": "Mai usato",
"page.new_api_key.title": "Nuova chiave API",
"page.offline.title": "Modalità offline",
"page.offline.message": "Sei offline",
"page.offline.refresh_page": "Prova ad aggiornare la pagina",
"alert.no_shared_entry": "Non ci sono voci condivise.",
"alert.no_bookmark": "Nessun preferito disponibile.",
"alert.no_category": "Nessuna categoria disponibile.",
@@ -218,6 +225,7 @@
"error.unlink_account_without_password": "Devi scegliere una password altrimenti la prossima volta non riuscirai ad accedere.",
"error.duplicate_linked_account": "Esiste già un account configurato per questo servizio!",
"error.duplicate_fever_username": "Esiste già un account Fever con lo stesso nome utente!",
"error.duplicate_googlereader_username": "Esiste già un account Google Reader con lo stesso nome utente!",
"error.pocket_request_token": "Non sono riuscito ad ottenere il request token da Pocket!",
"error.pocket_access_token": "Non sono riuscito ad ottenere l'access token da Pocket!",
"error.category_already_exists": "Questa categoria esiste già.",
@@ -253,6 +261,7 @@
"error.invalid_language": "Lingua non valida.",
"error.invalid_timezone": "Fuso orario non valido.",
"error.invalid_entry_direction": "Ordinamento non valido.",
"error.invalid_display_mode": "Modalità di visualizzazione web app non valida.",
"form.feed.label.title": "Titolo",
"form.feed.label.site_url": "URL del sito",
"form.feed.label.feed_url": "URL del feed",
@@ -261,14 +270,18 @@
"form.feed.label.feed_username": "Nome utente del feed",
"form.feed.label.feed_password": "Password del feed",
"form.feed.label.user_agent": "Usa user agent personalizzato",
"form.feed.label.cookie": "Installare i cookies",
"form.feed.label.scraper_rules": "Regole di estrazione del contenuto",
"form.feed.label.rewrite_rules": "Regole di impaginazione del contenuto",
"form.feed.label.blocklist_rules": "Regole di blocco",
"form.feed.label.keeplist_rules": "Regole di autorizzazione",
"form.feed.label.ignore_http_cache": "Ignora cache HTTP",
"form.feed.label.allow_self_signed_certificates": "Consenti certificati autofirmati o non validi",
"form.feed.label.fetch_via_proxy": "Recuperare tramite proxy",
"form.feed.label.disabled": "Non aggiornare questo feed",
"form.feed.label.hide_globally": "Nascondere le voci nella lista globale dei non letti",
"form.category.label.title": "Titolo",
"form.category.hide_globally": "Nascondere le voci nella lista globale dei non letti",
"form.user.label.username": "Nome utente",
"form.user.label.password": "Password",
"form.user.label.confirmation": "Conferma password",
@@ -278,18 +291,30 @@
"form.prefs.label.theme": "Tema",
"form.prefs.label.entry_sorting": "Ordinamento articoli",
"form.prefs.label.entries_per_page": "Articoli per pagina",
"form.prefs.label.display_mode": "Modalità di visualizzazione web app (necessita la reinstallazione)",
"form.prefs.select.older_first": "Prima i più vecchi",
"form.prefs.select.recent_first": "Prima i più recenti",
"form.prefs.select.fullscreen": "Schermo intero",
"form.prefs.select.standalone": "Autonoma",
"form.prefs.select.minimal_ui": "Minimale",
"form.prefs.select.browser": "Browser",
"form.prefs.select.publish_time": "Ora di pubblicazione dell'entrata",
"form.prefs.select.created_time": "Tempo di creazione dell'entrata",
"form.prefs.label.keyboard_shortcuts": "Abilita le scorciatoie da tastiera",
"form.prefs.label.entry_swipe": "Abilita il gesto di scorrimento sulle voci sul cellulare",
"form.prefs.label.show_reading_time": "Mostra il tempo di lettura stimato per gli articoli",
"form.prefs.label.custom_css": "CSS personalizzati",
"form.prefs.label.entry_order": "Colonna di ordinamento delle voci",
"form.import.label.file": "File OPML",
"form.import.label.url": "URL",
"form.integration.fever_activate": "Abilita l'API di Fever",
"form.integration.fever_username": "Nome utente dell'account Fever",
"form.integration.fever_password": "Password dell'account Fever",
"form.integration.fever_endpoint": "Endpoint dell'API di Fever:",
"form.integration.googlereader_activate": "Abilita l'API di Google Reader",
"form.integration.googlereader_username": "Nome utente dell'account Google Reader",
"form.integration.googlereader_password": "Password dell'account Google Reader",
"form.integration.googlereader_endpoint": "Endpoint dell'API di Google Reader:",
"form.integration.pinboard_activate": "Salva gli articoli su Pinboard",
"form.integration.pinboard_token": "Token dell'API di Pinboard",
"form.integration.pinboard_tags": "Tag di Pinboard",
@@ -310,6 +335,16 @@
"form.integration.nunux_keeper_activate": "Salva gli articoli su Nunux Keeper",
"form.integration.nunux_keeper_endpoint": "Endpoint dell'API di Nunux Keeper",
"form.integration.nunux_keeper_api_key": "API key dell'account Nunux Keeper",
"form.integration.espial_activate": "Salva gli articoli su Espial",
"form.integration.espial_endpoint": "Endpoint dell'API di Espial",
"form.integration.espial_api_key": "API key dell'account Espial",
"form.integration.espial_tags": "Tag di Espial",
"form.integration.telegram_bot_activate": "Invia nuovi articoli alla chat di Telegram",
"form.integration.telegram_bot_token": "Token bot",
"form.integration.telegram_chat_id": "ID chat",
"form.integration.linkding_activate": "Salva gli articoli su Linkding",
"form.integration.linkding_endpoint": "Endpoint dell'API di Linkding",
"form.integration.linkding_api_key": "API key dell'account Linkding",
"form.api_key.label.description": "Etichetta chiave API",
"form.submit.loading": "Caricamento in corso...",
"form.submit.saving": "Salvataggio in corso...",
+36 -1
View File
@@ -92,6 +92,7 @@
"%d 個の記事があります。",
"%d 個の記事があります。"
],
"page.categories.unread_counter": "未読記事の数",
"page.new_category.title": "新規カテゴリ",
"page.new_user.title": "新規ユーザー",
"page.edit_category.title": "カテゴリーを編集: %s",
@@ -114,6 +115,8 @@
"page.about.author": "作者:",
"page.about.license": "ライセンス:",
"page.about.global_config_options": "グローバル構成オプション",
"page.about.postgres_version": "Postgres バージョン:",
"page.about.go_version": "Go バージョン:",
"page.add_feed.title": "新規購読",
"page.add_feed.no_category": "カテゴリが存在しません。 少なくとも1つのカテゴリが必要です。",
"page.add_feed.label.url": "URL",
@@ -149,7 +152,8 @@
"page.keyboard_shortcuts.open_original_same_window": "現在のタブでオリジナルのリンクを開く",
"page.keyboard_shortcuts.open_comments": "コメントリンクを開く",
"page.keyboard_shortcuts.open_comments_same_window": "現在のタブでコメントリンクを開く",
"page.keyboard_shortcuts.toggle_read_status": "既読/未読 切り替え",
"page.keyboard_shortcuts.toggle_read_status_next": "既読/未読 切り替え, 次に焦点を合わせる",
"page.keyboard_shortcuts.toggle_read_status_prev": "既読/未読 切り替え, 前にフォーカス",
"page.keyboard_shortcuts.refresh_all_feeds": "全てのフィードをバックグラウンドで更新",
"page.keyboard_shortcuts.mark_page_as_read": "現在のページを既読にする",
"page.keyboard_shortcuts.download_content": "オリジナルの内容をダウンロード",
@@ -199,6 +203,9 @@
"page.api_keys.table.actions": "アクション",
"page.api_keys.never_used": "使われたことがない",
"page.new_api_key.title": "新しいAPIキー",
"page.offline.title": "オフラインモード",
"page.offline.message": "オフラインです",
"page.offline.refresh_page": "ページを更新してみてください",
"alert.no_shared_entry": "共有エントリはありません。",
"alert.no_bookmark": "現在星付きはありません。",
"alert.no_category": "カテゴリが存在しません。",
@@ -218,6 +225,7 @@
"error.unlink_account_without_password": "パスワードを設定しなければ再びログインすることはできません。",
"error.duplicate_linked_account": "別なユーザーが既にこのサービスの同じユーザーとリンクしています。",
"error.duplicate_fever_username": "既に同じ名前の Fever ユーザー名が使われています!",
"error.duplicate_googlereader_username": "既に同じ名前の Google Reader ユーザー名が使われています!",
"error.pocket_request_token": "Pocket の request token が取得できません!",
"error.pocket_access_token": "Pocket の access token が取得できません!",
"error.category_already_exists": "このカテゴリは既に存在しています。",
@@ -253,6 +261,7 @@
"error.invalid_language": "言語が無効です。",
"error.invalid_timezone": "タイムゾーンが無効です。",
"error.invalid_entry_direction": "ソート順が無効です。",
"error.invalid_display_mode": "Webアプリの表示モードが無効です。",
"form.feed.label.title": "タイトル",
"form.feed.label.site_url": "サイト URL",
"form.feed.label.feed_url": "フィード URL",
@@ -261,14 +270,18 @@
"form.feed.label.feed_username": "フィードのユーザー名",
"form.feed.label.feed_password": "フィードのパスワード",
"form.feed.label.user_agent": "ディフォルトの User Agent を上書きする",
"form.feed.label.cookie": "クッキーの設定",
"form.feed.label.scraper_rules": "スクラップルール",
"form.feed.label.rewrite_rules": "Rewrite ルール",
"form.feed.label.blocklist_rules": "ブロックルール",
"form.feed.label.keeplist_rules": "許可規則",
"form.feed.label.ignore_http_cache": "HTTPキャッシュを無視",
"form.feed.label.allow_self_signed_certificates": "自己署名証明書または無効な証明書を許可する",
"form.feed.label.fetch_via_proxy": "プロキシ経由でフェッチ",
"form.feed.label.disabled": "このフィードを更新しない",
"form.feed.label.hide_globally": "グローバル未読リストのエントリーを隠す",
"form.category.label.title": "タイトル",
"form.category.hide_globally": "グローバル未読リストのエントリーを隠す",
"form.user.label.username": "ユーザー名",
"form.user.label.password": "パスワード",
"form.user.label.confirmation": "パスワード確認",
@@ -278,18 +291,30 @@
"form.prefs.label.theme": "テーマ",
"form.prefs.label.entry_sorting": "記事の並べ替え",
"form.prefs.label.entries_per_page": "ページあたりのエントリ",
"form.prefs.label.display_mode": "Webアプリの表示モード (再インストールが必要)",
"form.prefs.select.older_first": "古い記事を最初に",
"form.prefs.select.recent_first": "新しい記事を最初に",
"form.prefs.select.fullscreen": "全画面表示",
"form.prefs.select.standalone": "スタンドアロン",
"form.prefs.select.minimal_ui": "最小限",
"form.prefs.select.browser": "ブラウザ",
"form.prefs.select.publish_time": "エントリー公開時間",
"form.prefs.select.created_time": "エントリー作成時間",
"form.prefs.label.keyboard_shortcuts": "キーボード・ショートカットを有効にする",
"form.prefs.label.entry_swipe": "モバイルのエントリでスワイプジェスチャーを有効にする",
"form.prefs.label.show_reading_time": "記事の推定読書時間を表示する",
"form.prefs.label.custom_css": "カスタムCSS",
"form.prefs.label.entry_order": "エントリーソートカラム",
"form.import.label.file": "OPML ファイル",
"form.import.label.url": "URL",
"form.integration.fever_activate": "Fever API を有効にする",
"form.integration.fever_username": "Fever の ユーザー名",
"form.integration.fever_password": "Fever の パスワード",
"form.integration.fever_endpoint": "Fever API endpoint:",
"form.integration.googlereader_activate": "Google Reader API を有効にする",
"form.integration.googlereader_username": "Google Reader の ユーザー名",
"form.integration.googlereader_password": "Google Reader の パスワード",
"form.integration.googlereader_endpoint": "Google Reader API endpoint:",
"form.integration.pinboard_activate": "Pinboard に記事を保存する",
"form.integration.pinboard_token": "Pinboard の API Token",
"form.integration.pinboard_tags": "Pinboard の Tag",
@@ -310,6 +335,16 @@
"form.integration.nunux_keeper_activate": "Nunux Keeper に記事を保存する",
"form.integration.nunux_keeper_endpoint": "Nunux Keeper の API Endpoint",
"form.integration.nunux_keeper_api_key": "Nunux Keeper の API key",
"form.integration.espial_activate": "Espial に記事を保存する",
"form.integration.espial_endpoint": "Espial の API Endpoint",
"form.integration.espial_api_key": "Espial の API key",
"form.integration.espial_tags": "Espial の Tag",
"form.integration.telegram_bot_activate": "新しい記事をTelegramチャットにプッシュする",
"form.integration.telegram_bot_token": "ボットトークン",
"form.integration.telegram_chat_id": "チャットID",
"form.integration.linkding_activate": "Linkding に記事を保存する",
"form.integration.linkding_endpoint": "Linkding の API Endpoint",
"form.integration.linkding_api_key": "Linkding の API key",
"form.api_key.label.description": "APIキーラベル",
"form.submit.loading": "読み込み中…",
"form.submit.saving": "保存中…",
+36 -1
View File
@@ -92,6 +92,7 @@
"Er is %d feed.",
"Er zijn %d feeds."
],
"page.categories.unread_counter": "Aantal ongelezen vermeldingen",
"page.new_category.title": "Nieuwe categorie",
"page.new_user.title": "Nieuwe gebruiker",
"page.edit_category.title": "Bewerken van categorie: %s",
@@ -115,6 +116,8 @@
"page.about.author": "Auteur:",
"page.about.license": "Licentie:",
"page.about.global_config_options": "globale configuratie-opties",
"page.about.postgres_version": "Postgres versie:",
"page.about.go_version": "Go versie:",
"page.add_feed.title": "Nieuwe feed",
"page.add_feed.no_category": "Er zijn geen categorieën. Je moet op zijn minst één caterogie hebben.",
"page.add_feed.label.url": "URL",
@@ -150,7 +153,8 @@
"page.keyboard_shortcuts.open_original_same_window": "Oorspronkelijke koppeling op huidig tabblad openen",
"page.keyboard_shortcuts.open_comments": "Open opmerkingen link",
"page.keyboard_shortcuts.open_comments_same_window": "Open de reactiekoppeling op het huidige tabblad",
"page.keyboard_shortcuts.toggle_read_status": "Markeer gelezen/ongelezen",
"page.keyboard_shortcuts.toggle_read_status_next": "Markeer gelezen/ongelezen, focus volgende",
"page.keyboard_shortcuts.toggle_read_status_prev": "Markeer gelezen/ongelezen, focus vorige",
"page.keyboard_shortcuts.refresh_all_feeds": "Vernieuw alle feeds in de achtergrond",
"page.keyboard_shortcuts.mark_page_as_read": "Markeer deze pagina als gelezen",
"page.keyboard_shortcuts.download_content": "Download originele content",
@@ -199,6 +203,9 @@
"page.api_keys.table.actions": "Acties",
"page.api_keys.never_used": "Nooit gebruikt",
"page.new_api_key.title": "Nieuwe API-sleutel",
"page.offline.title": "Offline modus",
"page.offline.message": "Je bent offline",
"page.offline.refresh_page": "Probeer de pagina te vernieuwen",
"alert.no_shared_entry": "Er is geen gedeelde toegang.",
"alert.no_bookmark": "Er zijn op dit moment geen favorieten.",
"alert.no_category": "Er zijn geen categorieën.",
@@ -218,6 +225,7 @@
"error.unlink_account_without_password": "U moet een wachtwoord definiëren anders kunt u zich niet opnieuw aanmelden.",
"error.duplicate_linked_account": "Er is al iemand geregistreerd met deze provider!",
"error.duplicate_fever_username": "Er is al iemand met dezelfde Fever gebruikersnaam!",
"error.duplicate_googlereader_username": "Er is al iemand met dezelfde Google Reader gebruikersnaam!",
"error.pocket_request_token": "Kon geen aanvraagtoken ophalen van Pocket!",
"error.pocket_access_token": "Kon geen toegangstoken ophalen van Pocket!",
"error.category_already_exists": "Deze categorie bestaat al.",
@@ -253,6 +261,7 @@
"error.invalid_language": "Ongeldige taal.",
"error.invalid_timezone": "Ongeldige tijdzone.",
"error.invalid_entry_direction": "Ongeldige sorteervolgorde.",
"error.invalid_display_mode": "Ongeldige weergavemodus voor webapp.",
"form.feed.label.title": "Naam",
"form.feed.label.site_url": "Website URL",
"form.feed.label.feed_url": "Feed URL",
@@ -261,14 +270,18 @@
"form.feed.label.feed_username": "Feed-gebruikersnaam",
"form.feed.label.feed_password": "Feed wachtwoord",
"form.feed.label.user_agent": "Standaard User Agent overschrijven",
"form.feed.label.cookie": "Cookies instellen",
"form.feed.label.scraper_rules": "Scraper regels",
"form.feed.label.rewrite_rules": "Rewrite regels",
"form.feed.label.blocklist_rules": "Blokkeer regels",
"form.feed.label.keeplist_rules": "toestemmingsregels",
"form.feed.label.ignore_http_cache": "Negeer HTTP-cache",
"form.feed.label.allow_self_signed_certificates": "Sta zelfondertekende of ongeldige certificaten toe",
"form.feed.label.fetch_via_proxy": "Ophalen via proxy",
"form.feed.label.disabled": "Vernieuw deze feed niet",
"form.feed.label.hide_globally": "Verberg items in de globale ongelezen lijst",
"form.category.label.title": "Naam",
"form.category.hide_globally": "Verberg items in de globale ongelezen lijst",
"form.user.label.username": "Gebruikersnaam",
"form.user.label.password": "Wachtwoord",
"form.user.label.confirmation": "Bevestig wachtwoord",
@@ -278,18 +291,30 @@
"form.prefs.label.theme": "Skin",
"form.prefs.label.entry_sorting": "Volgorde van items",
"form.prefs.label.entries_per_page": "Inzendingen per pagina",
"form.prefs.label.display_mode": "Weergavemodus voor webapp (moet opnieuw worden geïnstalleerd)",
"form.prefs.select.older_first": "Oudere items eerst",
"form.prefs.select.recent_first": "Recente items eerst",
"form.prefs.select.fullscreen": "Volledig scherm",
"form.prefs.select.standalone": "Standalone",
"form.prefs.select.minimal_ui": "Minimaal",
"form.prefs.select.browser": "Browser",
"form.prefs.select.publish_time": "Tijd van binnenkomst",
"form.prefs.select.created_time": "Tijdstip van binnenkomst",
"form.prefs.label.keyboard_shortcuts": "Schakel sneltoetsen in",
"form.prefs.label.entry_swipe": "Schakel veegbewegingen in voor items op mobiel",
"form.prefs.label.show_reading_time": "Toon geschatte leestijd voor artikelen",
"form.prefs.label.custom_css": "Aangepaste CSS",
"form.prefs.label.entry_order": "Ingang Sorteerkolom",
"form.import.label.file": "OPML-bestand",
"form.import.label.url": "URL",
"form.integration.fever_activate": "Activeer Fever API",
"form.integration.fever_username": "Fever gebruikersnaam",
"form.integration.fever_password": "Fever wachtwoord",
"form.integration.fever_endpoint": "Fever URL:",
"form.integration.googlereader_activate": "Activeer Google Reader API",
"form.integration.googlereader_username": "Google Reader gebruikersnaam",
"form.integration.googlereader_password": "Google Reader wachtwoord",
"form.integration.googlereader_endpoint": "Google Reader URL:",
"form.integration.pinboard_activate": "Artikelen opslaan naar Pinboard",
"form.integration.pinboard_token": "Pinboard API token",
"form.integration.pinboard_tags": "Pinboard tags",
@@ -310,6 +335,16 @@
"form.integration.nunux_keeper_activate": "Opslaan naar Nunux Keeper",
"form.integration.nunux_keeper_endpoint": "Nunux Keeper URL",
"form.integration.nunux_keeper_api_key": "Nunux Keeper API-sleutel",
"form.integration.espial_activate": "Opslaan naar Espial",
"form.integration.espial_endpoint": "Espial URL",
"form.integration.espial_api_key": "Espial API-sleutel",
"form.integration.espial_tags": "Espial tags",
"form.integration.telegram_bot_activate": "Push nieuwe artikelen naar Telegram-chat",
"form.integration.telegram_bot_token": "Bot token",
"form.integration.telegram_chat_id": "Chat ID",
"form.integration.linkding_activate": "Opslaan naar Linkding",
"form.integration.linkding_endpoint": "Linkding URL",
"form.integration.linkding_api_key": "Linkding API-sleutel",
"form.api_key.label.description": "API-sleutellabel",
"form.submit.loading": "Laden...",
"form.submit.saving": "Opslaag...",
+36 -1
View File
@@ -93,6 +93,7 @@
"Są %d kanały.",
"Jest %d kanałów."
],
"page.categories.unread_counter": "Liczba nieprzeczytanych wpisów",
"page.new_category.title": "Nowa kategoria",
"page.new_user.title": "Nowy użytkownik",
"page.edit_category.title": "Edycja Kategorii: %s",
@@ -115,6 +116,8 @@
"page.about.build_date": "Data opracowania:",
"page.about.author": "Autor:",
"page.about.license": "Licencja:",
"page.about.postgres_version": "Postgres wersja:",
"page.about.go_version": "Go wersja:",
"page.about.global_config_options": "globalne opcje konfiguracji",
"page.add_feed.title": "Nowa subskrypcja",
"page.add_feed.no_category": "Nie ma żadnej kategorii. Musisz mieć co najmniej jedną kategorię.",
@@ -151,7 +154,8 @@
"page.keyboard_shortcuts.open_original_same_window": "Otwórz oryginalny link w bieżącej karcie",
"page.keyboard_shortcuts.open_comments": "Otwórz link do komentarzy",
"page.keyboard_shortcuts.open_comments_same_window": "Otwórz link do komentarzy w bieżącej karcie",
"page.keyboard_shortcuts.toggle_read_status": "Oznacz jako przeczytane/nieprzeczytane",
"page.keyboard_shortcuts.toggle_read_status_next": "Oznacz jako przeczytane/nieprzeczytane, skup się dalej",
"page.keyboard_shortcuts.toggle_read_status_prev": "Oznacz jako przeczytane/nieprzeczytane, skup poprzednie",
"page.keyboard_shortcuts.refresh_all_feeds": "Odśwież wszystkie subskrypcje w tle",
"page.keyboard_shortcuts.mark_page_as_read": "Zaznacz aktualną stronę jako przeczytaną",
"page.keyboard_shortcuts.download_content": "Pobierz oryginalną zawartość",
@@ -201,6 +205,9 @@
"page.api_keys.table.actions": "Działania",
"page.api_keys.never_used": "Nigdy nie używany",
"page.new_api_key.title": "Nowy klucz API",
"page.offline.title": "Tryb offline",
"page.offline.message": "Jesteś odłączony od sieci",
"page.offline.refresh_page": "Spróbuj odświeżyć stronę",
"alert.no_shared_entry": "Brak wspólnego wpisu.",
"alert.no_bookmark": "Obecnie nie ma żadnych zakładek.",
"alert.no_category": "Nie ma żadnej kategorii!",
@@ -220,6 +227,7 @@
"error.unlink_account_without_password": "Musisz zdefiniować hasło, inaczej nie będziesz mógł się ponownie zalogować.",
"error.duplicate_linked_account": "Już ktoś jest powiązany z tym dostawcą!",
"error.duplicate_fever_username": "Już ktoś inny używa tej nazwy użytkownika Fever!",
"error.duplicate_googlereader_username": "Już ktoś inny używa tej nazwy użytkownika Google Reader!",
"error.pocket_request_token": "Nie można pobrać tokena żądania z Pocket!",
"error.pocket_access_token": "Nie można pobrać tokena dostępu z Pocket!",
"error.category_already_exists": "Ta kategoria już istnieje.",
@@ -255,6 +263,7 @@
"error.invalid_language": "Nieprawidłowy język.",
"error.invalid_timezone": "Nieprawidłowa strefa czasowa.",
"error.invalid_entry_direction": "Nieprawidłowa kolejność sortowania.",
"error.invalid_display_mode": "Nieprawidłowy tryb wyświetlania aplikacji internetowej.",
"form.feed.label.title": "Tytuł",
"form.feed.label.site_url": "URL strony",
"form.feed.label.feed_url": "URL kanału",
@@ -263,14 +272,18 @@
"form.feed.label.feed_username": "Subskrypcję nazwa użytkownika",
"form.feed.label.feed_password": "Subskrypcję Hasło",
"form.feed.label.user_agent": "Zastąp domyślny agent użytkownika",
"form.feed.label.cookie": "Ustawianie ciasteczek",
"form.feed.label.scraper_rules": "Zasady ekstrakcji",
"form.feed.label.rewrite_rules": "Reguły zapisu",
"form.feed.label.blocklist_rules": "Zasady blokowania",
"form.feed.label.keeplist_rules": "Zasady zezwoleń",
"form.feed.label.ignore_http_cache": "Zignoruj pamięć podręczną HTTP",
"form.feed.label.allow_self_signed_certificates": "Zezwalaj na certyfikaty z podpisem własnym lub nieprawidłowe certyfikaty",
"form.feed.label.fetch_via_proxy": "Pobierz przez proxy",
"form.feed.label.disabled": "Не обновлять этот канал",
"form.feed.label.hide_globally": "Ukryj wpisy na globalnej liście nieprzeczytanych",
"form.category.label.title": "Tytuł",
"form.category.hide_globally": "Ukryj wpisy na globalnej liście nieprzeczytanych",
"form.user.label.username": "Nazwa użytkownika",
"form.user.label.password": "Hasło",
"form.user.label.confirmation": "Potwierdzenie hasła",
@@ -280,18 +293,30 @@
"form.prefs.label.theme": "Wygląd",
"form.prefs.label.entry_sorting": "Sortowanie artykułów",
"form.prefs.label.entries_per_page": "Wpisy na stronie",
"form.prefs.label.display_mode": "Tryb wyświetlania aplikacji internetowej (wymaga ponownej instalacji)",
"form.prefs.select.older_first": "Najstarsze wpisy jako pierwsze",
"form.prefs.label.keyboard_shortcuts": "Włącz skróty klawiaturowe",
"form.prefs.label.entry_swipe": "Włącz gest przesuwania na wpisach na telefonie komórkowym",
"form.prefs.label.show_reading_time": "Pokaż szacowany czas czytania artykułów",
"form.prefs.select.recent_first": "Najnowsze wpisy jako pierwsze",
"form.prefs.select.fullscreen": "Pełny ekran",
"form.prefs.select.standalone": "Samodzielny",
"form.prefs.select.minimal_ui": "Minimalny",
"form.prefs.select.browser": "Przeglądarka",
"form.prefs.select.publish_time": "Czas publikacji wpisu",
"form.prefs.select.created_time": "Czas utworzenia wpisu",
"form.prefs.label.custom_css": "Niestandardowy CSS",
"form.prefs.label.entry_order": "Kolumna sortowania wpisów",
"form.import.label.file": "Plik OPML",
"form.import.label.url": "URL",
"form.integration.fever_activate": "Aktywuj Fever API",
"form.integration.fever_username": "Login do Fever",
"form.integration.fever_password": "Hasło do Fever",
"form.integration.fever_endpoint": "Punkt końcowy API gorączka:",
"form.integration.googlereader_activate": "Aktywuj Google Reader API",
"form.integration.googlereader_username": "Login do Google Reader",
"form.integration.googlereader_password": "Hasło do Google Reader",
"form.integration.googlereader_endpoint": "Punkt końcowy API gorączka:",
"form.integration.pinboard_activate": "Zapisz artykuł w Pinboard",
"form.integration.pinboard_token": "Token Pinboard API",
"form.integration.pinboard_tags": "Pinboard Tags",
@@ -312,6 +337,16 @@
"form.integration.nunux_keeper_activate": "Zapisz artykuly do Nunux Keeper",
"form.integration.nunux_keeper_endpoint": "Nunux Keeper URL",
"form.integration.nunux_keeper_api_key": "Nunux Keeper API key",
"form.integration.espial_activate": "Zapisz artykuly do Espial",
"form.integration.espial_endpoint": "Espial URL",
"form.integration.espial_api_key": "Espial API key",
"form.integration.espial_tags": "Espial Tags",
"form.integration.telegram_bot_activate": "Przesyłaj nowe artykuły do czatu Telegram",
"form.integration.telegram_bot_token": "Token bota",
"form.integration.telegram_chat_id": "Identyfikator czatu",
"form.integration.linkding_activate": "Zapisz artykuły do Linkding",
"form.integration.linkding_endpoint": "Linkding URL",
"form.integration.linkding_api_key": "Linkding API key",
"form.api_key.label.description": "Etykieta klucza API",
"form.submit.loading": "Ładowanie...",
"form.submit.saving": "Zapisywanie...",
+36 -1
View File
@@ -92,6 +92,7 @@
"Existe %d fonte.",
"Existem %d fontes."
],
"page.categories.unread_counter": "Numero de itens não lidos",
"page.new_category.title": "Nova categoria",
"page.new_user.title": "Novo usuário",
"page.edit_category.title": "Editar categoria: %s",
@@ -114,6 +115,8 @@
"page.about.author": "Autor:",
"page.about.license": "Licença:",
"page.about.global_config_options": "opções de configuração global",
"page.about.postgres_version": "Postgres versão:",
"page.about.go_version": "Go versão:",
"page.add_feed.title": "Nova inscrição",
"page.add_feed.no_category": "Não existe uma categoria. Deve existir pelo menos uma categoria.",
"page.add_feed.label.url": "URL",
@@ -149,7 +152,8 @@
"page.keyboard_shortcuts.open_original_same_window": "Abrir o conteúdo original na janela atual",
"page.keyboard_shortcuts.open_comments": "Abrir os comentários",
"page.keyboard_shortcuts.open_comments_same_window": "Abrir os comentários na janela atual",
"page.keyboard_shortcuts.toggle_read_status": "Inverter estado de leitura do item",
"page.keyboard_shortcuts.toggle_read_status_next": "Inverter estado de leitura do item, focar próximo item",
"page.keyboard_shortcuts.toggle_read_status_prev": "Inverter estado de leitura do item, focar item anterior",
"page.keyboard_shortcuts.refresh_all_feeds": "Atualizar todas as fontes",
"page.keyboard_shortcuts.mark_page_as_read": "Marcar página atual como lida",
"page.keyboard_shortcuts.download_content": "Buscar o conteúdo original",
@@ -199,6 +203,9 @@
"page.api_keys.table.actions": "Ações",
"page.api_keys.never_used": "Nunca usado",
"page.new_api_key.title": "Nova chave de API",
"page.offline.title": "Modo offline",
"page.offline.message": "Você está offline",
"page.offline.refresh_page": "Tente atualizar a página",
"alert.no_shared_entry": "Não há itens compartilhados.",
"alert.no_bookmark": "Não há favorito neste momento.",
"alert.no_category": "Não há categoria.",
@@ -218,6 +225,7 @@
"error.unlink_account_without_password": "Você deve definir uma senha, senão não será possível efetuar a sessão novamente.",
"error.duplicate_linked_account": "Alguém já está vinculado a esse serviço!",
"error.duplicate_fever_username": "Alguém já está utilizando esse nome de usuário do Fever!",
"error.duplicate_googlereader_username": "Alguém já está utilizando esse nome de usuário do Google Reader!",
"error.pocket_request_token": "Não foi possível obter um pedido de token no Pocket!",
"error.pocket_access_token": "Não foi possível obter um token de acesso no Pocket!",
"error.category_already_exists": "Esta categoria já existe.",
@@ -253,6 +261,7 @@
"error.invalid_language": "Idioma inválido.",
"error.invalid_timezone": "Fuso horário inválido.",
"error.invalid_entry_direction": "Direção de entrada inválida.",
"error.invalid_display_mode": "Modo de exibição de aplicativo inválido da web.",
"form.feed.label.title": "Título",
"form.feed.label.site_url": "URL do site",
"form.feed.label.feed_url": "URL da fonte",
@@ -261,14 +270,18 @@
"form.feed.label.feed_username": "Nome de usuário da fonte",
"form.feed.label.feed_password": "Senha da fonte",
"form.feed.label.user_agent": "Sobrescrever o agente de usuário (user-agent) padrão",
"form.feed.label.cookie": "Definir Cookies",
"form.feed.label.scraper_rules": "Regras do scraper",
"form.feed.label.rewrite_rules": "Regras para o Rewrite",
"form.feed.label.blocklist_rules": "Regras de bloqueio",
"form.feed.label.keeplist_rules": "Regras de permissão",
"form.feed.label.ignore_http_cache": "Ignorar cache HTTP",
"form.feed.label.allow_self_signed_certificates": "Permitir certificados autoassinados ou inválidos",
"form.feed.label.disabled": "Não atualizar esta fonte",
"form.feed.label.fetch_via_proxy": "Buscar via proxy",
"form.feed.label.hide_globally": "Ocultar entradas na lista global não lida",
"form.category.label.title": "Título",
"form.category.hide_globally": "Ocultar entradas na lista global não lida",
"form.user.label.username": "Nome de usuário",
"form.user.label.password": "Senha",
"form.user.label.confirmation": "Confirmação de senha",
@@ -278,18 +291,30 @@
"form.prefs.label.theme": "Tema",
"form.prefs.label.entry_sorting": "Ordenação dos itens",
"form.prefs.label.entries_per_page": "Itens por página",
"form.prefs.label.display_mode": "Modo de exibição do aplicativo Web (precisa ser reinstalado)",
"form.prefs.select.older_first": "Itens mais velhos primeiro",
"form.prefs.select.recent_first": "Itens mais recentes",
"form.prefs.select.fullscreen": "Tela completa",
"form.prefs.select.standalone": "Autônomo",
"form.prefs.select.minimal_ui": "Mínimo",
"form.prefs.select.browser": "Navegador",
"form.prefs.select.publish_time": "Entrada hora de publicação",
"form.prefs.select.created_time": "Entrada tempo criado",
"form.prefs.label.keyboard_shortcuts": "Habilitar atalhos do teclado",
"form.prefs.label.entry_swipe": "Ativar gesto de deslizar nas entradas no celular",
"form.prefs.label.show_reading_time": "Mostrar tempo estimado de leitura de artigos",
"form.prefs.label.custom_css": "CSS customizado",
"form.prefs.label.entry_order": "Coluna de Ordenação de Entrada",
"form.import.label.file": "Arquivo OPML",
"form.import.label.url": "URL",
"form.integration.fever_activate": "Ativar API do Fever",
"form.integration.fever_username": "Nome de usuário do Fever",
"form.integration.fever_password": "Senha do Fever",
"form.integration.fever_endpoint": "Endpoint da API do Fever:",
"form.integration.googlereader_activate": "Ativar API do Google Reader",
"form.integration.googlereader_username": "Nome de usuário do Google Reader",
"form.integration.googlereader_password": "Senha do Google Reader",
"form.integration.googlereader_endpoint": "Endpoint da API do Google Reader:",
"form.integration.pinboard_activate": "Salvar itens no Pinboard",
"form.integration.pinboard_token": "Token de API do Pinboard",
"form.integration.pinboard_tags": "Etiquetas (tags) do Pinboard",
@@ -310,6 +335,16 @@
"form.integration.nunux_keeper_activate": "Salvar itens no Nunux Keeper",
"form.integration.nunux_keeper_endpoint": "Endpoint de API do Nunux Keeper",
"form.integration.nunux_keeper_api_key": "Chave de API do Nunux Keeper",
"form.integration.espial_activate": "Salvar itens no Espial",
"form.integration.espial_endpoint": "Endpoint de API do Espial",
"form.integration.espial_api_key": "Chave de API do Espial",
"form.integration.espial_tags": "Etiquetas (tags) do Espial",
"form.integration.telegram_bot_activate": "Envie novos artigos para o chat do Telegram",
"form.integration.telegram_bot_token": "Token de bot",
"form.integration.telegram_chat_id": "ID de bate-papo",
"form.integration.linkding_activate": "Salvar itens no Linkding",
"form.integration.linkding_endpoint": "Endpoint de API do Linkding",
"form.integration.linkding_api_key": "Chave de API do Linkding",
"form.api_key.label.description": "Etiqueta da chave de API",
"form.submit.loading": "Carregando...",
"form.submit.saving": "Salvando...",
+36 -1
View File
@@ -93,6 +93,7 @@
"Есть %d подписки.",
"Есть %d подписок."
],
"page.categories.unread_counter": "Количество непрочитанных записей",
"page.new_category.title": "Новая категория",
"page.new_user.title": "Новый пользователь",
"page.edit_category.title": "Изменить категорию: %s",
@@ -115,6 +116,8 @@
"page.about.build_date": "Дата сборки:",
"page.about.author": "Автор:",
"page.about.license": "Лицензия:",
"page.about.postgres_version": "Postgres bерсия:",
"page.about.go_version": "Go bерсия:",
"page.about.global_config_options": "глобальные параметры конфигурации",
"page.add_feed.title": "Новая подписка",
"page.add_feed.no_category": "Категории отсутствуют. У вас должна быть хотя бы одна категория.",
@@ -151,7 +154,8 @@
"page.keyboard_shortcuts.open_original": "Открыть оригинальную ссылку",
"page.keyboard_shortcuts.open_comments_same_window": "Открыть ссылку на комментарии в текущей вкладке",
"page.keyboard_shortcuts.open_comments": "Открыть ссылку для комментариев",
"page.keyboard_shortcuts.toggle_read_status": "Переключатель прочитанного",
"page.keyboard_shortcuts.toggle_read_status_next": "Переключатель прочитанного, сосредоточиться на следующем",
"page.keyboard_shortcuts.toggle_read_status_prev": "Переключатель прочитанного, фокус предыдущий",
"page.keyboard_shortcuts.refresh_all_feeds": "Обновить все подписки в фоне",
"page.keyboard_shortcuts.mark_page_as_read": "Отметить текущую страницу прочитанной",
"page.keyboard_shortcuts.download_content": "Загрузить оригинальное содержимое",
@@ -201,6 +205,9 @@
"page.api_keys.table.actions": "Действия",
"page.api_keys.never_used": "Никогда не использовался",
"page.new_api_key.title": "Новый API-ключ",
"page.offline.title": "Автономный режим",
"page.offline.message": "Ты не в сети",
"page.offline.refresh_page": "Попробуйте обновить страницу",
"alert.no_shared_entry": "Общедоступные записи отсутствуют.",
"alert.no_bookmark": "Избранное отсутствует.",
"alert.no_category": "Категории отсутствуют.",
@@ -220,6 +227,7 @@
"error.unlink_account_without_password": "Вы должны установить пароль, иначе вы не сможете войти снова.",
"error.duplicate_linked_account": "Уже есть кто-то, кто ассоциирован с этим аккаунтом!",
"error.duplicate_fever_username": "Уже есть кто-то с таким же именем пользователя Fever!",
"error.duplicate_googlereader_username": "Уже есть кто-то с таким же именем пользователя Google Reader!",
"error.pocket_request_token": "Не удается извлечь request token из Pocket!",
"error.pocket_access_token": "Не удается извлечь access token из Pocket!",
"error.category_already_exists": "Эта категория уже существует.",
@@ -255,6 +263,7 @@
"error.invalid_language": "Неверный язык.",
"error.invalid_timezone": "Неверный часовой пояс.",
"error.invalid_entry_direction": "Неверное направление входа.",
"error.invalid_display_mode": "Недопустимый режим отображения веб-приложения.",
"form.feed.label.title": "Название",
"form.feed.label.site_url": "URL сайта",
"form.feed.label.feed_url": "URL подписки",
@@ -263,14 +272,18 @@
"form.feed.label.feed_username": "Имя пользователя подписки",
"form.feed.label.feed_password": "Пароль подписки",
"form.feed.label.user_agent": "Переопределить User Agent по умолчанию",
"form.feed.label.cookie": "Установить Ку́ки",
"form.feed.label.scraper_rules": "Правила Scraper",
"form.feed.label.rewrite_rules": "Правила Rewrite",
"form.feed.label.blocklist_rules": "Правила блокировки",
"form.feed.label.keeplist_rules": "правила разрешений",
"form.feed.label.ignore_http_cache": "Игнорировать HTTP-кеш",
"form.feed.label.allow_self_signed_certificates": "Разрешить самоподписанные или недействительные сертификаты",
"form.feed.label.fetch_via_proxy": "Получить через прокси",
"form.feed.label.disabled": "Не обновлять этот канал",
"form.feed.label.hide_globally": "Скрыть записи в глобальном списке непрочитанных",
"form.category.label.title": "Название",
"form.category.hide_globally": "Скрыть записи в глобальном списке непрочитанных",
"form.user.label.username": "Имя пользователя",
"form.user.label.password": "Пароль",
"form.user.label.confirmation": "Подтверждение пароля",
@@ -280,18 +293,30 @@
"form.prefs.label.theme": "Тема",
"form.prefs.label.entry_sorting": "Сортировка записей",
"form.prefs.label.entries_per_page": "Записи на странице",
"form.prefs.label.display_mode": "Режим отображения веб-приложения (требуется переустановка)",
"form.prefs.select.older_first": "Сначала старые записи",
"form.prefs.select.recent_first": "Сначала последние записи",
"form.prefs.select.fullscreen": "Полноэкранный",
"form.prefs.select.standalone": "Автономный",
"form.prefs.select.minimal_ui": "Минимальный",
"form.prefs.select.browser": "Браузер",
"form.prefs.select.publish_time": "Время публикации заявки",
"form.prefs.select.created_time": "Время создания записи",
"form.prefs.label.keyboard_shortcuts": "Включить сочетания клавиш",
"form.prefs.label.entry_swipe": "Включить жест смахивания для записей на мобильном устройстве",
"form.prefs.label.show_reading_time": "Показать примерное время чтения статей",
"form.prefs.label.custom_css": "Пользовательские CSS",
"form.prefs.label.entry_order": "Колонка сортировки ввода",
"form.import.label.file": "OPML файл",
"form.import.label.url": "URL",
"form.integration.fever_activate": "Активировать Fever API",
"form.integration.fever_username": "Имя пользователя Fever",
"form.integration.fever_password": "Пароль Fever",
"form.integration.fever_endpoint": "Конечная точка Fever API:",
"form.integration.googlereader_activate": "Активировать Google Reader API",
"form.integration.googlereader_username": "Имя пользователя Google Reader",
"form.integration.googlereader_password": "Пароль Google Reader",
"form.integration.googlereader_endpoint": "Конечная точка Google Reader API:",
"form.integration.pinboard_activate": "Сохранять статьи в Pinboard",
"form.integration.pinboard_token": "Pinboard API Token",
"form.integration.pinboard_tags": "Теги Pinboard",
@@ -312,6 +337,16 @@
"form.integration.nunux_keeper_activate": "Сохранять статьи в Nunux Keeper",
"form.integration.nunux_keeper_endpoint": "Конечная точка Nunux Keeper API",
"form.integration.nunux_keeper_api_key": "Nunux Keeper API Key",
"form.integration.espial_activate": "Сохранять статьи в Espial",
"form.integration.espial_endpoint": "Конечная точка Espial API",
"form.integration.espial_api_key": "Espial API key",
"form.integration.espial_tags": "Теги Espial",
"form.integration.telegram_bot_activate": "Публикуйте новые статьи в Telegram-чате",
"form.integration.telegram_bot_token": "Токен бота",
"form.integration.telegram_chat_id": "ID чата",
"form.integration.linkding_activate": "Сохранять статьи в Linkding",
"form.integration.linkding_endpoint": "Конечная точка Linkding API",
"form.integration.linkding_api_key": "Linkding API key",
"form.api_key.label.description": "Описание API-ключа",
"form.submit.loading": "Загрузка…",
"form.submit.saving": "Сохранение…",
+378
View File
@@ -0,0 +1,378 @@
{
"confirm.question": "Emin misiniz?",
"confirm.yes": "evet",
"confirm.no": "hayır",
"confirm.loading": "Devam ediyor...",
"action.subscribe": "Abone Ol",
"action.save": "Kaydet",
"action.or": "veya",
"action.cancel": "iptal",
"action.remove": "Kaldır",
"action.remove_feed": "Bu beslemeyi kaldır",
"action.update": "Güncelle",
"action.edit": "Düzenle",
"action.download": "İndir",
"action.import": "İçeri Aktar",
"action.login": "Giriş",
"action.home_screen": "Ana ekrana ekle",
"tooltip.keyboard_shortcuts": "Klavye Kısayolu: %s",
"tooltip.logged_user": "%s olarak giriş yapıldı",
"menu.unread": "Okunmadı",
"menu.starred": "Yıldız",
"menu.history": "Geçmiş",
"menu.feeds": "Beslemeler",
"menu.categories": "Kategoriler",
"menu.settings": "Ayarlar",
"menu.logout": "Çıkış",
"menu.preferences": "Tercihler",
"menu.integrations": "Bütünleşmeler",
"menu.sessions": "Oturumlar",
"menu.users": "Kullanıcılar",
"menu.about": "Hakkında",
"menu.export": "Dışarı Aktar",
"menu.import": "İçeri Aktar",
"menu.create_category": "Kategori oluştur",
"menu.mark_page_as_read": "Bu sayfayı okundu olarak işaretle",
"menu.mark_all_as_read": "Tümünü okundu olarak işaretle",
"menu.show_all_entries": "Tüm iletileri göster",
"menu.show_only_unread_entries": "Sadece okunmamış iletileri göster",
"menu.refresh_feed": "Yenile",
"menu.refresh_all_feeds": "Tüm beslemeleri arka planda yenile",
"menu.edit_feed": "Düzenle",
"menu.edit_category": "Düzenle",
"menu.add_feed": "Abonelik ekle",
"menu.add_user": "Kullanıcı ekle",
"menu.flush_history": "Geçmişi temizle",
"menu.feed_entries": "İletiler",
"menu.api_keys": "API Anahtarları",
"menu.create_api_key": "Yeni bir API anahtarı oluştur",
"menu.shared_entries": "Paylaşılan iletiler",
"search.label": "Ara",
"search.placeholder": "Ara...",
"pagination.next": "Sonraki",
"pagination.previous": "Önceki",
"entry.status.unread": "Okunmadı",
"entry.status.read": "Okundu",
"entry.status.toast.unread": "Okunmadı olarak işaretle",
"entry.status.toast.read": "Okundu olarak işaretle",
"entry.status.title": "İleti durumunu değiştir",
"entry.bookmark.toggle.on": "Yıldız ekle",
"entry.bookmark.toggle.off": "Yıldızı kaldır",
"entry.bookmark.toast.on": "Yıldızlı",
"entry.bookmark.toast.off": "Yıldızsız",
"entry.state.saving": "Kaydediliyor...",
"entry.state.loading": "Yükleniyor...",
"entry.save.label": "Kaydet",
"entry.save.title": "Bu makaleyi kaydet",
"entry.save.completed": "Bitti!",
"entry.save.toast.completed": "Makale kaydedildi",
"entry.scraper.label": "İndir",
"entry.scraper.title": "Orijinal içeriği çek",
"entry.scraper.completed": "Bitti!",
"entry.external_link.label": "Dış bağlantı",
"entry.comments.label": "Yorumlar",
"entry.comments.title": "Yorumları Göster",
"entry.share.label": "Paylaş",
"entry.share.title": "Bu makaleyi paylaş",
"entry.unshare.label": "Paylaşma",
"entry.shared_entry.title": "Herkese açık bağlantıyı aç",
"entry.shared_entry.label": "Paylaş",
"entry.estimated_reading_time": [
"%d dakikalık okuma",
"%d dakikalık okuma"
],
"page.shared_entries.title": "Paylaşılan iletiler",
"page.unread.title": "Okunmadı",
"page.starred.title": "Yıldızlı",
"page.categories.title": "Kategoriler",
"page.categories.no_feed": "Besleme yok.",
"page.categories.entries": "Makaleler",
"page.categories.feeds": "Abonelikler",
"page.categories.feed_count": [
"%d besleme var.",
"%d besleme var."
],
"page.categories.unread_counter": "Okunmamış iletilerin sayısı",
"page.new_category.title": "Yeni Kategori",
"page.new_user.title": "Yeni Kullanıcı",
"page.edit_category.title": "Kategoriyi Düzenle: %s",
"page.edit_user.title": "Kullanıcıyı Düzenle: %s",
"page.feeds.title": "Beslemeler",
"page.feeds.last_check": "Son kontrol:",
"page.feeds.unread_counter": "Okunmamış iletilerin sayısı",
"page.feeds.read_counter": "Okunmuş iletilerin sayısı",
"page.feeds.error_count": [
"%d hata",
"%d hata"
],
"page.history.title": "Geçmiş",
"page.import.title": "İçeri Aktar",
"page.search.title": "Arama Sonuçları",
"page.about.title": "Hakkında",
"page.about.credits": "Katkıda Bulunanlar",
"page.about.version": "Sürüm:",
"page.about.build_date": "Oluşturulma Tarihi:",
"page.about.author": "Yazar:",
"page.about.license": "Lisans:",
"page.about.global_config_options": "Global yapılandırma seçenekleri",
"page.about.postgres_version": "Postgres sürümü:",
"page.about.go_version": "Go sürümü:",
"page.add_feed.title": "Yeni Abonelik",
"page.add_feed.no_category": "Kategori yok. En az bir kategoriye sahip olmalısınız.",
"page.add_feed.label.url": "URL",
"page.add_feed.submit": "Bir abonelik bul",
"page.add_feed.legend.advanced_options": "Gelişmiş Seçenekler",
"page.add_feed.choose_feed": "Bir Abonelik Seçin",
"page.edit_feed.title": "Beslemeyi düzenle: %s",
"page.edit_feed.last_check": "Son kontrol:",
"page.edit_feed.last_modified_header": "LastModified başlığı:",
"page.edit_feed.etag_header": "ETag başlığı:",
"page.edit_feed.no_header": "Hiçbiri",
"page.edit_feed.last_parsing_error": "Son Ayrıştırma Hatası",
"page.entry.attachments": "Ekler",
"page.keyboard_shortcuts.title": "Klavye Kısayolları",
"page.keyboard_shortcuts.subtitle.sections": "Bölüm Gezinmesi",
"page.keyboard_shortcuts.subtitle.items": "Öğe Gezinmesi",
"page.keyboard_shortcuts.subtitle.pages": "Sayfa Gezinmesi",
"page.keyboard_shortcuts.subtitle.actions": "Hareketler",
"page.keyboard_shortcuts.go_to_unread": "Okunmamışa git",
"page.keyboard_shortcuts.go_to_starred": "Yer imlerine git",
"page.keyboard_shortcuts.go_to_history": "Geçmişe git",
"page.keyboard_shortcuts.go_to_feeds": "Beslemelere git",
"page.keyboard_shortcuts.go_to_categories": "Kategorilere git",
"page.keyboard_shortcuts.go_to_settings": "Ayarlara git",
"page.keyboard_shortcuts.show_keyboard_shortcuts": "Klavye kısayollarını göster",
"page.keyboard_shortcuts.go_to_previous_item": "Önceki öğeye git",
"page.keyboard_shortcuts.go_to_next_item": "Sonraki öğeye git",
"page.keyboard_shortcuts.go_to_feed": "Beslemeye git",
"page.keyboard_shortcuts.go_to_previous_page": "Önceki sayfaya git",
"page.keyboard_shortcuts.go_to_next_page": "Sonraki sayfaya git",
"page.keyboard_shortcuts.open_item": "Seçili öğeyi aç",
"page.keyboard_shortcuts.open_original": "Orijinal bağlantıyı aç",
"page.keyboard_shortcuts.open_original_same_window": "Orijinal bağlantıyı mevcut sekmede aç",
"page.keyboard_shortcuts.open_comments": "Yorumlar bağlantısını aç",
"page.keyboard_shortcuts.open_comments_same_window": "Yorumlar bağlantısını mevcut sekmede aç",
"page.keyboard_shortcuts.toggle_read_status_next": "Okundu/okunmadı arasında geçiş yap, sonrakine odaklan",
"page.keyboard_shortcuts.toggle_read_status_prev": "Okundu/okunmadı arasında geçiş yap, öncekine odaklan",
"page.keyboard_shortcuts.refresh_all_feeds": "Tüm beslemeleri arka planda yenile",
"page.keyboard_shortcuts.mark_page_as_read": "Mevcut sayfayı okundu olarak işaretle",
"page.keyboard_shortcuts.download_content": "Orijinal içeriği indir",
"page.keyboard_shortcuts.toggle_bookmark_status": "Yer işaretini değiştir",
"page.keyboard_shortcuts.save_article": "Makaleyi kaydet",
"page.keyboard_shortcuts.scroll_item_to_top": "Öğeyi en üste kaydır",
"page.keyboard_shortcuts.remove_feed": "Bu beslemeyi kaldır",
"page.keyboard_shortcuts.go_to_search": "Arama formuna odakla",
"page.keyboard_shortcuts.close_modal": "İletişim kutusunu kapat",
"page.users.title": "Kullanıcılar",
"page.users.username": "Kullanıcı adı",
"page.users.never_logged": "Asla",
"page.users.admin.yes": "Evet",
"page.users.admin.no": "Hayır",
"page.users.actions": "Hareketler",
"page.users.last_login": "Son Giriş",
"page.users.is_admin": "Yönetici",
"page.settings.title": "Ayarlar",
"page.settings.link_google_account": "Google hesabımı bağla",
"page.settings.unlink_google_account": "Google hesabımın bağlantısını kaldır",
"page.settings.link_oidc_account": "OpenID Connect hesabımı bağla",
"page.settings.unlink_oidc_account": "OpenID Connect hesabımın bağlantısını kaldır",
"page.login.title": "Oturum aç",
"page.login.google_signin": "Google ile oturum aç",
"page.login.oidc_signin": "OpenID Connect ile oturum aç",
"page.integrations.title": "Bütünleşmeler",
"page.integration.miniflux_api": "Miniflux API",
"page.integration.miniflux_api_endpoint": "API Uç Noktası",
"page.integration.miniflux_api_username": "Kullanıcı adı",
"page.integration.miniflux_api_password": "Parola",
"page.integration.miniflux_api_password_value": "Hesap parolan",
"page.integration.bookmarklet": "Bookmarklet",
"page.integration.bookmarklet.name": "Miniflux'a Ekle",
"page.integration.bookmarklet.instructions": "Bu bağlantıyı yer imlerinize sürükleyip bırakın",
"page.integration.bookmarklet.help": "Bu özel bağlantı, web tarayıcınızdaki yer imini kullanarak bir web sitesine doğrudan abone olmanızı sağlar.",
"page.sessions.title": "Oturumlar",
"page.sessions.table.date": "Tarih",
"page.sessions.table.ip": "IP Adresi",
"page.sessions.table.user_agent": "User Agent",
"page.sessions.table.actions": "Hareketler",
"page.sessions.table.current_session": "Mevcut Oturum",
"page.api_keys.title": "API Anahtarları",
"page.api_keys.table.description": "Açıklama",
"page.api_keys.table.token": "Token",
"page.api_keys.table.last_used_at": "Son Kullanılma",
"page.api_keys.table.created_at": "Oluşturulma Tarihi",
"page.api_keys.table.actions": "Hareketler",
"page.api_keys.never_used": "Hiç Kullanılmadı",
"page.new_api_key.title": "Yeni API Anahtarı",
"page.offline.title": "Çevrimdışı Modu",
"page.offline.message": "Çevrimdışısınız",
"page.offline.refresh_page": "Sayfayı yenilemeyi dene",
"alert.no_shared_entry": "Paylaşılan ileti yok.",
"alert.no_bookmark": "Şu anda hiç yer imi yok.",
"alert.no_category": "Hiç kategori yok.",
"alert.no_category_entry": "Bu kategoride hiç makale yok.",
"alert.no_feed_entry": "Bu besleme için makale yok.",
"alert.no_feed": "Hiç aboneliğiniz yok.",
"alert.no_feed_in_category": "Bu kategori için aboneliğiniz yok.",
"alert.no_history": "Şu anda hiç geçmiş yok.",
"alert.feed_error": "Bu beslemeyle ilgili bir problem var",
"alert.no_search_result": "Bu arama için sonuç yok",
"alert.no_unread_entry": "Okunmamış makale yok",
"alert.no_user": "Tek kullanıcı sizsiniz",
"alert.account_unlinked": "Harici hesabınızın bağlantısı kaldırıldı!",
"alert.account_linked": "Harici hesabınız bağlandı.",
"alert.pocket_linked": "Pocket hesabınız bağlandı.",
"alert.prefs_saved": "Tercihler kaydedildi!",
"error.unlink_account_without_password": "Bir şifre belirlemelisiniz, aksi takdirde tekrar oturum açamazsınız.",
"error.duplicate_linked_account": "Bu sağlayıcıyla ilişkilendirilmiş biri zaten var!",
"error.duplicate_fever_username": "Aynı Fever kullanıcı adına sahip başka biri zaten var!",
"error.duplicate_googlereader_username": "Aynı Google Reader kullanıcı adına sahip başka biri zaten var!",
"error.pocket_request_token": "Pocket'tan istek tokeni alınamıyor!",
"error.pocket_access_token": "Pocket'tan erişim tokeni alınamıyor!",
"error.category_already_exists": "Bu kategori zaten mevcut.",
"error.unable_to_create_category": "Bu kategori oluşturulamıyor.",
"error.unable_to_update_category": "Bu kategori güncellenemiyor.",
"error.user_already_exists": "Bu kullanıcı zaten mevcut.",
"error.unable_to_create_user": "Bu kullanıcı oluşturulamıyor.",
"error.unable_to_update_user": "Bu kullanıcı güncellenemiyor.",
"error.unable_to_update_feed": "Bu besleme güncellenemiyor.",
"error.subscription_not_found": "Herhangi bir abonelik bulunamadı.",
"error.invalid_theme": "Geçersiz tema.",
"error.invalid_language": "Geçersiz dil.",
"error.invalid_timezone": "Geçersiz saat dilimi",
"error.invalid_entry_direction": "Geçersiz giriş yönü.",
"error.invalid_display_mode": "Geçersiz web uygulaması görüntüleme modu.",
"error.empty_file": "Bu dosya boş.",
"error.bad_credentials": "Geçersiz kullanıcı veya parola.",
"error.fields_mandatory": "Tüm alanlar zorunlu.",
"error.title_required": "Başlık zorunlu.",
"error.different_passwords": "Parolalar eşleşmiyor.",
"error.password_min_length": "Parola en az 6 karakter içermeli.",
"error.settings_mandatory_fields": "Kullanıcı ad, tema, dil ve saat dilimi zorunlu.",
"error.entries_per_page_invalid": "Sayfa başına ileti sayısı geçersiz.",
"error.feed_mandatory_fields": "URL ve kategori zorunlu.",
"error.feed_already_exists": "Bu besleme zaten mevcut.",
"error.invalid_feed_url": "Geçersiz besleme URL'si.",
"error.invalid_site_url": "Geçersiz site URL'si.",
"error.feed_url_not_empty": "Besleme URL'si boş olamaz.",
"error.site_url_not_empty": "Site URL'si boş olamaz.",
"error.feed_title_not_empty": "Besleme başlığı boş olamaz.",
"error.feed_category_not_found": "Bu kategori mevcut değil ya da bu kullanıcıya ait değil.",
"error.feed_invalid_blocklist_rule": "Engelleme listesi kuralı geçersiz.",
"error.feed_invalid_keeplist_rule": "Saklama listesi kuralı geçersiz.",
"error.user_mandatory_fields": "Kullanıcı adı zorunlu.",
"error.api_key_already_exists": "Bu API anahtarı zaten mevcut.",
"error.unable_to_create_api_key": "Bu API anahtarı oluşturulamıyor.",
"form.feed.label.title": "Başlık",
"form.feed.label.site_url": "Site URL'si",
"form.feed.label.feed_url": "Besleme URL'si",
"form.feed.label.category": "Kategori",
"form.feed.label.crawler": "Orijinal içeriği çek",
"form.feed.label.feed_username": "Besleme Kullanıcı Adı",
"form.feed.label.feed_password": "Besleme Parolası",
"form.feed.label.user_agent": "Varsayılan User Agent'i Geçersiz Kıl",
"form.feed.label.cookie": "Çerezleri Ayarla",
"form.feed.label.scraper_rules": "Scrapper Kuralları",
"form.feed.label.rewrite_rules": "Yeniden Yazma Kuralları",
"form.feed.label.blocklist_rules": "Engelleme Kuralları",
"form.feed.label.keeplist_rules": "Saklama Kuralları",
"form.feed.label.ignore_http_cache": "HTTP önbelleğini yoksay",
"form.feed.label.allow_self_signed_certificates": "Kendinden imzalı veya geçersiz sertifikalara izin ver",
"form.feed.label.fetch_via_proxy": "Proxy ile çek",
"form.feed.label.disabled": "Bu beslemeyi yenileme",
"form.feed.label.hide_globally": "Genel okunmamış listesindeki girişleri gizle",
"form.category.label.title": "Başlık",
"form.category.hide_globally": "Genel okunmamış listesindeki girişleri gizle",
"form.user.label.username": "Kullanıcı Adı",
"form.user.label.password": "Parola",
"form.user.label.confirmation": "Parola Doğrulama",
"form.user.label.admin": "Yönetici",
"form.prefs.label.language": "Dil",
"form.prefs.label.timezone": "Saat Dilimi",
"form.prefs.label.theme": "Tema",
"form.prefs.label.entry_sorting": "İleti Sıralaması",
"form.prefs.label.entries_per_page": "Sayfa başına ileti",
"form.prefs.label.display_mode": "Web uygulaması görüntüleme modu (yeniden kurulum gerektirir)",
"form.prefs.select.older_first": "Önce eski iletiler",
"form.prefs.select.recent_first": "Önce yeni iletiler",
"form.prefs.select.fullscreen": "Tam Ekran",
"form.prefs.select.standalone": "Bağımsız",
"form.prefs.select.minimal_ui": "Minimal",
"form.prefs.select.browser": "Tarayıcı",
"form.prefs.select.publish_time": "Giriş yayınlanma zamanı",
"form.prefs.select.created_time": "Girişin oluşturulma zamanı",
"form.prefs.label.keyboard_shortcuts": "Klavye kısayollarını etkinleştir",
"form.prefs.label.entry_swipe": "Mobil cihazlarda iletiler için kaydırma hareketlerini etkinleştir",
"form.prefs.label.show_reading_time": "Makaleler için tahmini okuma süresini göster",
"form.prefs.label.custom_css": "Özel CSS",
"form.prefs.label.entry_order": "Giriş Sıralama Sütunu",
"form.import.label.file": "OPML dosyası",
"form.import.label.url": "URL",
"form.integration.fever_activate": "Fever API'yi Etkinleştir",
"form.integration.fever_username": "Fever Kullanıcı Adı",
"form.integration.fever_password": "Fever Parolası",
"form.integration.fever_endpoint": "Fever API uç noktası:",
"form.integration.googlereader_activate": "Google Reader API'yi Etkinleştir",
"form.integration.googlereader_username": "Google Reader Kullanıcı Adı",
"form.integration.googlereader_password": "Google Reader Parolası",
"form.integration.googlereader_endpoint": "Google Reader API uç noktası:",
"form.integration.pinboard_activate": "Makaleleri Pinboard'a kaydet",
"form.integration.pinboard_token": "Pinboard API Token",
"form.integration.pinboard_tags": "Pinboard Etiketleri",
"form.integration.pinboard_bookmark": "Yer imini okunmadı olarak işaretle",
"form.integration.instapaper_activate": "Makaleleri Instapaper'a kaydet",
"form.integration.instapaper_username": "Instapaper Kullanıcı Adı",
"form.integration.instapaper_password": "Instapaper Parolası",
"form.integration.pocket_activate": "Makaleleri Pocket'a kaydet",
"form.integration.pocket_consumer_key": "Pocket Consumer Anahtarı",
"form.integration.pocket_access_token": "Pocket Access Token",
"form.integration.pocket_connect_link": "Pocket hesabını bağla",
"form.integration.wallabag_activate": "Makaleleri Wallabag'e kaydet",
"form.integration.wallabag_endpoint": "Wallabag API Uç Noktası",
"form.integration.wallabag_client_id": "Wallabag Client ID",
"form.integration.wallabag_client_secret": "Wallabag Client Secret",
"form.integration.wallabag_username": "Wallabag Kullanıcı Adı",
"form.integration.wallabag_password": "Wallabag Parolası",
"form.integration.nunux_keeper_activate": "Makaleleri Nunux Keeper'a kaydet",
"form.integration.nunux_keeper_endpoint": "Nunux Keeper API Uç Noktası",
"form.integration.nunux_keeper_api_key": "Nunux Keeper API anahtarı",
"form.integration.espial_activate": "Makaleleri Espial'e kaydet",
"form.integration.espial_endpoint": "Espial API Uç Noktası",
"form.integration.espial_api_key": "Espial API Anahtarı",
"form.integration.espial_tags": "Espial Etiketleri",
"form.integration.telegram_bot_activate": "Yeni makaleleri Telegram sohbetine gönderin",
"form.integration.telegram_bot_token": "Bot jetonu",
"form.integration.telegram_chat_id": "Sohbet kimliği",
"form.integration.linkding_activate": "Makaleleri Linkding'e kaydet",
"form.integration.linkding_endpoint": "Linkding API Uç Noktası",
"form.integration.linkding_api_key": "Linkding API Anahtarı",
"form.api_key.label.description": "API Anahtar Etiketi",
"form.submit.loading": "Yükleniyor...",
"form.submit.saving": "Kaydediliyor...",
"time_elapsed.not_yet": "henüz değil",
"time_elapsed.yesterday": "dün",
"time_elapsed.now": "şimdi",
"time_elapsed.minutes": [
"%d dakika önce",
"%d dakika önce"
],
"time_elapsed.hours": [
"%d saat önce",
"%d saat önce"
],
"time_elapsed.days": [
"%d gün önce",
"%d gün önce"
],
"time_elapsed.weeks": [
"%d hafta önce",
"%d hafta önce"
],
"time_elapsed.months": [
"%d ay önce",
"%d ay önce"
],
"time_elapsed.years": [
"%d yıl önce",
"%d yıl önce"
]
}
+138 -103
View File
@@ -13,12 +13,12 @@
"action.edit": "编辑",
"action.download": "下载",
"action.import": "导入",
"action.login": "登",
"action.login": "登",
"action.home_screen": "添加到主屏幕",
"tooltip.keyboard_shortcuts": "快捷键: %s",
"tooltip.logged_user": "当前登录 %s",
"menu.unread": "未读",
"menu.starred": "星标",
"menu.starred": "收藏",
"menu.history": "历史",
"menu.feeds": "源",
"menu.categories": "分类",
@@ -34,41 +34,41 @@
"menu.create_category": "新建分类",
"menu.mark_page_as_read": "标记为已读",
"menu.mark_all_as_read": "全部标为已读",
"menu.show_all_entries": "显示所有条目",
"menu.show_all_entries": "显示所有文章",
"menu.show_only_unread_entries": "仅显示未读文章",
"menu.refresh_feed": "更新",
"menu.refresh_all_feeds": "在后台更新全部源",
"menu.edit_feed": "编辑",
"menu.edit_category": "编辑",
"menu.add_feed": "新增订阅",
"menu.add_feed": "新增",
"menu.add_user": "新建用户",
"menu.flush_history": "清理历史",
"menu.feed_entries": "文章",
"menu.api_keys": "API密钥",
"menu.create_api_key": "创建一个新的API密钥",
"menu.shared_entries": "共享条目",
"menu.api_keys": "API 密钥",
"menu.create_api_key": "创建一个新的 API 密钥",
"menu.shared_entries": "分享文章",
"search.label": "搜索",
"search.placeholder": "搜索…",
"pagination.next": "下一页",
"pagination.previous": "上一页",
"entry.status.unread": "未读",
"entry.status.unread": "标为未读",
"entry.status.read": "标为已读",
"entry.status.toast.unread": "已标为未读",
"entry.status.toast.read": "已标为已读",
"entry.status.title": "更改状态",
"entry.bookmark.toggle.on": "标记星标",
"entry.bookmark.toggle.off": "去掉星标",
"entry.bookmark.toast.on": "已标记星标",
"entry.bookmark.toast.off": "已去掉星标",
"entry.bookmark.toggle.on": "添加收藏",
"entry.bookmark.toggle.off": "取消收藏",
"entry.bookmark.toast.on": "已添加收藏",
"entry.bookmark.toast.off": "已取消收藏",
"entry.state.saving": "保存中…",
"entry.state.loading": "载入中…",
"entry.save.label": "保存",
"entry.save.title": "保存这篇文章",
"entry.save.completed": "完成",
"entry.save.toast.completed": "已保存文章",
"entry.scraper.label": "下载",
"entry.scraper.title": "抓取内容",
"entry.scraper.completed": "完成",
"entry.scraper.label": "抓取全文",
"entry.scraper.title": "抓取全文内容",
"entry.scraper.completed": "抓取完成",
"entry.external_link.label": "外部链接",
"entry.comments.label": "评论",
"entry.comments.title": "查看评论",
@@ -76,29 +76,30 @@
"entry.share.title": "分享这篇文章",
"entry.unshare.label": "取消分享",
"entry.shared_entry.title": "打开公共链接",
"entry.shared_entry.label": "分享分享",
"entry.shared_entry.label": "分享",
"entry.estimated_reading_time": [
"%d分钟阅读",
"%d分钟阅读"
"需要 %d 分钟阅读",
"需要 %d 分钟阅读"
],
"page.shared_entries.title": "共享条目",
"page.shared_entries.title": "分享文章",
"page.unread.title": "未读",
"page.starred.title": "星标",
"page.starred.title": "收藏",
"page.categories.title": "分类",
"page.categories.no_feed": "没有源",
"page.categories.entries": "文章",
"page.categories.feeds": "查看订阅",
"page.categories.entries": "查看内容",
"page.categories.feeds": "查看",
"page.categories.feed_count": [
"有 %d 个源"
],
"page.categories.unread_counter": "未读文章数",
"page.new_category.title": "新分类",
"page.new_user.title": "新用户",
"page.edit_category.title": "编辑分类 : %s",
"page.edit_user.title": "编辑用户 : %s",
"page.feeds.title": "源",
"page.feeds.last_check": "最后检查时间:",
"page.feeds.unread_counter": "未读条目数",
"page.feeds.read_counter": "读取条目数",
"page.feeds.unread_counter": "未读文章数",
"page.feeds.read_counter": "已读文章数",
"page.feeds.error_count": [
"%d 错误"
],
@@ -111,55 +112,58 @@
"page.about.build_date": "构建日期:",
"page.about.author": "作者:",
"page.about.license": "协议:",
"page.about.postgres_version": "Postgres 版本号:",
"page.about.go_version": "Go 版本号:",
"page.about.global_config_options": "全局配置选项",
"page.add_feed.title": "新增订阅",
"page.add_feed.no_category": "没有类别,您必须至少有一个类别",
"page.add_feed.title": "新增",
"page.add_feed.no_category": "没有类别,至少需要有一个类别",
"page.add_feed.label.url": "网址",
"page.add_feed.submit": "查找订阅",
"page.add_feed.submit": "查找",
"page.add_feed.legend.advanced_options": "高级选项",
"page.add_feed.choose_feed": "选择一个订阅",
"page.add_feed.choose_feed": "选择一个",
"page.edit_feed.title": "编辑源 : %s",
"page.edit_feed.last_check": "最后检查时间:",
"page.edit_feed.last_modified_header": "最后修改的 Header",
"page.edit_feed.etag_header": "ETag 标题:",
"page.edit_feed.no_header": "无",
"page.edit_feed.no_header": "无 Header",
"page.edit_feed.last_parsing_error": "最后一次解析错误",
"page.entry.attachments": "附件",
"page.keyboard_shortcuts.title": "快捷键",
"page.keyboard_shortcuts.subtitle.sections": "分区导航",
"page.keyboard_shortcuts.subtitle.items": "条目导航",
"page.keyboard_shortcuts.subtitle.items": "文章导航",
"page.keyboard_shortcuts.subtitle.pages": "页面导航",
"page.keyboard_shortcuts.subtitle.actions": "操作",
"page.keyboard_shortcuts.go_to_unread": "去往未读",
"page.keyboard_shortcuts.go_to_starred": "去往书签",
"page.keyboard_shortcuts.go_to_history": "去往历史",
"page.keyboard_shortcuts.go_to_feeds": "去往源",
"page.keyboard_shortcuts.go_to_categories": "去往分类",
"page.keyboard_shortcuts.go_to_settings": "去往设置",
"page.keyboard_shortcuts.show_keyboard_shortcuts": "显示快捷键",
"page.keyboard_shortcuts.go_to_previous_item": "上一条目",
"page.keyboard_shortcuts.go_to_next_item": "下一条目",
"page.keyboard_shortcuts.go_to_feed": "转到订阅",
"page.keyboard_shortcuts.go_to_unread": "打开未读页面",
"page.keyboard_shortcuts.go_to_starred": "打开收藏页面",
"page.keyboard_shortcuts.go_to_history": "打开历史页面",
"page.keyboard_shortcuts.go_to_feeds": "打开源页面",
"page.keyboard_shortcuts.go_to_categories": "打开分类页面",
"page.keyboard_shortcuts.go_to_settings": "打开设置页面",
"page.keyboard_shortcuts.show_keyboard_shortcuts": "显示快捷键帮助",
"page.keyboard_shortcuts.go_to_previous_item": "上一文章",
"page.keyboard_shortcuts.go_to_next_item": "下一文章",
"page.keyboard_shortcuts.go_to_feed": "转到源页面",
"page.keyboard_shortcuts.go_to_previous_page": "上一页",
"page.keyboard_shortcuts.go_to_next_page": "下一页",
"page.keyboard_shortcuts.open_item": "打开选定的条目",
"page.keyboard_shortcuts.open_item": "打开选定的文章",
"page.keyboard_shortcuts.open_original": "打开原始链接",
"page.keyboard_shortcuts.open_original_same_window": "在当前标签页中打开原始链接",
"page.keyboard_shortcuts.open_comments": "打开评论链接",
"page.keyboard_shortcuts.open_comments_same_window": "在当前标签页中打开评论链接",
"page.keyboard_shortcuts.toggle_read_status": "切换已读/未读状态",
"page.keyboard_shortcuts.toggle_read_status_next": "切换已读/未读状态, 关注下一个",
"page.keyboard_shortcuts.toggle_read_status_prev": "切换已读/未读状态, 关注前一个",
"page.keyboard_shortcuts.refresh_all_feeds": "在后台更新全部源",
"page.keyboard_shortcuts.mark_page_as_read": "标记当前页已读",
"page.keyboard_shortcuts.download_content": "下载原始内容",
"page.keyboard_shortcuts.download_content": "抓取全文内容",
"page.keyboard_shortcuts.toggle_bookmark_status": "切换收藏状态",
"page.keyboard_shortcuts.save_article": "保存文章",
"page.keyboard_shortcuts.scroll_item_to_top": "滚动到顶部",
"page.keyboard_shortcuts.remove_feed": "删除此Feed",
"page.keyboard_shortcuts.go_to_search": "将点放在搜索表单上",
"page.keyboard_shortcuts.close_modal": "关闭模态对话窗口",
"page.keyboard_shortcuts.remove_feed": "删除此",
"page.keyboard_shortcuts.go_to_search": "将点放在搜索表单上",
"page.keyboard_shortcuts.close_modal": "关闭对话窗口",
"page.users.title": "用户",
"page.users.username": "用户名",
"page.users.never_logged": "从未登",
"page.users.never_logged": "从未登",
"page.users.admin.yes": "是",
"page.users.admin.no": "否",
"page.users.actions": "操作",
@@ -170,9 +174,9 @@
"page.settings.unlink_google_account": "解除 Google 账号关联",
"page.settings.link_oidc_account": "关联我的 OpenID Connect 账户",
"page.settings.unlink_oidc_account": "解除 OpenID Connect 账号关联",
"page.login.title": "登",
"page.login.google_signin": "使用 Google 登",
"page.login.oidc_signin": "使用 OpenID Connect 登",
"page.login.title": "登",
"page.login.google_signin": "使用 Google 登",
"page.login.oidc_signin": "使用 OpenID Connect 登",
"page.integrations.title": "集成",
"page.integration.miniflux_api": "Miniflux API",
"page.integration.miniflux_api_endpoint": "API Endpoint",
@@ -180,42 +184,46 @@
"page.integration.miniflux_api_password": "密码",
"page.integration.miniflux_api_password_value": "您账户的密码",
"page.integration.bookmarklet": "书签小应用",
"page.integration.bookmarklet.name": "新增到Miniflux",
"page.integration.bookmarklet.instructions": "拖动这个链接到书签",
"page.integration.bookmarklet.help": "你可以打开这个特殊的书签来直接订阅网站",
"page.integration.bookmarklet.name": "收藏 Miniflux",
"page.integration.bookmarklet.instructions": "拖动这个链接到浏览器书签",
"page.integration.bookmarklet.help": "你可以打开这个特殊的书签来直接收藏网站",
"page.sessions.title": "会话",
"page.sessions.table.date": "日期",
"page.sessions.table.ip": "IP 地址",
"page.sessions.table.user_agent": "User-Agent",
"page.sessions.table.user_agent": "用户代理",
"page.sessions.table.actions": "操作",
"page.sessions.table.current_session": "当前会话",
"page.api_keys.title": "API密钥",
"page.api_keys.title": "API 密钥",
"page.api_keys.table.description": "描述",
"page.api_keys.table.token": "密钥",
"page.api_keys.table.last_used_at": "最后使用",
"page.api_keys.table.created_at": "创日期",
"page.api_keys.table.created_at": "创日期",
"page.api_keys.table.actions": "操作",
"page.api_keys.never_used": "没用过",
"page.new_api_key.title": "新的API密钥",
"alert.no_shared_entry": "没有共享条目。",
"alert.no_bookmark": "目前没有书签",
"page.new_api_key.title": "新的 API 密钥",
"page.offline.title": "离线模式",
"page.offline.message": "您已离线",
"page.offline.refresh_page": "尝试刷新页面",
"alert.no_shared_entry": "没有分享文章。",
"alert.no_bookmark": "目前没有收藏",
"alert.no_category": "目前没有分类",
"alert.no_category_entry": "该分类下没有文章",
"alert.no_feed_entry": "该源中没有文章",
"alert.no_feed": "目前没有订阅",
"alert.no_feed": "目前没有",
"alert.no_history": "目前没有历史",
"alert.feed_error": "该源存在问题",
"alert.no_search_result": "该搜索没有结果",
"alert.no_feed_in_category": "没有该类别的订阅。",
"alert.no_feed_in_category": "没有该类别的。",
"alert.no_unread_entry": "目前没有未读文章",
"alert.no_user": "您是目前仅有的用户",
"alert.account_unlinked": "您的外部帐户现已解除关联!",
"alert.account_linked": "您的外部账号已关联!",
"alert.pocket_linked": "您的Pocket帐户现已关联",
"alert.pocket_linked": "您的 Pocket 帐户现已关联",
"alert.prefs_saved": "设置已存储!",
"error.unlink_account_without_password": "您必须定义密码,否则您将无法再次登录。",
"error.unlink_account_without_password": "您必须设置密码,否则您将无法再次登录。",
"error.duplicate_linked_account": "该 Provider 已被关联!",
"error.duplicate_fever_username": "Fever 用户名已被占用!",
"error.duplicate_googlereader_username": "Google Reader 用户名已被占用!",
"error.pocket_request_token": "无法从 Pocket 获取请求令牌!",
"error.pocket_access_token": "无法从 Pocket 获取访问令牌!",
"error.category_already_exists": "分类已存在",
@@ -225,93 +233,120 @@
"error.unable_to_create_user": "无法创建此用户",
"error.unable_to_update_user": "无法更新此用户",
"error.unable_to_update_feed": "无法更新此源",
"error.subscription_not_found": "找不到任何订阅",
"error.subscription_not_found": "找不到任何",
"error.empty_file": "该文件为空",
"error.bad_credentials": "用户名或密码无效",
"error.fields_mandatory": "必须填写全部信息",
"error.title_required": "必须填写标题",
"error.different_passwords": "两次输入的密码不同",
"error.password_min_length": "请至少使用6个字符",
"error.password_min_length": "请至少输入 6 个字符",
"error.settings_mandatory_fields": "必须填写用户名、主题、语言以及时区",
"error.entries_per_page_invalid": "每页的条目数无效。",
"error.feed_mandatory_fields": "必须填写 URL 和分类",
"error.feed_already_exists": "此供稿已存在。",
"error.invalid_feed_url": "供稿网址无效。",
"error.invalid_site_url": "无效的网站网址。",
"error.feed_url_not_empty": "供稿网址不能为空。",
"error.site_url_not_empty": "网站网址不能为空。",
"error.feed_title_not_empty": "供稿标题不能为空。",
"error.entries_per_page_invalid": "每页的文章数无效。",
"error.feed_mandatory_fields": "必须填写网址和分类",
"error.feed_already_exists": "此已存在。",
"error.invalid_feed_url": "订阅源的网址无效。",
"error.invalid_site_url": "网站网址无效。",
"error.feed_url_not_empty": "订阅源的网址不能为空。",
"error.site_url_not_empty": "网站网址不能为空。",
"error.feed_title_not_empty": "订阅源的标题不能为空。",
"error.feed_category_not_found": "此类别不存在或不属于该用户。",
"error.feed_invalid_blocklist_rule": "阻止列表规则无效。",
"error.feed_invalid_keeplist_rule": "保留列表规则无效。",
"error.user_mandatory_fields": "必须填写用户名",
"error.api_key_already_exists": "此API密钥已存在。",
"error.unable_to_create_api_key": "无法创建此API密钥。",
"error.api_key_already_exists": "此 API 密钥已存在。",
"error.unable_to_create_api_key": "无法创建此 API 密钥。",
"error.invalid_theme": "无效的主题。",
"error.invalid_language": "语言无效。",
"error.invalid_language": "无效的语言。",
"error.invalid_timezone": "无效的时区。",
"error.invalid_entry_direction": "无效的输入方向。",
"error.invalid_display_mode": "无效的网页应用显示模式。",
"form.feed.label.title": "标题",
"form.feed.label.site_url": "站 URL",
"form.feed.label.feed_url": "源 URL",
"form.feed.label.site_url": "源网站 URL",
"form.feed.label.feed_url": "订阅源 URL",
"form.feed.label.category": "类别",
"form.feed.label.crawler": "获取原始内容",
"form.feed.label.crawler": "抓取全文内容",
"form.feed.label.feed_username": "源用户名",
"form.feed.label.feed_password": "源密码",
"form.feed.label.user_agent": "覆盖默认 User-Agent",
"form.feed.label.scraper_rules": "Scraper 规则",
"form.feed.label.user_agent": "覆盖默认的用户代理",
"form.feed.label.cookie": "设置 Cookies",
"form.feed.label.scraper_rules": "抓取规则",
"form.feed.label.rewrite_rules": "重写规则",
"form.feed.label.blocklist_rules": "封锁规则",
"form.feed.label.keeplist_rules": "许可规则",
"form.feed.label.ignore_http_cache": "忽略HTTP缓存",
"form.feed.label.blocklist_rules": "阻止规则",
"form.feed.label.keeplist_rules": "保留规则",
"form.feed.label.ignore_http_cache": "忽略 HTTP 缓存",
"form.feed.label.allow_self_signed_certificates": "允许自签名证书或无效证书",
"form.feed.label.fetch_via_proxy": "通过代理获取",
"form.feed.label.disabled": "请勿刷新此Feed",
"form.feed.label.disabled": "请勿刷新此",
"form.feed.label.hide_globally": "隐藏全局未读列表中的文章",
"form.category.label.title": "标题",
"form.category.hide_globally": "隐藏全局未读列表中的文章",
"form.user.label.username": "用户名",
"form.user.label.password": "密码",
"form.user.label.confirmation": "确认",
"form.user.label.confirmation": "再次输入密码",
"form.user.label.admin": "管理员",
"form.prefs.label.language": "语言",
"form.prefs.label.timezone": "时区",
"form.prefs.label.theme": "主题",
"form.prefs.label.entry_sorting": "内容排序",
"form.prefs.label.entries_per_page": "每页条目",
"form.prefs.label.entry_sorting": "文章排序",
"form.prefs.label.entries_per_page": "每页文章数",
"form.prefs.label.display_mode": "渐进式网页应用显示模式(需要重新添加)",
"form.prefs.select.older_first": "旧->新",
"form.prefs.select.recent_first": "新->旧",
"form.prefs.select.fullscreen": "全屏",
"form.prefs.select.standalone": "独立",
"form.prefs.select.minimal_ui": "最小",
"form.prefs.select.browser": "浏览器",
"form.prefs.select.publish_time": "文章发布时间",
"form.prefs.select.created_time": "文章创建时间",
"form.prefs.label.keyboard_shortcuts": "启用键盘快捷键",
"form.prefs.label.entry_swipe": "在移动设备上的条目上启用滑动手势",
"form.prefs.label.entry_swipe": "在移动设备上启用滑动手势",
"form.prefs.label.show_reading_time": "显示文章的预计阅读时间",
"form.prefs.label.custom_css": "自定义CSS",
"form.prefs.label.custom_css": "自定义 CSS",
"form.prefs.label.entry_order": "文章排序依据",
"form.import.label.file": "OPML 文件",
"form.import.label.url": "URL",
"form.integration.fever_activate": "启用 Fever API",
"form.integration.fever_username": "Fever 用户名",
"form.integration.fever_password": "Fever 密码",
"form.integration.fever_endpoint": "Fever API endpoint:",
"form.integration.fever_endpoint": "Fever API 端点",
"form.integration.googlereader_activate": "启用 Google Reader API",
"form.integration.googlereader_username": "Google Reader 用户名",
"form.integration.googlereader_password": "Google Reader 密码",
"form.integration.googlereader_endpoint": "Google Reader API 端点:",
"form.integration.pinboard_activate": "保存文章到 Pinboard",
"form.integration.pinboard_token": "Pinboard API Token",
"form.integration.pinboard_tags": "Pinboard 标签",
"form.integration.pinboard_bookmark": "标记为未读",
"form.integration.instapaper_activate": "保存文章到Instapaper",
"form.integration.instapaper_activate": "保存文章到 Instapaper",
"form.integration.instapaper_username": "Instapaper 用户名",
"form.integration.instapaper_password": "Instapaper 密码",
"form.integration.pocket_activate": "将文章保存到Pocket",
"form.integration.pocket_activate": "将文章保存到 Pocket",
"form.integration.pocket_consumer_key": "Pocket 用户密钥",
"form.integration.pocket_access_token": "Pocket Access Token",
"form.integration.pocket_access_token": "Pocket 访问密钥",
"form.integration.pocket_connect_link": "连接您的 Pocket 帐户",
"form.integration.wallabag_activate": "保存文章到 Wallabag",
"form.integration.wallabag_endpoint": "Wallabag URL",
"form.integration.wallabag_client_id": "Wallabag 客户端ID",
"form.integration.wallabag_client_id": "Wallabag 客户端 ID",
"form.integration.wallabag_client_secret": "Wallabag 客户端 Secret",
"form.integration.wallabag_username": "Wallabag 用户名",
"form.integration.wallabag_password": "Wallabag 密码",
"form.integration.nunux_keeper_activate": "保存文章到 Nunux Keeper",
"form.integration.nunux_keeper_endpoint": "Nunux Keeper API Endpoint",
"form.integration.nunux_keeper_endpoint": "Nunux Keeper API 端点",
"form.integration.nunux_keeper_api_key": "Nunux Keeper API 密钥",
"form.integration.espial_activate": "保存文章到 Espial",
"form.integration.espial_endpoint": "Espial API 端点",
"form.integration.espial_api_key": "Espial API 密钥",
"form.integration.espial_tags": "Espial 标签",
"form.integration.telegram_bot_activate": "将新文章推送到 Telegram",
"form.integration.telegram_bot_token": "机器人令牌",
"form.integration.telegram_chat_id": "聊天ID",
"form.integration.linkding_activate": "保存文章到 Linkding",
"form.integration.linkding_endpoint": "Linkding API 端点",
"form.integration.linkding_api_key": "Linkding API 密钥",
"form.api_key.label.description": "API密钥标签",
"form.submit.loading": "载入中…",
"form.submit.saving": "保存中…",
"time_elapsed.not_yet": "未",
"time_elapsed.not_yet": "未",
"time_elapsed.yesterday": "昨天",
"time_elapsed.now": "刚刚",
"time_elapsed.minutes": [
@@ -337,17 +372,17 @@
"Unable to open this link: %v": "无法打开这一链接: %v",
"Unable to analyze this page: %v": "无法分析这一页面: %v",
"Unable to execute request: %v": "无法执行这一请求: %v",
"Unable to parse OPML file: %q": "无法解析OPML文件: %q",
"Unable to parse RSS feed: %q": "无法解析RSS源: %q",
"Unable to parse Atom feed: %q": "无法解析Atom源: %q",
"Unable to parse JSON feed: %q": "无法解析JSON源: %q",
"Unable to parse RDF feed: %q": "无法解析RDF源: %q",
"Unable to parse OPML file: %q": "无法解析 OPML 文件: %q",
"Unable to parse RSS feed: %q": "无法解析 RSS 源: %q",
"Unable to parse Atom feed: %q": "无法解析 Atom 源: %q",
"Unable to parse JSON feed: %q": "无法解析 JSON 源: %q",
"Unable to parse RDF feed: %q": "无法解析 RDF 源: %q",
"Unable to read data: %q": "无法读取数据: %q",
"Unable to normalize encoding: %q": "无法正则化编码: %q",
"Category not found for this user": "未找到该用户的这一分类",
"This feed is empty": "该源是空的",
"This web page is empty": "该网页是空的",
"Invalid SSL certificate (original error: %q)": "无效的SSL证书 (原始错误: %q)",
"Invalid SSL certificate (original error: %q)": "无效的 SSL 证书 (原始错误: %q)",
"This website is temporarily unreachable (original error: %q)": "该网站暂时不可达 (原始错误: %q)",
"This website is permanently unreachable (original error: %q)": "该网站永久不可达 (原始错误: %q)",
"Website unreachable, the request timed out after %d seconds": "网站不可达, 请求已在 %d 秒后超时"
+397
View File
@@ -0,0 +1,397 @@
{
"confirm.question": "您確認嗎?",
"confirm.yes": "是",
"confirm.no": "否",
"confirm.loading": "執行中…",
"action.subscribe": "訂閱",
"action.save": "儲存",
"action.or": "或",
"action.cancel": "取消",
"action.remove": "刪除",
"action.remove_feed": "刪除此Feed",
"action.update": "更新",
"action.edit": "編輯",
"action.download": "下載",
"action.import": "匯入",
"action.login": "登入",
"action.home_screen": "新增到主螢幕",
"tooltip.keyboard_shortcuts": "快捷鍵: %s",
"tooltip.logged_user": "當前登入 %s",
"menu.unread": "未讀",
"menu.starred": "收藏",
"menu.history": "歷史",
"menu.feeds": "Feeds",
"menu.categories": "分類",
"menu.settings": "設定",
"menu.logout": "登出",
"menu.preferences": "設定",
"menu.integrations": "整合",
"menu.sessions": "會話",
"menu.users": "使用者",
"menu.about": "關於",
"menu.export": "匯出",
"menu.import": "匯入",
"menu.create_category": "新建分類",
"menu.mark_page_as_read": "將此頁面標記為已讀",
"menu.mark_all_as_read": "全部標為已讀",
"menu.show_all_entries": "顯示所有文章",
"menu.show_only_unread_entries": "僅顯示未讀文章",
"menu.refresh_feed": "更新",
"menu.refresh_all_feeds": "背景更新全部Feeds",
"menu.edit_feed": "編輯",
"menu.edit_category": "編輯",
"menu.add_feed": "新增Feed",
"menu.add_user": "新建使用者",
"menu.flush_history": "清理歷史",
"menu.feed_entries": "文章",
"menu.api_keys": "API 金鑰",
"menu.create_api_key": "建立一個新的 API 金鑰",
"menu.shared_entries": "分享文章",
"search.label": "搜尋",
"search.placeholder": "搜尋…",
"pagination.next": "下一頁",
"pagination.previous": "上一頁",
"entry.status.unread": "標為未讀",
"entry.status.read": "標為已讀",
"entry.status.toast.unread": "已標為未讀",
"entry.status.toast.read": "已標為已讀",
"entry.status.title": "更改狀態",
"entry.bookmark.toggle.on": "新增收藏",
"entry.bookmark.toggle.off": "取消收藏",
"entry.bookmark.toast.on": "已新增收藏",
"entry.bookmark.toast.off": "已取消收藏",
"entry.state.saving": "儲存中…",
"entry.state.loading": "載入中…",
"entry.save.label": "儲存",
"entry.save.title": "儲存這篇文章",
"entry.save.completed": "完成",
"entry.save.toast.completed": "已儲存文章",
"entry.scraper.label": "下載原文",
"entry.scraper.title": "下載原文內容",
"entry.scraper.completed": "下載完成",
"entry.external_link.label": "外部連結",
"entry.comments.label": "評論",
"entry.comments.title": "檢視評論",
"entry.share.label": "分享",
"entry.share.title": "分享這篇文章",
"entry.unshare.label": "取消分享",
"entry.shared_entry.title": "開啟公共連結",
"entry.shared_entry.label": "分享",
"entry.estimated_reading_time": [
"需要 %d 分鐘閱讀",
"需要 %d 分鐘閱讀"
],
"page.shared_entries.title": "分享文章",
"page.unread.title": "未讀",
"page.starred.title": "收藏",
"page.categories.title": "分類",
"page.categories.no_feed": "沒有Feed",
"page.categories.entries": "檢視內容",
"page.categories.feeds": "檢視Feeds",
"page.categories.feed_count": [
"有 %d 個Feed",
"有 %d 個Feeds"
],
"page.categories.unread_counter": "未讀文章數",
"page.new_category.title": "新分類",
"page.new_user.title": "新使用者",
"page.edit_category.title": "編輯分類 : %s",
"page.edit_user.title": "編輯使用者 : %s",
"page.feeds.title": "Feeds",
"page.feeds.last_check": "最後檢查時間:",
"page.feeds.unread_counter": "未讀文章數",
"page.feeds.read_counter": "已讀文章數",
"page.feeds.error_count": [
"%d 錯誤",
"%d 錯誤"
],
"page.history.title": "歷史",
"page.import.title": "匯入",
"page.search.title": "搜尋結果",
"page.about.title": "關於",
"page.about.credits": "版權",
"page.about.version": "版本號:",
"page.about.build_date": "構建日期:",
"page.about.author": "作者:",
"page.about.license": "授權:",
"page.about.postgres_version": "Postgres 版本號:",
"page.about.go_version": "Go 版本號:",
"page.about.global_config_options": "全域性配置選項",
"page.add_feed.title": "新增Feed",
"page.add_feed.no_category": "沒有類別,至少需要有一個類別",
"page.add_feed.label.url": "網址",
"page.add_feed.submit": "查詢Feed",
"page.add_feed.legend.advanced_options": "高階選項",
"page.add_feed.choose_feed": "選擇一個Feed",
"page.edit_feed.title": "編輯Feed : %s",
"page.edit_feed.last_check": "最後檢查時間:",
"page.edit_feed.last_modified_header": "最後修改的 Header",
"page.edit_feed.etag_header": "ETag 標題:",
"page.edit_feed.no_header": "無 Header",
"page.edit_feed.last_parsing_error": "最後一次解析錯誤",
"page.entry.attachments": "附件",
"page.keyboard_shortcuts.title": "快捷鍵",
"page.keyboard_shortcuts.subtitle.sections": "分割槽導航",
"page.keyboard_shortcuts.subtitle.items": "文章導航",
"page.keyboard_shortcuts.subtitle.pages": "頁面導航",
"page.keyboard_shortcuts.subtitle.actions": "操作",
"page.keyboard_shortcuts.go_to_unread": "開啟未讀頁面",
"page.keyboard_shortcuts.go_to_starred": "開啟收藏頁面",
"page.keyboard_shortcuts.go_to_history": "開啟歷史頁面",
"page.keyboard_shortcuts.go_to_feeds": "開啟Feed頁面",
"page.keyboard_shortcuts.go_to_categories": "開啟分類頁面",
"page.keyboard_shortcuts.go_to_settings": "開啟設定頁面",
"page.keyboard_shortcuts.show_keyboard_shortcuts": "顯示快捷鍵幫助",
"page.keyboard_shortcuts.go_to_previous_item": "上一文章",
"page.keyboard_shortcuts.go_to_next_item": "下一文章",
"page.keyboard_shortcuts.go_to_feed": "轉到Feed頁面",
"page.keyboard_shortcuts.go_to_previous_page": "上一頁",
"page.keyboard_shortcuts.go_to_next_page": "下一頁",
"page.keyboard_shortcuts.open_item": "開啟選定的文章",
"page.keyboard_shortcuts.open_original": "開啟原始連結",
"page.keyboard_shortcuts.open_original_same_window": "在當前標籤頁中開啟原始連結",
"page.keyboard_shortcuts.open_comments": "開啟評論連結",
"page.keyboard_shortcuts.open_comments_same_window": "在當前標籤頁中開啟評論連結",
"page.keyboard_shortcuts.toggle_read_status_next": "切換已讀/未讀狀態, 關注下一個",
"page.keyboard_shortcuts.toggle_read_status_prev": "切換已讀/未讀狀態, 關注前一個",
"page.keyboard_shortcuts.refresh_all_feeds": "背景更新全部Feeds",
"page.keyboard_shortcuts.mark_page_as_read": "將此頁面標記為已讀",
"page.keyboard_shortcuts.download_content": "下載原文內容",
"page.keyboard_shortcuts.toggle_bookmark_status": "切換收藏狀態",
"page.keyboard_shortcuts.save_article": "儲存文章",
"page.keyboard_shortcuts.scroll_item_to_top": "滾動到頂部",
"page.keyboard_shortcuts.remove_feed": "刪除此Feed",
"page.keyboard_shortcuts.go_to_search": "將焦點放在搜尋表單上",
"page.keyboard_shortcuts.close_modal": "關閉對話視窗",
"page.users.title": "使用者",
"page.users.username": "使用者名稱",
"page.users.never_logged": "從未登入",
"page.users.admin.yes": "是",
"page.users.admin.no": "否",
"page.users.actions": "操作",
"page.users.last_login": "最後登入時間",
"page.users.is_admin": "管理員",
"page.settings.title": "設定",
"page.settings.link_google_account": "關聯我的 Google 賬戶",
"page.settings.unlink_google_account": "解除 Google 帳號關聯",
"page.settings.link_oidc_account": "關聯我的 OpenID Connect 賬戶",
"page.settings.unlink_oidc_account": "解除 OpenID Connect 帳號關聯",
"page.login.title": "登入",
"page.login.google_signin": "使用 Google 登入",
"page.login.oidc_signin": "使用 OpenID Connect 登入",
"page.integrations.title": "整合",
"page.integration.miniflux_api": "Miniflux API",
"page.integration.miniflux_api_endpoint": "API Endpoint",
"page.integration.miniflux_api_username": "使用者名稱",
"page.integration.miniflux_api_password": "密碼",
"page.integration.miniflux_api_password_value": "您帳號的密碼",
"page.integration.bookmarklet": "書籤小應用",
"page.integration.bookmarklet.name": "收藏 Miniflux",
"page.integration.bookmarklet.instructions": "拖動這個連結到瀏覽器書籤欄",
"page.integration.bookmarklet.help": "你可以開啟這個特殊的書籤來直接收藏網站",
"page.sessions.title": "會話",
"page.sessions.table.date": "日期",
"page.sessions.table.ip": "IP 地址",
"page.sessions.table.user_agent": "使用者代理",
"page.sessions.table.actions": "操作",
"page.sessions.table.current_session": "當前會話",
"page.api_keys.title": "API 金鑰",
"page.api_keys.table.description": "描述",
"page.api_keys.table.token": "金鑰",
"page.api_keys.table.last_used_at": "最後使用",
"page.api_keys.table.created_at": "建立日期",
"page.api_keys.table.actions": "操作",
"page.api_keys.never_used": "沒用過",
"page.new_api_key.title": "新的 API 金鑰",
"page.offline.title": "離線模式",
"page.offline.message": "您已離線",
"page.offline.refresh_page": "嘗試重新整理頁面",
"alert.no_shared_entry": "沒有分享文章。",
"alert.no_bookmark": "目前沒有收藏",
"alert.no_category": "目前沒有分類",
"alert.no_category_entry": "該分類下沒有文章",
"alert.no_feed_entry": "該Feed中沒有文章",
"alert.no_feed": "目前沒有Feed",
"alert.no_history": "目前沒有歷史",
"alert.feed_error": "該Feed存在問題",
"alert.no_search_result": "該搜尋沒有結果",
"alert.no_feed_in_category": "沒有該類別的Feed。",
"alert.no_unread_entry": "目前沒有未讀文章",
"alert.no_user": "您是唯一的使用者",
"alert.account_unlinked": "您的外部帳戶現已解除關聯!",
"alert.account_linked": "您的外部帳號已關聯!",
"alert.pocket_linked": "您的 Pocket 帳戶現已關聯",
"alert.prefs_saved": "設定已儲存!",
"error.unlink_account_without_password": "您必須設定密碼,否則您將無法再次登入。",
"error.duplicate_linked_account": "該 Provider 已被關聯!",
"error.duplicate_fever_username": "Fever 使用者名稱已被佔用!",
"error.duplicate_googlereader_username": "Google Reader 使用者名稱已被佔用!",
"error.pocket_request_token": "無法從 Pocket 獲取請求令牌!",
"error.pocket_access_token": "無法從 Pocket 獲取訪問令牌!",
"error.category_already_exists": "分類已存在",
"error.unable_to_create_category": "無法建立這個分類",
"error.unable_to_update_category": "無法更新該分類",
"error.user_already_exists": "使用者已存在",
"error.unable_to_create_user": "無法建立此使用者",
"error.unable_to_update_user": "無法更新此使用者",
"error.unable_to_update_feed": "無法更新此源",
"error.subscription_not_found": "找不到任何源",
"error.empty_file": "該檔案為空",
"error.bad_credentials": "使用者名稱或密碼無效",
"error.fields_mandatory": "必須填寫全部資訊",
"error.title_required": "必須填寫標題",
"error.different_passwords": "兩次輸入的密碼不同",
"error.password_min_length": "請至少輸入 6 個字元",
"error.settings_mandatory_fields": "必須填寫使用者名稱、主題、語言以及時區",
"error.entries_per_page_invalid": "每頁的文章數無效。",
"error.feed_mandatory_fields": "必須填寫網址和分類",
"error.feed_already_exists": "此Feed已存在。",
"error.invalid_feed_url": "訂閱Feed的網址無效。",
"error.invalid_site_url": "Feed網站的網址無效。",
"error.feed_url_not_empty": "訂閱Feed的網址不能為空。",
"error.site_url_not_empty": "Feed網站的網址不能為空。",
"error.feed_title_not_empty": "訂閱Feed的標題不能為空。",
"error.feed_category_not_found": "此類別不存在或不屬於該使用者。",
"error.feed_invalid_blocklist_rule": "阻止列表規則無效。",
"error.feed_invalid_keeplist_rule": "保留列表規則無效。",
"error.user_mandatory_fields": "必須填寫使用者名稱",
"error.api_key_already_exists": "此 API 金鑰已存在。",
"error.unable_to_create_api_key": "無法建立此 API 金鑰。",
"error.invalid_theme": "無效的主題。",
"error.invalid_language": "無效的語言。",
"error.invalid_timezone": "無效的時區。",
"error.invalid_entry_direction": "無效的輸入方向。",
"error.invalid_display_mode": "無效的網頁應用顯示模式。",
"form.feed.label.title": "標題",
"form.feed.label.site_url": "網站 URL",
"form.feed.label.feed_url": "訂閱Feed URL",
"form.feed.label.category": "類別",
"form.feed.label.crawler": "下載原文內容",
"form.feed.label.feed_username": "Feed使用者名稱",
"form.feed.label.feed_password": "Feed密碼",
"form.feed.label.user_agent": "覆蓋預設的使用者代理",
"form.feed.label.cookie": "設定 Cookies",
"form.feed.label.scraper_rules": "抓取規則",
"form.feed.label.rewrite_rules": "重寫規則",
"form.feed.label.blocklist_rules": "過濾規則",
"form.feed.label.keeplist_rules": "保留規則",
"form.feed.label.ignore_http_cache": "忽略 HTTP 快取",
"form.feed.label.allow_self_signed_certificates": "允許自簽章憑證或無效憑證",
"form.feed.label.fetch_via_proxy": "透過代理獲取",
"form.feed.label.disabled": "請勿重新整理此Feed",
"form.feed.label.hide_globally": "隱藏全域性未讀列表中的文章",
"form.category.label.title": "標題",
"form.category.hide_globally": "隱藏全域性未讀列表中的文章",
"form.user.label.username": "使用者名稱",
"form.user.label.password": "密碼",
"form.user.label.confirmation": "再次輸入密碼",
"form.user.label.admin": "管理員",
"form.prefs.label.language": "語言",
"form.prefs.label.timezone": "時區",
"form.prefs.label.theme": "主題",
"form.prefs.label.entry_sorting": "文章排序",
"form.prefs.label.entries_per_page": "每頁文章數",
"form.prefs.label.display_mode": "漸進式網頁應用顯示模式(需要重新新增)",
"form.prefs.select.older_first": "舊->新",
"form.prefs.select.recent_first": "新->舊",
"form.prefs.select.fullscreen": "全屏",
"form.prefs.select.standalone": "獨立",
"form.prefs.select.minimal_ui": "最小",
"form.prefs.select.browser": "瀏覽器",
"form.prefs.select.publish_time": "文章釋出時間",
"form.prefs.select.created_time": "文章建立時間",
"form.prefs.label.keyboard_shortcuts": "啟用鍵盤快捷鍵",
"form.prefs.label.entry_swipe": "在移動裝置上啟用滑動手勢",
"form.prefs.label.show_reading_time": "顯示文章的預計閱讀時間",
"form.prefs.label.custom_css": "自定義 CSS",
"form.prefs.label.entry_order": "文章排序依據",
"form.import.label.file": "OPML 檔案",
"form.import.label.url": "URL",
"form.integration.fever_activate": "啟用 Fever API",
"form.integration.fever_username": "Fever 使用者名稱",
"form.integration.fever_password": "Fever 密碼",
"form.integration.fever_endpoint": "Fever API 端點",
"form.integration.googlereader_activate": "啟用 Google Reader API",
"form.integration.googlereader_username": "Google Reader 使用者名稱",
"form.integration.googlereader_password": "Google Reader 密碼",
"form.integration.googlereader_endpoint": "Google Reader API 端點:",
"form.integration.pinboard_activate": "儲存文章到 Pinboard",
"form.integration.pinboard_token": "Pinboard API Token",
"form.integration.pinboard_tags": "Pinboard 標籤",
"form.integration.pinboard_bookmark": "標記為未讀",
"form.integration.instapaper_activate": "儲存文章到 Instapaper",
"form.integration.instapaper_username": "Instapaper 使用者名稱",
"form.integration.instapaper_password": "Instapaper 密碼",
"form.integration.pocket_activate": "將文章儲存到 Pocket",
"form.integration.pocket_consumer_key": "Pocket 使用者金鑰",
"form.integration.pocket_access_token": "Pocket 訪問金鑰",
"form.integration.pocket_connect_link": "連線您的 Pocket 帳戶",
"form.integration.wallabag_activate": "儲存文章到 Wallabag",
"form.integration.wallabag_endpoint": "Wallabag URL",
"form.integration.wallabag_client_id": "Wallabag 客戶端 ID",
"form.integration.wallabag_client_secret": "Wallabag 客戶端 Secret",
"form.integration.wallabag_username": "Wallabag 使用者名稱",
"form.integration.wallabag_password": "Wallabag 密碼",
"form.integration.nunux_keeper_activate": "儲存文章到 Nunux Keeper",
"form.integration.nunux_keeper_endpoint": "Nunux Keeper API 端點",
"form.integration.nunux_keeper_api_key": "Nunux Keeper API 金鑰",
"form.integration.espial_activate": "儲存文章到 Espial",
"form.integration.espial_endpoint": "Espial API 端點",
"form.integration.espial_api_key": "Espial API 金鑰",
"form.integration.espial_tags": "Espial 標籤",
"form.integration.telegram_bot_activate": "將新文章推送到 Telegram",
"form.integration.telegram_bot_token": "Bot token",
"form.integration.telegram_chat_id": "Chat ID",
"form.integration.linkding_activate": "儲存文章到 Linkding",
"form.integration.linkding_endpoint": "Linkding API 端點",
"form.integration.linkding_api_key": "Linkding API 金鑰",
"form.api_key.label.description": "API金鑰標籤",
"form.submit.loading": "載入中…",
"form.submit.saving": "儲存中…",
"time_elapsed.not_yet": "未來",
"time_elapsed.yesterday": "昨天",
"time_elapsed.now": "剛剛",
"time_elapsed.minutes": [
"%d 分鐘前",
"%d 分鐘前"
],
"time_elapsed.hours": [
"%d 小時前",
"%d 小時前"
],
"time_elapsed.days": [
"%d 天前",
"%d 天前"
],
"time_elapsed.weeks": [
"%d 周前",
"%d 周前"
],
"time_elapsed.months": [
"%d 月前",
"%d 月前"
],
"time_elapsed.years": [
"%d 年前",
"%d 年前"
],
"This feed already exists (%s)": "Feed已存在 (%s)",
"Unable to fetch feed (Status Code = %d)": "無法獲取Feed (錯誤程式碼=%d)",
"Unable to open this link: %v": "無法開啟這一連結: %v",
"Unable to analyze this page: %v": "無法分析這一頁面: %v",
"Unable to execute request: %v": "無法執行這一請求: %v",
"Unable to parse OPML file: %q": "無法解析 OPML 檔案: %q",
"Unable to parse RSS feed: %q": "無法解析 RSS Feed: %q",
"Unable to parse Atom feed: %q": "無法解析 Atom Feed: %q",
"Unable to parse JSON feed: %q": "無法解析 JSON Feed: %q",
"Unable to parse RDF feed: %q": "無法解析 RDF Feed: %q",
"Unable to read data: %q": "無法讀取資料: %q",
"Unable to normalize encoding: %q": "無法正則化編碼: %q",
"Category not found for this user": "未找到該使用者的這一分類",
"This feed is empty": "該Feed是空的",
"This web page is empty": "該網頁是空的",
"Invalid SSL certificate (original error: %q)": "無效的 SSL 憑證 (錯誤: %q)",
"This website is temporarily unreachable (original error: %q)": "該網站暫時無法訪問 (原始錯誤: %q)",
"This website is permanently unreachable (original error: %q)": "該網站永久無法訪問(原始錯誤: %q)",
"Website unreachable, the request timed out after %d seconds": "網站無法訪問, 請求已在 %d 秒後超時"
}
-66
View File
@@ -1,66 +0,0 @@
// Copyright 2018 Frédéric Guillot. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.
package locale // import "miniflux.app/locale"
import "testing"
func TestAllLanguagesHaveCatalog(t *testing.T) {
for language := range AvailableLanguages() {
if _, found := translations[language]; !found {
t.Fatalf(`This language do not have a catalog: %q`, language)
}
}
}
func TestAllKeysHaveValue(t *testing.T) {
for language := range AvailableLanguages() {
messages, err := parseTranslationDict(translations[language])
if err != nil {
t.Fatalf(`Parsing error for language %q`, language)
}
if len(messages) == 0 {
t.Fatalf(`The language %q doesn't have any messages`, language)
}
for k, v := range messages {
switch value := v.(type) {
case string:
if value == "" {
t.Errorf(`The key %q for the language %q have an empty string as value`, k, language)
}
case []string:
if len(value) == 0 {
t.Errorf(`The key %q for the language %q have an empty list as value`, k, language)
}
}
}
}
}
func TestMissingTranslations(t *testing.T) {
refLang := "en_US"
references, err := parseTranslationDict(translations[refLang])
if err != nil {
t.Fatal(`Unable to parse reference language`)
}
for language := range AvailableLanguages() {
if language == refLang {
continue
}
messages, err := parseTranslationDict(translations[language])
if err != nil {
t.Fatalf(`Parsing error for language %q`, language)
}
for key := range references {
if _, found := messages[key]; !found {
t.Fatalf(`Translation key %q not found in language %q`, key, language)
}
}
}
}
-8
View File
@@ -4,14 +4,6 @@
package main // import "miniflux.app"
//go:generate go run generate.go
//go:generate gofmt -s -w ui/static/css.go
//go:generate gofmt -s -w ui/static/bin.go
//go:generate gofmt -s -w ui/static/js.go
//go:generate gofmt -s -w template/views.go
//go:generate gofmt -s -w template/common.go
//go:generate gofmt -s -w locale/translations.go
import (
"miniflux.app/cli"
)
+72
View File
@@ -78,6 +78,62 @@ var (
},
[]string{"status"},
)
dbOpenConnectionsGauge = prometheus.NewGauge(
prometheus.GaugeOpts{
Namespace: "miniflux",
Name: "db_open_connections",
Help: "The number of established connections both in use and idle",
},
)
dbConnectionsInUseGauge = prometheus.NewGauge(
prometheus.GaugeOpts{
Namespace: "miniflux",
Name: "db_connections_in_use",
Help: "The number of connections currently in use",
},
)
dbConnectionsIdleGauge = prometheus.NewGauge(
prometheus.GaugeOpts{
Namespace: "miniflux",
Name: "db_connections_idle",
Help: "The number of idle connections",
},
)
dbConnectionsWaitCountGauge = prometheus.NewGauge(
prometheus.GaugeOpts{
Namespace: "miniflux",
Name: "db_connections_wait_count",
Help: "The total number of connections waited for",
},
)
dbConnectionsMaxIdleClosedGauge = prometheus.NewGauge(
prometheus.GaugeOpts{
Namespace: "miniflux",
Name: "db_connections_max_idle_closed",
Help: "The total number of connections closed due to SetMaxIdleConns",
},
)
dbConnectionsMaxIdleTimeClosedGauge = prometheus.NewGauge(
prometheus.GaugeOpts{
Namespace: "miniflux",
Name: "db_connections_max_idle_time_closed",
Help: "The total number of connections closed due to SetConnMaxIdleTime",
},
)
dbConnectionsMaxLifetimeClosedGauge = prometheus.NewGauge(
prometheus.GaugeOpts{
Namespace: "miniflux",
Name: "db_connections_max_lifetime_closed",
Help: "The total number of connections closed due to SetConnMaxLifetime",
},
)
)
// Collector represents a metric collector.
@@ -95,6 +151,13 @@ func NewCollector(store *storage.Storage, refreshInterval int) *Collector {
prometheus.MustRegister(feedsGauge)
prometheus.MustRegister(brokenFeedsGauge)
prometheus.MustRegister(entriesGauge)
prometheus.MustRegister(dbOpenConnectionsGauge)
prometheus.MustRegister(dbConnectionsInUseGauge)
prometheus.MustRegister(dbConnectionsIdleGauge)
prometheus.MustRegister(dbConnectionsWaitCountGauge)
prometheus.MustRegister(dbConnectionsMaxIdleClosedGauge)
prometheus.MustRegister(dbConnectionsMaxIdleTimeClosedGauge)
prometheus.MustRegister(dbConnectionsMaxLifetimeClosedGauge)
return &Collector{store, refreshInterval}
}
@@ -116,5 +179,14 @@ func (c *Collector) GatherStorageMetrics() {
for status, count := range entriesCount {
entriesGauge.WithLabelValues(status).Set(float64(count))
}
dbStats := c.store.DBStats()
dbOpenConnectionsGauge.Set(float64(dbStats.OpenConnections))
dbConnectionsInUseGauge.Set(float64(dbStats.InUse))
dbConnectionsIdleGauge.Set(float64(dbStats.Idle))
dbConnectionsWaitCountGauge.Set(float64(dbStats.WaitCount))
dbConnectionsMaxIdleClosedGauge.Set(float64(dbStats.MaxIdleClosed))
dbConnectionsMaxIdleTimeClosedGauge.Set(float64(dbStats.MaxIdleTimeClosed))
dbConnectionsMaxLifetimeClosedGauge.Set(float64(dbStats.MaxLifetimeClosed))
}
}
+36 -3
View File
@@ -1,5 +1,5 @@
.\" Manpage for miniflux.
.TH "MINIFLUX" "1" "September 28, 2020" "\ \&" "\ \&"
.TH "MINIFLUX" "1" "January 5, 2022" "\ \&" "\ \&"
.SH NAME
miniflux \- Minimalist and opinionated feed reader
@@ -43,6 +43,13 @@ Show debug logs\&.
Flush all sessions (disconnect users)\&.
.RE
.PP
.B \-healthcheck
.RS 4
Perform a health check on the given endpoint\&.
.br
The value "auto" try to guess the health check endpoint\&.
.RE
.PP
.B \-i
.RS 4
Show application information\&.
@@ -100,6 +107,12 @@ Set the value to 1 to enable debug logs\&.
.br
Disabled by default\&.
.TP
.B FETCH_YOUTUBE_WATCH_TIME
Set the value to 1 to scrape video duration from YouTube website and
use it as a reading time\&.
.br
Disabled by default\&.
.TP
.B SERVER_TIMING_HEADER
Set the value to 1 to enable server-timing headers\&.
.br
@@ -123,7 +136,7 @@ Default is 60 minutes\&.
.B BATCH_SIZE
Number of feeds to send to the queue for each interval\&.
.br
Default is 10 feeds\&.
Default is 100 feeds\&.
.TP
.B POLLING_SCHEDULER
Scheduler used for polling feeds. Possible values are "round_robin" or "entry_frequency"\&.
@@ -161,6 +174,11 @@ Path to a secret key exposed as a file, it should contain $DATABASE_URL value\&.
.br
Default is empty\&.
.TP
.B DATABASE_CONNECTION_LIFETIME
Set the maximum amount of time a connection may be reused\&.
.br
Default is 5 minutes\&.
.TP
.B DATABASE_MAX_CONNS
Maximum number of database connections\&.
.br
@@ -205,6 +223,11 @@ Set to -1 to keep all unread entries.
.br
Default is 180 days\&.
.TP
.B CLEANUP_ARCHIVE_BATCH_SIZE
Number of entries to archive for each job interval\&.
.br
Default is 10000 entries\&.
.TP
.B CLEANUP_REMOVE_SESSIONS_DAYS
Number of days after removing old sessions from the database\&.
.br
@@ -387,7 +410,17 @@ Disabled by default\&.
.B MAINTENANCE_MESSAGE
Define a custom maintenance message\&.
.br
Default is "Miniflux is currently under maintenance".
Default is "Miniflux is currently under maintenance"\&.
.TP
.B WATCHDOG
Enable or disable Systemd watchdog\&.
.br
Enabled by default\&.
.TP
.B INVIDIOUS_INSTANCE
Set a custom invidious instance to use\&.
.br
Default is yewtu.be\&.
.SH AUTHORS
.P
+9 -5
View File
@@ -8,10 +8,12 @@ import "fmt"
// Category represents a feed category.
type Category struct {
ID int64 `json:"id"`
Title string `json:"title"`
UserID int64 `json:"user_id"`
FeedCount int `json:"-"`
ID int64 `json:"id"`
Title string `json:"title"`
UserID int64 `json:"user_id"`
HideGlobally bool `json:"hide_globally"`
FeedCount int `json:"-"`
TotalUnread int `json:"-"`
}
func (c *Category) String() string {
@@ -20,12 +22,14 @@ func (c *Category) String() string {
// CategoryRequest represents the request to create or update a category.
type CategoryRequest struct {
Title string `json:"title"`
Title string `json:"title"`
HideGlobally string `json:"hide_globally"`
}
// Patch updates category fields.
func (cr *CategoryRequest) Patch(category *Category) {
category.Title = cr.Title
category.HideGlobally = cr.HideGlobally != ""
}
// Categories represents a list of categories.
+1
View File
@@ -29,6 +29,7 @@ type Entry struct {
CommentsURL string `json:"comments_url"`
Date time.Time `json:"published_at"`
CreatedAt time.Time `json:"created_at"`
ChangedAt time.Time `json:"changed_at"`
Content string `json:"content"`
Author string `json:"author"`
ShareCode string `json:"share_code"`
+81 -55
View File
@@ -24,33 +24,41 @@ const (
// Feed represents a feed in the application.
type Feed struct {
ID int64 `json:"id"`
UserID int64 `json:"user_id"`
FeedURL string `json:"feed_url"`
SiteURL string `json:"site_url"`
Title string `json:"title"`
CheckedAt time.Time `json:"checked_at"`
NextCheckAt time.Time `json:"next_check_at"`
EtagHeader string `json:"etag_header"`
LastModifiedHeader string `json:"last_modified_header"`
ParsingErrorMsg string `json:"parsing_error_message"`
ParsingErrorCount int `json:"parsing_error_count"`
ScraperRules string `json:"scraper_rules"`
RewriteRules string `json:"rewrite_rules"`
Crawler bool `json:"crawler"`
BlocklistRules string `json:"blocklist_rules"`
KeeplistRules string `json:"keeplist_rules"`
UserAgent string `json:"user_agent"`
Username string `json:"username"`
Password string `json:"password"`
Disabled bool `json:"disabled"`
IgnoreHTTPCache bool `json:"ignore_http_cache"`
FetchViaProxy bool `json:"fetch_via_proxy"`
Category *Category `json:"category,omitempty"`
Entries Entries `json:"entries,omitempty"`
Icon *FeedIcon `json:"icon"`
UnreadCount int `json:"-"`
ReadCount int `json:"-"`
ID int64 `json:"id"`
UserID int64 `json:"user_id"`
FeedURL string `json:"feed_url"`
SiteURL string `json:"site_url"`
Title string `json:"title"`
CheckedAt time.Time `json:"checked_at"`
NextCheckAt time.Time `json:"next_check_at"`
EtagHeader string `json:"etag_header"`
LastModifiedHeader string `json:"last_modified_header"`
ParsingErrorMsg string `json:"parsing_error_message"`
ParsingErrorCount int `json:"parsing_error_count"`
ScraperRules string `json:"scraper_rules"`
RewriteRules string `json:"rewrite_rules"`
Crawler bool `json:"crawler"`
BlocklistRules string `json:"blocklist_rules"`
KeeplistRules string `json:"keeplist_rules"`
UserAgent string `json:"user_agent"`
Cookie string `json:"cookie"`
Username string `json:"username"`
Password string `json:"password"`
Disabled bool `json:"disabled"`
IgnoreHTTPCache bool `json:"ignore_http_cache"`
AllowSelfSignedCertificates bool `json:"allow_self_signed_certificates"`
FetchViaProxy bool `json:"fetch_via_proxy"`
Category *Category `json:"category,omitempty"`
Entries Entries `json:"entries,omitempty"`
Icon *FeedIcon `json:"icon"`
HideGlobally bool `json:"hide_globally"`
UnreadCount int `json:"-"`
ReadCount int `json:"-"`
}
type FeedCounters struct {
ReadCounters map[int64]int `json:"reads"`
UnreadCounters map[int64]int `json:"unreads"`
}
func (f *Feed) String() string {
@@ -117,38 +125,44 @@ func (f *Feed) ScheduleNextCheck(weeklyCount int) {
// FeedCreationRequest represents the request to create a feed.
type FeedCreationRequest struct {
FeedURL string `json:"feed_url"`
CategoryID int64 `json:"category_id"`
UserAgent string `json:"user_agent"`
Username string `json:"username"`
Password string `json:"password"`
Crawler bool `json:"crawler"`
Disabled bool `json:"disabled"`
IgnoreHTTPCache bool `json:"ignore_http_cache"`
FetchViaProxy bool `json:"fetch_via_proxy"`
ScraperRules string `json:"scraper_rules"`
RewriteRules string `json:"rewrite_rules"`
BlocklistRules string `json:"blocklist_rules"`
KeeplistRules string `json:"keeplist_rules"`
FeedURL string `json:"feed_url"`
CategoryID int64 `json:"category_id"`
UserAgent string `json:"user_agent"`
Cookie string `json:"cookie"`
Username string `json:"username"`
Password string `json:"password"`
Crawler bool `json:"crawler"`
Disabled bool `json:"disabled"`
IgnoreHTTPCache bool `json:"ignore_http_cache"`
AllowSelfSignedCertificates bool `json:"allow_self_signed_certificates"`
FetchViaProxy bool `json:"fetch_via_proxy"`
ScraperRules string `json:"scraper_rules"`
RewriteRules string `json:"rewrite_rules"`
BlocklistRules string `json:"blocklist_rules"`
KeeplistRules string `json:"keeplist_rules"`
HideGlobally bool `json:"hide_globally"`
}
// FeedModificationRequest represents the request to update a feed.
type FeedModificationRequest struct {
FeedURL *string `json:"feed_url"`
SiteURL *string `json:"site_url"`
Title *string `json:"title"`
ScraperRules *string `json:"scraper_rules"`
RewriteRules *string `json:"rewrite_rules"`
BlocklistRules *string `json:"blocklist_rules"`
KeeplistRules *string `json:"keeplist_rules"`
Crawler *bool `json:"crawler"`
UserAgent *string `json:"user_agent"`
Username *string `json:"username"`
Password *string `json:"password"`
CategoryID *int64 `json:"category_id"`
Disabled *bool `json:"disabled"`
IgnoreHTTPCache *bool `json:"ignore_http_cache"`
FetchViaProxy *bool `json:"fetch_via_proxy"`
FeedURL *string `json:"feed_url"`
SiteURL *string `json:"site_url"`
Title *string `json:"title"`
ScraperRules *string `json:"scraper_rules"`
RewriteRules *string `json:"rewrite_rules"`
BlocklistRules *string `json:"blocklist_rules"`
KeeplistRules *string `json:"keeplist_rules"`
Crawler *bool `json:"crawler"`
UserAgent *string `json:"user_agent"`
Cookie *string `json:"cookie"`
Username *string `json:"username"`
Password *string `json:"password"`
CategoryID *int64 `json:"category_id"`
Disabled *bool `json:"disabled"`
IgnoreHTTPCache *bool `json:"ignore_http_cache"`
AllowSelfSignedCertificates *bool `json:"allow_self_signed_certificates"`
FetchViaProxy *bool `json:"fetch_via_proxy"`
HideGlobally *bool `json:"hide_globally"`
}
// Patch updates a feed with modified values.
@@ -189,6 +203,10 @@ func (f *FeedModificationRequest) Patch(feed *Feed) {
feed.UserAgent = *f.UserAgent
}
if f.Cookie != nil {
feed.Cookie = *f.Cookie
}
if f.Username != nil {
feed.Username = *f.Username
}
@@ -209,9 +227,17 @@ func (f *FeedModificationRequest) Patch(feed *Feed) {
feed.IgnoreHTTPCache = *f.IgnoreHTTPCache
}
if f.AllowSelfSignedCertificates != nil {
feed.AllowSelfSignedCertificates = *f.AllowSelfSignedCertificates
}
if f.FetchViaProxy != nil {
feed.FetchViaProxy = *f.FetchViaProxy
}
if f.HideGlobally != nil {
feed.HideGlobally = *f.HideGlobally
}
}
// Feeds is a list of feed
+2 -2
View File
@@ -22,10 +22,10 @@ func (i *Icon) DataURL() string {
return fmt.Sprintf("%s;base64,%s", i.MimeType, base64.StdEncoding.EncodeToString(i.Content))
}
// Icons represents a list of icon.
// Icons represents a list of icons.
type Icons []*Icon
// FeedIcon is a jonction table between feeds and icons
// FeedIcon is a junction table between feeds and icons.
type FeedIcon struct {
FeedID int64 `json:"feed_id"`
IconID int64 `json:"icon_id"`
+13
View File
@@ -17,6 +17,9 @@ type Integration struct {
FeverEnabled bool
FeverUsername string
FeverToken string
GoogleReaderEnabled bool
GoogleReaderUsername string
GoogleReaderPassword string
WallabagEnabled bool
WallabagURL string
WallabagClientID string
@@ -26,7 +29,17 @@ type Integration struct {
NunuxKeeperEnabled bool
NunuxKeeperURL string
NunuxKeeperAPIKey string
EspialEnabled bool
EspialURL string
EspialAPIKey string
EspialTags string
PocketEnabled bool
PocketAccessToken string
PocketConsumerKey string
TelegramBotEnabled bool
TelegramBotToken string
TelegramBotChatID string
LinkdingEnabled bool
LinkdingURL string
LinkdingAPIKey string
}

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