Compare commits

..

56 Commits

Author SHA1 Message Date
binwiederhier
aecf0a5f25 Remove Rate-Topics 2024-03-07 10:07:42 -05:00
binwiederhier
1d2b759dc0 Bump 2024-01-21 11:27:10 -05:00
binwiederhier
8e62d2af6f Merge branch 'main' of https://hosted.weblate.org/git/ntfy/web 2024-01-21 11:24:13 -05:00
109247019824
020968961e Translated using Weblate (Bulgarian)
Currently translated at 88.2% (337 of 382 strings)

Translation: ntfy/Web app
Translate-URL: https://hosted.weblate.org/projects/ntfy/web/bg/
2024-01-05 23:06:59 +00:00
Jonne Saloranta
2cfe18c9c8 Translated using Weblate (Finnish)
Currently translated at 100.0% (382 of 382 strings)

Translation: ntfy/Web app
Translate-URL: https://hosted.weblate.org/projects/ntfy/web/fi/
2024-01-02 20:09:12 +00:00
Costin Stroie
39dc90e795 Translated using Weblate (Romanian)
Currently translated at 32.4% (124 of 382 strings)

Translation: ntfy/Web app
Translate-URL: https://hosted.weblate.org/projects/ntfy/web/ro/
2023-12-31 11:09:05 +01:00
Costin Stroie
fe4101d26c Translated using Weblate (Romanian)
Currently translated at 28.2% (108 of 382 strings)

Translation: ntfy/Web app
Translate-URL: https://hosted.weblate.org/projects/ntfy/web/ro/
2023-12-30 10:09:27 +01:00
tinect
80861d33b4 Translated using Weblate (German)
Currently translated at 100.0% (382 of 382 strings)

Translation: ntfy/Web app
Translate-URL: https://hosted.weblate.org/projects/ntfy/web/de/
2023-12-30 00:45:43 +01:00
tinect
1383b41062 Translated using Weblate (German)
Currently translated at 100.0% (382 of 382 strings)

Translation: ntfy/Web app
Translate-URL: https://hosted.weblate.org/projects/ntfy/web/de/
2023-12-29 01:09:12 +01:00
Panda
99c03b0e83 Translated using Weblate (Danish)
Currently translated at 100.0% (382 of 382 strings)

Translation: ntfy/Web app
Translate-URL: https://hosted.weblate.org/projects/ntfy/web/da/
2023-12-14 00:06:55 +01:00
Philipp C. Heckel
bb4b5d2bc8 Merge pull request #976 from drizzt/patch-1
Fix typo in it.json
2023-12-10 20:19:41 -05:00
M G
4916806298 Added translation using Weblate (Esperanto) 2023-12-10 19:26:48 +01:00
Timothy Redaelli
f350b81da4 Fix typo in it.json 2023-12-09 01:20:48 +01:00
109247019824
1e306e08c9 Translated using Weblate (Bulgarian)
Currently translated at 88.2% (337 of 382 strings)

Translation: ntfy/Web app
Translate-URL: https://hosted.weblate.org/projects/ntfy/web/bg/
2023-12-07 06:05:18 +01:00
binwiederhier
518505fa9d Thank you @beekeeb for your generous sponsorship 2023-12-04 13:27:33 -05:00
binwiederhier
1f4a0601f8 Thank you @tka85 for your generous donation! 2023-12-04 13:25:50 -05:00
binwiederhier
0347fdc192 Merge branch 'main' of https://hosted.weblate.org/git/ntfy/web 2023-12-04 13:25:07 -05:00
Piotr Icikowski
d207f3ca26 Translated using Weblate (Polish)
Currently translated at 91.8% (351 of 382 strings)

Translation: ntfy/Web app
Translate-URL: https://hosted.weblate.org/projects/ntfy/web/pl/
2023-12-03 23:08:22 +01:00
binwiederhier
154e3945c9 Release notes 2023-11-27 21:56:22 -05:00
binwiederhier
0a82729650 Merge branch 'main' of https://hosted.weblate.org/git/ntfy/web 2023-11-27 21:56:06 -05:00
Philipp C. Heckel
5876c923a2 Merge pull request #964 from wunter8/patch-5
Update docker compose example
2023-11-27 09:01:24 -05:00
wunter8
a2b56154a7 Update docker compose example
Noticed that this wasn't consistent with the other paths
2023-11-27 06:33:33 -07:00
Philipp C. Heckel
6e2f9a5bdd Merge pull request #961 from cmj2002/main
Update docker-dev go version
2023-11-24 15:33:47 -05:00
Cao Mingjun
87bcf59bd4 Update docker-dev go version 2023-11-24 14:00:16 +00:00
Caio Facchinato
4817e33942 Translated using Weblate (Portuguese (Brazil))
Currently translated at 74.3% (284 of 382 strings)

Translation: ntfy/Web app
Translate-URL: https://hosted.weblate.org/projects/ntfy/web/pt_BR/
2023-11-24 06:02:05 +01:00
binwiederhier
141cbad5ad Thank you @ubipo for your sponsorship 2023-11-22 07:19:51 -05:00
binwiederhier
aaa4976c7d Install page 2023-11-19 16:48:19 -05:00
binwiederhier
2525ef71a9 Release notes 2023-11-19 16:42:04 -05:00
binwiederhier
75a2cb9236 A few links 2023-11-19 09:08:49 -05:00
binwiederhier
6b82cb2b7a cache file 2023-11-19 06:55:52 -05:00
binwiederhier
260e24f68b Added Docker Compose docs 2023-11-19 06:49:24 -05:00
binwiederhier
3c8fabe409 Update deps 2023-11-19 05:49:40 -05:00
binwiederhier
1c3ed3ea40 Do not allow empty passwords when creating users 2023-11-19 05:47:41 -05:00
binwiederhier
497f45e5cd Merge branch 'main' of github.com:binwiederhier/ntfy 2023-11-18 21:54:38 -05:00
binwiederhier
f9a411c307 Release notes 2023-11-18 21:54:26 -05:00
binwiederhier
7d755ce604 Add comments and another test to ACL fix 2023-11-18 21:50:01 -05:00
binwiederhier
f64dbcb6b2 Merge branch 'main' into sandman7920/main 2023-11-18 06:28:48 -05:00
Philipp C. Heckel
0b41356179 Merge pull request #949 from cyqsimon/python-pip-override
Allow overriding `python3` & `pip3` binary in Makefile
2023-11-17 05:35:56 -05:00
cyqsimon
0928d99813 Fix mkdocs call 2023-11-17 10:41:16 +08:00
cyqsimon
e724aace49 Allow overriding python3 & pip3 binary in Makefile 2023-11-17 10:13:06 +08:00
binwiederhier
b65044712b Fix linter 2023-11-16 21:04:49 -05:00
binwiederhier
22f48c5ad3 Change mod path 2023-11-16 20:54:58 -05:00
Philipp C. Heckel
1b4d55fb30 Merge pull request #941 from jhotmann/module-version-2
Append /v2 to module so version 2 can be used as a go package
2023-11-16 20:49:57 -05:00
Philipp C. Heckel
ad8b22482b Merge pull request #939 from artemislena/fix-docs-badges
Fix broken badge image links in docs
2023-11-16 20:48:11 -05:00
binwiederhier
780c149c81 Merge branch 'html-emails' 2023-11-16 09:58:51 -05:00
binwiederhier
859a4e4f79 Added another test 2023-11-16 09:58:32 -05:00
Philipp C. Heckel
d0c2b00fbf Merge pull request #693 from binwiederhier/html-emails
WIP: HTML emails
2023-11-16 09:51:45 -05:00
Jordan Hotmann
92cfa4040b Append /v2 to module so version 2 can be used as a go package 2023-11-08 13:06:50 -07:00
artemislena
3f3af275e7 T.: Fixed broken links 2023-11-07 20:00:10 +01:00
Nikolay Zlatev
1aa82ff06a FullScenario_Default_DenyAll: add user "john" test case
Add new test for user john

The user should have:

"deny" to mytopic_deny*,
  "ro" to mytopic_ro*,
  "rw" to mytopic*,
  "ro" to the rest
2023-10-24 14:02:52 +03:00
Nikolay Zlatev
0ff1f6520a TestMigrationFrom4: move the longest rule on top 2023-10-24 14:02:04 +03:00
Nikolay Zlatev
ff2a354333 TestMigrationFrom1: move the longest rule on top 2023-10-24 14:00:46 +03:00
Nikolay Zlatev
543709336c TestManager_UserManagement: move the longest rule on top 2023-10-24 13:59:39 +03:00
Nikolay Zlatev
afd6d2e0ee Default_DenyAll: move the longest rule on top 2023-10-24 13:57:46 +03:00
Nikolay Zlatev
7a5572ad7c user.Manager: further improve ACL write/read order
For each user, we should test in order `THE_LONGEST_RULE`->`WRITE_PERMISSION`
2023-10-16 09:41:49 +03:00
Nikolay Zlatev
5c9cebf059 user.Manager: fix ACL write, read order
This should fix "read-only access to topic *" being applied before "read-write access to topic _PREFIX_*"
Before this if we have:

ntfy access user "mytopic*" rw
ntfy access user "*" ro

read-only access rule was applied first and user couldn't write to
mytopic*
2023-10-13 15:41:17 +03:00
88 changed files with 2950 additions and 1434 deletions

View File

@@ -1,30 +1,24 @@
name: build
on: [push, pull_request]
on: [ push, pull_request ]
jobs:
build:
runs-on: ubuntu-latest
steps:
-
name: Checkout code
- name: Checkout code
uses: actions/checkout@v3
-
name: Install Go
- name: Install Go
uses: actions/setup-go@v4
with:
go-version: '1.21.x'
-
name: Install node
go-version: '1.22.x'
- name: Install node
uses: actions/setup-node@v3
with:
node-version: '20'
cache: 'npm'
cache-dependency-path: './web/package-lock.json'
-
name: Install dependencies
- name: Install dependencies
run: make build-deps-ubuntu
-
name: Build all the things
- name: Build all the things
run: make build
-
name: Print build results and checksums
- name: Print build results and checksums
run: make cli-build-results

View File

@@ -7,35 +7,28 @@ jobs:
release:
runs-on: ubuntu-latest
steps:
-
name: Checkout code
- name: Checkout code
uses: actions/checkout@v3
-
name: Install Go
- name: Install Go
uses: actions/setup-go@v4
with:
go-version: '1.21.x'
-
name: Install node
go-version: '1.22.x'
- name: Install node
uses: actions/setup-node@v3
with:
node-version: '20'
cache: 'npm'
cache-dependency-path: './web/package-lock.json'
-
name: Docker login
- name: Docker login
uses: docker/login-action@v2
with:
username: ${{ github.repository_owner }}
password: ${{ secrets.DOCKER_HUB_TOKEN }}
-
name: Install dependencies
- name: Install dependencies
run: make build-deps-ubuntu
-
name: Build and publish
- name: Build and publish
run: make release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-
name: Print build results and checksums
- name: Print build results and checksums
run: make cli-build-results

View File

@@ -1,39 +1,30 @@
name: test
on: [push, pull_request]
on: [ push, pull_request ]
jobs:
test:
runs-on: ubuntu-latest
steps:
-
name: Checkout code
- name: Checkout code
uses: actions/checkout@v3
-
name: Install Go
- name: Install Go
uses: actions/setup-go@v4
with:
go-version: '1.21.x'
-
name: Install node
go-version: '1.22.x'
- name: Install node
uses: actions/setup-node@v3
with:
node-version: '20'
cache: 'npm'
cache-dependency-path: './web/package-lock.json'
-
name: Install dependencies
- name: Install dependencies
run: make build-deps-ubuntu
-
name: Build docs (required for tests)
- name: Build docs (required for tests)
run: make docs
-
name: Build web app (required for tests)
- name: Build web app (required for tests)
run: make web
-
name: Run tests, formatting, vetting and linting
- name: Run tests, formatting, vetting and linting
run: make check
-
name: Run coverage
- name: Run coverage
run: make coverage
-
name: Upload coverage to codecov.io
- name: Upload coverage to codecov.io
run: make coverage-upload

View File

@@ -1,4 +1,4 @@
FROM golang:1.20-bullseye as builder
FROM golang:1.21-bullseye as builder
ARG VERSION=dev
ARG COMMIT=unknown

View File

@@ -1,4 +1,6 @@
MAKEFLAGS := --jobs=1
PYTHON := python3
PIP := pip3
VERSION := $(shell git describe --tag)
COMMIT := $(shell git rev-parse --short HEAD)
@@ -115,16 +117,16 @@ build-deps-ubuntu:
docs: docs-deps docs-build
docs-venv: .PHONY
python3 -m venv ./venv
$(PYTHON) -m venv ./venv
docs-build: docs-venv
(. venv/bin/activate && mkdocs build)
(. venv/bin/activate && $(PYTHON) -m mkdocs build)
docs-deps: docs-venv
(. venv/bin/activate && pip3 install -r requirements.txt)
(. venv/bin/activate && $(PIP) install -r requirements.txt)
docs-deps-update: .PHONY
(. venv/bin/activate && pip3 install -r requirements.txt --upgrade)
(. venv/bin/activate && $(PIP) install -r requirements.txt --upgrade)
# Web app

View File

@@ -2,7 +2,7 @@
# ntfy.sh | Send push notifications to your phone or desktop via PUT/POST
[![Release](https://img.shields.io/github/release/binwiederhier/ntfy.svg?color=success&style=flat-square)](https://github.com/binwiederhier/ntfy/releases/latest)
[![Go Reference](https://pkg.go.dev/badge/heckel.io/ntfy.svg)](https://pkg.go.dev/heckel.io/ntfy)
[![Go Reference](https://pkg.go.dev/badge/heckel.io/ntfy.svg)](https://pkg.go.dev/heckel.io/ntfy/v2)
[![Tests](https://github.com/binwiederhier/ntfy/workflows/test/badge.svg)](https://github.com/binwiederhier/ntfy/actions)
[![Go Report Card](https://goreportcard.com/badge/github.com/binwiederhier/ntfy)](https://goreportcard.com/report/github.com/binwiederhier/ntfy)
[![codecov](https://codecov.io/gh/binwiederhier/ntfy/branch/main/graph/badge.svg?token=A597KQ463G)](https://codecov.io/gh/binwiederhier/ntfy)
@@ -165,6 +165,9 @@ account costs. Even small donations are very much appreciated. A big fat **Thank
<a href="https://github.com/pgwiebes"><img src="https://github.com/pgwiebes.png" width="40px" /></a>
<a href="https://github.com/ralhei"><img src="https://github.com/ralhei.png" width="40px" /></a>
<a href="https://github.com/TechMDW"><img src="https://github.com/TechMDW.png" width="40px" /></a>
<a href="https://github.com/ubipo"><img src="https://github.com/ubipo.png" width="40px" /></a>
<a href="https://github.com/tka85"><img src="https://github.com/tka85.png" width="40px" /></a>
<a href="https://github.com/beekeeb"><img src="https://github.com/beekeeb.png" width="40px" /></a>
I'd also like to thank JetBrains for their awesome [IntelliJ IDEA](https://www.jetbrains.com/idea/),
and [DigitalOcean](https://m.do.co/c/442b929528db) (*referral link*) for supporting the project:

View File

@@ -7,8 +7,8 @@ import (
"encoding/json"
"errors"
"fmt"
"heckel.io/ntfy/log"
"heckel.io/ntfy/util"
"heckel.io/ntfy/v2/log"
"heckel.io/ntfy/v2/util"
"io"
"net/http"
"regexp"

View File

@@ -3,9 +3,9 @@ package client_test
import (
"fmt"
"github.com/stretchr/testify/require"
"heckel.io/ntfy/client"
"heckel.io/ntfy/log"
"heckel.io/ntfy/test"
"heckel.io/ntfy/v2/client"
"heckel.io/ntfy/v2/log"
"heckel.io/ntfy/v2/test"
"os"
"testing"
"time"

View File

@@ -2,7 +2,7 @@ package client_test
import (
"github.com/stretchr/testify/require"
"heckel.io/ntfy/client"
"heckel.io/ntfy/v2/client"
"os"
"path/filepath"
"testing"

View File

@@ -2,7 +2,7 @@ package client
import (
"fmt"
"heckel.io/ntfy/util"
"heckel.io/ntfy/v2/util"
"net/http"
"strings"
"time"

View File

@@ -6,8 +6,8 @@ import (
"errors"
"fmt"
"github.com/urfave/cli/v2"
"heckel.io/ntfy/user"
"heckel.io/ntfy/util"
"heckel.io/ntfy/v2/user"
"heckel.io/ntfy/v2/util"
)
func init() {

View File

@@ -4,8 +4,8 @@ import (
"fmt"
"github.com/stretchr/testify/require"
"github.com/urfave/cli/v2"
"heckel.io/ntfy/server"
"heckel.io/ntfy/test"
"heckel.io/ntfy/v2/server"
"heckel.io/ntfy/v2/test"
"testing"
)

View File

@@ -5,7 +5,7 @@ import (
"fmt"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v2/altsrc"
"heckel.io/ntfy/log"
"heckel.io/ntfy/v2/log"
"os"
"regexp"
)

View File

@@ -4,8 +4,8 @@ import (
"bytes"
"encoding/json"
"github.com/urfave/cli/v2"
"heckel.io/ntfy/client"
"heckel.io/ntfy/log"
"heckel.io/ntfy/v2/client"
"heckel.io/ntfy/v2/log"
"os"
"strings"
"testing"

View File

@@ -5,7 +5,7 @@ import (
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v2/altsrc"
"gopkg.in/yaml.v2"
"heckel.io/ntfy/util"
"heckel.io/ntfy/v2/util"
"os"
)

View File

@@ -4,9 +4,9 @@ import (
"errors"
"fmt"
"github.com/urfave/cli/v2"
"heckel.io/ntfy/client"
"heckel.io/ntfy/log"
"heckel.io/ntfy/util"
"heckel.io/ntfy/v2/client"
"heckel.io/ntfy/v2/log"
"heckel.io/ntfy/v2/util"
"io"
"os"
"os/exec"

View File

@@ -3,8 +3,8 @@ package cmd
import (
"fmt"
"github.com/stretchr/testify/require"
"heckel.io/ntfy/test"
"heckel.io/ntfy/util"
"heckel.io/ntfy/v2/test"
"heckel.io/ntfy/v2/util"
"net/http"
"net/http/httptest"
"os"

View File

@@ -6,7 +6,7 @@ import (
"errors"
"fmt"
"github.com/stripe/stripe-go/v74"
"heckel.io/ntfy/user"
"heckel.io/ntfy/v2/user"
"io/fs"
"math"
"net"
@@ -17,12 +17,12 @@ import (
"syscall"
"time"
"heckel.io/ntfy/log"
"heckel.io/ntfy/v2/log"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v2/altsrc"
"heckel.io/ntfy/server"
"heckel.io/ntfy/util"
"heckel.io/ntfy/v2/server"
"heckel.io/ntfy/v2/util"
)
func init() {

View File

@@ -12,15 +12,11 @@ import (
"github.com/gorilla/websocket"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"heckel.io/ntfy/client"
"heckel.io/ntfy/test"
"heckel.io/ntfy/util"
"heckel.io/ntfy/v2/client"
"heckel.io/ntfy/v2/test"
"heckel.io/ntfy/v2/util"
)
func init() {
rand.Seed(time.Now().UnixMilli())
}
func TestCLI_Serve_Unix_Curl(t *testing.T) {
sockFile := filepath.Join(t.TempDir(), "ntfy.sock")
configFile := newEmptyFile(t) // Avoid issues with existing server.yml file on system

View File

@@ -4,9 +4,9 @@ import (
"errors"
"fmt"
"github.com/urfave/cli/v2"
"heckel.io/ntfy/client"
"heckel.io/ntfy/log"
"heckel.io/ntfy/util"
"heckel.io/ntfy/v2/client"
"heckel.io/ntfy/v2/log"
"heckel.io/ntfy/v2/util"
"os"
"os/exec"
"os/user"

View File

@@ -6,8 +6,8 @@ import (
"errors"
"fmt"
"github.com/urfave/cli/v2"
"heckel.io/ntfy/user"
"heckel.io/ntfy/util"
"heckel.io/ntfy/v2/user"
"heckel.io/ntfy/v2/util"
)
func init() {

View File

@@ -3,8 +3,8 @@ package cmd
import (
"github.com/stretchr/testify/require"
"github.com/urfave/cli/v2"
"heckel.io/ntfy/server"
"heckel.io/ntfy/test"
"heckel.io/ntfy/v2/server"
"heckel.io/ntfy/v2/test"
"testing"
)

View File

@@ -6,8 +6,8 @@ import (
"errors"
"fmt"
"github.com/urfave/cli/v2"
"heckel.io/ntfy/user"
"heckel.io/ntfy/util"
"heckel.io/ntfy/v2/user"
"heckel.io/ntfy/v2/util"
"net/netip"
"time"
)

View File

@@ -4,8 +4,8 @@ import (
"fmt"
"github.com/stretchr/testify/require"
"github.com/urfave/cli/v2"
"heckel.io/ntfy/server"
"heckel.io/ntfy/test"
"heckel.io/ntfy/v2/server"
"heckel.io/ntfy/v2/test"
"regexp"
"testing"
)

View File

@@ -6,13 +6,13 @@ import (
"crypto/subtle"
"errors"
"fmt"
"heckel.io/ntfy/user"
"heckel.io/ntfy/v2/user"
"os"
"strings"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v2/altsrc"
"heckel.io/ntfy/util"
"heckel.io/ntfy/v2/util"
)
const (
@@ -198,7 +198,6 @@ func execUserAdd(c *cli.Context) error {
if err != nil {
return err
}
password = p
}
if err := manager.AddUser(username, password, role); err != nil {
@@ -343,6 +342,8 @@ func readPasswordAndConfirm(c *cli.Context) (string, error) {
password, err := util.ReadPassword(c.App.Reader)
if err != nil {
return "", err
} else if len(password) == 0 {
return "", errors.New("password cannot be empty")
}
fmt.Fprintf(c.App.ErrWriter, "\r%s\rconfirm: ", strings.Repeat(" ", 25))
confirm, err := util.ReadPassword(c.App.Reader)

View File

@@ -3,9 +3,9 @@ package cmd
import (
"github.com/stretchr/testify/require"
"github.com/urfave/cli/v2"
"heckel.io/ntfy/server"
"heckel.io/ntfy/test"
"heckel.io/ntfy/user"
"heckel.io/ntfy/v2/server"
"heckel.io/ntfy/v2/test"
"heckel.io/ntfy/v2/user"
"os"
"path/filepath"
"testing"

View File

@@ -5,7 +5,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/urfave/cli/v2"
"heckel.io/ntfy/server"
"heckel.io/ntfy/v2/server"
)
func TestCLI_WebPush_GenerateKeys(t *testing.T) {

View File

@@ -24,7 +24,7 @@ get a list of [command line options](#command-line-options).
The most basic settings are `base-url` (the external URL of the ntfy server), the HTTP/HTTPS listen address (`listen-http`
and `listen-https`), and socket path (`listen-unix`). All the other things are additional features.
Here are a few working sample configs:
Here are a few working sample configs using a `/etc/ntfy/server.yml` file:
=== "server.yml (HTTP-only, with cache + attachments)"
``` yaml
@@ -73,6 +73,58 @@ Here are a few working sample configs:
keepalive-interval: "45s"
```
Alternatively, you can also use command line arguments or environment variables to configure the server. Here's an example
using Docker Compose (i.e. `docker-compose.yml`):
=== "Docker Compose (w/ auth, cache, attachments)"
``` yaml
version: '3'
services:
ntfy:
image: binwiederhier/ntfy
restart: unless-stopped
environment:
NTFY_BASE_URL: http://ntfy.example.com
NTFY_CACHE_FILE: /var/lib/ntfy/cache.db
NTFY_AUTH_FILE: /var/lib/ntfy/auth.db
NTFY_AUTH_DEFAULT_ACCESS: deny-all
NTFY_BEHIND_PROXY: true
NTFY_ATTACHMENT_CACHE_DIR: /var/lib/ntfy/attachments
NTFY_ENABLE_LOGIN: true
volumes:
- ./:/var/lib/ntfy
ports:
- 80:80
command: serve
```
=== "Docker Compose (w/ auth, cache, web push, iOS)"
``` yaml
version: '3'
services:
ntfy:
image: binwiederhier/ntfy
restart: unless-stopped
environment:
NTFY_BASE_URL: http://ntfy.example.com
NTFY_CACHE_FILE: /var/lib/ntfy/cache.db
NTFY_AUTH_FILE: /var/lib/ntfy/auth.db
NTFY_AUTH_DEFAULT_ACCESS: deny-all
NTFY_BEHIND_PROXY: true
NTFY_ATTACHMENT_CACHE_DIR: /var/lib/ntfy/attachments
NTFY_ENABLE_LOGIN: true
NTFY_UPSTREAM_BASE_URL: https://ntfy.sh
NTFY_WEB_PUSH_PUBLIC_KEY: <public_key>
NTFY_WEB_PUSH_PRIVATE_KEY: <private_key>
NTFY_WEB_PUSH_FILE: /var/lib/ntfy/webpush.db
NTFY_WEB_PUSH_EMAIL_ADDRESS: <email>
volumes:
- ./:/var/lib/ntfy
ports:
- 8093:80
command: serve
```
## Message cache
If desired, ntfy can temporarily keep notifications in an in-memory or an on-disk cache. Caching messages for a short period
of time is important to allow [phones](subscribe/phone.md) and other devices with brittle Internet connections to be able to retrieve
@@ -1026,20 +1078,23 @@ By default, ntfy puts almost all rate limits on the message publisher, e.g. numb
size are all based on the visitor who publishes a message. **Subscriber-based rate limiting is a way to use the rate limits
of a topic's subscriber, instead of the limits of the publisher.**
If enabled, subscribers may opt to have published messages counted against their own rate limits, as opposed
to the publisher's rate limits. This is especially useful to increase the amount of messages that high-volume
publishers (e.g. Matrix/Mastodon servers) are allowed to send.
If subscriber-based rate limiting is enabled, **messages published on UnifiedPush topics** (topics starting with `up`, e.g. `up123456789012`)
will be counted towards the "rate visitor" of the topic. A "rate visitor" is the first subscriber to the topic.
Once enabled, a client may send a `Rate-Topics: <topic1>,<topic2>,...` header when subscribing to topics via
HTTP stream, or websockets, thereby registering itself as the "rate visitor", i.e. the visitor whose rate limits
to use when publishing on this topic. Note that setting the rate visitor requires **read-write permission** on the topic.
Once enabled, a client subscribing to UnifiedPush topics via HTTP stream, or websockets, will be automatically registered as
a "rate visitor", i.e. the visitor whose rate limits will be used when publishing on this topic. Note that setting the rate visitor
requires **read-write permission** on the topic.
UnifiedPush only: If this setting is enabled, publishing to UnifiedPush topics will lead to an `HTTP 507 Insufficient Storage`
If this setting is enabled, publishing to UnifiedPush topics will lead to an `HTTP 507 Insufficient Storage`
response if no "rate visitor" has been previously registered. This is to avoid burning the publisher's
`visitor-message-daily-limit`.
To enable subscriber-based rate limiting, set `visitor-subscriber-rate-limiting: true`.
!!! info
Due to a denial-of-service issue, support for the `Rate-Topics` header was removed entirely. This is unfortunate,
but subscriber-based rate limiting will still work for `up*` topics.
## Tuning for scale
If you're running ntfy for your home server, you probably don't need to worry about scale at all. In its default config,
if it's not behind a proxy, the ntfy server can keep about **as many connections as the open file limit allows**.

View File

@@ -1,4 +1,4 @@
# Deprecation notices
# Deprecations and breaking changes
This page is used to list deprecation notices for ntfy. Deprecated commands and options will be
**removed after 1-3 months** from the time they were deprecated. How long the feature is deprecated
before the behavior is changed depends on the severity of the change, and how prominent the feature is.

View File

@@ -3,9 +3,9 @@ ntfy lets you **send push notifications to your phone or desktop via scripts fro
or POST requests. I use it to notify myself when scripts fail, or long-running commands complete.
## Step 1: Get the app
<a href="https://play.google.com/store/apps/details?id=io.heckel.ntfy"><img src="../../static/img/badge-googleplay.png"></a>
<a href="https://f-droid.org/en/packages/io.heckel.ntfy/"><img src="../../static/img/badge-fdroid.png"></a>
<a href="https://apps.apple.com/us/app/ntfy/id1625396347"><img src="../../static/img/badge-appstore.png"></a>
<a href="https://play.google.com/store/apps/details?id=io.heckel.ntfy"><img src="static/img/badge-googleplay.png"></a>
<a href="https://f-droid.org/en/packages/io.heckel.ntfy/"><img src="static/img/badge-fdroid.png"></a>
<a href="https://apps.apple.com/us/app/ntfy/id1625396347"><img src="static/img/badge-appstore.png"></a>
To [receive notifications on your phone](subscribe/phone.md), install the app, either via Google Play or F-Droid.
Once installed, open it and subscribe to a topic of your choosing. Topics don't have to explicitly be created, so just

View File

@@ -30,37 +30,37 @@ deb/rpm packages.
=== "x86_64/amd64"
```bash
wget https://github.com/binwiederhier/ntfy/releases/download/v2.7.0/ntfy_2.7.0_linux_amd64.tar.gz
tar zxvf ntfy_2.7.0_linux_amd64.tar.gz
sudo cp -a ntfy_2.7.0_linux_amd64/ntfy /usr/local/bin/ntfy
sudo mkdir /etc/ntfy && sudo cp ntfy_2.7.0_linux_amd64/{client,server}/*.yml /etc/ntfy
wget https://github.com/binwiederhier/ntfy/releases/download/v2.8.0/ntfy_2.8.0_linux_amd64.tar.gz
tar zxvf ntfy_2.8.0_linux_amd64.tar.gz
sudo cp -a ntfy_2.8.0_linux_amd64/ntfy /usr/local/bin/ntfy
sudo mkdir /etc/ntfy && sudo cp ntfy_2.8.0_linux_amd64/{client,server}/*.yml /etc/ntfy
sudo ntfy serve
```
=== "armv6"
```bash
wget https://github.com/binwiederhier/ntfy/releases/download/v2.7.0/ntfy_2.7.0_linux_armv6.tar.gz
tar zxvf ntfy_2.7.0_linux_armv6.tar.gz
sudo cp -a ntfy_2.7.0_linux_armv6/ntfy /usr/bin/ntfy
sudo mkdir /etc/ntfy && sudo cp ntfy_2.7.0_linux_armv6/{client,server}/*.yml /etc/ntfy
wget https://github.com/binwiederhier/ntfy/releases/download/v2.8.0/ntfy_2.8.0_linux_armv6.tar.gz
tar zxvf ntfy_2.8.0_linux_armv6.tar.gz
sudo cp -a ntfy_2.8.0_linux_armv6/ntfy /usr/bin/ntfy
sudo mkdir /etc/ntfy && sudo cp ntfy_2.8.0_linux_armv6/{client,server}/*.yml /etc/ntfy
sudo ntfy serve
```
=== "armv7/armhf"
```bash
wget https://github.com/binwiederhier/ntfy/releases/download/v2.7.0/ntfy_2.7.0_linux_armv7.tar.gz
tar zxvf ntfy_2.7.0_linux_armv7.tar.gz
sudo cp -a ntfy_2.7.0_linux_armv7/ntfy /usr/bin/ntfy
sudo mkdir /etc/ntfy && sudo cp ntfy_2.7.0_linux_armv7/{client,server}/*.yml /etc/ntfy
wget https://github.com/binwiederhier/ntfy/releases/download/v2.8.0/ntfy_2.8.0_linux_armv7.tar.gz
tar zxvf ntfy_2.8.0_linux_armv7.tar.gz
sudo cp -a ntfy_2.8.0_linux_armv7/ntfy /usr/bin/ntfy
sudo mkdir /etc/ntfy && sudo cp ntfy_2.8.0_linux_armv7/{client,server}/*.yml /etc/ntfy
sudo ntfy serve
```
=== "arm64"
```bash
wget https://github.com/binwiederhier/ntfy/releases/download/v2.7.0/ntfy_2.7.0_linux_arm64.tar.gz
tar zxvf ntfy_2.7.0_linux_arm64.tar.gz
sudo cp -a ntfy_2.7.0_linux_arm64/ntfy /usr/bin/ntfy
sudo mkdir /etc/ntfy && sudo cp ntfy_2.7.0_linux_arm64/{client,server}/*.yml /etc/ntfy
wget https://github.com/binwiederhier/ntfy/releases/download/v2.8.0/ntfy_2.8.0_linux_arm64.tar.gz
tar zxvf ntfy_2.8.0_linux_arm64.tar.gz
sudo cp -a ntfy_2.8.0_linux_arm64/ntfy /usr/bin/ntfy
sudo mkdir /etc/ntfy && sudo cp ntfy_2.8.0_linux_arm64/{client,server}/*.yml /etc/ntfy
sudo ntfy serve
```
@@ -110,7 +110,7 @@ Manually installing the .deb file:
=== "x86_64/amd64"
```bash
wget https://github.com/binwiederhier/ntfy/releases/download/v2.7.0/ntfy_2.7.0_linux_amd64.deb
wget https://github.com/binwiederhier/ntfy/releases/download/v2.8.0/ntfy_2.8.0_linux_amd64.deb
sudo dpkg -i ntfy_*.deb
sudo systemctl enable ntfy
sudo systemctl start ntfy
@@ -118,7 +118,7 @@ Manually installing the .deb file:
=== "armv6"
```bash
wget https://github.com/binwiederhier/ntfy/releases/download/v2.7.0/ntfy_2.7.0_linux_armv6.deb
wget https://github.com/binwiederhier/ntfy/releases/download/v2.8.0/ntfy_2.8.0_linux_armv6.deb
sudo dpkg -i ntfy_*.deb
sudo systemctl enable ntfy
sudo systemctl start ntfy
@@ -126,7 +126,7 @@ Manually installing the .deb file:
=== "armv7/armhf"
```bash
wget https://github.com/binwiederhier/ntfy/releases/download/v2.7.0/ntfy_2.7.0_linux_armv7.deb
wget https://github.com/binwiederhier/ntfy/releases/download/v2.8.0/ntfy_2.8.0_linux_armv7.deb
sudo dpkg -i ntfy_*.deb
sudo systemctl enable ntfy
sudo systemctl start ntfy
@@ -134,7 +134,7 @@ Manually installing the .deb file:
=== "arm64"
```bash
wget https://github.com/binwiederhier/ntfy/releases/download/v2.7.0/ntfy_2.7.0_linux_arm64.deb
wget https://github.com/binwiederhier/ntfy/releases/download/v2.8.0/ntfy_2.8.0_linux_arm64.deb
sudo dpkg -i ntfy_*.deb
sudo systemctl enable ntfy
sudo systemctl start ntfy
@@ -144,28 +144,28 @@ Manually installing the .deb file:
=== "x86_64/amd64"
```bash
sudo rpm -ivh https://github.com/binwiederhier/ntfy/releases/download/v2.7.0/ntfy_2.7.0_linux_amd64.rpm
sudo rpm -ivh https://github.com/binwiederhier/ntfy/releases/download/v2.8.0/ntfy_2.8.0_linux_amd64.rpm
sudo systemctl enable ntfy
sudo systemctl start ntfy
```
=== "armv6"
```bash
sudo rpm -ivh https://github.com/binwiederhier/ntfy/releases/download/v2.7.0/ntfy_2.7.0_linux_armv6.rpm
sudo rpm -ivh https://github.com/binwiederhier/ntfy/releases/download/v2.8.0/ntfy_2.8.0_linux_armv6.rpm
sudo systemctl enable ntfy
sudo systemctl start ntfy
```
=== "armv7/armhf"
```bash
sudo rpm -ivh https://github.com/binwiederhier/ntfy/releases/download/v2.7.0/ntfy_2.7.0_linux_armv7.rpm
sudo rpm -ivh https://github.com/binwiederhier/ntfy/releases/download/v2.8.0/ntfy_2.8.0_linux_armv7.rpm
sudo systemctl enable ntfy
sudo systemctl start ntfy
```
=== "arm64"
```bash
sudo rpm -ivh https://github.com/binwiederhier/ntfy/releases/download/v2.7.0/ntfy_2.7.0_linux_arm64.rpm
sudo rpm -ivh https://github.com/binwiederhier/ntfy/releases/download/v2.8.0/ntfy_2.8.0_linux_arm64.rpm
sudo systemctl enable ntfy
sudo systemctl start ntfy
```
@@ -195,18 +195,18 @@ NixOS also supports [declarative setup of the ntfy server](https://search.nixos.
## macOS
The [ntfy CLI](subscribe/cli.md) (`ntfy publish` and `ntfy subscribe` only) is supported on macOS as well.
To install, please [download the tarball](https://github.com/binwiederhier/ntfy/releases/download/v2.7.0/ntfy_2.7.0_darwin_all.tar.gz),
To install, please [download the tarball](https://github.com/binwiederhier/ntfy/releases/download/v2.8.0/ntfy_2.8.0_darwin_all.tar.gz),
extract it and place it somewhere in your `PATH` (e.g. `/usr/local/bin/ntfy`).
If run as `root`, ntfy will look for its config at `/etc/ntfy/client.yml`. For all other users, it'll look for it at
`~/Library/Application Support/ntfy/client.yml` (sample included in the tarball).
```bash
curl -L https://github.com/binwiederhier/ntfy/releases/download/v2.7.0/ntfy_2.7.0_darwin_all.tar.gz > ntfy_2.7.0_darwin_all.tar.gz
tar zxvf ntfy_2.7.0_darwin_all.tar.gz
sudo cp -a ntfy_2.7.0_darwin_all/ntfy /usr/local/bin/ntfy
curl -L https://github.com/binwiederhier/ntfy/releases/download/v2.8.0/ntfy_2.8.0_darwin_all.tar.gz > ntfy_2.8.0_darwin_all.tar.gz
tar zxvf ntfy_2.8.0_darwin_all.tar.gz
sudo cp -a ntfy_2.8.0_darwin_all/ntfy /usr/local/bin/ntfy
mkdir ~/Library/Application\ Support/ntfy
cp ntfy_2.7.0_darwin_all/client/client.yml ~/Library/Application\ Support/ntfy/client.yml
cp ntfy_2.8.0_darwin_all/client/client.yml ~/Library/Application\ Support/ntfy/client.yml
ntfy --help
```
@@ -224,7 +224,7 @@ brew install ntfy
## Windows
The [ntfy CLI](subscribe/cli.md) (`ntfy publish` and `ntfy subscribe` only) is supported on Windows as well.
To install, please [download the latest ZIP](https://github.com/binwiederhier/ntfy/releases/download/v2.7.0/ntfy_2.7.0_windows_amd64.zip),
To install, please [download the latest ZIP](https://github.com/binwiederhier/ntfy/releases/download/v2.8.0/ntfy_2.8.0_windows_amd64.zip),
extract it and place the `ntfy.exe` binary somewhere in your `%Path%`.
The default path for the client config file is at `%AppData%\ntfy\client.yml` (not created automatically, sample in the ZIP file).

View File

@@ -135,7 +135,9 @@ I've added a ⭐ to projects or posts that have a significant following, or had
- [systemd-ntfy](https://hackage.haskell.org/package/systemd-ntfy) - monitor a set of systemd services an send a notification to ntfy.sh whenever their status changes
- [RouterOS Scripts](https://git.eworm.de/cgit/routeros-scripts/about/) - a collection of scripts for MikroTik RouterOS
- [ntfy-android-builder](https://github.com/TheBlusky/ntfy-android-builder) - Script for building ntfy-android with custom Firebase configuration (Docker/Shell)
- [jetspotter](https://github.com/vvanouytsel/jetspotter) - a tool to send notifications whenever specified types of aircraft are spotted near a specified location
- [jetspotter](https://github.com/vvanouytsel/jetspotter) - send notifications when planes are spotted near you (Go)
- [monitoring_ntfy](https://www.drupal.org/project/monitoring_ntfy) - Drupal monitoring Ntfy.sh integration (PHP/Drupal)
- [Notify](https://flathub.org/apps/com.ranfdev.Notify) - Native GTK4 client for ntfy (Rust)
## Blog + forum posts
@@ -151,6 +153,7 @@ I've added a ⭐ to projects or posts that have a significant following, or had
- [How to install Ntfy.sh on Portainer / Docker Compose](https://www.youtube.com/watch?v=utD9GNbAwyg) - youtube.com - 9/2023
- [ntfy - Push-Benachrichtigungen // Push Notifications](https://www.youtube.com/watch?v=LE3vRPPqZOU) - youtube.com - 9/2023
- [Podman Update Notifications via Ntfy](https://rair.dev/podman-upadte-notifications-ntfy/) - rair.dev - 9/2023
- [How to Send Alerts From Raspberry Pi Pico W to a Phone or Tablet](https://www.tomshardware.com/how-to/send-alerts-raspberry-pi-pico-w-to-mobile-device) - tomshardware.com - 8/2023
- [NetworkChunk - how did I NOT know about this?](https://www.youtube.com/watch?v=poDIT2ruQ9M) ⭐ - youtube.com - 8/2023
- [NTFY - Command-Line Notifications](https://academy.networkchuck.com/blog/ntfy/) - academy.networkchuck.com - 8/2023
- [Open Source Push Notifications! Get notified of any event you can imagine. Triggers abound!](https://www.youtube.com/watch?v=WJgwWXt79pE) ⭐ - youtube.com - 8/2023

View File

@@ -2,6 +2,36 @@
Binaries for all releases can be found on the GitHub releases pages for the [ntfy server](https://github.com/binwiederhier/ntfy/releases)
and the [ntfy Android app](https://github.com/binwiederhier/ntfy-android/releases).
## ntfy iOS app v1.3
Released Nov 26, 2023
This release (hopefully) fixes the issues with the iOS UI not updating properly when new notifications arrive, as well as notifications not being received (anymore) after previously working. Both issues have been annoying and known bugs for a long time, and I hope that they are finally fixed.
Many thanks to [@tcaputi](https://github.com/tcaputi) for fixing the issues, and to the anonymous donor for sponsoring these fixes.
**Bug fixes:**
* UI not updating properly ([#267](https://github.com/binwiederhier/ntfy/issues/267)/[#402](https://github.com/binwiederhier/ntfy/issues/402), thanks to [@tcaputi](https://github.com/tcaputi))
### ntfy server v2.8.0
Released November 19, 2023
This release brings a handful of random bug fixes: two unrelated access control list fixes, a fix around web app crashes for languages with underscores in the language code (e.g. `zh_Hant`, `zh_Hans`, `pt_BR`, ...), a workaround for the `Priority` header (often used in Cloudflare setups), and support among others support for HTML-only emails (finally), web app crash fixes
**Bug fixes + maintenance:**
* Support for HTML-only emails ([#690](https://github.com/binwiederhier/ntfy/issues/690)/[#693](https://github.com/binwiederhier/ntfy/pull/693), thanks to [@teastrainer](https://github.com/teastrainer) and [@CrazyWolf13](https://github.com/CrazyWolf13) for reporting)
* Fix ACL issue with topic patterns containing underscores ([#840](https://github.com/binwiederhier/ntfy/issues/840), thanks to [@Joe-0237](https://github.com/Joe-0237) for reporting)
* Fix ACL issue with order of read/write rules ([#914](https://github.com/binwiederhier/ntfy/issues/914)/[#917](https://github.com/binwiederhier/ntfy/pull/917), thanks to [@sandman7920](https://github.com/sandman7920))
* Re-add `tzdata` to Docker images for amd64 image ([#894](https://github.com/binwiederhier/ntfy/issues/894), [#307](https://github.com/binwiederhier/ntfy/pull/307))
* Add special logic to ignore `Priority` header if it resembles an RFC 9218 value ([#851](https://github.com/binwiederhier/ntfy/pull/851)/[#895](https://github.com/binwiederhier/ntfy/pull/895), thanks to [@gusdleon](https://github.com/gusdleon), see also [#351](https://github.com/binwiederhier/ntfy/issues/351), [#353](https://github.com/binwiederhier/ntfy/issues/353), [#461](https://github.com/binwiederhier/ntfy/issues/461))
* PWA: hide install prompt on macOS 14 Safari ([#899](https://github.com/binwiederhier/ntfy/pull/899), thanks to [@nihalgonsalves](https://github.com/nihalgonsalves))
* Fix web app crash in Edge for languages with underline in locale ([#922](https://github.com/binwiederhier/ntfy/pull/922)/[#912](https://github.com/binwiederhier/ntfy/issues/912)/[#852](https://github.com/binwiederhier/ntfy/issues/852), thanks to [@imkero](https://github.com/imkero))
**Additional languages:**
* Finnish (thanks to [@Seppo](https://hosted.weblate.org/user/Seppo/))
## ntfy server v2.7.0
Released August 17, 2023
@@ -1283,20 +1313,11 @@ and the [ntfy Android app](https://github.com/binwiederhier/ntfy-android/release
## Not released yet
### ntfy server v2.8.0 (UNRELEASED)
### ntfy server v2.9.0
**Bug fixes + maintenance:**
* Support for HTML-only emails ([#690](https://github.com/binwiederhier/ntfy/issues/690)/[#693](https://github.com/binwiederhier/ntfy/pull/693), thanks to [@teastrainer](https://github.com/teastrainer) and [@CrazyWolf13](https://github.com/CrazyWolf13) for reporting)
* Fix ACL issue with topic patterns containing underscores ([#840](https://github.com/binwiederhier/ntfy/issues/840), thanks to [@Joe-0237](https://github.com/Joe-0237) for reporting)
* Re-add `tzdata` to Docker images for amd64 image ([#894](https://github.com/binwiederhier/ntfy/issues/894), [#307](https://github.com/binwiederhier/ntfy/pull/307))
* Add special logic to ignore `Priority` header if it resembled a RFC 9218 value ([#851](https://github.com/binwiederhier/ntfy/pull/851)/[#895](https://github.com/binwiederhier/ntfy/pull/895), thanks to [@gusdleon](https://github.com/gusdleon), see also [#351](https://github.com/binwiederhier/ntfy/issues/351), [#353](https://github.com/binwiederhier/ntfy/issues/353), [#461](https://github.com/binwiederhier/ntfy/issues/461))
* PWA: hide install prompt on macOS 14 Safari ([#899](https://github.com/binwiederhier/ntfy/pull/899), thanks to [@nihalgonsalves](https://github.com/nihalgonsalves))
* Fix web app crash in Edge for languages with underline in locale ([#922](https://github.com/binwiederhier/ntfy/pull/922)/[#912](https://github.com/binwiederhier/ntfy/issues/912)/[#852](https://github.com/binwiederhier/ntfy/issues/852), thanks to [@imkero](https://github.com/imkero))
**Additional languages:**
* Finnish (thanks to [@Seppo](https://hosted.weblate.org/user/Seppo/)
* Remove `Rate-Topics` header due to DoS security issue if `visitor-subscriber-rate-limiting: true` ([#1048](https://github.com/binwiederhier/ntfy/issues/1048))
### ntfy Android app v1.16.1 (UNRELEASED)

72
go.mod
View File

@@ -1,27 +1,27 @@
module heckel.io/ntfy
module heckel.io/ntfy/v2
go 1.21
toolchain go1.21.3
require (
cloud.google.com/go/firestore v1.14.0 // indirect
cloud.google.com/go/storage v1.35.1 // indirect
cloud.google.com/go/firestore v1.15.0 // indirect
cloud.google.com/go/storage v1.39.0 // indirect
github.com/BurntSushi/toml v1.3.2 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect
github.com/emersion/go-smtp v0.18.0
github.com/gabriel-vasile/mimetype v1.4.3
github.com/gorilla/websocket v1.5.1
github.com/mattn/go-sqlite3 v1.14.18
github.com/mattn/go-sqlite3 v1.14.22
github.com/olebedev/when v1.0.0
github.com/stretchr/testify v1.8.4
github.com/urfave/cli/v2 v2.25.7
golang.org/x/crypto v0.15.0
golang.org/x/oauth2 v0.14.0 // indirect
golang.org/x/sync v0.5.0
golang.org/x/term v0.14.0
golang.org/x/time v0.4.0
google.golang.org/api v0.150.0
github.com/urfave/cli/v2 v2.27.1
golang.org/x/crypto v0.21.0
golang.org/x/oauth2 v0.18.0 // indirect
golang.org/x/sync v0.6.0
golang.org/x/term v0.18.0
golang.org/x/time v0.5.0
google.golang.org/api v0.168.0
gopkg.in/yaml.v2 v2.4.0
)
@@ -30,19 +30,19 @@ replace github.com/emersion/go-smtp => github.com/emersion/go-smtp v0.17.0 // Pi
require github.com/pkg/errors v0.9.1 // indirect
require (
firebase.google.com/go/v4 v4.12.1
firebase.google.com/go/v4 v4.13.0
github.com/SherClockHolmes/webpush-go v1.3.0
github.com/microcosm-cc/bluemonday v1.0.26
github.com/prometheus/client_golang v1.17.0
github.com/prometheus/client_golang v1.19.0
github.com/stripe/stripe-go/v74 v74.30.0
)
require (
cloud.google.com/go v0.110.10 // indirect
cloud.google.com/go/compute v1.23.3 // indirect
cloud.google.com/go v0.112.1 // indirect
cloud.google.com/go/compute v1.25.0 // indirect
cloud.google.com/go/compute/metadata v0.2.3 // indirect
cloud.google.com/go/iam v1.1.5 // indirect
cloud.google.com/go/longrunning v0.5.4 // indirect
cloud.google.com/go/iam v1.1.6 // indirect
cloud.google.com/go/longrunning v0.5.5 // indirect
github.com/AlekSi/pointer v1.2.0 // indirect
github.com/MicahParks/keyfunc v1.9.0 // indirect
github.com/aymerick/douceur v0.2.0 // indirect
@@ -50,35 +50,41 @@ require (
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/go-logr/logr v1.4.1 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/s2a-go v0.1.7 // indirect
github.com/google/uuid v1.4.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
github.com/googleapis/gax-go/v2 v2.12.0 // indirect
github.com/googleapis/gax-go/v2 v2.12.2 // indirect
github.com/gorilla/css v1.0.1 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_model v0.5.0 // indirect
github.com/prometheus/common v0.45.0 // indirect
github.com/prometheus/procfs v0.12.0 // indirect
github.com/prometheus/client_model v0.6.0 // indirect
github.com/prometheus/common v0.50.0 // indirect
github.com/prometheus/procfs v0.13.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/stretchr/objx v0.5.0 // indirect
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
github.com/xrash/smetrics v0.0.0-20231213231151-1d8dd44e695e // indirect
go.opencensus.io v0.24.0 // indirect
golang.org/x/net v0.18.0 // indirect
golang.org/x/sys v0.14.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect
go.opentelemetry.io/otel v1.24.0 // indirect
go.opentelemetry.io/otel/metric v1.24.0 // indirect
go.opentelemetry.io/otel/trace v1.24.0 // indirect
golang.org/x/net v0.22.0 // indirect
golang.org/x/sys v0.18.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
google.golang.org/appengine v1.6.8 // indirect
google.golang.org/appengine/v2 v2.0.5 // indirect
google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 // indirect
google.golang.org/grpc v1.59.0 // indirect
google.golang.org/protobuf v1.31.0 // indirect
google.golang.org/genproto v0.0.0-20240304212257-790db918fca8 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240304212257-790db918fca8 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240304212257-790db918fca8 // indirect
google.golang.org/grpc v1.62.1 // indirect
google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

170
go.sum
View File

@@ -1,22 +1,20 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.110.10 h1:LXy9GEO+timppncPIAZoOj3l58LIU9k+kn48AN7IO3Y=
cloud.google.com/go v0.110.10/go.mod h1:v1OoFqYxiBkUrruItNM3eT4lLByNjxmJSV/xDKJNnic=
cloud.google.com/go/compute v1.23.3 h1:6sVlXXBmbd7jNX0Ipq0trII3e4n1/MsADLK6a+aiVlk=
cloud.google.com/go/compute v1.23.3/go.mod h1:VCgBUoMnIVIR0CscqQiPJLAG25E3ZRZMzcFZeQ+h8CI=
cloud.google.com/go v0.112.1 h1:uJSeirPke5UNZHIb4SxfZklVSiWWVqW4oXlETwZziwM=
cloud.google.com/go v0.112.1/go.mod h1:+Vbu+Y1UU+I1rjmzeMOb/8RfkKJK2Gyxi1X6jJCZLo4=
cloud.google.com/go/compute v1.25.0 h1:H1/4SqSUhjPFE7L5ddzHOfY2bCAvjwNRZPNl6Ni5oYU=
cloud.google.com/go/compute v1.25.0/go.mod h1:GR7F0ZPZH8EhChlMo9FkLd7eUTwEymjqQagxzilIxIE=
cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
cloud.google.com/go/firestore v1.14.0 h1:8aLcKnMPoldYU3YHgu4t2exrKhLQkqaXAGqT0ljrFVw=
cloud.google.com/go/firestore v1.14.0/go.mod h1:96MVaHLsEhbvkBEdZgfN+AS/GIkco1LRpH9Xp9YZfzQ=
cloud.google.com/go/iam v1.1.5 h1:1jTsCu4bcsNsE4iiqNT5SHwrDRCfRmIaaaVFhRveTJI=
cloud.google.com/go/iam v1.1.5/go.mod h1:rB6P/Ic3mykPbFio+vo7403drjlgvoWfYpJhMXEbzv8=
cloud.google.com/go/longrunning v0.5.4 h1:w8xEcbZodnA2BbW6sVirkkoC+1gP8wS57EUUgGS0GVg=
cloud.google.com/go/longrunning v0.5.4/go.mod h1:zqNVncI0BOP8ST6XQD1+VcvuShMmq7+xFSzOL++V0dI=
cloud.google.com/go/storage v1.34.1 h1:H2Af2dU5J0PF7A5B+ECFIce+RqxVnrVilO+cu0TS3MI=
cloud.google.com/go/storage v1.34.1/go.mod h1:VN1ElqqvR9adg1k9xlkUJ55cMOP1/QjnNNuT5xQL6dY=
cloud.google.com/go/storage v1.35.1 h1:B59ahL//eDfx2IIKFBeT5Atm9wnNmj3+8xG/W4WB//w=
cloud.google.com/go/storage v1.35.1/go.mod h1:M6M/3V/D3KpzMTJyPOR/HU6n2Si5QdaXYEsng2xgOs8=
firebase.google.com/go/v4 v4.12.1 h1:tDNvobifGsx/1HSFLnM0fmNfx/CDZSgsTO2KhZtgpcs=
firebase.google.com/go/v4 v4.12.1/go.mod h1:60c36dWLK4+j05Vw5XMllek3b3PCynU3BfI46OSwsUE=
cloud.google.com/go/firestore v1.15.0 h1:/k8ppuWOtNuDHt2tsRV42yI21uaGnKDEQnRFeBpbFF8=
cloud.google.com/go/firestore v1.15.0/go.mod h1:GWOxFXcv8GZUtYpWHw/w6IuYNux/BtmeVTMmjrm4yhk=
cloud.google.com/go/iam v1.1.6 h1:bEa06k05IO4f4uJonbB5iAgKTPpABy1ayxaIZV/GHVc=
cloud.google.com/go/iam v1.1.6/go.mod h1:O0zxdPeGBoFdWW3HWmBxJsk0pfvNM/p/qa82rWOGTwI=
cloud.google.com/go/longrunning v0.5.5 h1:GOE6pZFdSrTb4KAiKnXsJBtlE6mEyaW44oKyMILWnOg=
cloud.google.com/go/longrunning v0.5.5/go.mod h1:WV2LAxD8/rg5Z1cNW6FJ/ZpX4E4VnDnoTk0yawPBB7s=
cloud.google.com/go/storage v1.39.0 h1:brbjUa4hbDHhpQf48tjqMaXEV+f1OGoaTmQau9tmCsA=
cloud.google.com/go/storage v1.39.0/go.mod h1:OAEj/WZwUYjA3YHQ10/YcN9ttGuEpLwvaoyBXIPikEk=
firebase.google.com/go/v4 v4.13.0 h1:meFz9nvDNh/FDyrEykoAzSfComcQbmnQSjoHrePRqeI=
firebase.google.com/go/v4 v4.13.0/go.mod h1:e1/gaR6EnbQfsmTnAMx1hnz+ninJIrrr/RAh59Tpfn8=
github.com/AlekSi/pointer v1.2.0 h1:glcy/gc4h8HnG2Z3ZECSzZ1IX1x2JxRVuDzaJwQE0+w=
github.com/AlekSi/pointer v1.2.0/go.mod h1:gZGfd3dpW4vEc/UlyfKKi1roIqcCgwOIvb0tSNSBle0=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
@@ -42,8 +40,6 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
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/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ=
github.com/emersion/go-sasl v0.0.0-20220912192320-0145f2c60ead h1:fI1Jck0vUrXT8bnphprS1EoVRe2Q5CKCX8iDlpqjQ/Y=
github.com/emersion/go-sasl v0.0.0-20220912192320-0145f2c60ead/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ=
github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43 h1:hH4PQfOndHDlpzYfLAAfl63E8Le6F2+EL/cdhlkyRJY=
github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ=
github.com/emersion/go-smtp v0.17.0 h1:tq90evlrcyqRfE6DSXaWVH54oX6OuZOQECEmhWBMEtI=
@@ -52,8 +48,15 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF
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/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
@@ -75,8 +78,8 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD
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/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
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=
@@ -85,30 +88,28 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/martian/v3 v3.3.2 h1:IqNFLAmvJOgVlpdEBiQbDc2EwKW77amAycfTuWKdfvw=
github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk=
github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o=
github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs=
github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0=
github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas=
github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU=
github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
github.com/googleapis/gax-go/v2 v2.12.2 h1:mhN09QQW1jEWeMF74zGR81R30z4VJzjZsfkUhuHF+DA=
github.com/googleapis/gax-go/v2 v2.12.2/go.mod h1:61M8vcyyXR2kqKFxKrfA22jaA8JGF7Dc8App1U3H6jc=
github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=
github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mattn/go-sqlite3 v1.14.18 h1:JL0eqdCOq6DJVNPSvArO/bIV9/P7fbGrV00LZHc+5aI=
github.com/mattn/go-sqlite3 v1.14.18/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg=
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k=
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/microcosm-cc/bluemonday v1.0.26 h1:xbqSvqzQMeEHCqMi64VAs4d8uy6Mequs3rQ0k/Khz58=
github.com/microcosm-cc/bluemonday v1.0.26/go.mod h1:JyzOCs9gkyQyjs+6h10UEVSe02CGwkhd72Xdqh78TWs=
github.com/olebedev/when v1.0.0 h1:T2DZCj8HxUhOVxcqaLOmzuTr+iZLtMHsZEim7mjIA2w=
@@ -117,16 +118,17 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q=
github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY=
github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU=
github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM=
github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY=
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
github.com/prometheus/client_model v0.6.0 h1:k1v3CzpSRUTrKMppY35TLwPvxHqBu0bYgxZzqGIgaos=
github.com/prometheus/client_model v0.6.0/go.mod h1:NTQHnmxFpouOD0DpvP4XujX3CdOAGQPoaGhyTchlyt8=
github.com/prometheus/common v0.50.0 h1:YSZE6aa9+luNa2da6/Tik0q0A5AbR+U003TItK57CPQ=
github.com/prometheus/common v0.50.0/go.mod h1:wHFBCEVWVmHMUpg7pYcOm2QUR/ocQdYSJVQJKnHc3xQ=
github.com/prometheus/procfs v0.13.0 h1:GqzLlQyfsPbaEHaQkO7tbDlriv/4o5Hudv6OXHGKX7o=
github.com/prometheus/procfs v0.13.0/go.mod h1:cd4PFCR54QLnGKPaKGA6l+cfuNXtht43ZKY6tow0Y1g=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@@ -136,27 +138,36 @@ github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpE
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stripe/stripe-go/v74 v74.30.0 h1:0Kf0KkeFnY7iRhOwvTerX0Ia1BRw+eV1CVJ51mGYAUY=
github.com/stripe/stripe-go/v74 v74.30.0/go.mod h1:f9L6LvaXa35ja7eyvP6GQswoaIPaBRvGAimAO+udbBw=
github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs=
github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
github.com/urfave/cli/v2 v2.27.1 h1:8xSQ6szndafKVRmfyeUMxkNUJQMjL1F2zmsZ+qHpfho=
github.com/urfave/cli/v2 v2.27.1/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ=
github.com/xrash/smetrics v0.0.0-20231213231151-1d8dd44e695e h1:+SOyEddqYF09QP7vr7CgJ1eti3pY9Fn3LHO1M1r/0sI=
github.com/xrash/smetrics v0.0.0-20231213231151-1d8dd44e695e/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 h1:4Pp6oUg3+e/6M4C0A/3kJ2VYa++dsWVTtGgLVj5xtHg=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0/go.mod h1:Mjt1i1INqiaoZOMGR1RIUJN+i3ChKoFRqzrRQhlkbs0=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw=
go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo=
go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo=
go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI=
go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco=
go.opentelemetry.io/otel/sdk v1.22.0 h1:6coWHw9xw7EfClIC/+O31R8IY3/+EiRFHevmHafB2Gw=
go.opentelemetry.io/otel/sdk v1.22.0/go.mod h1:iu7luyVGYovrRpe2fmj3CVKouQNdTOkxtLzPvPz1DOc=
go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI=
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA=
golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g=
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
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=
@@ -175,22 +186,18 @@ golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg=
golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ=
golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.13.0 h1:jDDenyj+WgFtmV3zYVoi8aE2BwtXFLWOA67ZfNWftiY=
golang.org/x/oauth2 v0.13.0/go.mod h1:/JMhi4ZRXAf4HG9LiNmxvk+45+96RUlVThiH8FzNBn0=
golang.org/x/oauth2 v0.14.0 h1:P0Vrf/2538nmC0H+pEQ3MNFRRnVR7RlqyVw+bvm26z0=
golang.org/x/oauth2 v0.14.0/go.mod h1:lAtNWgaWfL4cm7j2OV8TxGi9Qb7ECORx8DktCY74OwM=
golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI=
golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8=
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=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -202,16 +209,14 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek=
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
golang.org/x/term v0.14.0 h1:LGK9IlZ8T9jvdy6cTdfKUCltatMFOehAQo9SRC46UQ8=
golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww=
golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8=
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
@@ -221,8 +226,8 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.4.0 h1:Z81tqI5ddIoXDPvVQ7/7CC9TnLM7ubaFG2qXYd5BbYY=
golang.org/x/time v0.4.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
@@ -235,10 +240,8 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU=
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
google.golang.org/api v0.149.0 h1:b2CqT6kG+zqJIVKRQ3ELJVLN1PwHZ6DJ3dW8yl82rgY=
google.golang.org/api v0.149.0/go.mod h1:Mwn1B7JTXrzXtnvmzQE2BD6bYZQ8DShKZDZbeN9I7qI=
google.golang.org/api v0.150.0 h1:Z9k22qD289SZ8gCJrk4DrWXkNjtfvKAUo/l1ma8eBYE=
google.golang.org/api v0.150.0/go.mod h1:ccy+MJ6nrYFgE3WgRx/AMXOxOmU8Q4hSa+jjibzhxcg=
google.golang.org/api v0.168.0 h1:MBRe+Ki4mMN93jhDDbpuRLjRddooArz4FeSObvUMmjY=
google.golang.org/api v0.168.0/go.mod h1:gpNOiMA2tZ4mf5R9Iwf4rK/Dcz0fbdIgWYWVoxmsyLg=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
@@ -248,25 +251,19 @@ google.golang.org/appengine/v2 v2.0.5/go.mod h1:WoEXGoXNfa0mLvaH5sV3ZSGXwVmy8yf7
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20231030173426-d783a09b4405 h1:I6WNifs6pF9tNdSob2W24JtyxIYjzFB9qDlpUC76q+U=
google.golang.org/genproto v0.0.0-20231030173426-d783a09b4405/go.mod h1:3WDQMjmJk36UQhjQ89emUzb1mdaHcPeeAh4SCBKznB4=
google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 h1:wpZ8pe2x1Q3f2KyT5f8oP/fa9rHAKgFPr/HZdNuS+PQ=
google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:J7XzRzVy1+IPwWHZUzoD0IccYZIrXILAQpc+Qy9CMhY=
google.golang.org/genproto/googleapis/api v0.0.0-20231030173426-d783a09b4405 h1:HJMDndgxest5n2y77fnErkM62iUsptE/H8p0dC2Huo4=
google.golang.org/genproto/googleapis/api v0.0.0-20231030173426-d783a09b4405/go.mod h1:oT32Z4o8Zv2xPQTg0pbVaPr0MPOH6f14RgXt7zfIpwg=
google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 h1:JpwMPBpFN3uKhdaekDpiNlImDdkUAyiJ6ez/uxGaUSo=
google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:0xJLfVdJqpAPl8tDg1ujOCGzx6LFLttXT5NhllGOXY4=
google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405 h1:AB/lmRny7e2pLhFEYIbl5qkDAUt2h0ZRO4wGPhZf+ik=
google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405/go.mod h1:67X1fPuzjcrkymZzZV1vvkFeTn2Rvc6lYF9MYFGCcwE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 h1:Jyp0Hsi0bmHXG6k9eATXoYtjd6e2UzZ1SCn/wIupY14=
google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:oQ5rr10WTTMvP4A36n8JpR1OrO1BEiV4f78CneXZxkA=
google.golang.org/genproto v0.0.0-20240304212257-790db918fca8 h1:Fe8QycXyEd9mJgnwB9kmw00WgB43eQ/xYO5C6gceybQ=
google.golang.org/genproto v0.0.0-20240304212257-790db918fca8/go.mod h1:yA7a1bW1kwl459Ol0m0lV4hLTfrL/7Bkk4Mj2Ir1mWI=
google.golang.org/genproto/googleapis/api v0.0.0-20240304212257-790db918fca8 h1:8eadJkXbwDEMNwcB5O0s5Y5eCfyuCLdvaiOIaGTrWmQ=
google.golang.org/genproto/googleapis/api v0.0.0-20240304212257-790db918fca8/go.mod h1:O1cOfN1Cy6QEYr7VxtjOyP5AdAuR0aJ/MYZaaof623Y=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240304212257-790db918fca8 h1:IR+hp6ypxjH24bkMfEJ0yHR21+gwPWdV+/IBrPQyn3k=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240304212257-790db918fca8/go.mod h1:UCOku4NytXMJuLQE5VuqA5lX3PcHCBo8pxNyvkf4xBs=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk=
google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98=
google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk=
google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE=
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=
@@ -279,10 +276,11 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -3,7 +3,7 @@ package log
import (
"encoding/json"
"fmt"
"heckel.io/ntfy/util"
"heckel.io/ntfy/v2/util"
"log"
"os"
"sort"

View File

@@ -3,7 +3,7 @@ package main
import (
"fmt"
"github.com/urfave/cli/v2"
"heckel.io/ntfy/cmd"
"heckel.io/ntfy/v2/cmd"
"os"
"runtime"
)

View File

@@ -64,6 +64,7 @@ markdown_extensions:
- attr_list
- md_in_html
- pymdownx.emoji:
emoji_index: !!python/name:material.extensions.emoji.twemoji
emoji_generator: !!python/name:materialx.emoji.to_svg
plugins:
@@ -75,28 +76,28 @@ plugins:
on_post_build: "docs.hooks:copy_fonts"
nav:
- "Getting started": index.md
- "Publishing":
- "Sending messages": publish.md
- "Subscribing":
- "From your phone": subscribe/phone.md
- "From the Web app": subscribe/web.md
- "From the Desktop": subscribe/pwa.md
- "From the CLI": subscribe/cli.md
- "Using the API": subscribe/api.md
- "Self-hosting":
- "Installation": install.md
- "Configuration": config.md
- "Other things":
- "FAQs": faq.md
- "Examples": examples.md
- "Integrations + projects": integrations.md
- "Release notes": releases.md
- "Emojis 🥳 🎉": emojis.md
- "Troubleshooting": troubleshooting.md
- "Known issues": known-issues.md
- "Deprecation notices": deprecations.md
- "Development": develop.md
- "Privacy policy": privacy.md
- "Getting started": index.md
- "Publishing":
- "Sending messages": publish.md
- "Subscribing":
- "From your phone": subscribe/phone.md
- "From the Web app": subscribe/web.md
- "From the Desktop": subscribe/pwa.md
- "From the CLI": subscribe/cli.md
- "Using the API": subscribe/api.md
- "Self-hosting":
- "Installation": install.md
- "Configuration": config.md
- "Other things":
- "FAQs": faq.md
- "Examples": examples.md
- "Integrations + projects": integrations.md
- "Release notes": releases.md
- "Emojis 🥳 🎉": emojis.md
- "Troubleshooting": troubleshooting.md
- "Known issues": known-issues.md
- "Deprecation notices": deprecations.md
- "Development": develop.md
- "Privacy policy": privacy.md

View File

@@ -4,7 +4,7 @@ import (
"encoding/json"
"errors"
"fmt"
"heckel.io/ntfy/util"
"heckel.io/ntfy/v2/util"
"regexp"
"strings"
"unicode/utf8"

View File

@@ -5,7 +5,7 @@ import (
"net/netip"
"time"
"heckel.io/ntfy/user"
"heckel.io/ntfy/v2/user"
)
// Defines default config settings (excluding limits, see below)

View File

@@ -2,7 +2,7 @@ package server_test
import (
"github.com/stretchr/testify/assert"
"heckel.io/ntfy/server"
"heckel.io/ntfy/v2/server"
"testing"
)

View File

@@ -3,7 +3,7 @@ package server
import (
"encoding/json"
"fmt"
"heckel.io/ntfy/log"
"heckel.io/ntfy/v2/log"
"net/http"
)

View File

@@ -3,8 +3,8 @@ package server
import (
"errors"
"fmt"
"heckel.io/ntfy/log"
"heckel.io/ntfy/util"
"heckel.io/ntfy/v2/log"
"heckel.io/ntfy/v2/util"
"io"
"os"
"path/filepath"

View File

@@ -4,7 +4,7 @@ import (
"bytes"
"fmt"
"github.com/stretchr/testify/require"
"heckel.io/ntfy/util"
"heckel.io/ntfy/v2/util"
"os"
"strings"
"testing"

View File

@@ -4,8 +4,8 @@ import (
"fmt"
"github.com/emersion/go-smtp"
"github.com/gorilla/websocket"
"heckel.io/ntfy/log"
"heckel.io/ntfy/util"
"heckel.io/ntfy/v2/log"
"heckel.io/ntfy/v2/util"
"net/http"
"strings"
"unicode/utf8"

View File

@@ -10,8 +10,8 @@ import (
"time"
_ "github.com/mattn/go-sqlite3" // SQLite driver
"heckel.io/ntfy/log"
"heckel.io/ntfy/util"
"heckel.io/ntfy/v2/log"
"heckel.io/ntfy/v2/util"
)
var (

View File

@@ -30,9 +30,9 @@ import (
"github.com/gorilla/websocket"
"github.com/prometheus/client_golang/prometheus/promhttp"
"golang.org/x/sync/errgroup"
"heckel.io/ntfy/log"
"heckel.io/ntfy/user"
"heckel.io/ntfy/util"
"heckel.io/ntfy/v2/log"
"heckel.io/ntfy/v2/user"
"heckel.io/ntfy/v2/util"
)
// Server is the main server, providing the UI and API for ntfy
@@ -743,8 +743,8 @@ func (s *Server) handlePublishInternal(r *http.Request, v *visitor) (*message, e
return nil, e.With(t)
}
if unifiedpush && s.config.VisitorSubscriberRateLimiting && t.RateVisitor() == nil {
// UnifiedPush clients must subscribe before publishing to allow proper subscriber-based rate limiting (see
// Rate-Topics header). The 5xx response is because some app servers (in particular Mastodon) will remove
// UnifiedPush clients must subscribe before publishing to allow proper subscriber-based rate limiting.
// The 5xx response is because some app servers (in particular Mastodon) will remove
// the subscription as invalid if any 400-499 code (except 429/408) is returned.
// See https://github.com/mastodon/mastodon/blob/730bb3e211a84a2f30e3e2bbeae3f77149824a68/app/workers/web/push_notification_worker.rb#L35-L46
return nil, errHTTPInsufficientStorageUnifiedPush.With(t)
@@ -1182,7 +1182,7 @@ func (s *Server) handleSubscribeHTTP(w http.ResponseWriter, r *http.Request, v *
if err != nil {
return err
}
poll, since, scheduled, filters, rateTopics, err := parseSubscribeParams(r)
poll, since, scheduled, filters, err := parseSubscribeParams(r)
if err != nil {
return err
}
@@ -1212,7 +1212,7 @@ func (s *Server) handleSubscribeHTTP(w http.ResponseWriter, r *http.Request, v *
}
return nil
}
if err := s.maybeSetRateVisitors(r, v, topics, rateTopics); err != nil {
if err := s.maybeSetRateVisitors(r, v, topics); err != nil {
return err
}
w.Header().Set("Access-Control-Allow-Origin", s.config.AccessControlAllowOrigin) // CORS, allow cross-origin requests
@@ -1278,7 +1278,7 @@ func (s *Server) handleSubscribeWS(w http.ResponseWriter, r *http.Request, v *vi
if err != nil {
return err
}
poll, since, scheduled, filters, rateTopics, err := parseSubscribeParams(r)
poll, since, scheduled, filters, err := parseSubscribeParams(r)
if err != nil {
return err
}
@@ -1364,7 +1364,7 @@ func (s *Server) handleSubscribeWS(w http.ResponseWriter, r *http.Request, v *vi
}
return conn.WriteJSON(msg)
}
if err := s.maybeSetRateVisitors(r, v, topics, rateTopics); err != nil {
if err := s.maybeSetRateVisitors(r, v, topics); err != nil {
return err
}
w.Header().Set("Access-Control-Allow-Origin", s.config.AccessControlAllowOrigin) // CORS, allow cross-origin requests
@@ -1397,7 +1397,7 @@ func (s *Server) handleSubscribeWS(w http.ResponseWriter, r *http.Request, v *vi
return err
}
func parseSubscribeParams(r *http.Request) (poll bool, since sinceMarker, scheduled bool, filters *queryFilter, rateTopics []string, err error) {
func parseSubscribeParams(r *http.Request) (poll bool, since sinceMarker, scheduled bool, filters *queryFilter, err error) {
poll = readBoolParam(r, false, "x-poll", "poll", "po")
scheduled = readBoolParam(r, false, "x-scheduled", "scheduled", "sched")
since, err = parseSince(r, poll)
@@ -1408,7 +1408,6 @@ func parseSubscribeParams(r *http.Request) (poll bool, since sinceMarker, schedu
if err != nil {
return
}
rateTopics = readCommaSeparatedParam(r, "x-rate-topics", "rate-topics")
return
}
@@ -1420,9 +1419,8 @@ func parseSubscribeParams(r *http.Request) (poll bool, since sinceMarker, schedu
// - or the topic is reserved, and v.user is the owner
// - or the topic is not reserved, and v.user has write access
//
// Note: This TEMPORARILY also registers all topics starting with "up" (= UnifiedPush). This is to ease the transition
// until the Android app will send the "Rate-Topics" header.
func (s *Server) maybeSetRateVisitors(r *http.Request, v *visitor, topics []*topic, rateTopics []string) error {
// This only applies to UnifiedPush topics ("up...").
func (s *Server) maybeSetRateVisitors(r *http.Request, v *visitor, topics []*topic) error {
// Bail out if not enabled
if !s.config.VisitorSubscriberRateLimiting {
return nil
@@ -1431,7 +1429,7 @@ func (s *Server) maybeSetRateVisitors(r *http.Request, v *visitor, topics []*top
// Make a list of topics that we'll actually set the RateVisitor on
eligibleRateTopics := make([]*topic, 0)
for _, t := range topics {
if (strings.HasPrefix(t.ID, unifiedPushTopicPrefix) && len(t.ID) == unifiedPushTopicLength) || util.Contains(rateTopics, t.ID) {
if strings.HasPrefix(t.ID, unifiedPushTopicPrefix) && len(t.ID) == unifiedPushTopicLength {
eligibleRateTopics = append(eligibleRateTopics, t)
}
}

View File

@@ -277,15 +277,14 @@
# Rate limiting: Enable subscriber-based rate limiting (mostly used for UnifiedPush)
#
# If enabled, subscribers may opt to have published messages counted against their own rate limits, as opposed
# to the publisher's rate limits. This is especially useful to increase the amount of messages that high-volume
# publishers (e.g. Matrix/Mastodon servers) are allowed to send.
# If subscriber-based rate limiting is enabled, messages published on UnifiedPush topics** (topics starting with "up")
# will be counted towards the "rate visitor" of the topic. A "rate visitor" is the first subscriber to the topic.
#
# Once enabled, a client may send a "Rate-Topics: <topic1>,<topic2>,..." header when subscribing to topics via
# HTTP stream, or websockets, thereby registering itself as the "rate visitor", i.e. the visitor whose rate limits
# to use when publishing on this topic. Note: Setting the rate visitor requires READ-WRITE permission on the topic.
# Once enabled, a client subscribing to UnifiedPush topics via HTTP stream, or websockets, will be automatically registered as
# a "rate visitor", i.e. the visitor whose rate limits will be used when publishing on this topic. Note that setting the rate visitor
# requires **read-write permission** on the topic.
#
# UnifiedPush only: If this setting is enabled, publishing to UnifiedPush topics will lead to a HTTP 507 response if
# If this setting is enabled, publishing to UnifiedPush topics will lead to a HTTP 507 response if
# no "rate visitor" has been previously registered. This is to avoid burning the publisher's "visitor-message-daily-limit".
#
# visitor-subscriber-rate-limiting: false

View File

@@ -2,9 +2,9 @@ package server
import (
"encoding/json"
"heckel.io/ntfy/log"
"heckel.io/ntfy/user"
"heckel.io/ntfy/util"
"heckel.io/ntfy/v2/log"
"heckel.io/ntfy/v2/user"
"heckel.io/ntfy/v2/util"
"net/http"
"net/netip"
"strings"

View File

@@ -3,9 +3,9 @@ package server
import (
"fmt"
"github.com/stretchr/testify/require"
"heckel.io/ntfy/log"
"heckel.io/ntfy/user"
"heckel.io/ntfy/util"
"heckel.io/ntfy/v2/log"
"heckel.io/ntfy/v2/user"
"heckel.io/ntfy/v2/util"
"io"
"net/netip"
"path/filepath"

View File

@@ -1,7 +1,7 @@
package server
import (
"heckel.io/ntfy/user"
"heckel.io/ntfy/v2/user"
"net/http"
)

View File

@@ -2,8 +2,8 @@ package server
import (
"github.com/stretchr/testify/require"
"heckel.io/ntfy/user"
"heckel.io/ntfy/util"
"heckel.io/ntfy/v2/user"
"heckel.io/ntfy/v2/util"
"sync/atomic"
"testing"
"time"

View File

@@ -8,8 +8,8 @@ import (
"firebase.google.com/go/v4/messaging"
"fmt"
"google.golang.org/api/option"
"heckel.io/ntfy/user"
"heckel.io/ntfy/util"
"heckel.io/ntfy/v2/user"
"heckel.io/ntfy/v2/util"
"strings"
)

View File

@@ -4,7 +4,7 @@ import (
"encoding/json"
"errors"
"fmt"
"heckel.io/ntfy/user"
"heckel.io/ntfy/v2/user"
"net/netip"
"strings"
"sync"

View File

@@ -1,8 +1,8 @@
package server
import (
"heckel.io/ntfy/log"
"heckel.io/ntfy/util"
"heckel.io/ntfy/v2/log"
"heckel.io/ntfy/v2/util"
"strings"
)

View File

@@ -4,7 +4,7 @@ import (
"bytes"
"encoding/json"
"fmt"
"heckel.io/ntfy/util"
"heckel.io/ntfy/v2/util"
"io"
"net/http"
"strings"

View File

@@ -3,7 +3,7 @@ package server
import (
"net/http"
"heckel.io/ntfy/util"
"heckel.io/ntfy/v2/util"
)
type contextKey int

View File

@@ -11,9 +11,9 @@ import (
"github.com/stripe/stripe-go/v74/price"
"github.com/stripe/stripe-go/v74/subscription"
"github.com/stripe/stripe-go/v74/webhook"
"heckel.io/ntfy/log"
"heckel.io/ntfy/user"
"heckel.io/ntfy/util"
"heckel.io/ntfy/v2/log"
"heckel.io/ntfy/v2/user"
"heckel.io/ntfy/v2/util"
"io"
"net/http"
"net/netip"

View File

@@ -6,8 +6,8 @@ import (
"github.com/stretchr/testify/require"
"github.com/stripe/stripe-go/v74"
"golang.org/x/time/rate"
"heckel.io/ntfy/user"
"heckel.io/ntfy/util"
"heckel.io/ntfy/v2/user"
"heckel.io/ntfy/v2/util"
"io"
"net/netip"
"path/filepath"

View File

@@ -3,13 +3,13 @@ package server
import (
"bufio"
"context"
"crypto/rand"
"encoding/base64"
"encoding/json"
"fmt"
"golang.org/x/crypto/bcrypt"
"heckel.io/ntfy/user"
"heckel.io/ntfy/v2/user"
"io"
"math/rand"
"net/http"
"net/http/httptest"
"net/netip"
@@ -24,8 +24,8 @@ import (
"github.com/SherClockHolmes/webpush-go"
"github.com/stretchr/testify/require"
"heckel.io/ntfy/log"
"heckel.io/ntfy/util"
"heckel.io/ntfy/v2/log"
"heckel.io/ntfy/v2/util"
)
func TestMain(m *testing.M) {
@@ -1346,9 +1346,7 @@ func TestServer_PublishUnifiedPushBinary_AndPoll(t *testing.T) {
s := newTestServer(t, newTestConfig(t))
// Register a UnifiedPush subscriber
response := request(t, s, "GET", "/up123456789012/json?poll=1", "", map[string]string{
"Rate-Topics": "up123456789012",
})
response := request(t, s, "GET", "/up123456789012/json?poll=1", "", nil)
require.Equal(t, 200, response.Code)
// Publish message to topic
@@ -1379,9 +1377,7 @@ func TestServer_PublishUnifiedPushBinary_Truncated(t *testing.T) {
s := newTestServer(t, newTestConfig(t))
// Register a UnifiedPush subscriber
response := request(t, s, "GET", "/mytopic/json?poll=1", "", map[string]string{
"Rate-Topics": "mytopic",
})
response := request(t, s, "GET", "/mytopic/json?poll=1", "", nil)
require.Equal(t, 200, response.Code)
// Publish message to topic
@@ -1400,9 +1396,7 @@ func TestServer_PublishUnifiedPushText(t *testing.T) {
s := newTestServer(t, newTestConfig(t))
// Register a UnifiedPush subscriber
response := request(t, s, "GET", "/mytopic/json?poll=1", "", map[string]string{
"Rate-Topics": "mytopic",
})
response := request(t, s, "GET", "/mytopic/json?poll=1", "", nil)
require.Equal(t, 200, response.Code)
// Publish UnifiedPush text message
@@ -1434,9 +1428,7 @@ func TestServer_MatrixGateway_Discovery_Failure_Unconfigured(t *testing.T) {
func TestServer_MatrixGateway_Push_Success(t *testing.T) {
s := newTestServer(t, newTestConfig(t))
response := request(t, s, "GET", "/mytopic/json?poll=1", "", map[string]string{
"Rate-Topics": "mytopic", // Register first!
})
response := request(t, s, "GET", "/mytopic/json?poll=1", "", nil)
require.Equal(t, 200, response.Code)
notification := `{"notification":{"devices":[{"pushkey":"http://127.0.0.1:12345/mytopic?up=1"}]}}`
@@ -2266,16 +2258,14 @@ func TestServer_SubscriberRateLimiting_Success(t *testing.T) {
c.VisitorSubscriberRateLimiting = true
s := newTestServer(t, c)
// "Register" visitor 1.2.3.4 to topic "subscriber1topic" as a rate limit visitor
// "Register" visitor 1.2.3.4 to topic "upAAAAAAAAAAAA" as a rate limit visitor
subscriber1Fn := func(r *http.Request) {
r.RemoteAddr = "1.2.3.4"
}
rr := request(t, s, "GET", "/subscriber1topic/json?poll=1", "", map[string]string{
"Rate-Topics": "subscriber1topic",
}, subscriber1Fn)
rr := request(t, s, "GET", "/upAAAAAAAAAAAA/json?poll=1", "", nil, subscriber1Fn)
require.Equal(t, 200, rr.Code)
require.Equal(t, "", rr.Body.String())
require.Equal(t, "1.2.3.4", s.topics["subscriber1topic"].rateVisitor.ip.String())
require.Equal(t, "1.2.3.4", s.topics["upAAAAAAAAAAAA"].rateVisitor.ip.String())
// "Register" visitor 8.7.7.1 to topic "up012345678912" as a rate limit visitor (implicitly via topic name)
subscriber2Fn := func(r *http.Request) {
@@ -2289,10 +2279,10 @@ func TestServer_SubscriberRateLimiting_Success(t *testing.T) {
// Publish 2 messages to "subscriber1topic" as visitor 9.9.9.9. It'd be 3 normally, but the
// GET request before is also counted towards the request limiter.
for i := 0; i < 2; i++ {
rr := request(t, s, "PUT", "/subscriber1topic", "some message", nil)
rr := request(t, s, "PUT", "/upAAAAAAAAAAAA", "some message", nil)
require.Equal(t, 200, rr.Code)
}
rr = request(t, s, "PUT", "/subscriber1topic", "some message", nil)
rr = request(t, s, "PUT", "/upAAAAAAAAAAAA", "some message", nil)
require.Equal(t, 429, rr.Code)
// Publish another 2 messages to "up012345678912" as visitor 9.9.9.9
@@ -2325,14 +2315,12 @@ func TestServer_SubscriberRateLimiting_NotEnabled_Failed(t *testing.T) {
// Subscriber rate limiting is disabled!
// Registering visitor 1.2.3.4 to topic has no effect
rr := request(t, s, "GET", "/subscriber1topic/json?poll=1", "", map[string]string{
"Rate-Topics": "subscriber1topic",
}, func(r *http.Request) {
rr := request(t, s, "GET", "/upAAAAAAAAAAAA/json?poll=1", "", nil, func(r *http.Request) {
r.RemoteAddr = "1.2.3.4"
})
require.Equal(t, 200, rr.Code)
require.Equal(t, "", rr.Body.String())
require.Nil(t, s.topics["subscriber1topic"].rateVisitor)
require.Nil(t, s.topics["upAAAAAAAAAAAA"].rateVisitor)
// Registering visitor 8.7.7.1 to topic has no effect
rr = request(t, s, "GET", "/up012345678912/json?poll=1", "", nil, func(r *http.Request) {
@@ -2342,7 +2330,7 @@ func TestServer_SubscriberRateLimiting_NotEnabled_Failed(t *testing.T) {
require.Equal(t, "", rr.Body.String())
require.Nil(t, s.topics["up012345678912"].rateVisitor)
// Publish 3 messages to "subscriber1topic" as visitor 9.9.9.9
// Publish 3 messages to "upAAAAAAAAAAAA" as visitor 9.9.9.9
for i := 0; i < 3; i++ {
rr := request(t, s, "PUT", "/subscriber1topic", "some message", nil)
require.Equal(t, 200, rr.Code)
@@ -2415,80 +2403,30 @@ func TestServer_SubscriberRateLimiting_VisitorExpiration(t *testing.T) {
subscriberFn := func(r *http.Request) {
r.RemoteAddr = "1.2.3.4"
}
rr := request(t, s, "GET", "/mytopic/json?poll=1", "", map[string]string{
"rate-topics": "mytopic",
}, subscriberFn)
rr := request(t, s, "GET", "/upAAAAAAAAAAAA/json?poll=1", "", nil, subscriberFn)
require.Equal(t, 200, rr.Code)
require.Equal(t, "1.2.3.4", s.topics["mytopic"].rateVisitor.ip.String())
require.Equal(t, s.visitors["ip:1.2.3.4"], s.topics["mytopic"].rateVisitor)
require.Equal(t, "1.2.3.4", s.topics["upAAAAAAAAAAAA"].rateVisitor.ip.String())
require.Equal(t, s.visitors["ip:1.2.3.4"], s.topics["upAAAAAAAAAAAA"].rateVisitor)
// Publish message, observe rate visitor tokens being decreased
response := request(t, s, "POST", "/mytopic", "some message", nil)
response := request(t, s, "POST", "/upAAAAAAAAAAAA", "some message", nil)
require.Equal(t, 200, response.Code)
require.Equal(t, int64(0), s.visitors["ip:9.9.9.9"].messagesLimiter.Value())
require.Equal(t, int64(1), s.topics["mytopic"].rateVisitor.messagesLimiter.Value())
require.Equal(t, s.visitors["ip:1.2.3.4"], s.topics["mytopic"].rateVisitor)
require.Equal(t, int64(1), s.topics["upAAAAAAAAAAAA"].rateVisitor.messagesLimiter.Value())
require.Equal(t, s.visitors["ip:1.2.3.4"], s.topics["upAAAAAAAAAAAA"].rateVisitor)
// Expire visitor
s.visitors["ip:1.2.3.4"].seen = time.Now().Add(-1 * 25 * time.Hour)
s.pruneVisitors()
// Publish message again, observe that rateVisitor is not used anymore and is reset
response = request(t, s, "POST", "/mytopic", "some message", nil)
response = request(t, s, "POST", "/upAAAAAAAAAAAA", "some message", nil)
require.Equal(t, 200, response.Code)
require.Equal(t, int64(1), s.visitors["ip:9.9.9.9"].messagesLimiter.Value())
require.Nil(t, s.topics["mytopic"].rateVisitor)
require.Nil(t, s.topics["upAAAAAAAAAAAA"].rateVisitor)
require.Nil(t, s.visitors["ip:1.2.3.4"])
}
func TestServer_SubscriberRateLimiting_ProtectedTopics(t *testing.T) {
c := newTestConfigWithAuthFile(t)
c.AuthDefault = user.PermissionDenyAll
c.VisitorSubscriberRateLimiting = true
s := newTestServer(t, c)
// Create some ACLs
require.Nil(t, s.userManager.AddTier(&user.Tier{
Code: "test",
MessageLimit: 5,
}))
require.Nil(t, s.userManager.AddUser("ben", "ben", user.RoleUser))
require.Nil(t, s.userManager.ChangeTier("ben", "test"))
require.Nil(t, s.userManager.AllowAccess("ben", "announcements", user.PermissionReadWrite))
require.Nil(t, s.userManager.AllowAccess(user.Everyone, "announcements", user.PermissionRead))
require.Nil(t, s.userManager.AllowAccess(user.Everyone, "public_topic", user.PermissionReadWrite))
require.Nil(t, s.userManager.AddUser("phil", "phil", user.RoleUser))
require.Nil(t, s.userManager.ChangeTier("phil", "test"))
require.Nil(t, s.userManager.AddReservation("phil", "reserved-for-phil", user.PermissionReadWrite))
// Set rate visitor as user "phil" on topic
// - "reserved-for-phil": Allowed, because I am the owner
// - "public_topic": Allowed, because it has read-write permissions for everyone
// - "announcements": NOT allowed, because it has read-only permissions for everyone
rr := request(t, s, "GET", "/reserved-for-phil,public_topic,announcements/json?poll=1", "", map[string]string{
"Authorization": util.BasicAuth("phil", "phil"),
"Rate-Topics": "reserved-for-phil,public_topic,announcements",
})
require.Equal(t, 200, rr.Code)
require.Equal(t, "phil", s.topics["reserved-for-phil"].rateVisitor.user.Name)
require.Equal(t, "phil", s.topics["public_topic"].rateVisitor.user.Name)
require.Nil(t, s.topics["announcements"].rateVisitor)
// Set rate visitor as user "ben" on topic
// - "reserved-for-phil": NOT allowed, because I am not the owner
// - "public_topic": Allowed, because it has read-write permissions for everyone
// - "announcements": Allowed, because I have read-write permissions
rr = request(t, s, "GET", "/reserved-for-phil,public_topic,announcements/json?poll=1", "", map[string]string{
"Authorization": util.BasicAuth("ben", "ben"),
"Rate-Topics": "reserved-for-phil,public_topic,announcements",
})
require.Equal(t, 200, rr.Code)
require.Equal(t, "phil", s.topics["reserved-for-phil"].rateVisitor.user.Name)
require.Equal(t, "ben", s.topics["public_topic"].rateVisitor.user.Name)
require.Equal(t, "ben", s.topics["announcements"].rateVisitor.user.Name)
}
func TestServer_SubscriberRateLimiting_ProtectedTopics_WithDefaultReadWrite(t *testing.T) {
c := newTestConfigWithAuthFile(t)
c.AuthDefault = user.PermissionReadWrite

View File

@@ -4,9 +4,9 @@ import (
"bytes"
"encoding/xml"
"fmt"
"heckel.io/ntfy/log"
"heckel.io/ntfy/user"
"heckel.io/ntfy/util"
"heckel.io/ntfy/v2/log"
"heckel.io/ntfy/v2/user"
"heckel.io/ntfy/v2/util"
"io"
"net/http"
"net/url"

View File

@@ -2,8 +2,8 @@ package server
import (
"github.com/stretchr/testify/require"
"heckel.io/ntfy/user"
"heckel.io/ntfy/util"
"heckel.io/ntfy/v2/user"
"heckel.io/ntfy/v2/util"
"io"
"net/http"
"net/http/httptest"

View File

@@ -8,8 +8,8 @@ import (
"strings"
"github.com/SherClockHolmes/webpush-go"
"heckel.io/ntfy/log"
"heckel.io/ntfy/user"
"heckel.io/ntfy/v2/log"
"heckel.io/ntfy/v2/user"
)
const (

View File

@@ -4,8 +4,8 @@ import (
"encoding/json"
"fmt"
"github.com/stretchr/testify/require"
"heckel.io/ntfy/user"
"heckel.io/ntfy/util"
"heckel.io/ntfy/v2/user"
"heckel.io/ntfy/v2/util"
"io"
"net/http"
"net/http/httptest"

View File

@@ -11,8 +11,8 @@ import (
"sync"
"time"
"heckel.io/ntfy/log"
"heckel.io/ntfy/util"
"heckel.io/ntfy/v2/log"
"heckel.io/ntfy/v2/util"
)
type mailer interface {

View File

@@ -1335,6 +1335,36 @@ Sent by
writeAndReadUntilLine(t, email, c, scanner, "250 2.0.0 OK: queued")
}
func TestSmtpBackend_HTMLOnly_FromDiskStation(t *testing.T) {
email := `EHLO example.com
MAIL FROM: synology@mydomain.me
RCPT TO: synology@mydomain.me
DATA
From: "=?UTF-8?B?Um9iYmll?=" <synology@mydomain.me>
To: <synology@mydomain.me>
Message-Id: <640e6f562895d.6c9584bcfa491ac9c546b480b32ffc1d@mydomain.me>
MIME-Version: 1.0
Subject: =?UTF-8?B?W1N5bm9sb2d5IE5BU10gVGVzdCBNZXNzYWdlIGZyb20gTGl0dHNfTkFT?=
Content-Type: text/html; charset=utf-8
Content-Transfer-Encoding: 8bit
Congratulations! You have successfully set up the email notification on Synology_NAS.<BR>For further system configurations, please visit http://192.168.1.28:5000/, http://172.16.60.5:5000/.<BR>(If you cannot connect to the server, please contact the administrator.)<BR><BR>From Synology_NAS<BR><BR><BR>
.
`
s, c, conf, scanner := newTestSMTPServer(t, func(w http.ResponseWriter, r *http.Request) {
require.Equal(t, "/synology", r.URL.Path)
require.Equal(t, "[Synology NAS] Test Message from Litts_NAS", r.Header.Get("Title"))
actual := readAll(t, r.Body)
expected := `Congratulations! You have successfully set up the email notification on Synology_NAS. For further system configurations, please visit http://192.168.1.28:5000/, http://172.16.60.5:5000/. (If you cannot connect to the server, please contact the administrator.) From Synology_NAS`
require.Equal(t, expected, actual)
})
conf.SMTPServerDomain = "mydomain.me"
conf.SMTPServerAddrPrefix = ""
defer s.Close()
defer c.Close()
writeAndReadUntilLine(t, email, c, scanner, "250 2.0.0 OK: queued")
}
func TestSmtpBackend_PlaintextWithToken(t *testing.T) {
email := `EHLO example.com
MAIL FROM: phil@example.com

View File

@@ -5,8 +5,8 @@ import (
"sync"
"time"
"heckel.io/ntfy/log"
"heckel.io/ntfy/util"
"heckel.io/ntfy/v2/log"
"heckel.io/ntfy/v2/util"
)
const (

View File

@@ -69,7 +69,7 @@ func TestTopic_Subscribe_DuplicateID(t *testing.T) {
t.Parallel()
to := newTopic("mytopic")
// Fix random seed to force same number generation
//lint:ignore SA1019 Fix random seed to force same number generation
rand.Seed(1)
a := rand.Int()
to.subscribers[a] = &topicSubscriber{
@@ -82,7 +82,7 @@ func TestTopic_Subscribe_DuplicateID(t *testing.T) {
return nil
}
// Force rand.Int to generate the same id once more
//lint:ignore SA1019 Force rand.Int to generate the same id once more
rand.Seed(1)
id := to.Subscribe(subFn, "b", func() {})
res := to.subscribers[id]

View File

@@ -5,10 +5,10 @@ import (
"net/netip"
"time"
"heckel.io/ntfy/log"
"heckel.io/ntfy/user"
"heckel.io/ntfy/v2/log"
"heckel.io/ntfy/v2/user"
"heckel.io/ntfy/util"
"heckel.io/ntfy/v2/util"
)
// List of possible events

View File

@@ -3,7 +3,7 @@ package server
import (
"context"
"fmt"
"heckel.io/ntfy/util"
"heckel.io/ntfy/v2/util"
"io"
"mime"
"net/http"

View File

@@ -2,14 +2,14 @@ package server
import (
"fmt"
"heckel.io/ntfy/log"
"heckel.io/ntfy/user"
"heckel.io/ntfy/v2/log"
"heckel.io/ntfy/v2/user"
"net/netip"
"sync"
"time"
"golang.org/x/time/rate"
"heckel.io/ntfy/util"
"heckel.io/ntfy/v2/util"
)
const (

View File

@@ -3,7 +3,7 @@ package server
import (
"database/sql"
"errors"
"heckel.io/ntfy/util"
"heckel.io/ntfy/v2/util"
"net/netip"
"time"

View File

@@ -2,18 +2,13 @@ package test
import (
"fmt"
"heckel.io/ntfy/server"
"heckel.io/ntfy/v2/server"
"math/rand"
"net/http"
"path/filepath"
"testing"
"time"
)
func init() {
rand.Seed(time.Now().UnixMilli())
}
// StartServer starts a server.Server with a random port and waits for the server to be up
func StartServer(t *testing.T) (*server.Server, int) {
return StartServerWithConfig(t, server.NewConfig())

View File

@@ -9,8 +9,8 @@ import (
"github.com/mattn/go-sqlite3"
"github.com/stripe/stripe-go/v74"
"golang.org/x/crypto/bcrypt"
"heckel.io/ntfy/log"
"heckel.io/ntfy/util"
"heckel.io/ntfy/v2/log"
"heckel.io/ntfy/v2/util"
"net/netip"
"strings"
"sync"
@@ -161,7 +161,7 @@ const (
FROM user_access a
JOIN user u ON u.id = a.user_id
WHERE (u.user = ? OR u.user = ?) AND ? LIKE a.topic ESCAPE '\'
ORDER BY u.user DESC
ORDER BY u.user DESC, LENGTH(a.topic) DESC, a.write DESC
`
insertUserQuery = `
@@ -197,13 +197,13 @@ const (
selectUserAllAccessQuery = `
SELECT user_id, topic, read, write
FROM user_access
ORDER BY write DESC, read DESC, topic
ORDER BY LENGTH(topic) DESC, write DESC, read DESC, topic
`
selectUserAccessQuery = `
SELECT topic, read, write
FROM user_access
WHERE user_id = (SELECT id FROM user WHERE user = ?)
ORDER BY write DESC, read DESC, topic
ORDER BY LENGTH(topic) DESC, write DESC, read DESC, topic
`
selectUserReservationsQuery = `
SELECT a_user.topic, a_user.read, a_user.write, a_everyone.read AS everyone_read, a_everyone.write AS everyone_write
@@ -833,8 +833,10 @@ func (a *Manager) Authorize(user *User, topic string, perm Permission) error {
if user != nil {
username = user.Name
}
// Select the read/write permissions for this user/topic combo. The query may return two
// rows (one for everyone, and one for the user), but prioritizes the user.
// Select the read/write permissions for this user/topic combo.
// - The query may return two rows (one for everyone, and one for the user), but prioritizes the user.
// - Furthermore, the query prioritizes more specific permissions (longer!) over more generic ones, e.g. "test*" > "*"
// - It also prioritizes write permissions over read permissions
rows, err := a.db.Query(selectTopicPermsQuery, Everyone, username, topic)
if err != nil {
return err

View File

@@ -6,7 +6,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/stripe/stripe-go/v74"
"golang.org/x/crypto/bcrypt"
"heckel.io/ntfy/util"
"heckel.io/ntfy/v2/util"
"net/netip"
"path/filepath"
"strings"
@@ -20,10 +20,15 @@ func TestManager_FullScenario_Default_DenyAll(t *testing.T) {
a := newTestManagerFromFile(t, filepath.Join(t.TempDir(), "user.db"), "", PermissionDenyAll, DefaultUserPasswordBcryptCost, DefaultUserStatsQueueWriterInterval)
require.Nil(t, a.AddUser("phil", "phil", RoleAdmin))
require.Nil(t, a.AddUser("ben", "ben", RoleUser))
require.Nil(t, a.AddUser("john", "john", RoleUser))
require.Nil(t, a.AllowAccess("ben", "mytopic", PermissionReadWrite))
require.Nil(t, a.AllowAccess("ben", "readme", PermissionRead))
require.Nil(t, a.AllowAccess("ben", "writeme", PermissionWrite))
require.Nil(t, a.AllowAccess("ben", "everyonewrite", PermissionDenyAll)) // How unfair!
require.Nil(t, a.AllowAccess("john", "*", PermissionRead))
require.Nil(t, a.AllowAccess("john", "mytopic*", PermissionReadWrite))
require.Nil(t, a.AllowAccess("john", "mytopic_ro*", PermissionRead))
require.Nil(t, a.AllowAccess("john", "mytopic_deny*", PermissionDenyAll))
require.Nil(t, a.AllowAccess(Everyone, "announcements", PermissionRead))
require.Nil(t, a.AllowAccess(Everyone, "everyonewrite", PermissionReadWrite))
require.Nil(t, a.AllowAccess(Everyone, "up*", PermissionWrite)) // Everyone can write to /up*
@@ -47,12 +52,27 @@ func TestManager_FullScenario_Default_DenyAll(t *testing.T) {
benGrants, err := a.Grants("ben")
require.Nil(t, err)
require.Equal(t, []Grant{
{"everyonewrite", PermissionDenyAll},
{"mytopic", PermissionReadWrite},
{"writeme", PermissionWrite},
{"readme", PermissionRead},
{"everyonewrite", PermissionDenyAll},
}, benGrants)
john, err := a.Authenticate("john", "john")
require.Nil(t, err)
require.Equal(t, "john", john.Name)
require.True(t, strings.HasPrefix(john.Hash, "$2a$10$"))
require.Equal(t, RoleUser, john.Role)
johnGrants, err := a.Grants("john")
require.Nil(t, err)
require.Equal(t, []Grant{
{"mytopic_deny*", PermissionDenyAll},
{"mytopic_ro*", PermissionRead},
{"mytopic*", PermissionReadWrite},
{"*", PermissionRead},
}, johnGrants)
notben, err := a.Authenticate("ben", "this is wrong")
require.Nil(t, notben)
require.Equal(t, ErrUnauthenticated, err)
@@ -78,6 +98,20 @@ func TestManager_FullScenario_Default_DenyAll(t *testing.T) {
require.Nil(t, a.Authorize(ben, "announcements", PermissionRead))
require.Equal(t, ErrUnauthorized, a.Authorize(ben, "announcements", PermissionWrite))
// User john should have
// "deny" to mytopic_deny*,
// "ro" to mytopic_ro*,
// "rw" to mytopic*,
// "ro" to the rest
require.Equal(t, ErrUnauthorized, a.Authorize(john, "mytopic_deny_case", PermissionRead))
require.Equal(t, ErrUnauthorized, a.Authorize(john, "mytopic_deny_case", PermissionWrite))
require.Nil(t, a.Authorize(john, "mytopic_ro_test_case", PermissionRead))
require.Equal(t, ErrUnauthorized, a.Authorize(john, "mytopic_ro_test_case", PermissionWrite))
require.Nil(t, a.Authorize(john, "mytopic_case1", PermissionRead))
require.Nil(t, a.Authorize(john, "mytopic_case1", PermissionWrite))
require.Nil(t, a.Authorize(john, "readme", PermissionRead))
require.Equal(t, ErrUnauthorized, a.Authorize(john, "writeme", PermissionWrite))
// Everyone else can do barely anything
require.Equal(t, ErrUnauthorized, a.Authorize(nil, "sometopicnotinthelist", PermissionRead))
require.Equal(t, ErrUnauthorized, a.Authorize(nil, "sometopicnotinthelist", PermissionWrite))
@@ -95,6 +129,22 @@ func TestManager_FullScenario_Default_DenyAll(t *testing.T) {
require.Nil(t, a.Authorize(nil, "up5678", PermissionWrite))
}
func TestManager_Access_Order_LengthWriteRead(t *testing.T) {
// This test validates issue #914 / #917, i.e. that write permissions are prioritized over read permissions,
// and longer ACL rules are prioritized as well.
a := newTestManagerFromFile(t, filepath.Join(t.TempDir(), "user.db"), "", PermissionDenyAll, DefaultUserPasswordBcryptCost, DefaultUserStatsQueueWriterInterval)
require.Nil(t, a.AddUser("ben", "ben", RoleUser))
require.Nil(t, a.AllowAccess("ben", "test*", PermissionReadWrite))
require.Nil(t, a.AllowAccess("ben", "*", PermissionRead))
ben, err := a.Authenticate("ben", "ben")
require.Nil(t, err)
require.Nil(t, a.Authorize(ben, "any-topic-can-be-read", PermissionRead))
require.Nil(t, a.Authorize(ben, "this-too", PermissionRead))
require.Nil(t, a.Authorize(ben, "test123", PermissionWrite))
}
func TestManager_AddUser_Invalid(t *testing.T) {
a := newTestManager(t, PermissionDenyAll)
require.Equal(t, ErrInvalidArgument, a.AddUser(" invalid ", "pass", RoleAdmin))
@@ -227,10 +277,10 @@ func TestManager_UserManagement(t *testing.T) {
benGrants, err := a.Grants("ben")
require.Nil(t, err)
require.Equal(t, []Grant{
{"everyonewrite", PermissionDenyAll},
{"mytopic", PermissionReadWrite},
{"writeme", PermissionWrite},
{"readme", PermissionRead},
{"everyonewrite", PermissionDenyAll},
}, benGrants)
everyone, err := a.User(Everyone)
@@ -1101,10 +1151,10 @@ func TestMigrationFrom1(t *testing.T) {
require.Equal(t, syncTopicLength, len(ben.SyncTopic))
require.NotEqual(t, ben.SyncTopic, phil.SyncTopic)
require.Equal(t, 2, len(benGrants))
require.Equal(t, "stats", benGrants[0].TopicPattern)
require.Equal(t, PermissionReadWrite, benGrants[0].Allow)
require.Equal(t, "secret", benGrants[1].TopicPattern)
require.Equal(t, PermissionRead, benGrants[1].Allow)
require.Equal(t, "secret", benGrants[0].TopicPattern)
require.Equal(t, PermissionRead, benGrants[0].Allow)
require.Equal(t, "stats", benGrants[1].TopicPattern)
require.Equal(t, PermissionReadWrite, benGrants[1].Allow)
require.Equal(t, "u_everyone", everyone.ID)
require.Equal(t, Everyone, everyone.Name)
@@ -1225,9 +1275,9 @@ func TestMigrationFrom4(t *testing.T) {
require.Nil(t, err)
require.Equal(t, 4, len(everyoneGrants))
require.Equal(t, "down_*", everyoneGrants[0].TopicPattern)
require.Equal(t, "left_*", everyoneGrants[1].TopicPattern)
require.Equal(t, "mytopic_", everyoneGrants[2].TopicPattern)
require.Equal(t, "mytopic_", everyoneGrants[0].TopicPattern)
require.Equal(t, "down_*", everyoneGrants[1].TopicPattern)
require.Equal(t, "left_*", everyoneGrants[2].TopicPattern)
require.Equal(t, "up*", everyoneGrants[3].TopicPattern)
// Check they are stored correctly in the database

View File

@@ -3,7 +3,7 @@ package user
import (
"errors"
"github.com/stripe/stripe-go/v74"
"heckel.io/ntfy/log"
"heckel.io/ntfy/v2/log"
"net/netip"
"regexp"
"strings"

View File

@@ -2,7 +2,7 @@ package util_test
import (
"github.com/stretchr/testify/require"
"heckel.io/ntfy/util"
"heckel.io/ntfy/v2/util"
"math/rand"
"sync"
"testing"

View File

@@ -253,6 +253,8 @@ func ReadPassword(in io.Reader) ([]byte, error) {
password, err := term.ReadPassword(int(f.Fd())) // This is always going to be 0
if err != nil {
return nil, err
} else if len(password) == 0 {
return nil, errors.New("password cannot be empty")
}
return password, nil
}
@@ -272,7 +274,9 @@ func ReadPassword(in io.Reader) ([]byte, error) {
}
password = append(password, buf[0])
}
if len(password) == 0 {
return nil, errors.New("password cannot be empty")
}
return password, nil
}

3018
web/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -60,9 +60,9 @@
"notifications_click_copy_url_button": "Копиране на препратка",
"notifications_click_open_button": "Отваряне",
"notifications_click_copy_url_title": "Копиране на препратката в междинната памет",
"notifications_none_for_topic_title": "Липсват известия в темата.",
"notifications_none_for_topic_title": "Темата е все още празна",
"notifications_none_for_any_title": "Липсват известия.",
"notifications_none_for_topic_description": "За да изпратите известия в тази тема направете заявка чрез методите PUT или POST към адреса й.",
"notifications_none_for_topic_description": "За да изпратите известия в тази тема направете заявка чрез методите PUT или POST към адреса ѝ.",
"notifications_none_for_any_description": "За да изпратите известия в тема направете заявка чрез методите PUT или POST към адреса ѝ. Ето пример с една от вашите теми.",
"notifications_no_subscriptions_description": "Щракнете върху „{{linktext}}“, за да създадете тема или да се абонирате. След това като направите заявка чрез методите PUT или POST ще ги получите тук.",
"notifications_more_details": "За допълнителна информация посетете <websiteLink>страницата</websiteLink> или <docsLink>документацията</docsLink>.",
@@ -155,7 +155,7 @@
"notifications_actions_not_supported": "Действието не се поддържа от приложението за интернет",
"action_bar_show_menu": "Показване на менюто",
"action_bar_logo_alt": "Логотип на ntfy",
"action_bar_toggle_mute": "Заглушаване или пускне на известията",
"action_bar_toggle_mute": "Заглушаване или пускане на известията",
"action_bar_toggle_action_menu": "Отваряне или затваряне на менюто с действията",
"nav_button_muted": "Известията са заглушени",
"notifications_list": "Списък с известия",
@@ -333,5 +333,7 @@
"account_upgrade_dialog_button_pay_now": "Плащане и абониране",
"account_upgrade_dialog_tier_selected_label": "Избрано",
"account_upgrade_dialog_button_update_subscription": "Премяна на абонамент",
"account_upgrade_dialog_reservations_warning_other": "Избраното ниво разрешава по-малко резервирани теми, от колкото текущото. Преди промяна на нивото <strong>изтрийте най-малко {{count}} резервирани теми</strong>. Можете да премахвате теми в <Link>Настройки</Link>."
"account_upgrade_dialog_reservations_warning_other": "Избраното ниво разрешава по-малко резервирани теми, от колкото текущото. Преди промяна на нивото <strong>изтрийте най-малко {{count}} резервирани теми</strong>. Можете да премахвате теми в <Link>Настройки</Link>.",
"account_tokens_table_expires_header": "Изтича",
"account_tokens_table_never_expires": "Никога"
}

View File

@@ -35,7 +35,7 @@
"action_bar_sign_up": "Opret konto",
"message_bar_type_message": "Skriv en besked her",
"nav_button_settings": "Indstillinger",
"message_bar_publish": "Offentliggør besked",
"message_bar_publish": "Udgiv besked",
"nav_topics_title": "Tilmeldte emner",
"nav_button_all_notifications": "Alle notifikationer",
"nav_button_connecting": "forbinder",
@@ -103,7 +103,7 @@
"account_basics_tier_free": "Gratis",
"account_basics_tier_admin_suffix_no_tier": "(intet niveau)",
"account_basics_tier_admin_suffix_with_tier": "(med {{tier}}} niveau)",
"account_usage_messages_title": "Offentliggjorte meddelelser",
"account_usage_messages_title": "Udgivne beskeder",
"account_delete_dialog_button_submit": "Slet konto permanent",
"account_upgrade_dialog_tier_features_attachment_file_size": "{{filesize}} pr. fil",
"account_upgrade_dialog_button_redirect_signup": "Tilmeld dig nu",
@@ -279,5 +279,106 @@
"reservation_delete_dialog_action_keep_title": "Behold cachelagrede meddelelser og vedhæftede filer",
"reservation_delete_dialog_action_delete_title": "Slet cachelagrede meddelelser og vedhæftede filer",
"error_boundary_title": "Oh nej, ntfy brød sammen",
"error_boundary_description": "Dette bør naturligvis ikke ske. Det beklager vi meget.<br/>Hvis du har et øjeblik, bedes du <githubLink>rapportere dette på GitHub</githubLink>, eller give os besked via <discordLink>Discord</discordLink> eller <matrixLink>Matrix</matrixLink>."
"error_boundary_description": "Dette bør naturligvis ikke ske. Det beklager vi meget.<br/>Hvis du har et øjeblik, bedes du <githubLink>rapportere dette på GitHub</githubLink>, eller give os besked via <discordLink>Discord</discordLink> eller <matrixLink>Matrix</matrixLink>.",
"account_upgrade_dialog_tier_features_no_calls": "Ingen telefonopkald",
"account_upgrade_dialog_billing_contact_email": "For faktureringsspørgsmål bedes du <Link>kontakte os</Link> direkte.",
"account_basics_tier_interval_monthly": "månedlig",
"publish_dialog_checkbox_publish_another": "Udgiv en anden",
"account_upgrade_dialog_tier_features_calls_one": "{{calls}} daglige telefonopkald",
"publish_dialog_filename_placeholder": "Vedhæftet filnavn",
"prefs_users_description": "Tilføj/fjern brugere til dine beskyttede emner her. Vær opmærksom på, at brugernavn og adgangskode er gemt i browserens lokale lager.",
"account_basics_phone_numbers_dialog_number_label": "Telefonnummer",
"subscribe_dialog_subscribe_description": "Emner kan ikke beskyttes med adgangskode, så vælg et navn, der ikke er let at gætte. Når du har abonneret, kan du PUT/POST notifikationer.",
"account_basics_phone_numbers_dialog_check_verification_button": "Bekræft kode",
"account_upgrade_dialog_interval_yearly_discount_save_up_to": "spar op til {{discount}}%",
"account_upgrade_dialog_proration_info": "<strong>Proration</strong>: Når du opgraderer mellem betalte planer, vil prisforskellen blive <strong>opkrævet med det samme</strong>. Ved nedgradering til et lavere niveau, vil saldoen blive brugt til at betale for fremtidige faktureringsperioder.",
"account_usage_attachment_storage_title": "opbevaring af vedhæftede filer",
"message_bar_error_publishing": "Der opstod en fejl under udgivelse af meddelelse",
"publish_dialog_chip_delay_label": "Forsinke leveringen",
"prefs_reservations_table_not_subscribed": "Ikke abonneret",
"account_upgrade_dialog_tier_features_calls_other": "{{calls}} daglige telefonopkald",
"account_basics_phone_numbers_dialog_verify_button_sms": "Send SMS",
"prefs_reservations_table_everyone_read_only": "Jeg kan udgive og abonnere, alle kan abonnere",
"prefs_reservations_table_everyone_deny_all": "Kun jeg kan udgive og abonnere",
"publish_dialog_chip_topic_label": "Skift emne",
"account_basics_phone_numbers_dialog_description": "For at bruge opkaldsmeddelelsesfunktionen skal du tilføje og bekræfte mindst ét telefonnummer. Bekræftelse kan gøres via SMS eller et telefonopkald.",
"account_upgrade_dialog_tier_features_reservations_one": "{{reservations}} reserveret emne",
"account_upgrade_dialog_tier_features_no_reservations": "Ingen reserverede emner",
"publish_dialog_base_url_label": "Tjeneste-URL",
"prefs_users_table_cannot_delete_or_edit": "Kan ikke slette eller redigere en aktiv bruger",
"publish_dialog_title_no_topic": "Udgiv notifikation",
"publish_dialog_attach_label": "URL til vedhæftede filer",
"nav_button_muted": "Notifikationer slået fra",
"prefs_notifications_min_priority_description_x_or_higher": "Vis notifikationer hvis prioritet er {{number}} ({{name}}) eller højere",
"reservation_delete_dialog_description": "Fjernelse af en reservation opgiver ejerskabet over emnet og giver andre mulighed for at reservere det. Du kan beholde eller slette eksisterende beskeder og vedhæftede filer.",
"prefs_reservations_table_everyone_read_write": "Alle kan udgive og abonnere",
"account_upgrade_dialog_interval_monthly": "månedlig",
"account_basics_phone_numbers_no_phone_numbers_yet": "Ingen telefonnumre endnu",
"notifications_no_subscriptions_description": "Klik på linket \"{{linktext}}\" for at oprette eller abonnere på et emne. Derefter kan du sende beskeder via PUT eller POST, og du vil modtage notifikationer her.",
"publish_dialog_message_published": "Notifikation udgivet",
"publish_dialog_chip_call_label": "Telefon opkald",
"account_basics_phone_numbers_dialog_title": "Tilføj telefonnummer",
"account_tokens_delete_dialog_description": "Før du sletter et adgangstoken, skal du sikre dig, at ingen programmer eller scripts aktivt bruger det. <strong>Denne handling kan ikke fortrydes</strong>.",
"account_upgrade_dialog_billing_contact_website": "For spørgsmål om fakturering, se venligst vores <Link>hjemmeside</Link>.",
"account_usage_reservations_none": "Ingen reserverede emner til denne konto",
"account_tokens_description": "Brug adgangstokens, når du udgiver og abonnerer via ntfy API, så du ikke behøver at sende dine kontooplysninger. Tjek <Link>dokumentationen</Link> for at få mere at vide.",
"prefs_reservations_table": "Reserverede emner tabel",
"account_upgrade_dialog_tier_features_emails_one": "{{emails}} daglig e-mail",
"prefs_reservations_description": "Her kan du reservere emnenavne til personlig brug. Reservering af et emne giver dig ejerskab over emnet og giver dig mulighed for at definere adgangstilladelser for andre brugere over emnet.",
"prefs_users_description_no_sync": "Brugere og adgangskoder er ikke synkroniseret til din konto.",
"nav_button_publish_message": "Udgiv notifikation",
"prefs_users_table_base_url_header": "Tjeneste-URL",
"publish_dialog_attach_reset": "Fjern URL til vedhæftede filer",
"account_upgrade_dialog_tier_features_messages_one": "{{messages}} daglig besked",
"account_upgrade_dialog_reservations_warning_one": "Det valgte niveau tillader færre reserverede emner end dit nuværende niveau. Før du ændrer dit niveau, <strong>slet venligst mindst én reservation</strong>. Du kan fjerne reservationer i <Link>Indstillinger</Link>.",
"error_boundary_unsupported_indexeddb_description": "ntfy-webappen har brug for IndexedDB for at fungere, og din browser understøtter ikke IndexedDB i privat browsing-tilstand.<br/><br/>Selv om dette er uheldigt, giver det heller ikke ret meget mening at bruge ntfy-webappen i privat browsing-tilstand alligevel, fordi alt er gemt i browserens lager. Du kan læse mere om det <githubLink>i dette GitHub issue</githubLink>, eller tale med os på <discordLink>Discord</discordLink> eller <matrixLink>Matrix</matrixLink>.",
"publish_dialog_title_placeholder": "Notifikationstitel, f.eks. Advarsel om diskplads",
"account_basics_tier_description": "Din kontos niveau",
"account_basics_phone_numbers_description": "For notifikationer via telefonopkald",
"account_upgrade_dialog_cancel_warning": "Dette vil <strong>annullere dit abonnement</strong> og nedgradere din konto den {{date}}. På den dato <strong>slettes</strong> emnereservationer samt meddelelser, der er gemt på serveren.",
"publish_dialog_chip_call_no_verified_numbers_tooltip": "Ingen verificerede telefonnumre",
"publish_dialog_call_label": "Telefon opkald",
"account_usage_calls_title": "Telefonopkald foretaget",
"prefs_notifications_min_priority_description_any": "Viser alle notifikationer, uanset prioritet",
"error_boundary_gathering_info": "Indsaml mere info…",
"reservation_delete_dialog_action_keep_description": "Beskeder og vedhæftede filer, der er cachelagret på serveren, bliver offentligt synlige for personer med kendskab til emnenavnet.",
"account_basics_phone_numbers_copied_to_clipboard": "Telefonnummer kopieret til udklipsholder",
"prefs_reservations_dialog_description": "Reservering af et emne giver dig ejerskab over emnet og giver dig mulighed for at definere adgangstilladelser for andre brugere over emnet.",
"publish_dialog_title_topic": "Udgiv til {{topic}}",
"account_basics_phone_numbers_dialog_number_placeholder": "f.eks. +4512345678",
"account_basics_phone_numbers_dialog_code_placeholder": "f.eks. 123456",
"account_basics_username_description": "Hej, der er du ❤",
"publish_dialog_base_url_placeholder": "Tjeneste-URL, f.eks. https://example.com",
"account_basics_tier_interval_yearly": "årligt",
"account_upgrade_dialog_tier_price_billed_monthly": "{{price}} årligt. Faktureres månedligt.",
"account_basics_phone_numbers_dialog_channel_call": "Opkald",
"publish_dialog_attachment_limits_file_and_quota_reached": "overskrider filgrænsen og kvoten på {{fileSizeLimit}}, {{remainingBytes}} tilbage",
"account_upgrade_dialog_interval_yearly": "årligt",
"account_upgrade_dialog_tier_price_billed_yearly": "{{price}} faktureres årligt. Spar {{save}}.",
"account_usage_basis_ip_description": "Brugsstatistikker og begrænsninger for denne konto er baseret på din IP-adresse, så de kan være delt med andre brugere. Ovenstående grænser er omtrentlige baseret på de eksisterende hastigheds grænser.",
"account_basics_password_dialog_title": "Skift kodeord",
"account_basics_phone_numbers_title": "Telefonnumre",
"account_upgrade_dialog_interval_yearly_discount_save": "spar {{discount}}%",
"publish_dialog_drop_file_here": "Smid filen her",
"prefs_reservations_table_everyone_write_only": "Jeg kan udgive og abonnere, alle kan udgive",
"account_tokens_table_cannot_delete_or_edit": "Kan ikke redigere eller slette nuværende sessionstoken",
"publish_dialog_attached_file_filename_placeholder": "Vedhæftet filnavn",
"subscribe_dialog_subscribe_base_url_label": "Tjeneste-URL",
"account_upgrade_dialog_tier_price_per_month": "måned",
"message_bar_show_dialog": "Vis udgivelsesdialogen",
"account_usage_calls_none": "Der kan ikke foretages telefonopkald med denne konto",
"nav_upgrade_banner_description": "Reserver emner, flere beskeder og e-mails og større vedhæftede filer",
"publish_dialog_call_reset": "Fjern telefon opkald",
"account_basics_phone_numbers_dialog_code_label": "Verifikationskode",
"reservation_delete_dialog_action_delete_description": "Cachelagrede beskeder og vedhæftede filer slettes permanent. Denne handling kan ikke fortrydes.",
"alert_grant_button": "Tillad nu",
"account_usage_attachment_storage_description": "{{filesize}} pr. fil, slettet efter {{expiry}}",
"publish_dialog_chip_click_label": "Klik på URL",
"account_basics_phone_numbers_dialog_verify_button_call": "Ring til mig",
"publish_dialog_call_item": "Ring til tlf. {{number}}",
"prefs_users_dialog_base_url_label": "Tjeneste-URL, f.eks. https://ntfy.sh",
"account_basics_phone_numbers_dialog_channel_sms": "SMS",
"account_delete_dialog_billing_warning": "Hvis du sletter din konto, så annulleres dit abonnement med det samme. Du vil ikke længere have adgang til faktureringspanelet.",
"prefs_notifications_min_priority_description_max": "Vis notifikationer, hvis prioritet er 5 (maks.)",
"account_upgrade_dialog_reservations_warning_other": "Det valgte niveau tillader færre reserverede emner end dit nuværende niveau. Før du ændrer dit niveau, <strong>slet venligst mindst {{count}} reservationer</strong>. Du kan fjerne reservationer i <Link>Indstillinger</Link>."
}

View File

@@ -25,7 +25,7 @@
"notifications_click_copy_url_title": "Link-URL in Zwischenablage kopieren",
"publish_dialog_priority_low": "Niedrige Priorität",
"publish_dialog_message_label": "Nachricht",
"action_bar_unsubscribe": "Abmelden",
"action_bar_unsubscribe": "Abbestellen",
"notifications_copied_to_clipboard": "In Zwischenablage kopiert",
"notifications_loading": "Benachrichtigungen werden geladen …",
"notifications_attachment_open_title": "Gehe zu {{url}}",
@@ -82,7 +82,7 @@
"publish_dialog_attach_placeholder": "Datei von URL anhängen, z.B. https://f-droid.org/F-Droid.apk",
"publish_dialog_filename_placeholder": "Dateiname des Anhangs",
"publish_dialog_delay_label": "Verzögerung",
"publish_dialog_email_placeholder": "E-Mail-Adresse, an welche die Benachrichtigung gesendet werden soll, z. B. phil@example.com",
"publish_dialog_email_placeholder": "E-Mail-Adresse, an welche die Benachrichtigung gesendet werden soll, z.B. phil@example.com",
"publish_dialog_chip_click_label": "Klick-URL",
"publish_dialog_button_cancel_sending": "Senden abbrechen",
"publish_dialog_drop_file_here": "Datei hierher ziehen",
@@ -180,7 +180,7 @@
"error_boundary_unsupported_indexeddb_description": "Die ntfy Web-App benötigt eine IndexedDB für eine korrekte Funktion, und Dein Browser unterstützt in privaten Tabs keinen IndexedDB.<br/><br/>Das ist zwar ärgerlich, eine Nutzung von ntfy in einem privaten Tab macht aber auch wenig Sinn da alle Daten im Browser gespeichert werden. Weitere Informationen gibt es <githubLink>in diesem GitHub-Issue</githubLink>, oder im Chat bei <discordLink>Discord</discordLink> oder <matrixLink>Matrix</matrixLink>.",
"action_bar_toggle_action_menu": "Aktionsmenü öffnen/schließen",
"notifications_new_indicator": "Neue Benachrichtigung",
"publish_dialog_email_reset": "Email-Weiterleitung entfernen",
"publish_dialog_email_reset": "E-Mail-Weiterleitung entfernen",
"action_bar_logo_alt": "ntfy Logo",
"nav_button_muted": "Benachrichtigungen stummgeschaltet",
"notifications_list_item": "Benachrichtigung",
@@ -217,7 +217,7 @@
"signup_form_password": "Kennwort",
"signup_form_toggle_password_visibility": "Kennwort-Sichtbarkeit umschalten",
"nav_button_account": "Konto",
"nav_upgrade_banner_description": "Themen reservieren, mehr Nachrichten & Emails, größere Anhänge",
"nav_upgrade_banner_description": "Themen reservieren, mehr Nachrichten & E-Mails und größere Anhänge",
"display_name_dialog_title": "Anzeigennamen ändern",
"display_name_dialog_placeholder": "Anzeigename",
"reserve_dialog_checkbox_label": "Thema reservieren und Zugriffsrechte konfigurieren",
@@ -245,7 +245,7 @@
"account_basics_tier_payment_overdue": "Deine Zahlung ist überfällig. Bitte aktualisiere Deine Zahlungsmethode, oder Dein Konto wird herabgestuft.",
"account_basics_tier_manage_billing_button": "Zahlung verwalten",
"account_usage_messages_title": "Veröffentlichte Nachrichten",
"account_usage_emails_title": "Gesendete Emails",
"account_usage_emails_title": "Gesendete E-Mails",
"account_usage_reservations_title": "Reservierte Themen",
"account_usage_reservations_none": "Keine reservierten Themen für dieses Konto",
"account_usage_attachment_storage_title": "Speicherplatz für Anhänge",
@@ -266,7 +266,7 @@
"account_upgrade_dialog_reservations_warning_other": "Das gewählte Level erlaubt weniger reservierte Themen als Dein aktueller Level. <strong>Bitte löschen vor dem Wechsel Deines Levels mindestens {{count}} Reservierungen</strong>. Du kannst Reservierungen in den <Link>Einstellungen</Link> löschen.",
"account_upgrade_dialog_tier_features_reservations_other": "{{reservations}} reservierte Themen",
"account_upgrade_dialog_tier_features_messages_other": "{{messages}} Nachrichten pro Tag",
"account_upgrade_dialog_tier_features_emails_other": "{{emails}} Emails pro Tag",
"account_upgrade_dialog_tier_features_emails_other": "{{emails}} E-Mails pro Tag",
"account_upgrade_dialog_tier_features_attachment_file_size": "{{filesize}} pro Datei",
"account_upgrade_dialog_tier_features_attachment_total_size": "{{totalsize}} gesamter Speicherplatz",
"account_upgrade_dialog_tier_selected_label": "Ausgewählt",

View File

@@ -0,0 +1 @@
{}

View File

@@ -60,7 +60,7 @@
"publish_dialog_tags_placeholder": "Pilkuilla eroteltu luettelo tunnisteista, esim. varoitus, srv1-varmuuskopio",
"account_delete_title": "Poista tili",
"publish_dialog_attached_file_remove": "Poista liitetiedosto",
"nav_button_connecting": "yhdistää",
"nav_button_connecting": "yhdistetään",
"account_delete_dialog_label": "Salasana",
"subscribe_dialog_login_button_login": "Kirjaudu",
"account_upgrade_dialog_tier_features_no_reservations": "Ei varattuja topikkeja",
@@ -89,7 +89,7 @@
"action_bar_toggle_mute": "Hiljennä/poista hiljennys",
"reservation_delete_dialog_submit_button": "Poista varaus",
"account_basics_title": "Tili",
"nav_button_documentation": "Käyttäjä oppaat",
"nav_button_documentation": "Dokumentointi",
"prefs_reservations_limit_reached": "Olet saavuttanut varattujen topikkien rajan.",
"account_upgrade_dialog_interval_monthly": "Kuukausittain",
"prefs_users_add_button": "Lisää käyttäjä",
@@ -142,7 +142,7 @@
"account_basics_tier_upgrade_button": "Päivitä Pro versioon",
"prefs_users_description_no_sync": "Käyttäjiä ja salasanoja ei ole synkronoitu tiliisi.",
"account_tokens_dialog_title_edit": "Muokkaa käyttöoikeustunnusta",
"nav_button_publish_message": "Julkaisutiedot",
"nav_button_publish_message": "Julkaise ilmoitus",
"prefs_users_table_base_url_header": "Palvelin URL",
"notifications_click_copy_url_title": "Kopioi linkin URL-osoite leikepöydälle",
"publish_dialog_attach_reset": "Poista liitteen URL-osoite",
@@ -160,7 +160,7 @@
"publish_dialog_priority_low": "Matala tärkeys",
"publish_dialog_priority_label": "Prioriteetti",
"prefs_reservations_delete_button": "Poista topikin oikeudet",
"account_basics_tier_admin_suffix_no_tier": "(no tier)",
"account_basics_tier_admin_suffix_no_tier": "(e tasoa)",
"prefs_notifications_delete_after_one_week_description": "Ilmoitukset poistetaan automaattisesti viikon kuluttua",
"error_boundary_unsupported_indexeddb_description": "Ntfy-verkkosovellus tarvitsee IndexedDB:n toimiakseen, eikä selaimesi tue IndexedDB:tä yksityisessä selaustilassa.<br/><br/>Vaikka tämä on valitettavaa, ntfy-verkon käyttäminen ei myöskään ole kovin järkevää yksityisessä selaustilassa, koska kaikki on tallennettu selaimen tallennustilaan. Voit lukea siitä lisää <githubLink>tästä GitHub-numerosta</githubLink> tai puhua meille <discordLink>Discordissa</discordLink> tai <matrixLink>Matrixissa</matrixLink>.",
"subscribe_dialog_subscribe_button_cancel": "Peruuta",
@@ -175,7 +175,7 @@
"notifications_click_copy_url_button": "Kopioi linkki",
"account_basics_tier_admin": "Admin",
"subscribe_dialog_subscribe_title": "Tilaa topikki",
"nav_topics_title": "Tilatut topikit",
"nav_topics_title": "Tilatut aiheet",
"prefs_notifications_sound_title": "Ilmoitusääni",
"prefs_notifications_min_priority_default_and_higher": "Oletusprioriteetti ja korkeammat",
"prefs_reservations_table_access_header": "Oikeudet",
@@ -232,7 +232,7 @@
"account_basics_username_description": "Hei, se olet sinä ❤",
"prefs_reservations_dialog_topic_label": "Topik",
"account_basics_password_dialog_confirm_password_label": "Vahvista salasana",
"action_bar_reservation_edit": "Muokkaa varatopikkia",
"action_bar_reservation_edit": "Muokkaa varausta",
"publish_dialog_base_url_placeholder": "Palvelun URL-osoite, esim. https://example.com",
"prefs_users_title": "Hallinnoi käyttäjiä",
"account_basics_tier_interval_yearly": "vuosittain",
@@ -247,7 +247,7 @@
"publish_dialog_delay_label": "Viive",
"error_boundary_button_copy_stack_trace": "Kopioi pinon jälki",
"publish_dialog_button_send": "Lähetä",
"action_bar_reservation_delete": "Poista varatopikit",
"action_bar_reservation_delete": "Poista varaus",
"publish_dialog_button_cancel_sending": "Peruuta lähetys",
"account_tokens_dialog_title_delete": "Poista käyttöoikeustunnus",
"account_usage_of_limit": "limiitistä {{limit}}",
@@ -260,7 +260,7 @@
"account_upgrade_dialog_interval_yearly": "Vuosittain",
"publish_dialog_tags_label": "Tagit",
"signup_form_password": "Salasana",
"action_bar_reservation_limit_reached": "Varatopikien raja",
"action_bar_reservation_limit_reached": "Raja saavutettu",
"account_upgrade_dialog_button_redirect_signup": "Kirjaudu nyt",
"publish_dialog_click_placeholder": "URL-osoite, joka avautuu, kun ilmoitusta napsautetaan",
"alert_not_supported_title": "Ilmoituksia ei tueta",
@@ -314,14 +314,14 @@
"notifications_click_open_button": "Avaa linkki",
"account_tokens_table_current_session": "Nykyinen selainistunto",
"account_upgrade_dialog_button_pay_now": "Maksa nyt ja tilaa",
"nav_upgrade_banner_description": "Varaa aiheita, lisää viestejä ja sähköposteja sekä suurempia liitteitä",
"nav_upgrade_banner_description": "Varaa aiheita, lisää viestejä ja sähköposteja, sekä suurempia liitteitä",
"publish_dialog_call_reset": "Poista puhelu",
"publish_dialog_other_features": "Muut ominaisuudet:",
"subscribe_dialog_subscribe_use_another_label": "Käytä toista palvelinta",
"reservation_delete_dialog_action_delete_title": "Poista välimuistissa olevat viestit ja liitteet",
"signup_error_username_taken": "Käyttäjätunnus {{username}} on jo varattu",
"account_basics_phone_numbers_dialog_code_label": "Vahvistuskoodi",
"nav_button_subscribe": "Tilaa topik",
"nav_button_subscribe": "Tilaa aihe",
"publish_dialog_topic_label": "Topikin nimi",
"reservation_delete_dialog_action_delete_description": "Välimuistissa olevat viestit ja liitteet poistetaan pysyvästi. Tätä toimintoa ei voi kumota.",
"alert_grant_button": "Myönnä nyt",
@@ -341,7 +341,7 @@
"prefs_users_dialog_base_url_label": "Palvelin URL, esim. https://ntfy.sh",
"account_usage_emails_title": "Sähköpostit lähetetty",
"account_basics_phone_numbers_dialog_channel_sms": "SMS",
"action_bar_reservation_add": "Varalla oleva topikki",
"action_bar_reservation_add": "Varalla oleva aihe",
"account_upgrade_dialog_tier_selected_label": "Valittu",
"account_upgrade_dialog_button_update_subscription": "Päivitä tilaus",
"notifications_attachment_file_video": "videotiedosto",
@@ -363,6 +363,22 @@
"notifications_none_for_topic_description": "Jos haluat lähettää ilmoituksia tähän topikkiin, PUT tai POST topikin URL-osoitteeseen.",
"notifications_none_for_any_description": "Jos haluat lähettää ilmoituksia topikkiin, PUT tai POST topikin URL-osoitteeseen. Tässä on esimerkki yhden topikin käyttämisestä.",
"notifications_no_subscriptions_title": "Näyttää siltä, että sinulla ei ole vielä tilauksia.",
"notifications_none_for_topic_title": "Et ole vielä saanut ilmoituksia tästä topikista.",
"notifications_actions_http_request_title": "Lähetä HTTP {{method}} to {{url}}"
"notifications_none_for_topic_title": "Et ole vielä saanut ilmoituksia tästä aiheesta.",
"notifications_actions_http_request_title": "Lähetä HTTP {{method}} to {{url}}",
"reserve_dialog_checkbox_label": "Käänteinen aihe ja aseta pääsy",
"publish_dialog_progress_uploading": "Lähetetään …",
"publish_dialog_title_no_topic": "Julkaise ilmoitus",
"notifications_example": "Esimerkki",
"notifications_loading": "Ladataan ilmoituksia …",
"notifications_no_subscriptions_description": "Klikkaa \"{{linktext}}\" linkkiä luodaksesi tai tilataksesi aihe. Sen jälkeen voit lähettää viestejä PUT tai POST metodeilla ja saat ilmoituksesi täällä.",
"display_name_dialog_description": "Aseta vaihtoehtoinen nimi aiheelle, joka on näytetty tilaus-listassa. Tämä auttaa tunnistamaan aiheet helpommin, joilla on hankalat nimet.",
"publish_dialog_message_published": "Ilmoitus julkaistu",
"notifications_more_details": "Saadaksesi lisää tietoa, katso <websiteLink>nettisivu</websiteLink> tai <docsLink>documentointi</docsLink>.",
"publish_dialog_attachment_limits_quota_reached": "ylittää kiintiön, {{remainingBytes}} jäljellä",
"publish_dialog_title_topic": "Julkaise aiheeseen {{topic}}",
"display_name_dialog_placeholder": "Näyttönimi",
"publish_dialog_attachment_limits_file_and_quota_reached": "ylittää {{fileSizeLimit}} tiedostokoon rajan ja määrän, {{remainingBytes}} jäljellä",
"publish_dialog_attachment_limits_file_reached": "ylittää {{fileSizeLimit}} tiedostokoon rajan",
"publish_dialog_progress_uploading_detail": "Lähetetään {{loaded}}/{{total}} ({{percent}}%) …",
"display_name_dialog_title": "Vaihda näyttönimi"
}

View File

@@ -189,7 +189,7 @@
"prefs_notifications_delete_after_three_hours_description": "Le notifiche vengono eliminate automaticamente dopo tre ore",
"error_boundary_unsupported_indexeddb_description": "L'app web ntfy ha bisogno di IndexedDB per funzionare e il tuo browser non supporta IndexedDB in modalità di navigazione privata.<br/><br/>Anche se questo è un peccato, non ha molto senso usare il web ntfy app in modalità di navigazione privata comunque, perché tutto è archiviato nella memoria del browser. Puoi leggere di più a riguardo <githubLink>in questo numero di GitHub</githubLink> o parlarci su <discordLink>Discord</discordLink> o <matrixLink>Matrix</matrixLink>.",
"nav_upgrade_banner_label": "Passa alla versione Pro di ntfy",
"alert_not_supported_context_description": "Le Notificche sono supportate solo tramite HTTPS. Questa è una limitazione delle <mdnLink>Notifications API</mdnLink>.",
"alert_not_supported_context_description": "Le Notifiche sono supportate solo tramite HTTPS. Questa è una limitazione delle <mdnLink>Notifications API</mdnLink>.",
"account_basics_password_dialog_new_password_label": "Nuova password",
"action_bar_profile_logout": "Esci",
"account_basics_tier_interval_monthly": "mensile",

View File

@@ -317,5 +317,43 @@
"account_upgrade_dialog_tier_features_emails_many": "{{emails}} maili dziennie",
"account_upgrade_dialog_tier_features_messages_one": "{{messages}} wiadomość dziennie",
"account_upgrade_dialog_tier_features_messages_few": "{{messages}} wiadomości dziennie",
"account_upgrade_dialog_tier_features_messages_many": "{{messages}} wiadomości dziennie"
"account_upgrade_dialog_tier_features_messages_many": "{{messages}} wiadomości dziennie",
"account_upgrade_dialog_tier_features_no_calls": "Brak połączeń telefonicznych",
"account_upgrade_dialog_tier_features_calls_one": "Dzienne rozmowy telefoniczne: {{calls}}",
"account_upgrade_dialog_tier_current_label": "Bieżące",
"account_basics_phone_numbers_dialog_number_label": "Numer telefonu",
"account_basics_phone_numbers_dialog_check_verification_button": "Potwierdź kod",
"account_upgrade_dialog_proration_info": "<strong>Proporcja</strong>: Przy ulepszaniu pomiędzy płatnymi planami, różnica ceny będzie <strong>pobrana natychmiast</strong>. Przy obniżaniu planu do niższych planów, środki zostaną użyte do rozliczenia przyszłych okresów subskrypcji.",
"account_upgrade_dialog_tier_features_emails_other": "Dzienne wiadomości e-mail: {{emails}}",
"account_upgrade_dialog_tier_features_calls_other": "Dzienne rozmowy telefoniczne: {{calls}}",
"account_basics_phone_numbers_dialog_verify_button_sms": "Wyślij SMS",
"account_tokens_dialog_expires_never": "Token nigdy nie wygasa",
"account_tokens_dialog_expires_x_days": "Token wygasa za {{days}} dni",
"account_basics_phone_numbers_dialog_description": "Aby używać funkcji powiadomień telefonicznych, musisz dodać i zweryfikować no najmniej jeden numer telefonu. Weryfikacja może być dokonana przez SMS lub połączenie telefoniczne.",
"account_upgrade_dialog_tier_features_messages_other": "Dzienne wiadomości: {{messages}}",
"account_basics_phone_numbers_no_phone_numbers_yet": "Brak numerów telefonów",
"account_tokens_delete_dialog_title": "Usuń token dostępu",
"publish_dialog_chip_call_label": "Rozmowa telefoniczna",
"account_basics_phone_numbers_dialog_title": "Dodaj numer telefonu",
"account_upgrade_dialog_tier_features_reservations_other": "Zarezerwowane tematy: {{reservations}}",
"account_upgrade_dialog_reservations_warning_one": "Wybrany plan zezwala na mniejszą liczbę zarezerwowanych tematów niż obecny. Przed zmianą planu, <strong>usuń co najmniej jedną rezerwację</strong>. Rezerwacje możesz usunąć w <Link>Ustawieniach</Link>.",
"account_basics_phone_numbers_description": "Dla powiadomień telefonicznych",
"account_upgrade_dialog_cancel_warning": "To <strong>anuluje Twoją subskrypcję</strong> i obniży status Twojego konta {{date}}. Tego dnia rezerwacja tematów oraz wiadomości przechowywane na serwerze <strong>zostaną usunięte</strong>.",
"account_tokens_dialog_expires_x_hours": "Token wygasa za {{hours}} godzin(y)",
"publish_dialog_chip_call_no_verified_numbers_tooltip": "Brak zweryfikowanych numerów telefonów",
"publish_dialog_call_label": "Rozmowa telefoniczna",
"account_usage_calls_title": "Wykonane połączenia telefoniczne",
"account_basics_phone_numbers_copied_to_clipboard": "Numer telefonu skopiowany do schowka",
"account_basics_phone_numbers_dialog_number_placeholder": "np. +1222333444",
"account_basics_phone_numbers_dialog_code_placeholder": "np. 123456",
"account_basics_phone_numbers_dialog_channel_call": "Zadzwoń",
"account_basics_phone_numbers_title": "Numery telefonów",
"account_usage_calls_none": "Nie wykonano żadnych połączeń z tego konta",
"publish_dialog_call_reset": "Usuń rozmowę telefoniczną",
"account_basics_phone_numbers_dialog_code_label": "Kod weryfikacyjny",
"account_basics_phone_numbers_dialog_verify_button_call": "Zadzwoń do mnie",
"publish_dialog_call_item": "Zadzwoń pod numer {{number}}",
"account_basics_phone_numbers_dialog_channel_sms": "SMS",
"account_upgrade_dialog_tier_selected_label": "Wybrane",
"account_upgrade_dialog_reservations_warning_other": "Wybrany plan zezwala na mniejszą liczbę zarezerwowanych tematów niż obecny. Przed zmianą planu, <strong>usuń co najmniej tyle rezerwacji: {{count}}</strong>. Rezerwacje możesz usunąć w <Link>Ustawieniach</Link>."
}

View File

@@ -219,5 +219,69 @@
"signup_already_have_account": "Já possui uma conta? Entrar!",
"nav_upgrade_banner_label": "Atualizar para ntfy Pro",
"account_basics_phone_numbers_dialog_description": "Para usar o recurso de notificação de chamada, é necessários adicionar e verificar pelo menos um número de telefone. A verificação pode ser feita por SMS ou chamada telefônica.",
"account_basics_phone_numbers_description": "Para notificações de chamada telefônica"
"account_basics_phone_numbers_description": "Para notificações de chamada telefônica",
"account_basics_tier_interval_monthly": "mensal",
"account_basics_tier_canceled_subscription": "Sua assinatura foi cancelada e será rebaixada para uma conta gratuita em {{date}}.",
"account_basics_password_dialog_current_password_incorrect": "Senha incorreta",
"account_basics_phone_numbers_dialog_number_label": "Número de telefone",
"account_basics_password_dialog_button_submit": "Alterar senha",
"reserve_dialog_checkbox_label": "Guardar tópico e configurar acesso",
"account_basics_username_title": "Nome de usuário",
"account_basics_phone_numbers_dialog_check_verification_button": "Confirmar código",
"account_usage_attachment_storage_title": "Armazenamento de anexos",
"account_usage_messages_title": "Mensagens publicadas",
"account_basics_phone_numbers_dialog_verify_button_sms": "Enviar SMS",
"account_basics_tier_change_button": "Mudar",
"account_basics_tier_admin_suffix_with_tier": "(com nível {{tier}})",
"account_basics_title": "Conta",
"account_basics_phone_numbers_no_phone_numbers_yet": "Ainda não há números de telefone",
"subscribe_dialog_subscribe_button_generate_topic_name": "Gerar nome",
"display_name_dialog_description": "Defina um nome alternativo para o tópico exibido na lista de inscrições. Isso pode ajudar a identificar mais facilmente tópicos com nomes complicados.",
"publish_dialog_chip_call_label": "Chamada telefônica",
"account_basics_phone_numbers_dialog_title": "Adicionar número de telefone",
"account_basics_username_admin_tooltip": "Você é Administrador",
"account_usage_reservations_none": "Nenhum tópico reservado para esta conta",
"account_usage_title": "Uso",
"account_basics_tier_upgrade_button": "Atualizar para Pro",
"subscribe_dialog_error_topic_already_reserved": "Tópico já reservado",
"account_basics_tier_admin_suffix_no_tier": "(sem nível)",
"account_basics_tier_payment_overdue": "O teu pagamento está atrasado. Por favor, atualize seu método de pagamento, ou sua conta será rebaixada em breve.",
"account_basics_tier_description": "Nível de poder da sua conta",
"account_basics_tier_free": "Grátis",
"account_basics_tier_admin": "Administrador",
"publish_dialog_chip_call_no_verified_numbers_tooltip": "Nenhum número de telefone verificado",
"account_basics_password_description": "Alterar a senha da sua conta",
"publish_dialog_call_label": "Chamada telefônica",
"account_usage_calls_title": "Chamadas de telefone feitas",
"account_basics_tier_basic": "Básico",
"alert_not_supported_context_description": "Notificações são suportadas apenas através de HTTPS. Esta é uma limitação da <mdnLink>API de Notificações</mdnLink>.",
"account_basics_phone_numbers_copied_to_clipboard": "Número de telefone copiado para a área de transferência",
"account_basics_tier_title": "Tipo de conta",
"account_basics_phone_numbers_dialog_number_placeholder": "ex. +1222333444",
"account_basics_phone_numbers_dialog_code_placeholder": "ex. 123456",
"account_basics_tier_manage_billing_button": "Gerenciar faturamento",
"account_basics_username_description": "Ei, é você ❤",
"account_basics_password_dialog_confirm_password_label": "Confirmar senha",
"account_basics_tier_interval_yearly": "anual",
"account_basics_phone_numbers_dialog_channel_call": "Ligar",
"account_basics_password_title": "Senha",
"account_basics_password_dialog_new_password_label": "Nova senha",
"display_name_dialog_placeholder": "Nome de exibição",
"account_usage_of_limit": "de {{limit}}",
"account_basics_password_dialog_title": "Alterar senha",
"account_usage_limits_reset_daily": "Os limites de uso são redefinidos diariamente à meia-noite (UTC)",
"account_usage_unlimited": "Ilimitado",
"account_basics_password_dialog_current_password_label": "Senha atual",
"account_usage_reservations_title": "Tópicos reservados",
"account_usage_calls_none": "Nenhum telefonema pode ser feito com esta conta",
"display_name_dialog_title": "Alterar o nome de exibição",
"nav_upgrade_banner_description": "Guarde tópicos, mais mensagens & emails e anexos grandes",
"publish_dialog_call_reset": "Remover chamada telefônica",
"account_basics_phone_numbers_dialog_code_label": "Código de verificação",
"account_basics_tier_paid_until": "Assinatura paga até {{date}}, será renovada automaticamente",
"account_usage_attachment_storage_description": "{{filesize}} por arquivo, excluído após {{expiry}}",
"account_basics_phone_numbers_dialog_verify_button_call": "Ligar pra mim",
"publish_dialog_call_item": "Ligue para o número de telefone {{number}}",
"account_usage_emails_title": "Emails enviados",
"account_basics_phone_numbers_dialog_channel_sms": "SMS"
}

View File

@@ -101,5 +101,27 @@
"notifications_click_open_button": "Deschide link",
"publish_dialog_emoji_picker_show": "Alege un emoji",
"notifications_loading": "Încărcare notificări…",
"publish_dialog_priority_low": "Prioritate joasă"
"publish_dialog_priority_low": "Prioritate joasă",
"signup_form_username": "Nume de utilizator",
"signup_form_button_submit": "Înscrie-te",
"common_copy_to_clipboard": "Copiază în clipboard",
"signup_form_toggle_password_visibility": "Schimbă vizibilitatea parolei",
"signup_title": "Crează un cont ntfy",
"signup_already_have_account": "Deja ai un cont? Autentifică-te!",
"login_disabled": "Autentificarea este dezactivată",
"signup_error_creation_limit_reached": "S-a atins limita de conturi",
"action_bar_toggle_action_menu": "Deschide/Închide meniul de acțiuni",
"action_bar_sign_up": "Înscriere",
"message_bar_publish": "Publică mesajul",
"login_link_signup": "Înscrie-te",
"action_bar_sign_in": "Autentificare",
"action_bar_reservation_edit": "Schimbă rezervarea",
"action_bar_reservation_delete": "Șterge rezervarea",
"login_form_button_submit": "Autentifică-te",
"signup_disabled": "Înscrierea este dezactivată",
"action_bar_profile_logout": "Ieșire",
"message_bar_show_dialog": "Arată dialogul de publicare",
"signup_error_username_taken": "Numele de utilizator {{username}} este deja folosit",
"login_title": "Autentifică-te în contul ntfy",
"action_bar_reservation_add": "Rezervă topicul"
}