Compare commits

...

62 commits

Author SHA1 Message Date
MaximilianKohler
95dabe593b
Update upgrade.md +binary info (#1613) 2023-12-02 22:41:27 +05:30
MaximilianKohler
e06d379953
Update installation.md +tutorials (#1611) 2023-12-01 08:52:06 +05:30
MaximilianKohler
7c991677eb
Update installation.md +changing port (#1607) 2023-11-28 12:28:40 +05:30
Isaak Tsalicoglou
fa506643a4
Add i18n Greek translation (#1605) 2023-11-27 12:53:28 +05:30
Kailash Nadh
53eb71a83b Add 404 HTTP handlers to prevent those requests going to BasicAuth endpoints. 2023-11-25 11:53:56 +05:30
relikd
2ce2a11c7e
feat: docker compose use alpine for postgres (#1603) 2023-11-24 14:48:57 +05:30
relikd
52ee79bf86
chore: noreferrer for listmonk url in footer (#1601)
* chore: noreferrer for listmonk url in footer

* chore: noreferrer for email templates
2023-11-23 06:29:31 +05:30
dependabot[bot]
524be2753b
Bump tinymce from 5.10.8 to 5.10.9 in /frontend (#1592)
Bumps [tinymce](https://github.com/tinymce/tinymce/tree/HEAD/modules/tinymce) from 5.10.8 to 5.10.9.
- [Changelog](https://github.com/tinymce/tinymce/blob/5.10.9/modules/tinymce/CHANGELOG.md)
- [Commits](https://github.com/tinymce/tinymce/commits/5.10.9/modules/tinymce)

---
updated-dependencies:
- dependency-name: tinymce
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-21 11:07:08 +05:30
Luc Didry
75befe5214
📝🐛 — Fix /api/subscribers/lists doc (#1594) 2023-11-21 11:06:42 +05:30
guangwu
4577868567
chore: remove refs to deprecated io/ioutil (#1593)
Signed-off-by: guoguangwu <guoguangwu@magic-shield.com>
2023-11-16 13:57:00 +05:30
Kailash Nadh
c59825f3a5 Fix broken sorting (lists -> subcount, subscribers -> status) in queries. Closes #1076. 2023-11-12 10:29:32 +05:30
Kailash Nadh
06b4494200 Fix incorrect sanitisation of search queries on list/campaign frontend. 2023-11-12 10:29:32 +05:30
dependabot[bot]
82c74cd544
Bump golang.org/x/image from 0.6.0 to 0.10.0 (#1580)
Bumps [golang.org/x/image](https://github.com/golang/image) from 0.6.0 to 0.10.0.
- [Commits](https://github.com/golang/image/compare/v0.6.0...v0.10.0)

---
updated-dependencies:
- dependency-name: golang.org/x/image
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-11 20:33:06 +05:30
MaximilianKohler
4c8c19ebb9
Update configuration.md - update path due to recent install path change (#1581)
Since `&& cd listmonk` was added to the installation, this path is changed.
2023-11-11 20:32:40 +05:30
dependabot[bot]
d439ecfd84
Bump axios from 0.27.2 to 1.6.0 in /frontend (#1587)
Bumps [axios](https://github.com/axios/axios) from 0.27.2 to 1.6.0.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v0.27.2...v1.6.0)

---
updated-dependencies:
- dependency-name: axios
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-11 20:31:09 +05:30
Kailash Nadh
ef084956b4 Make the unsub form on opt-in confirmation e-mail open 'manage' by default. Closes #1515. 2023-11-11 20:28:42 +05:30
Kailash Nadh
44d3462559 Fix 'confirmed' subscriptions becoming 'unconfirmed' on public form re-signup. Closes #1441. 2023-11-11 18:46:38 +05:30
Kailash Nadh
62be5e2181 Fix mysteriously broken frontend build by switching eslint parser dep. 2023-11-11 15:56:42 +05:30
Kailash Nadh
49ec11ca9c Merge branch 'refactor-docs' 2023-11-10 22:44:24 +05:30
Kailash Nadh
e8ecdf8cde Fix mkdocs links in docs. 2023-11-10 22:44:08 +05:30
Kailash Nadh
ff135ecee3 Add 'copy code' button to code snippets in docs. 2023-11-10 22:37:05 +05:30
Kailash Nadh
be4be729a9 Refactor and clean up API Markdown docs.
This has been pending for several years. Finally, managed to do with
GPT-4 "auto" cleaning language and other consistency issues.
90% GPT4, 10% manual hand-wringing.

GPT-4 prompt:
```
- Fix grammar, simplify language, and make language and terms consistent.
- Remove superfluous language, avoid unncessary "the"s etc.
- Change endpoint description in tables that list endpoints, change to terse imperative language.
- Parameter / field description in Parameter tables, don't be imperative or use active sentences. Only describe the parameter passively and tersely.
- Remove backticks from all headings and table values.
- Format markdown tables.
- Standardize "data type" columns values into number, string[], string, JSON.
- Remove the "parameter type" column from tables.
- Wrap ID parameters in URIs in braces. Eg: /lists/list_id to /lists/{list_id}
- Add a markdown line above every API path heading that begins with GET, POST, PUT, DELETE.
- Leave the lines starting with `#code-` untouched
- Do not add new sections, headings, or any new content. Only edit existing content as described above.
- Do not remove any sections or any existing content.
```
2023-11-10 22:30:14 +05:30
Kailash Nadh
f8a55f84fa Fix mkdocs navbar shadow 2023-11-10 12:17:11 +05:30
Thomas-LCP
9e3af91dc5
Update fr.json (#1586) 2023-11-10 11:47:14 +05:30
Kailash Nadh
f720e88b44 Refactor <hr> style in mkdocs template. 2023-11-10 09:03:47 +05:30
Kailash Nadh
b29f565b68 Fix broken table in campaign doc. Closes #1585. 2023-11-09 16:48:45 +05:30
Kailash Nadh
3641f74c64 Add missing header field in campaign creation docs. Closes #1561. 2023-11-01 18:49:48 +05:30
dependabot[bot]
ccceaa6e92
Bump @babel/traverse from 7.21.3 to 7.23.2 in /frontend (#1563)
Bumps [@babel/traverse](https://github.com/babel/babel/tree/HEAD/packages/babel-traverse) from 7.21.3 to 7.23.2.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.23.2/packages/babel-traverse)

---
updated-dependencies:
- dependency-name: "@babel/traverse"
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-31 11:04:57 +05:30
dependabot[bot]
c7882bb220
Bump tinymce from 5.10.7 to 5.10.8 in /frontend (#1564)
Bumps [tinymce](https://github.com/tinymce/tinymce/tree/HEAD/modules/tinymce) from 5.10.7 to 5.10.8.
- [Changelog](https://github.com/tinymce/tinymce/blob/5.10.8/modules/tinymce/CHANGELOG.md)
- [Commits](https://github.com/tinymce/tinymce/commits/5.10.8/modules/tinymce)

---
updated-dependencies:
- dependency-name: tinymce
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-31 11:04:51 +05:30
MaximilianKohler
fd05a6d17d
Update installation.md - add cd listmonk (#1576) 2023-10-31 11:04:38 +05:30
Fussel132
06332d52a0
Update de.json (#1572)
The settings idleTimeout and waitTimeout were exactly the same in the German translation and could not be distinguished
2023-10-30 10:55:28 +05:30
Kailash Nadh
1ebd80c577 Add Hebrew translation. Closes #1517.
Completed @liy1414's pending PR with automated GPT-4 translation.

Co-Authored-By: liy1414 <88595225+liy1414@users.noreply.github.com>
2023-10-15 21:00:12 +05:30
Kailash Nadh
491fab38cb Update i18n language files. 2023-10-15 20:55:46 +05:30
dependabot[bot]
764a052ecc
Bump golang.org/x/net from 0.8.0 to 0.17.0 (#1555)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.8.0 to 0.17.0.
- [Commits](https://github.com/golang/net/compare/v0.8.0...v0.17.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-15 20:42:38 +05:30
Robert R George
c911aeb20e
Fix attachments being omitted from postback (#1557) 2023-10-14 02:06:42 +05:30
Kailash Nadh
954ed45009 Fix incorrect Slovak language code. Closes #1533. 2023-09-25 11:14:29 +05:30
Kailash Nadh
a61a9d8c04 Fix preference management logic to avoid unnecessary DB calls. 2023-09-25 11:14:29 +05:30
Jannes Blobel
99c71a2a0a
fix: update inlang settings (#1529) 2023-09-20 14:19:08 +05:30
Kailash Nadh
82c3c6878b Clean root URL of trailing slashes when updating settings. 2023-09-19 15:23:32 +05:30
Kailash Nadh
04e571d43a Fix file fetch in attachments failing for signed URLs. Closes #1499. 2023-09-19 15:20:27 +05:30
Kailash Nadh
8f2a08b8db Fix invalid suffix 'd' in timestring string in s3 expiry config. 2023-09-19 14:45:51 +05:30
Kailash Nadh
11f90b2f62 Fix typo in i18n S3 expiry description. 2023-09-19 13:51:26 +05:30
Kailash Nadh
5af6252b14 Fix make not picking up semver from git archive builds. Fixes #1380. 2023-09-19 11:42:07 +05:30
Kailash Nadh
717a6362d2 Add Sprig and other tpl functions to static templates as well. Closes #1527. 2023-09-19 11:19:34 +05:30
Heiko
7019f26280
Fix docs for public subscription api (#1522) 2023-09-12 21:32:23 +05:30
David Lorenz
9423c74d96
Docker Multi Arch (esp. ARM) builds: Improving Build File (#1451)
Co-authored-by: David Lorenz <david@wahnsinn.design>
2023-09-07 22:23:17 +05:30
Kailash Nadh
2b95c88188
Add Postmark bounce webhook support (refactor #1385) (#1485)
Co-authored-by: Thomas Siebers <tom@tsiebers.de>
2023-08-31 21:27:34 +05:30
MaximilianKohler
e5ac111747
Update installation.md - fly.io instructions not working (#1487) 2023-08-31 19:43:40 +05:30
MaximilianKohler
34d86fc387
Update installation.md (#1494)
Add note about docker version
2023-08-30 10:51:20 +05:30
Karan Sharma
5664e5cc9f
fix: replace docker-compose with docker compose (#1490)
Fixes https://github.com/knadh/listmonk/issues/1431
2023-08-28 20:13:03 +05:30
Kailash Nadh
83c88d73d7 Escape search query in list search. Closes #1471. 2023-08-27 14:35:42 +05:30
MaximilianKohler
16eeb9401e
added "tutorials" section and other minor edits (#1444) 2023-08-27 13:34:22 +05:30
Kailash Nadh
e317f2c5ff Modify sed flag based on OS. Closes #1474.
Co-authored-by: Holden Moreland <holden@hdmoreland.com>
2023-08-27 13:32:57 +05:30
Ryan Lahfa
25513b8104
go: bump to 1.20 (#1479) 2023-08-27 13:24:47 +05:30
Kailash Nadh
eefcbc30a3 Fix hardcoded DB name in 'about' SQL query. Closes #1477. 2023-08-27 13:20:41 +05:30
MaximilianKohler
52a7298a9d
Update bounces.md (#1468) 2023-08-27 12:54:31 +05:30
Kailash Nadh
6af904dbd4 Lowercase email search on UI to match lowercase email index in DB. Closes #1464. 2023-08-27 12:17:50 +05:30
Kailash Nadh
07a9632860 Update latest version on homepage. 2023-08-21 10:12:36 +05:30
Kailash Nadh
4b05ab1920 Add check for SES 'invalid domain' transient bounces. Closes #1463. 2023-08-20 09:48:21 +05:30
MaximilianKohler
e2f1313566
Update configuration.md (#1465) 2023-08-20 09:41:30 +05:30
MaximilianKohler
fcdea6050e
Update bounces.md (#1466) 2023-08-20 09:41:13 +05:30
MaximilianKohler
2888dcdd45
Update upgrade.md (#1467) 2023-08-20 09:40:44 +05:30
99 changed files with 2922 additions and 910 deletions

View file

@ -41,6 +41,10 @@ jobs:
run: |
make dist
- name: Check Docker Version
run: |
docker version
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v4
with:

View file

@ -1,6 +1,8 @@
env:
- GO111MODULE=on
- CGO_ENABLED=0
- GITHUB_ORG=knadh
- DOCKER_ORG=listmonk
before:
hooks:
@ -10,9 +12,9 @@ builds:
- binary: listmonk
main: ./cmd
goos:
- linux
- windows
- darwin
- linux
- freebsd
- openbsd
- netbsd
@ -41,123 +43,123 @@ dockers:
goos: linux
goarch: amd64
ids:
- listmonk
- listmonk
image_templates:
- "listmonk/{{ .ProjectName }}:latest-amd64"
- "listmonk/{{ .ProjectName }}:{{ .Tag }}-amd64"
- "ghcr.io/knadh/{{ .ProjectName }}:latest-amd64"
- "ghcr.io/knadh/{{ .ProjectName }}:{{ .Tag }}-amd64"
- "{{ .Env.DOCKER_ORG }}/{{ .ProjectName }}:latest-amd64"
- "{{ .Env.DOCKER_ORG }}/{{ .ProjectName }}:{{ .Tag }}-amd64"
- "ghcr.io/{{ .Env.GITHUB_ORG }}/{{ .ProjectName }}:latest-amd64"
- "ghcr.io/{{ .Env.GITHUB_ORG }}/{{ .ProjectName }}:{{ .Tag }}-amd64"
build_flag_templates:
- --platform=linux/amd64
- --label=org.opencontainers.image.title={{ .ProjectName }}
- --label=org.opencontainers.image.description={{ .ProjectName }}
- --label=org.opencontainers.image.url=https://github.com/knadh/{{ .ProjectName }}
- --label=org.opencontainers.image.source=https://github.com/knadh/{{ .ProjectName }}
- --label=org.opencontainers.image.version={{ .Version }}
- --label=org.opencontainers.image.created={{ time "2006-01-02T15:04:05Z07:00" }}
- --label=org.opencontainers.image.revision={{ .FullCommit }}
- --label=org.opencontainers.image.licenses=AGPL-3.0
- --platform=linux/amd64
- --label=org.opencontainers.image.title={{ .ProjectName }}
- --label=org.opencontainers.image.description={{ .ProjectName }}
- --label=org.opencontainers.image.url=https://github.com/{{ .Env.GITHUB_ORG }}/{{ .ProjectName }}
- --label=org.opencontainers.image.source=https://github.com/{{ .Env.GITHUB_ORG }}/{{ .ProjectName }}
- --label=org.opencontainers.image.version={{ .Version }}
- --label=org.opencontainers.image.created={{ time "2006-01-02T15:04:05Z07:00" }}
- --label=org.opencontainers.image.revision={{ .FullCommit }}
- --label=org.opencontainers.image.licenses=AGPL-3.0
dockerfile: Dockerfile
extra_files:
- config.toml.sample
- config-demo.toml
- config.toml.sample
- config-demo.toml
- use: buildx
goos: linux
goarch: arm64
ids:
- listmonk
- listmonk
image_templates:
- "listmonk/{{ .ProjectName }}:latest-arm64v8"
- "listmonk/{{ .ProjectName }}:{{ .Tag }}-arm64v8"
- "ghcr.io/knadh/{{ .ProjectName }}:latest-arm64v8"
- "ghcr.io/knadh/{{ .ProjectName }}:{{ .Tag }}-arm64v8"
- "{{ .Env.DOCKER_ORG }}/{{ .ProjectName }}:latest-arm64v8"
- "{{ .Env.DOCKER_ORG }}/{{ .ProjectName }}:{{ .Tag }}-arm64v8"
- "ghcr.io/{{ .Env.GITHUB_ORG }}/{{ .ProjectName }}:latest-arm64v8"
- "ghcr.io/{{ .Env.GITHUB_ORG }}/{{ .ProjectName }}:{{ .Tag }}-arm64v8"
build_flag_templates:
- --platform=linux/arm64/v8
- --label=org.opencontainers.image.title={{ .ProjectName }}
- --label=org.opencontainers.image.description={{ .ProjectName }}
- --label=org.opencontainers.image.url=https://github.com/knadh/{{ .ProjectName }}
- --label=org.opencontainers.image.source=https://github.com/knadh/{{ .ProjectName }}
- --label=org.opencontainers.image.version={{ .Version }}
- --label=org.opencontainers.image.created={{ time "2006-01-02T15:04:05Z07:00" }}
- --label=org.opencontainers.image.revision={{ .FullCommit }}
- --label=org.opencontainers.image.licenses=AGPL-3.0
- --platform=linux/arm64/v8
- --label=org.opencontainers.image.title={{ .ProjectName }}
- --label=org.opencontainers.image.description={{ .ProjectName }}
- --label=org.opencontainers.image.url=https://github.com/{{ .Env.GITHUB_ORG }}/{{ .ProjectName }}
- --label=org.opencontainers.image.source=https://github.com/{{ .Env.GITHUB_ORG }}/{{ .ProjectName }}
- --label=org.opencontainers.image.version={{ .Version }}
- --label=org.opencontainers.image.created={{ time "2006-01-02T15:04:05Z07:00" }}
- --label=org.opencontainers.image.revision={{ .FullCommit }}
- --label=org.opencontainers.image.licenses=AGPL-3.0
dockerfile: Dockerfile
extra_files:
- config.toml.sample
- config-demo.toml
- config.toml.sample
- config-demo.toml
- use: buildx
goos: linux
goarch: arm
goarm: 6
ids:
- listmonk
- listmonk
image_templates:
- "listmonk/{{ .ProjectName }}:latest-armv6"
- "listmonk/{{ .ProjectName }}:{{ .Tag }}-armv6"
- "ghcr.io/knadh/{{ .ProjectName }}:latest-armv6"
- "ghcr.io/knadh/{{ .ProjectName }}:{{ .Tag }}-armv6"
- "{{ .Env.DOCKER_ORG }}/{{ .ProjectName }}:latest-armv6"
- "{{ .Env.DOCKER_ORG }}/{{ .ProjectName }}:{{ .Tag }}-armv6"
- "ghcr.io/{{ .Env.GITHUB_ORG }}/{{ .ProjectName }}:latest-armv6"
- "ghcr.io/{{ .Env.GITHUB_ORG }}/{{ .ProjectName }}:{{ .Tag }}-armv6"
build_flag_templates:
- --platform=linux/arm/v6
- --label=org.opencontainers.image.title={{ .ProjectName }}
- --label=org.opencontainers.image.description={{ .ProjectName }}
- --label=org.opencontainers.image.url=https://github.com/knadh/{{ .ProjectName }}
- --label=org.opencontainers.image.source=https://github.com/knadh/{{ .ProjectName }}
- --label=org.opencontainers.image.version={{ .Version }}
- --label=org.opencontainers.image.created={{ time "2006-01-02T15:04:05Z07:00" }}
- --label=org.opencontainers.image.revision={{ .FullCommit }}
- --label=org.opencontainers.image.licenses=AGPL-3.0
- --platform=linux/arm/v6
- --label=org.opencontainers.image.title={{ .ProjectName }}
- --label=org.opencontainers.image.description={{ .ProjectName }}
- --label=org.opencontainers.image.url=https://github.com/{{ .Env.GITHUB_ORG }}/{{ .ProjectName }}
- --label=org.opencontainers.image.source=https://github.com/{{ .Env.GITHUB_ORG }}/{{ .ProjectName }}
- --label=org.opencontainers.image.version={{ .Version }}
- --label=org.opencontainers.image.created={{ time "2006-01-02T15:04:05Z07:00" }}
- --label=org.opencontainers.image.revision={{ .FullCommit }}
- --label=org.opencontainers.image.licenses=AGPL-3.0
dockerfile: Dockerfile
extra_files:
- config.toml.sample
- config-demo.toml
- config.toml.sample
- config-demo.toml
- use: buildx
goos: linux
goarch: arm
goarm: 7
ids:
- listmonk
- listmonk
image_templates:
- "listmonk/{{ .ProjectName }}:latest-armv7"
- "listmonk/{{ .ProjectName }}:{{ .Tag }}-armv7"
- "ghcr.io/knadh/{{ .ProjectName }}:latest-armv7"
- "ghcr.io/knadh/{{ .ProjectName }}:{{ .Tag }}-armv7"
- "{{ .Env.DOCKER_ORG }}/{{ .ProjectName }}:latest-armv7"
- "{{ .Env.DOCKER_ORG }}/{{ .ProjectName }}:{{ .Tag }}-armv7"
- "ghcr.io/{{ .Env.GITHUB_ORG }}/{{ .ProjectName }}:latest-armv7"
- "ghcr.io/{{ .Env.GITHUB_ORG }}/{{ .ProjectName }}:{{ .Tag }}-armv7"
build_flag_templates:
- --platform=linux/arm/v7
- --label=org.opencontainers.image.title={{ .ProjectName }}
- --label=org.opencontainers.image.description={{ .ProjectName }}
- --label=org.opencontainers.image.url=https://github.com/knadh/{{ .ProjectName }}
- --label=org.opencontainers.image.source=https://github.com/knadh/{{ .ProjectName }}
- --label=org.opencontainers.image.version={{ .Version }}
- --label=org.opencontainers.image.created={{ time "2006-01-02T15:04:05Z07:00" }}
- --label=org.opencontainers.image.revision={{ .FullCommit }}
- --label=org.opencontainers.image.licenses=AGPL-3.0
- --platform=linux/arm/v7
- --label=org.opencontainers.image.title={{ .ProjectName }}
- --label=org.opencontainers.image.description={{ .ProjectName }}
- --label=org.opencontainers.image.url=https://github.com/{{ .Env.GITHUB_ORG }}/{{ .ProjectName }}
- --label=org.opencontainers.image.source=https://github.com/{{ .Env.GITHUB_ORG }}/{{ .ProjectName }}
- --label=org.opencontainers.image.version={{ .Version }}
- --label=org.opencontainers.image.created={{ time "2006-01-02T15:04:05Z07:00" }}
- --label=org.opencontainers.image.revision={{ .FullCommit }}
- --label=org.opencontainers.image.licenses=AGPL-3.0
dockerfile: Dockerfile
extra_files:
- config.toml.sample
- config-demo.toml
- config.toml.sample
- config-demo.toml
docker_manifests:
- name_template: listmonk/{{ .ProjectName }}:latest
- name_template: "{{ .Env.DOCKER_ORG }}/{{ .ProjectName }}:latest"
image_templates:
- listmonk/{{ .ProjectName }}:latest-amd64
- listmonk/{{ .ProjectName }}:latest-arm64v8
- listmonk/{{ .ProjectName }}:latest-armv6
- listmonk/{{ .ProjectName }}:latest-armv7
- name_template: listmonk/{{ .ProjectName }}:{{ .Tag }}
- "{{ .Env.DOCKER_ORG }}/{{ .ProjectName }}:latest-amd64"
- "{{ .Env.DOCKER_ORG }}/{{ .ProjectName }}:latest-arm64v8"
- "{{ .Env.DOCKER_ORG }}/{{ .ProjectName }}:latest-armv6"
- "{{ .Env.DOCKER_ORG }}/{{ .ProjectName }}:latest-armv7"
- name_template: "{{ .Env.DOCKER_ORG }}/{{ .ProjectName }}:{{ .Tag }}"
image_templates:
- listmonk/{{ .ProjectName }}:{{ .Tag }}-amd64
- listmonk/{{ .ProjectName }}:{{ .Tag }}-arm64v8
- listmonk/{{ .ProjectName }}:{{ .Tag }}-armv6
- listmonk/{{ .ProjectName }}:{{ .Tag }}-armv7
- name_template: ghcr.io/knadh/{{ .ProjectName }}:latest
- "{{ .Env.DOCKER_ORG }}/{{ .ProjectName }}:{{ .Tag }}-amd64"
- "{{ .Env.DOCKER_ORG }}/{{ .ProjectName }}:{{ .Tag }}-arm64v8"
- "{{ .Env.DOCKER_ORG }}/{{ .ProjectName }}:{{ .Tag }}-armv6"
- "{{ .Env.DOCKER_ORG }}/{{ .ProjectName }}:{{ .Tag }}-armv7"
- name_template: ghcr.io/{{ .Env.GITHUB_ORG }}/{{ .ProjectName }}:latest
image_templates:
- ghcr.io/knadh/{{ .ProjectName }}:latest-amd64
- ghcr.io/knadh/{{ .ProjectName }}:latest-arm64v8
- ghcr.io/knadh/{{ .ProjectName }}:latest-armv6
- ghcr.io/knadh/{{ .ProjectName }}:latest-armv7
- name_template: ghcr.io/knadh/{{ .ProjectName }}:{{ .Tag }}
- ghcr.io/{{ .Env.GITHUB_ORG }}/{{ .ProjectName }}:latest-amd64
- ghcr.io/{{ .Env.GITHUB_ORG }}/{{ .ProjectName }}:latest-arm64v8
- ghcr.io/{{ .Env.GITHUB_ORG }}/{{ .ProjectName }}:latest-armv6
- ghcr.io/{{ .Env.GITHUB_ORG }}/{{ .ProjectName }}:latest-armv7
- name_template: ghcr.io/{{ .Env.GITHUB_ORG }}/{{ .ProjectName }}:{{ .Tag }}
image_templates:
- ghcr.io/knadh/{{ .ProjectName }}:{{ .Tag }}-amd64
- ghcr.io/knadh/{{ .ProjectName }}:{{ .Tag }}-arm64v8
- ghcr.io/knadh/{{ .ProjectName }}:{{ .Tag }}-armv6
- ghcr.io/knadh/{{ .ProjectName }}:{{ .Tag }}-armv7
- ghcr.io/{{ .Env.GITHUB_ORG }}/{{ .ProjectName }}:{{ .Tag }}-amd64
- ghcr.io/{{ .Env.GITHUB_ORG }}/{{ .ProjectName }}:{{ .Tag }}-arm64v8
- ghcr.io/{{ .Env.GITHUB_ORG }}/{{ .ProjectName }}:{{ .Tag }}-armv6
- ghcr.io/{{ .Env.GITHUB_ORG }}/{{ .ProjectName }}:{{ .Tag }}-armv7

View file

@ -1,8 +1,8 @@
# Try to get the commit hash from 1) git 2) the VERSION file 3) fallback.
LAST_COMMIT := $(or $(shell git rev-parse --short HEAD 2> /dev/null),$(shell head -n 1 VERSION | grep -oP -m 1 "^[a-z0-9]+$$"),"UNKNOWN")
LAST_COMMIT := $(or $(shell git rev-parse --short HEAD 2> /dev/null),$(shell head -n 1 VERSION | grep -oP -m 1 "^[a-z0-9]+$$"),"")
# Try to get the semver from 1) git 2) the VERSION file 3) fallback.
VERSION := $(or $(shell git describe --tags --abbrev=0 2> /dev/null),$(shell grep -oP "tag: \K(.*)(?=,)" VERSION),"v0.0.0")
VERSION := $(or $(shell git describe --tags --abbrev=0 2> /dev/null),$(shell grep -oP 'tag: \Kv\d+\.\d+\.\d+(-[a-zA-Z0-9.-]+)?' VERSION),"v0.0.0")
BUILDSTR := ${VERSION} (\#${LAST_COMMIT} $(shell date -u +"%Y-%m-%dT%H:%M:%S%z"))
@ -89,13 +89,13 @@ release:
.PHONY: build-dev-docker
build-dev-docker: build ## Build docker containers for the entire suite (Front/Core/PG).
cd dev; \
docker-compose build ; \
docker compose build ; \
# Spin a local docker suite for local development.
.PHONY: dev-docker
dev-docker: build-dev-docker ## Build and spawns docker containers for the entire suite (Front/Core/PG).
cd dev; \
docker-compose up
docker compose up
# Run the backend in docker-dev mode. The frontend assets in dev mode are loaded from disk from frontend/dist.
.PHONY: run-backend-docker
@ -106,10 +106,10 @@ run-backend-docker:
.PHONY: rm-dev-docker
rm-dev-docker: build ## Delete the docker containers including DB volumes.
cd dev; \
docker-compose down -v ; \
docker compose down -v ; \
# Setup the db for local dev docker suite.
.PHONY: init-dev-docker
init-dev-docker: build-dev-docker ## Delete the docker containers including DB volumes.
cd dev; \
docker-compose run --rm backend sh -c "make dist && ./listmonk --install --idempotent --yes --config dev/config.toml"
docker compose run --rm backend sh -c "make dist && ./listmonk --install --idempotent --yes --config dev/config.toml"

View file

@ -2,7 +2,7 @@ package main
import (
"encoding/json"
"io/ioutil"
"io"
"net/http"
"strconv"
"time"
@ -121,7 +121,7 @@ func handleBounceWebhook(c echo.Context) error {
)
// Read the request body instead of using c.Bind() to read to save the entire raw request as meta.
rawReq, err := ioutil.ReadAll(c.Request().Body)
rawReq, err := io.ReadAll(c.Request().Body)
if err != nil {
app.log.Printf("error reading ses notification body: %v", err)
return echo.NewHTTPError(http.StatusBadRequest, app.i18n.Ts("globals.messages.internalError"))
@ -191,6 +191,19 @@ func handleBounceWebhook(c echo.Context) error {
}
bounces = append(bounces, bs...)
// Postmark.
case service == "postmark" && app.constants.BouncePostmarkEnabled:
bs, err := app.bounce.Postmark.ProcessBounce(rawReq, c)
if err != nil {
app.log.Printf("error processing postmark notification: %v", err)
if _, ok := err.(*echo.HTTPError); ok {
return err
}
return echo.NewHTTPError(http.StatusBadRequest, app.i18n.T("globals.messages.invalidData"))
}
bounces = append(bounces, bs...)
default:
return echo.NewHTTPError(http.StatusBadRequest, app.i18n.Ts("bounces.unknownService"))
}

View file

@ -69,6 +69,7 @@ func initHTTPHandlers(e *echo.Echo, app *App) {
e.GET("/", func(c echo.Context) error {
return c.Render(http.StatusOK, "home", publicTpl{Title: "listmonk"})
})
g.GET(path.Join(adminRoot, ""), handleAdminPage)
g.GET(path.Join(adminRoot, "/custom.css"), serveCustomApperance("admin.custom_css"))
g.GET(path.Join(adminRoot, "/custom.js"), serveCustomApperance("admin.custom_js"))
@ -213,6 +214,18 @@ func initHTTPHandlers(e *echo.Echo, app *App) {
// Public health API endpoint.
e.GET("/health", handleHealthCheck)
// 404 pages.
e.RouteNotFound("/*", func(c echo.Context) error {
return c.Render(http.StatusNotFound, tplMessage,
makeMsgTpl("404 - "+app.i18n.T("public.notFoundTitle"), "", ""))
})
e.RouteNotFound("/api/*", func(c echo.Context) error {
return echo.NewHTTPError(http.StatusNotFound, "404 unknown endpoint")
})
e.RouteNotFound("/admin/*", func(c echo.Context) error {
return echo.NewHTTPError(http.StatusNotFound, "404 page not found")
})
}
// handleAdminPage is the root handler that renders the Javascript admin frontend.

View file

@ -3,7 +3,7 @@ package main
import (
"encoding/json"
"io"
"io/ioutil"
"os"
"net/http"
"strings"
@ -66,7 +66,7 @@ func handleImportSubscribers(c echo.Context) error {
}
defer src.Close()
out, err := ioutil.TempFile("", "listmonk")
out, err := os.CreateTemp("", "listmonk")
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError,
app.i18n.Ts("import.errorCopyingFile", "error", err.Error()))

View file

@ -103,6 +103,7 @@ type constants struct {
BounceWebhooksEnabled bool
BounceSESEnabled bool
BounceSendgridEnabled bool
BouncePostmarkEnabled bool
}
type notifTpls struct {
@ -400,6 +401,8 @@ func initConstants() *constants {
c.BounceWebhooksEnabled = ko.Bool("bounce.webhooks_enabled")
c.BounceSESEnabled = ko.Bool("bounce.ses_enabled")
c.BounceSendgridEnabled = ko.Bool("bounce.sendgrid_enabled")
c.BouncePostmarkEnabled = ko.Bool("bounce.postmark.enabled")
return &c
}
@ -572,6 +575,7 @@ func initMediaStore() media.Store {
case "s3":
var o s3.Opt
ko.Unmarshal("upload.s3", &o)
up, err := s3.NewS3Store(o)
if err != nil {
lo.Fatalf("error initializing s3 upload provider %s", err)
@ -602,33 +606,7 @@ func initMediaStore() media.Store {
// initNotifTemplates compiles and returns e-mail notification templates that are
// used for sending ad-hoc notifications to admins and subscribers.
func initNotifTemplates(path string, fs stuffbin.FileSystem, i *i18n.I18n, cs *constants) *notifTpls {
// Register utility functions that the e-mail templates can use.
funcs := template.FuncMap{
"RootURL": func() string {
return cs.RootURL
},
"LogoURL": func() string {
return cs.LogoURL
},
"Date": func(layout string) string {
if layout == "" {
layout = time.ANSIC
}
return time.Now().Format(layout)
},
"L": func() *i18n.I18n {
return i
},
"Safe": func(safeHTML string) template.HTML {
return template.HTML(safeHTML)
},
}
for k, v := range sprig.GenericFuncMap() {
funcs[k] = v
}
tpls, err := stuffbin.ParseTemplatesGlob(funcs, fs, "/static/email-templates/*.html")
tpls, err := stuffbin.ParseTemplatesGlob(initTplFuncs(i, cs), fs, "/static/email-templates/*.html")
if err != nil {
lo.Fatalf("error parsing e-mail notif templates: %v", err)
}
@ -668,7 +646,15 @@ func initBounceManager(app *App) *bounce.Manager {
SESEnabled: ko.Bool("bounce.ses_enabled"),
SendgridEnabled: ko.Bool("bounce.sendgrid_enabled"),
SendgridKey: ko.String("bounce.sendgrid_key"),
Postmark: struct {
Enabled bool
Username string
Password string
}{
ko.Bool("bounce.postmark.enabled"),
ko.String("bounce.postmark.username"),
ko.String("bounce.postmark.password"),
},
RecordBounceCB: app.core.RecordBounce,
}
@ -749,11 +735,7 @@ func initHTTPServer(app *App) *echo.Echo {
}
})
// Parse and load user facing templates.
tpl, err := stuffbin.ParseTemplatesGlob(template.FuncMap{
"L": func() *i18n.I18n {
return app.i18n
}}, app.fs, "/public/templates/*.html")
tpl, err := stuffbin.ParseTemplatesGlob(initTplFuncs(app.i18n, app.constants), app.fs, "/public/templates/*.html")
if err != nil {
lo.Fatalf("error parsing public templates: %v", err)
}
@ -847,3 +829,32 @@ func joinFSPaths(root string, paths []string) []string {
return out
}
func initTplFuncs(i *i18n.I18n, cs *constants) template.FuncMap {
funcs := template.FuncMap{
"RootURL": func() string {
return cs.RootURL
},
"LogoURL": func() string {
return cs.LogoURL
},
"Date": func(layout string) string {
if layout == "" {
layout = time.ANSIC
}
return time.Now().Format(layout)
},
"L": func() *i18n.I18n {
return i
},
"Safe": func(safeHTML string) template.HTML {
return template.HTML(safeHTML)
},
}
for k, v := range sprig.GenericFuncMap() {
funcs[k] = v
}
return funcs
}

View file

@ -3,7 +3,6 @@ package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"regexp"
"strings"
@ -224,7 +223,7 @@ func newConfigFile(path string) error {
ReplaceAll(b, []byte(fmt.Sprintf(`admin_password = "%s"`, pwd)))
}
return ioutil.WriteFile(path, b, 0644)
return os.WriteFile(path, b, 0644)
}
// checkSchema checks if the DB schema is installed.

View file

@ -200,16 +200,6 @@ func handleSubscriptionPage(c echo.Context) error {
out.AllowWipe = app.constants.Privacy.AllowWipe
out.AllowPreferences = app.constants.Privacy.AllowPreferences
if app.constants.Privacy.AllowPreferences {
out.ShowManage = showManage
}
// Get the subscriber's lists.
subs, err := app.core.GetSubscriptions(0, subUUID, false)
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, app.i18n.T("public.errorFetchingLists"))
}
s, err := app.core.GetSubscriber(0, subUUID, "")
if err != nil {
return c.Render(http.StatusInternalServerError, tplMessage,
@ -222,8 +212,17 @@ func handleSubscriptionPage(c echo.Context) error {
makeMsgTpl(app.i18n.T("public.noSubTitle"), "", app.i18n.Ts("public.blocklisted")))
}
// Filter out unrelated private lists.
if showManage {
// Only show preference management if it's enabled in settings.
if app.constants.Privacy.AllowPreferences {
out.ShowManage = showManage
}
if out.ShowManage {
// Get the subscriber's lists.
subs, err := app.core.GetSubscriptions(0, subUUID, false)
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, app.i18n.T("public.errorFetchingLists"))
}
out.Subscriptions = make([]models.Subscription, 0, len(subs))
for _, s := range subs {
if s.Type == models.ListTypePrivate {

View file

@ -2,7 +2,7 @@ package main
import (
"bytes"
"io/ioutil"
"io"
"net/http"
"regexp"
"runtime"
@ -69,6 +69,7 @@ func handleGetSettings(c echo.Context) error {
s.UploadS3AwsSecretAccessKey = strings.Repeat(pwdMask, utf8.RuneCountInString(s.UploadS3AwsSecretAccessKey))
s.SendgridKey = strings.Repeat(pwdMask, utf8.RuneCountInString(s.SendgridKey))
s.SecurityCaptchaSecret = strings.Repeat(pwdMask, utf8.RuneCountInString(s.SecurityCaptchaSecret))
s.BouncePostmark.Password = strings.Repeat(pwdMask, utf8.RuneCountInString(s.BouncePostmark.Password))
return c.JSON(http.StatusOK, okResp{s})
}
@ -120,6 +121,8 @@ func handleUpdateSettings(c echo.Context) error {
return echo.NewHTTPError(http.StatusBadRequest, app.i18n.T("settings.errorNoSMTP"))
}
set.AppRootURL = strings.TrimRight(set.AppRootURL, "/")
// Bounce boxes.
for i, s := range set.BounceBoxes {
// Assign a UUID. The frontend only sends a password when the user explicitly
@ -183,6 +186,9 @@ func handleUpdateSettings(c echo.Context) error {
if set.SendgridKey == "" {
set.SendgridKey = cur.SendgridKey
}
if set.BouncePostmark.Password == "" {
set.BouncePostmark.Password = cur.BouncePostmark.Password
}
if set.SecurityCaptchaSecret == "" {
set.SecurityCaptchaSecret = cur.SecurityCaptchaSecret
}
@ -238,7 +244,7 @@ func handleTestSMTPSettings(c echo.Context) error {
app := c.Get("app").(*App)
// Copy the raw JSON post body.
reqBody, err := ioutil.ReadAll(c.Request().Body)
reqBody, err := io.ReadAll(c.Request().Body)
if err != nil {
app.log.Printf("error reading SMTP test: %v", err)
return echo.NewHTTPError(http.StatusBadRequest, app.i18n.Ts("globals.messages.internalError"))

View file

@ -3,7 +3,7 @@ package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"io"
"net/http"
"net/textproto"
"strings"
@ -49,7 +49,7 @@ func handleSendTxMessage(c echo.Context) error {
}
defer file.Close()
b, err := ioutil.ReadAll(file)
b, err := io.ReadAll(file)
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError,
app.i18n.Ts("globals.messages.invalidFields", "name", fmt.Sprintf("file: %s", err.Error())))

View file

@ -2,7 +2,7 @@ package main
import (
"encoding/json"
"io/ioutil"
"io"
"net/http"
"regexp"
"time"
@ -48,7 +48,7 @@ func checkUpdates(curVersion string, interval time.Duration, app *App) {
continue
}
b, err := ioutil.ReadAll(resp.Body)
b, err := io.ReadAll(resp.Body)
if err != nil {
app.log.Printf("error reading remote update payload: %v", err)
continue

View file

@ -36,6 +36,7 @@ var migList = []migFunc{
{"v2.3.0", migrations.V2_3_0},
{"v2.4.0", migrations.V2_4_0},
{"v2.5.0", migrations.V2_5_0},
{"v2.6.0", migrations.V2_6_0},
}
// upgrade upgrades the database to the current version by running SQL migration files

View file

@ -15,7 +15,7 @@ x-app-defaults: &app-defaults
- TZ=Etc/UTC
x-db-defaults: &db-defaults
image: postgres:13
image: postgres:13-alpine
ports:
- "9432:5432"
networks:

View file

@ -44,15 +44,15 @@ All timestamp fields are in the format `2019-01-01T09:00:00.000000+05:30`. The s
### Common HTTP error codes
| code | &nbsp; |
| Code | |
| ----- | ------------------------------------------------------------------------ |
| `400` | Missing or bad request parameters or values |
| `403` | Session expired or invalidate. Must relogin |
| `404` | Request resource was not found |
| `405` | Request method (GET, POST etc.) is not allowed on the requested endpoint |
| `410` | The requested resource is gone permanently |
| `429` | Too many requests to the API (rate limiting) |
| `500` | Something unexpected went wrong |
| `502` | The backend OMS is down and the API is unable to communicate with it |
| `503` | Service unavailable; the API is down |
| `504` | Gateway timeout; the API is unreachable |
| 400 | Missing or bad request parameters or values |
| 403 | Session expired or invalidate. Must relogin |
| 404 | Request resource was not found |
| 405 | Request method (GET, POST etc.) is not allowed on the requested endpoint |
| 410 | The requested resource is gone permanently |
| 429 | Too many requests to the API (rate limiting) |
| 500 | Something unexpected went wrong |
| 502 | The backend OMS is down and the API is unable to communicate with it |
| 503 | Service unavailable; the API is down |
| 504 | Gateway timeout; the API is unreachable |

View file

@ -1,20 +1,22 @@
# API / Campaigns
Method | Endpoint | Description
---------|------------------------------------------------------------------------------|-------------------------------------------------------------
`GET` | [/api/campaigns](#get-apicampaigns) | Gets all campaigns.
`GET` | [/api/campaigns/:`campaign_id`](#get-apicampaignscampaign_id) | Gets a single campaign.
`GET` | [/api/campaigns/:`campaign_id`/preview](#get-apicampaignscampaign_idpreview) | Gets the HTML preview of a campaign body.
`GET` | [/api/campaigns/running/stats](#get-apicampaignsrunningstats) | Gets the stats of a given set of campaigns.
`POST` | [/api/campaigns](#post-apicampaigns) | Creates a new campaign.
`POST` | /api/campaigns/:`campaign_id`/test | Posts campaign message to arbitrary subscribers for testing.
`PUT` | /api/campaigns/:`campaign_id` | Modifies a campaign.
`PUT` | [/api/campaigns/:`campaign_id`/status](#put-apicampaignscampaign_idstatus) | Start / pause / cancel / schedule a campaign.
`DELETE` | [/api/campaigns/:`campaign_id`](#delete-apicampaignscampaign_id) | Deletes a campaign.
| Method | Endpoint | Description |
|:-------|:----------------------------------------------------------------------------|:------------------------------------------|
| GET | [/api/campaigns](#get-apicampaigns) | Retrieve all campaigns. |
| GET | [/api/campaigns/{campaign_id}](#get-apicampaignscampaign_id) | Retrieve a specific campaign. |
| GET | [/api/campaigns/{campaign_id}/preview](#get-apicampaignscampaign_idpreview) | Retrieve preview of a campaign. |
| GET | [/api/campaigns/running/stats](#get-apicampaignsrunningstats) | Retrieve stats of specified campaigns. |
| POST | [/api/campaigns](#post-apicampaigns) | Create a new campaign. |
| POST | [/api/campaigns/{campaign_id}/test](#post-apicampaignscampaign_idtest) | Test campaign with arbitrary subscribers. |
| PUT | [/api/campaigns/{campaign_id}](#put-apicampaignscampaign_id) | Update a campaign. |
| PUT | [/api/campaigns/{campaign_id}/status](#put-apicampaignscampaign_idstatus) | Change status of a campaign. |
| DELETE | [/api/campaigns/{campaign_id}](#delete-apicampaignscampaign_id) | Delete a campaign. |
#### ```GET``` /api/campaigns
______________________________________________________________________
Gets all campaigns.
#### GET /api/campaigns
Retrieve all campaigns.
##### Example Request
@ -23,18 +25,18 @@ Gets all campaigns.
```
##### Parameters
Name | Type | Required/Optional | Description
--------|--------------------|-------------|---------------------|---------------------
`query` | string | Optional | Optional string to search a list by name.
`order_by` | string | Optional | Field to sort results by. `name|status|created_at|updated_at`
`order` | string | Optional | `ASC|DESC`Sort by ascending or descending order.
`page` | number | Optional | Page number for paginated results.
`per_page` | number | Optional | Results to return per page. Setting this to `all` skips pagination and returns all results.
| Name | Type | Required | Description |
|:---------|:-------|:---------|:---------------------------------------------------------------------|
| order | string | | Sorting order: ASC for ascending, DESC for descending. |
| order_by | string | | Result sorting field. Options: name, status, created_at, updated_at. |
| query | string | | SQL query expression to filter subscribers. |
| page | number | | Page number for paginated results. |
| per_page | number | | Results per page. Set as 'all' for all results. |
##### Example Response
``` json
```json
{
"data": {
"results": [
@ -42,7 +44,6 @@ Name | Type | Required/Optional | Description
"id": 1,
"created_at": "2020-03-14T17:36:41.29451+01:00",
"updated_at": "2020-03-14T17:36:41.29451+01:00",
"CampaignID": 0,
"views": 0,
"clicks": 0,
"lists": [
@ -78,31 +79,32 @@ Name | Type | Required/Optional | Description
}
```
#### ```GET``` /api/campaigns/:`campaign_id`
______________________________________________________________________
Gets a single campaign.
#### GET /api/campaigns/{campaign_id}
##### Parameters
Name | Parameter Type | Data Type | Required/Optional | Description
--------------|----------------|-----------|-------------------|-----------------------------------------------
`campaign_id` | Path Parameter | Number | Required | The id value of the campaign you want to get.
Retrieve a specific campaign.
##### Parameters
| Name | Type | Required | Description |
|:------------|:----------|:---------|:-------------|
| campaign_id | number | Yes | Campaign ID. |
##### Example Request
``` shell
```shell
curl -u "username:password" -X GET 'http://localhost:9000/api/campaigns/1'
```
##### Example Response
``` json
```json
{
"data": {
"id": 1,
"created_at": "2020-03-14T17:36:41.29451+01:00",
"updated_at": "2020-03-14T17:36:41.29451+01:00",
"CampaignID": 0,
"views": 0,
"clicks": 0,
"lists": [
@ -132,20 +134,17 @@ curl -u "username:password" -X GET 'http://localhost:9000/api/campaigns/1'
}
```
______________________________________________________________________
#### GET /api/campaigns/{campaign_id}/preview
Preview a specific campaign.
#### ```GET``` /api/campaigns/:`campaign_id`/preview
Gets the html preview of a campaign body.
##### Parameters
Name | Parameter Type | Data Type | Required/Optional | Description
--------------|----------------|-----------|-------------------|----------------------------------------------
`campaign_id` | Path Parameter | Number | Required | The id value of the campaign to be previewed.
##### Parameters
| Name | Type | Required | Description |
|:------------|:----------|:---------|:------------------------|
| campaign_id | number | Yes | Campaign ID to preview. |
##### Example Request
@ -155,70 +154,69 @@ curl -u "username:password" -X GET 'http://localhost:9000/api/campaigns/1/previe
##### Example Response
``` html
```html
<h3>Hi John!</h3>
This is a test e-mail campaign. Your second name is Doe and you are from Bengaluru.
```
#### ```GET``` /api/campaigns/running/stats
______________________________________________________________________
Gets the running stat of a given set of campaigns.
#### GET /api/campaigns/running/stats
Retrieve stats of specified campaigns.
##### Parameters
Name | Parameter Type | Data Type | Required/Optional | Description
------------|------------------|-----------|-------------------|-----------------------------------------------------------
campaign_id | Query Parameters | Number | Required | The id values of the campaigns whose stat you want to get.
| Name | Type | Required | Description |
|:------------|:----------|:---------|:-------------------------------|
| campaign_id | number | Yes | Campaign IDs to get stats for. |
##### Example Request
``` shell
```shell
curl -u "username:password" -X GET 'http://localhost:9000/api/campaigns/running/stats?campaign_id=1'
```
##### Example Response
``` json
```json
{
"data": []
}
```
______________________________________________________________________
#### POST /api/campaigns
Create a new campaign.
##### Parameters
### ```POST ``` /api/campaigns
| Name | Type | Required | Description |
|:-------------|:----------|:---------|:----------------------------------------------------------------------------------------|
| name | string | Yes | Campaign name. |
| subject | string | Yes | Campaign email subject. |
| lists | number\[\] | Yes | List IDs to send campaign to. |
| from_email | string | | 'From' email in campaign emails. Defaults to value from settings if not provided. |
| type | string | Yes | Campaign type: 'regular' or 'optin'. |
| content_type | string | Yes | Content type: 'richtext', 'html', 'markdown', 'plain'. |
| body | string | Yes | Content body of campaign. |
| altbody | string | | Alternate plain text body for HTML (and richtext) emails. |
| send_at | string | | Timestamp to schedule campaign. Format: 'YYYY-MM-DDTHH:MM:SS'. |
| messenger | string | | 'email' or a custom messenger defined in settings. Defaults to 'email' if not provided. |
| template_id | number | | Template ID to use. Defaults to default template if not provided. |
| tags | string\[\] | | Tags to mark campaign. |
| headers | JSON | | Key-value pairs to send as SMTP headers. Example: \[{"x-custom-header": "value"}\]. |
Creates a new campaign.
#### Parameters
| Name | Data type | Required/Optional | Description |
|----------------|-----------|-------------------|--------------------------------------------------------------------------------------------------------|
| `name` | String | Required | Name of the campaign. |
| `subject` | String | Required | (E-mail) subject of the campaign. |
| `lists` | []Number | Required | Array of list IDs to send the campaign to. |
| `from_email` | String | Optional | `From` e-mail to show on the campaign e-mails. If left empty, the default value from settings is used. |
| `type` | String | Required | `regular` or `optin` campaign. |
| `content_type` | String | Required | `richtext`, `html`, `markdown`, `plain` |
| `body` | String | Required | Campaign content body. |
| `altbody` | String | Optional | Alternate plain text body for HTML (and richtext) e-mails. |
| `send_at` | String | Optional | A timestamp to schedule the campaign at. Eg: `2021-12-25T06:00:00` (YYYY-MM-DDTHH:MM:SS) |
| `messenger` | String | Optional | `email` or a custom messenger defined in the settings. If left empty, `email` is used. |
| `template_id` | Number | Optional | ID of the template to use. If left empty, the default template is used. |
| `tags` | []String | Optional | Array of string tags to mark the campaign. |
#### Example request
##### Example request
```shell
curl -u "username:password" 'http://localhost:9000/api/campaigns' -X POST -H 'Content-Type: application/json;charset=utf-8' --data-raw '{"name":"Test campaign","subject":"Hello, world","lists":[1],"from_email":"listmonk <noreply@listmonk.yoursite.com>","content_type":"richtext","messenger":"email","type":"regular","tags":["test"],"template_id":1}'
```
#### Example response
##### Example response
```json
{
"data": {
@ -252,25 +250,55 @@ curl -u "username:password" 'http://localhost:9000/api/campaigns' -X POST -H 'Co
}
```
______________________________________________________________________
#### ```PUT``` /api/campaigns/:`campaign_id`/status
#### POST /api/campaigns/{campaign_id}/test
Modifies a campaign status to start, pause, cancel, or schedule a campaign.
Test campaign with arbitrary subscribers.
##### Parameters
Use the same parameters in [POST /api/campaigns](#post-apicampaigns) in addition to the below parameters.
Name | Parameter Type | Data Type | Required/Optional | Description
--------------|----------------|-----------|-------------------|-------------------------------------------------------------
`campaign_id` | Path Parameter | Number | Required | The id value of the campaign whose status is to be modified.
`status` | Request Body | String | Required | `scheduled`, `running`, `paused`, `cancelled`.
##### Parameters
| Name | Type | Required | Description |
|:------------|:---------|:---------|:---------------------------------------------------|
| subscribers | string\[\] | Yes | List of subscriber e-mails to send the message to. |
###### Note:
> * Only "scheduled" campaigns can be saved as "draft".
* Only "draft" campaigns can be "scheduled".
* Only "paused" campaigns and "draft" campaigns can be started.
* Only "running" campaigns can be "cancelled" and "paused".
______________________________________________________________________
#### PUT /api/campaigns/{campaign_id}
Update a campaign.
> Refer to parameters from [POST /api/campaigns](#post-apicampaigns)
______________________________________________________________________
#### PUT /api/campaigns/{campaign_id}
Update a specific campaign.
> Refer to parameters from [POST /api/campaigns](#post-apicampaigns)
______________________________________________________________________
#### PUT /api/campaigns/{campaign_id}/status
Change status of a campaign.
##### Parameters
| Name | Type | Required | Description |
|:------------|:----------|:---------|:------------------------------------------------------------------------|
| campaign_id | number | Yes | Campaign ID to change status. |
| status | string | Yes | New status for campaign: 'scheduled', 'running', 'paused', 'cancelled'. |
##### Note
> - Only 'scheduled' campaigns can change status to 'draft'.
> - Only 'draft' campaigns can change status to 'scheduled'.
> - Only 'paused' and 'draft' campaigns can start ('running' status).
> - Only 'running' campaigns can change status to 'cancelled' and 'paused'.
##### Example Request
@ -288,7 +316,6 @@ curl -u "username:password" -X PUT 'http://localhost:9000/api/campaigns/1/status
"id": 1,
"created_at": "2020-03-14T17:36:41.29451+01:00",
"updated_at": "2020-04-08T19:35:17.331867+01:00",
"CampaignID": 0,
"views": 0,
"clicks": 0,
"lists": [
@ -318,16 +345,17 @@ curl -u "username:password" -X PUT 'http://localhost:9000/api/campaigns/1/status
}
```
#### ```DELETE``` /api/campaigns/:`campaign_id`
______________________________________________________________________
Deletes a campaign, only scheduled campaigns that have not yet been started can be deleted.
#### DELETE /api/campaigns/{campaign_id}
Delete a campaign.
##### Parameters
Name | Parameter Type | Data Type | Required/Optional | Description
--------------|----------------|-----------|-------------------|-------------------------------------------------
`campaign_id` | Path Parameter | Number | Required | The id value of the campaign you want to delete.
| Name | Type | Required | Description |
|:------------|:----------|:---------|:-----------------------|
| campaign_id | number | Yes | Campaign ID to delete. |
##### Example Request

View file

@ -1,22 +1,26 @@
# API / Import
Method | Endpoint | Description
---------|--------------------------------------------------------------|----------------------------------------------------------
`GET` | [api/import/subscribers](#get-apiimportsubscribers) | Gets a import statistics.
`GET` | [api/import/subscribers/logs](#get-apiimportsubscriberslogs) | Get a import statistics .
`POST` | [api/import/subscribers](#post-apiimportsubscribers) | Upload a ZIP file or CSV file to bulk import subscribers.
`DELETE` | [api/import/subscribers](#delete-apiimportsubscribers) | Stops and deletes a import.
Method | Endpoint | Description
---------|-------------------------------------------------|------------------------------------------------
GET | [/api/import/subscribers](#get-apiimportsubscribers) | Retrieve import statistics.
GET | [/api/import/subscribers/logs](#get-apiimportsubscriberslogs) | Retrieve import logs.
POST | [/api/import/subscribers](#post-apiimportsubscribers) | Upload a file for bulk subscriber import.
DELETE | [/api/import/subscribers](#delete-apiimportsubscribers) | Stop and remove an import.
______________________________________________________________________
#### **`GET`** api/import/subscribers
Gets import status.
#### GET /api/import/subscribers
##### Example Request
```shell
Retrieve the status of an import.
##### Example Request
```shell
curl -u "username:username" -X GET 'http://localhost:9000/api/import/subscribers'
```
##### Example Response
##### Example Response
```json
{
"data": {
@ -28,35 +32,40 @@ curl -u "username:username" -X GET 'http://localhost:9000/api/import/subscribers
}
```
#### **`GET`** api/import/subscribers/logs
Gets import logs.
______________________________________________________________________
#### GET /api/import/subscribers/logs
Retrieve logs related to imports.
##### Example Request
##### Example Request
```shell
curl -u "username:username" -X GET 'http://localhost:9000/api/import/subscribers/logs'
```
##### Example Response
```json
{
"data": "2020/04/08 21:55:20 processing 'import.csv'\n2020/04/08 21:55:21 imported finished\n"
}
```
______________________________________________________________________
#### POST /api/import/subscribers
#### **`POST`** api/import/subscribers
Post a CSV (optionally zipped) file to do a bulk import. The request should be a multipart form POST.
Send a CSV (optionally ZIP compressed) file to import subscribers. Use a multipart form POST.
##### Parameters
Name | Parameter type | Data type | Required/Optional | Description
---------|----------------|-----------|-------------------|------------------------------------
`params` | Request body | String | Required | Stringified JSON with import params
`file` | Request body | File | Required | File to upload
| Name | Type | Required | Description |
|:-------|:------------|:---------|:-----------------------------------------|
| params | JSON string | Yes | Stringified JSON with import parameters. |
| file | File | Yes | File for upload. |
***params*** (JSON string)
**`params`** (JSON string)
```json
{
@ -67,16 +76,20 @@ Name | Parameter type | Data type | Required/Optional | Description
}
```
______________________________________________________________________
#### **`DELETE`** api/import/subscribers
Stops and deletes an import.
#### DELETE /api/import/subscribers
Stop and delete an ongoing import.
##### Example Request
```shell
curl -u "username:username" -X DELETE 'http://localhost:9000/api/import/subscribers'
```
##### Example Response
```json
{
"data": {
@ -86,4 +99,4 @@ curl -u "username:username" -X DELETE 'http://localhost:9000/api/import/subscrib
"status": "none"
}
}
```
```

View file

@ -1,31 +1,37 @@
# API / Lists
Method | Endpoint | Description
------------|------------------------------------------------------|----------------------------------------------
`GET` | [/api/lists](#get-apilists) | Gets all lists.
`GET` | [/api/lists/:`list_id`](#get-apilistslist_id) | Gets a single list.
`POST` | [/api/lists](#post-apilists) | Creates a new list.
`PUT` | /api/lists/:`list_id` | Modifies a list.
`DELETE` | [/api/lists/:`list_id`](#put-apilistslist_id) | Deletes a list.
| Method | Endpoint | Description |
|:-------|:------------------------------------------------|:--------------------------|
| GET | [/api/lists](#get-apilists) | Retrieve all lists. |
| GET | [/api/lists/{list_id}](#get-apilistslist_id) | Retrieve a specific list. |
| POST | [/api/lists](#post-apilists) | Create a new list. |
| PUT | [/api/lists/{list_id}](#put-apilistslist_id) | Update a list. |
| DELETE | [/api/lists/{list_id}](#delete-apilistslist_id) | Delete a list. |
#### **`GET`** /api/lists
Gets lists.
______________________________________________________________________
#### GET /api/lists
Retrieve lists.
##### Parameters
Name | Type | Required/Optional | Description
-----------|--------|--------------------|-----------------------------------------
`query` | string | Optional | Optional string to search a list by name.
`order_by` | string | Optional | Field to sort results by. `name|status|created_at|updated_at`
`order` | string | Optional | `ASC|DESC`Sort by ascending or descending order.
`page` | number | Optional | Page number for paginated results.
`per_page` | number | Optional | Results to return per page. Setting this to `all` skips pagination and returns all results.
| Name | Type | Required | Description |
|:---------|:----------|:---------|:-----------------------------------------------------------|
| query | string | | string for list name search. |
| order_by | string | | Sort field. Options: name, status, created_at, updated_at. |
| order | string | | Sorting order. Options: ASC, DESC. |
| page | number | | Page number for pagination. |
| per_page | number | | Results per page. Set to 'all' to return all results. |
##### Example Request
```shell
curl -u "username:username" -X GET 'http://localhost:9000/api/lists?page=1&per_page=100'
```
##### Example Response
```json
{
"data": {
@ -62,20 +68,26 @@ curl -u "username:username" -X GET 'http://localhost:9000/api/lists?page=1&per_p
}
```
#### **`GET`** /api/lists/:`list_id`
Gets a single list.
______________________________________________________________________
#### GET /api/lists/{list_id}
Retrieve a specific list.
##### Parameters
Name | Parameter type | Data type | Required/Optional | Description
----------|--------------------|-------------|---------------------|---------------------
`list_id` | Path parameter | number | Required | The id value of the list you want to get.
| Name | Type | Required | Description |
|:--------|:----------|:---------|:----------------------------|
| list_id | number | Yes | ID of the list to retrieve. |
##### Example Request
``` shell
```shell
curl -u "username:username" -X GET 'http://localhost:9000/api/lists/5'
```
##### Example Response
```json
{
"data": {
@ -92,23 +104,29 @@ curl -u "username:username" -X GET 'http://localhost:9000/api/lists/5'
}
```
#### **`POST`** /api/lists
Creates a new list.
______________________________________________________________________
#### POST /api/lists
Create a new list.
##### Parameters
Name | Parameter type | Data type | Required/Optional | Description
--------|-----------------|-------------|--------------------|----------------
name | Request body | string | Required | The new list name.
type | Request body | string | Required | List type, can be set to `private` or `public`.
optin | Request body | string | Required | `single` or `double` optin.
tags | Request body | string[] | Optional | The tags associated with the list.
| Name | Type | Required | Description |
|:------|:----------|:---------|:----------------------------------------|
| name | string | Yes | Name of the new list. |
| type | string | Yes | Type of list. Options: private, public. |
| optin | string | Yes | Opt-in type. Options: single, double. |
| tags | string\[\] | | Associated tags for a list. |
##### Example Request
``` shell
```shell
curl -u "username:username" -X POST 'http://localhost:9000/api/lists'
```
##### Example Response
```json
{
"data": {
@ -125,19 +143,24 @@ curl -u "username:username" -X POST 'http://localhost:9000/api/lists'
null
```
#### **`PUT`** /api/lists/`list_id`
Modifies a list.
______________________________________________________________________
#### PUT /api/lists/{list_id}
Update a list.
##### Parameters
Name | Parameter type | Data type | Required/Optional | Description
----------|--------------------|--------------|-----------------------|-------------------------
`list_id` | Path parameter | number | Required | The id of the list to be modified.
name | Request body | string | Optional | The name which the old name will be modified to.
type | Request body | string | Optional | List type, can be set to `private` or `public`.
optin | Request body | string | Optional | `single` or `double` optin.
tags | Request body | string[] | Optional | The tags associated with the list.
| Name | Type | Required | Description |
|:--------|:----------|:---------|:----------------------------------------|
| list_id | number | Yes | ID of the list to update. |
| name | string | | New name for the list. |
| type | string | | Type of list. Options: private, public. |
| optin | string | | Opt-in type. Options: single, double. |
| tags | string\[\] | | Associated tags for the list. |
##### Example Request
```shell
curl -u "username:username" -X PUT 'http://localhost:9000/api/lists/5' \
--form 'name=modified test list' \
@ -145,7 +168,8 @@ curl -u "username:username" -X PUT 'http://localhost:9000/api/lists/5' \
```
##### Example Response
``` json
```json
{
"data": {
"id": 5,
@ -160,3 +184,29 @@ curl -u "username:username" -X PUT 'http://localhost:9000/api/lists/5' \
}
}
```
______________________________________________________________________
#### DELETE /api/lists/{list_id}
Delete a specific subscriber.
##### Parameters
| Name | Type | Required | Description |
|:--------|:----------|:---------|:--------------------------|
| list_id | Number | Yes | ID of the list to delete. |
##### Example Request
```shell
curl -u 'username:password' -X DELETE 'http://localhost:9000/api/lists/1'
```
##### Example Response
```json
{
"data": true
}
```

View file

@ -1,20 +1,26 @@
# API / Media
Method | Endpoint | Description
---------|----------------------------------------------------|------------------------------
`GET` | [/api/media](#get-apimedia) | Gets an uploaded media file.
`POST` | [/api/media](#post-apimedia) | Uploads a media file.
`DELETE` | [/api/media/:`media_id`](#delete-apimediamedia_id) | Deletes uploaded media files.
#### **`GET`** /api/media
Gets an uploaded media file.
Method | Endpoint | Description
-------|------------------------------------------------|------------------------------
GET | [/api/media](#get-apimedia) | Get uploaded media file
POST | [/api/media](#post-apimedia) | Upload media file
DELETE | [/api/media/{media_id}](#delete-apimediamedia_id) | Delete uploaded media file
______________________________________________________________________
#### GET /api/media
Get an uploaded media file.
##### Example Request
```shell
curl -u "username:username" -X GET 'http://localhost:9000/api/media' \
--header 'Content-Type: multipart/form-data; boundary=--------------------------093715978792575906250298'
```
##### Example Response
```json
{
"data": [
@ -30,37 +36,29 @@ curl -u "username:username" -X GET 'http://localhost:9000/api/media' \
}
```
Response definitions
The following table describes each item in the response.
______________________________________________________________________
| Response item | Description | Data type |
|:-------------:|:----------------------------------------------------------------------------------------------|:----------------------:|
| data | Array of the media file objects, which contains an information about the uploaded media files | array |
| id | Media file object ID | number (int) |
| uuid | Media file uuid | string (uuid) |
| filename | Name of the media file | string |
| created_at | Date and time, when the media file object was created | String (localDateTime) |
| thumb_uri | The thumbnail URI of the media file | string |
| uri | URI of the media file | string |
#### POST /api/media
#### **`POST`** /api/media
Uploads a media file.
Upload a media file.
##### Parameters
Name | Parameter Type | Data Type | Required/Optional | Description
-----|----------------|------------|-------------------|-------------------------------
file | Request body | Media file | Required | The media file to be uploaded.
| Field | Type | Required | Description |
|-------|-----------|----------|---------------------|
| file | File | Yes | Media file to upload|
##### Example Request
```shell
```shell
curl -u "username:username" -X POST 'http://localhost:9000/api/media' \
--header 'Content-Type: multipart/form-data; boundary=--------------------------183679989870526937212428' \
--form 'file=@/path/to/image.jpg'
```
##### Example Response
``` json
```json
{
"data": {
"id": 1,
@ -72,36 +70,29 @@ curl -u "username:username" -X POST 'http://localhost:9000/api/media' \
}
}
```
Response definitions
| Response item | Description | Data type |
|:-------------:|:--------------------------------------------------------:|:---------:|
| data | True means that the media file was successfully uploaded | boolean |
______________________________________________________________________
#### **`DELETE`** /api/media/:`media_id`
Deletes an uploaded media file.
#### DELETE /api/media/{media_id}
Delete an uploaded media file.
##### Parameters
Name | Parameter Type | Data Type | Required/Optional | Description
-----------|----------------|-----------|-------------------|---------------------------------------------
`Media_id` | Path Parameter | Number | Required | The id of the media file you want to delete.
| Field | Type | Required | Description |
|----------|-----------|----------|-------------------------|
| media_id | number | Yes | ID of media file to delete |
##### Example Request
```shell
curl -u "username:username" -X DELETE 'http://localhost:9000/api/media/1'
```
##### Example Response
```json
{
"data": true
}
```
Response definitions
| Response item | Description | Data type |
|:-------------:|:-------------------------------------------------------:|:---------:|
| data | True means that the media file was successfully deleted | boolean |

View file

@ -1,35 +1,56 @@
# API / Subscribers
Method | Endpoint | Description
---------|-----------------------------------------------------------------------|-----------------------------------------------------------
`GET` | [/api/subscribers](#get-apisubscribers) | Gets all subscribers.
`GET` | [/api/subscribers/:`id`](#get-apisubscribersid) | Gets a single subscriber.
`GET` | /api/subscribers/lists/:`id` | Gets subscribers in a list.
`GET` | [/api/subscribers](#get-apisubscriberslist_id) | Gets subscribers in one or more lists.
`GET` | [/api/subscribers](#get-apisubscribers_1) | Gets subscribers filtered by an arbitrary SQL expression.
`POST` | [/api/subscribers](#post-apisubscribers) | Creates a new subscriber.
`POST` | [/api/subscribers](#post-apisubscriberspublic) | Unauthenticated API that enables public subscription.
`PUT` | [/api/subscribers/lists](#put-apisubscriberslists) | Modify subscribers' list memberships.
`PUT` | [/api/subscribers/:`id`](#put-apisubscribersid) | Updates a subscriber by ID.
`PUT` | [/api/subscribers/:`id`/blocklist](#put-apisubscribersidblocklist) | Blocklists a single subscriber.
`PUT` | /api/subscribers/blocklist | Blocklists one or more subscribers.
`PUT` | [/api/subscribers/query/blocklist](#put-apisubscribersqueryblocklist) | Blocklists subscribers with an arbitrary SQL expression.
`DELETE` | [/api/subscribers/:`id`](#delete-apisubscribersid) | Deletes a single subscriber.
`DELETE` | [/api/subscribers](#delete-apisubscribers) | Deletes one or more subscribers .
`POST` | [/api/subscribers/query/delete](#post-apisubscribersquerydelete) | Deletes subscribers with an arbitrary SQL expression.
| Method | Endpoint | Description |
| ------ | --------------------------------------------------------------------------------------- | ---------------------------------------------- |
| GET | [/api/subscribers](#get-apisubscribers) | Retrieve all subscribers. |
| GET | [/api/subscribers/{subscriber_id}](#get-apisubscriberssubscriber_id) | Retrieve a specific subscriber. |
| GET | [/api/subscribers/lists/{list_id}](#get-apisubscriberslistslist_id) | Retrieve subscribers in a specific list. |
| POST | [/api/subscribers](#post-apisubscribers) | Create a new subscriber. |
| POST | [/api/public/subscription](#post-apipublicsubscription) | Create a public subscription. |
| PUT | [/api/subscribers/lists](#put-apisubscriberslists) | Modify subscriber list memberships. |
| PUT | [/api/subscribers/{subscriber_id}](#put-apisubscriberssubscriber_id) | Update a specific subscriber. |
| PUT | [/api/subscribers/{subscriber_id}/blocklist](#put-apisubscriberssubscriber_idblocklist) | Blocklist a specific subscriber. |
| PUT | /api/subscribers/blocklist | Blocklist one or more subscribers. |
| PUT | [/api/subscribers/query/blocklist](#put-apisubscribersqueryblocklist) | Blocklist subscribers based on SQL expression. |
| DELETE | [/api/subscribers/{subscriber_id}](#delete-apisubscriberssubscriber_id) | Delete a specific subscriber. |
| DELETE | [/api/subscribers](#delete-apisubscribers) | Delete one or more subscribers. |
| POST | [/api/subscribers/query/delete](#post-apisubscribersquerydelete) | Delete subscribers based on SQL expression. |
______________________________________________________________________
#### **`GET`** /api/subscribers
Gets all subscribers.
#### GET /api/subscribers
Retrieve all subscribers.
##### Query parameters
| Name | Type | Required | Description |
|:---------|:-------|:---------|:---------------------------------------------------------------------|
| query | string | | Subscriber search term by name. |
| order_by | string | | Result sorting field. Options: name, status, created_at, updated_at. |
| order | string | | Sorting order: ASC for ascending, DESC for descending. |
| page | number | | Page number for paginated results. |
| per_page | number | | Results per page. Set as 'all' for all results. |
##### Example Request
```shell
curl -u 'username:password' 'http://localhost:9000/api/subscribers?page=1&per_page=100'
```
To skip pagination and retrieve all records, pass `per_page=all`.
```shell
curl -u 'username:password' 'http://localhost:9000/api/subscribers?list_id=1&list_id=2&page=1&per_page=100'
```
```shell
curl -u 'username:password' -X GET 'http://localhost:9000/api/subscribers' \
--url-query 'page=1' \
--url-query 'per_page=100' \
--url-query "query=subscribers.name LIKE 'Test%' AND subscribers.attribs->>'city' = 'Bengaluru'"
```
##### Example Response
```json
{
"data": {
@ -106,22 +127,26 @@ To skip pagination and retrieve all records, pass `per_page=all`.
}
```
______________________________________________________________________
#### **`GET`** /api/subscribers/:`id`
Gets a single subscriber.
#### GET /api/subscribers/{subscriber_id}
Retrieve a specific subscriber.
##### Parameters
Name | Parameter type |Data type | Required/Optional | Description
---------|----------------|----------------|-------------------|-----------------------
`id` | Path parameter | Number | Required | The id value of the subscriber you want to get.
| Name | Type | Required | Description |
|:--------------|:----------|:---------|:-----------------|
| subscriber_id | Number | Yes | Subscriber's ID. |
##### Example Request
```shell
curl -u 'username:password' 'http://localhost:9000/api/subscribers/1'
```
##### Example Response
```json
{
"data": {
@ -155,143 +180,33 @@ curl -u 'username:password' 'http://localhost:9000/api/subscribers/1'
}
```
______________________________________________________________________
#### GET /api/subscribers/lists/{list_id}
#### **`GET`** /api/subscribers
Gets subscribers in one or more lists.
Retrieve subscribers in a specific list.
> Refer to the response structure in [GET /api/subscribers](#get-apisubscribers).
______________________________________________________________________
#### POST /api/subscribers
Create a new subscriber.
##### Parameters
Name | Parameter type | Data type | Required/Optional | Description
----------|-----------------|-------------|---------------------|---------------
`List_id` | Request body | Number | Required | ID of the list to fetch subscribers from.
| Name | Type | Required | Description |
|:-------------------------|:----------|:---------|:-----------------------------------------------------------------------------------------------------|
| email | string | Yes | Subscriber's email address. |
| name | string | Yes | Subscriber's name. |
| status | string | Yes | Subscriber's status: `enabled`, `disabled`, `blocklisted`. |
| lists | number\[\] | | List of list IDs to to subscribe to. |
| attribs | JSON | | Attributes of the new subscriber. |
| preconfirm_subscriptions | bool | | If true, subscriptions are marked as confirmed and no-optin emails are sent for double opt-in lists. |
##### Example Request
```shell
curl -u 'username:password' 'http://localhost:9000/api/subscribers?list_id=1&list_id=2&page=1&per_page=100'
```
To skip pagination and retrieve all records, pass `per_page=all`.
##### Example Response
```json
{
"data": {
"results": [
{
"id": 1,
"created_at": "2019-06-26T16:51:54.37065+05:30",
"updated_at": "2019-07-03T11:53:53.839692+05:30",
"uuid": "5e91dda1-1c16-467d-9bf9-2a21bf22ae21",
"email": "test@test.com",
"name": "Test Subscriber",
"attribs": {
"city": "Bengaluru",
"projects": 3,
"stack": {
"languages": ["go", "python"]
}
},
"status": "enabled",
"lists": [
{
"subscription_status": "unconfirmed",
"id": 1,
"uuid": "41badaf2-7905-4116-8eac-e8817c6613e4",
"name": "Default list",
"type": "public",
"tags": ["test"],
"created_at": "2019-06-26T16:51:54.367719+05:30",
"updated_at": "2019-06-26T16:51:54.367719+05:30"
}
]
}
],
"query": "",
"total": 1,
"per_page": 20,
"page": 1
}
}
```
#### **`GET`** /api/subscribers
Gets subscribers with an SQL expression.
##### Example Request
```shell
curl -u 'username:password' -X GET 'http://localhost:9000/api/subscribers' \
--url-query 'page=1' \
--url-query 'per_page=100' \
--url-query "query=subscribers.name LIKE 'Test%' AND subscribers.attribs->>'city' = 'Bengaluru'"
```
To skip pagination and retrieve all records, pass `per_page=all`.
>Refer to the [querying and segmentation](/docs/querying-and-segmentation#querying-and-segmenting-subscribers) section for more information on how to query subscribers with SQL expressions.
##### Example Response
```json
{
"data": {
"results": [
{
"id": 1,
"created_at": "2019-06-26T16:51:54.37065+05:30",
"updated_at": "2019-07-03T11:53:53.839692+05:30",
"uuid": "5e91dda1-1c16-467d-9bf9-2a21bf22ae21",
"email": "test@test.com",
"name": "Test Subscriber",
"attribs": {
"city": "Bengaluru",
"projects": 3,
"stack": {
"frameworks": ["echo", "go"],
"languages": ["go", "python"]
}
},
"status": "enabled",
"lists": [
{
"subscription_status": "unconfirmed",
"id": 1,
"uuid": "41badaf2-7905-4116-8eac-e8817c6613e4",
"name": "Default list",
"type": "public",
"tags": ["test"],
"created_at": "2019-06-26T16:51:54.367719+05:30",
"updated_at": "2019-06-26T16:51:54.367719+05:30"
}
]
}
],
"query": "subscribers.name LIKE 'Test%' AND subscribers.attribs-\u003e\u003e'city' = 'Bengaluru'",
"total": 1,
"per_page": 20,
"page": 1
}
}
```
#### **`POST`** /api/subscribers
Creates a new subscriber.
##### Parameters
Name | Parameter type | Data type | Required/Optional | Description
-------------------------|------------------|------------|-------------------|----------------------------
email | Request body | String | Required | The email address of the new subscriber.
name | Request body | String | Required | The name of the new subscriber.
status | Request body | String | Required | The status of the new subscriber. Can be enabled, disabled or blocklisted.
lists | Request body | Numbers | Optional | Array of list IDs to subscribe to (marked as `unconfirmed` by default).
attribs | Request body | json | Optional | JSON list containing new subscriber's attributes.
preconfirm_subscriptions | Request body | Bool | Optional | If `true`, marks subscriptions as `confirmed` and no-optin e-mails are sent for double opt-in lists.
##### Example Request
```shell
curl -u 'username:password' 'http://localhost:9000/api/subscribers' -H 'Content-Type: application/json' \
--data '{"email":"subsriber@domain.com","name":"The Subscriber","status":"enabled","lists":[1],"attribs":{"city":"Bengaluru","projects":3,"stack":{"languages":["go","python"]}}}'
@ -319,33 +234,35 @@ curl -u 'username:password' 'http://localhost:9000/api/subscribers' -H 'Content-
}
```
______________________________________________________________________
#### **`POST`** /api/public/subscription
#### POST /api/public/subscription
This is a public, unauthenticated API meant for directly integrating forms for public subscription. The API supports both
form encoded or a JSON encoded body.
Create a public subscription, accepts both form encoded or JSON encoded body.
##### Parameters
##### Parameters
Name | Parameter type | Data type | Required/Optional | Description
-------------------------|------------------|------------|-------------------|----------------------------
email | Request body | String | Required | The email address of the subscriber.
name | Request body | String | Optional | The name of the new subscriber.
list_uuids | Request body | Strings | Required | Array of list UUIDs.
| Name | Type | Required | Description |
|:-----------|:----------|:---------|:----------------------------|
| email | string | Yes | Subscriber's email address. |
| name | string | | Subscriber's name. |
| list_uuids | string\[\] | Yes | List of list UUIDs. |
##### Example JSON Request
```shell
curl -u 'http://localhost:9000/api/public/subscription' -H 'Content-Type: application/json' \
--data '{"email":"subsriber@domain.com","name":"The Subscriber", "lists": ["eb420c55-4cfb-4972-92ba-c93c34ba475d", "0c554cfb-eb42-4972-92ba-c93c34ba475d"]}'
curl 'http://localhost:9000/api/public/subscription' -H 'Content-Type: application/json' \
--data '{"email":"subsriber@domain.com","name":"The Subscriber","list_uuids": ["eb420c55-4cfb-4972-92ba-c93c34ba475d", "0c554cfb-eb42-4972-92ba-c93c34ba475d"]}'
```
##### Example Form Request
```shell
curl -u 'http://localhost:9000/api/public/subscription' \
-d 'email=subsriber@domain.com' -d 'name=The Subscriber' -d 'l=eb420c55-4cfb-4972-92ba-c93c34ba475d' -d 'l=0c554cfb-eb42-4972-92ba-c93c34ba475d'
```
Notice that in form request, there param is `l` that is repeated for multiple lists, and not `lists` like in JSON.
Note: For form request, use `l` for multiple lists instead of `lists`.
##### Example Response
@ -355,153 +272,162 @@ Notice that in form request, there param is `l` that is repeated for multiple li
}
```
______________________________________________________________________
#### PUT /api/subscribers/lists
#### **`PUT`** /api/subscribers/lists
Modify subscribers list memberships.
Modify subscriber list memberships.
##### Parameters
Name | Parameter type | Data type | Required/Optional | Description
------------------|----------------|-----------|--------------------|-------------------------------------------------------
`ids` | Request body | Numbers | Required | The ids of the subscribers to be modified.
`action` | Request body | String | Required | Whether to `add`, `remove`, or `unsubscribe` the users.
`target_list_ids` | Request body | Numbers | Required | The ids of the lists to be modified.
`status` | Request body | String | Required for `add` | `confirmed`, `unconfirmed`, or `unsubscribed` status.
| Name | Type | Required | Description |
|:----------------|:----------|:-------------------|:------------------------------------------------------------------|
| ids | number\[\] | Yes | Array of user IDs to be modified. |
| action | string | Yes | Action to be applied: `add`, `remove`, or `unsubscribe`. |
| target_list_ids | number\[\] | Yes | Array of list IDs to be modified. |
| status | string | Required for `add` | Subscriber status: `confirmed`, `unconfirmed`, or `unsubscribed`. |
##### Example Request
To subscribe users 1, 2, and 3 to lists 4, 5, and 6:
```shell
curl -u 'username:password' -X PUT 'http://localhost:9000/api/subscribers/lists' \
-H 'Content-Type: application/json' \
--data-raw '{"ids": [1, 2, 3], "action": "add", "target_list_ids": [4, 5, 6], "status": "confirmed"}'
```
##### Example Response
``` json
```json
{
"data": true
}
```
#### **`PUT`** /api/subscribers/:`id`
______________________________________________________________________
Updates a single subscriber.
#### PUT /api/subscribers/{subscriber_id}
##### Parameters
Update a specific subscriber.
Parameters are the same as [POST /api/subscribers](#post-apisubscribers) used for subscriber creation.
> Refer to parameters from [POST /api/subscribers](#post-apisubscribers). Note: All parameters must be set, if not, the subscriber will be removed from all previously assigned lists.
> Please note that this is a `PUT` request, so all the parameters have to be set. For example if you don't provide `lists`, the subscriber will be deleted from all the lists he was previously signed on.
______________________________________________________________________
#### **`PUT`** /api/subscribers/:`id`/blocklist
Blocklists a single subscriber.
#### PUT /api/subscribers/{subscriber_id}/blocklist
##### Parameters
Blocklist a specific subscriber.
Name | Parameter type | Data type | Required/Optional | Description
------|----------------|------------|-------------------|-------------
`id` | Path parameter | Number | Required | The id value of the subscriber you want to blocklist.
##### Parameters
##### Example Request
| Name | Type | Required | Description |
|:--------------|:----------|:---------|:-----------------|
| subscriber_id | Number | Yes | Subscriber's ID. |
##### Example Request
```shell
curl -u 'username:password' -X PUT 'http://localhost:9000/api/subscribers/9/blocklist'
```
##### Example Response
##### Example Response
``` json
```json
{
"data": true
}
```
#### **`PUT`** /api/subscribers/query/blocklist
Blocklists subscribers with an arbitrary sql expression.
______________________________________________________________________
#### PUT /api/subscribers/query/blocklist
Blocklist subscribers based on SQL expression.
> Refer to the [querying and segmentation](../querying-and-segmentation.md#querying-and-segmenting-subscribers) section for more information on how to query subscribers with SQL expressions.
##### Example Request
``` shell
```shell
curl -u 'username:password' -X PUT 'http://localhost:9000/api/subscribers/query/blocklist' \
--data-raw '"query=subscribers.name LIKE '\''John Doe'\'' AND subscribers.attribs->>'\''city'\'' = '\''Bengaluru'\''"'
```
>Refer to the [querying and segmentation](/querying-and-segmentation#querying-and-segmenting-subscribers) section for more information on how to query subscribers with SQL expressions.
##### Example Response
``` json
```json
{
"data": true
}
```
#### **`DELETE`** /api/subscribers/:`id`
Deletes a single subscriber.
______________________________________________________________________
##### Parameters
#### DELETE /api/subscribers/{subscriber_id}
Name | Parameter type | Data type | Required/Optional | Description
--------|------------------|-------------|--------------------|------------------
`id` | Path parameter | Number | Required | The id of the subscriber you want to delete.
Delete a specific subscriber.
##### Example Request
##### Parameters
``` shell
| Name | Type | Required | Description |
|:--------------|:----------|:---------|:-----------------|
| subscriber_id | Number | Yes | Subscriber's ID. |
##### Example Request
```shell
curl -u 'username:password' -X DELETE 'http://localhost:9000/api/subscribers/9'
```
##### Example Response
##### Example Response
``` json
```json
{
"data": true
}
```
#### **`DELETE`** /api/subscribers
Deletes one or more subscribers.
______________________________________________________________________
##### Parameters
#### DELETE /api/subscribers
Name | Parameter type | Data type | Required/Optional | Description
--------|---------------------|----------------|-----------------------|--------------
id | Query parameters | Number | Required | The id of the subscribers you want to delete.
Delete one or more subscribers.
##### Parameters
| Name | Type | Required | Description |
|:-----|:----------|:---------|:---------------------------|
| id | number\[\] | Yes | Array of subscriber's IDs. |
##### Example Request
``` shell
```shell
curl -u 'username:password' -X DELETE 'http://localhost:9000/api/subscribers?id=10&id=11'
```
##### Example Response
##### Example Response
``` json
```json
{
"data": true
}
```
______________________________________________________________________
#### POST /api/subscribers/query/delete
#### **`POST`** /api/subscribers/query/delete
Deletes subscribers with an arbitrary SQL expression.
Delete subscribers based on SQL expression.
##### Example Request
``` shell
```shell
curl -u 'username:password' -X POST 'http://localhost:9000/api/subscribers/query/delete' \
--data-raw '"query=subscribers.name LIKE '\''John Doe'\'' AND subscribers.attribs->>'\''city'\'' = '\''Bengaluru'\''"'
```
>Refer to the [querying and segmentation](/querying-and-segmentation#querying-and-segmenting-subscribers) section for more information on how to query subscribers with SQL expressions.
##### Example Response
``` json
```json
{
"data": true
}

View file

@ -1,26 +1,31 @@
# API / Templates
Method | Endpoint | Description
---------|------------------------------------------------------------------------------|-----------------------------------------
`GET` | [/api/templates](#get-apitemplates) | Gets all templates.
`GET` | [/api/templates/:`template_id`](#get-apitemplatestemplate_id) | Gets a single template.
`GET` | [/api/templates/:`template_id`/preview](#get-apitemplatestemplate_idpreview) | Gets the HTML preview of a template.
`POST` | /api/templates/preview |
`POST` | /api/templates | Creates a template.
`PUT` | /api/templates/:`template_id` | Modifies a template.
`PUT` | [/api/templates/:`template_id`/default](#put-apitemplatestemplate_iddefault) | Sets a template to the default template.
`DELETE` | [/api/templates/:`template_id`](#delete-apitemplatestemplate_id) | Deletes a template.
| Method | Endpoint | Description |
|:-------|:------------------------------------------------------------------------------|:-------------------------------|
| GET | [/api/templates](#get-apitemplates) | Retrieve all templates |
| GET | [/api/templates/{template_id}](#get-apitemplates-template_id) | Retrieve a template |
| GET | [/api/templates/{template_id}/preview](#get-apitemplates-template_id-preview) | Retrieve template HTML preview |
| POST | [/api/templates](#post-apitemplates) | Create a template |
| POST | /api/templates/preview | Render and preview a template |
| PUT | [/api/templates/{template_id}](#put-apitemplatestemplate_id) | Update a template |
| PUT | [/api/templates/{template_id}/default](#put-apitemplates-template_id-default) | Set default template |
| DELETE | [/api/templates/{template_id}](#delete-apitemplates-template_id) | Delete a template |
#### **`GET`** /api/templates
Gets all templates.
______________________________________________________________________
#### GET /api/templates
Retrieve all templates.
##### Example Request
```shell
curl -u "username:username" -X GET 'http://localhost:9000/api/templates'
```
```shell
curl -u "username:username" -X GET 'http://localhost:9000/api/templates'
```
##### Example Response
``` json
```json
{
"data": [
{
@ -36,20 +41,26 @@ Gets all templates.
}
```
#### **`GET`** /api/templates/:`template_id`
Gets a single template.
______________________________________________________________________
#### GET /api/templates/{template_id}
Retrieve a specific template.
##### Parameters
Name | Parameter Type | Data Type | Required/Optional | Description
--------------|----------------|-----------|-------------------|----------------------------------------------
`template_id` | Path Parameter | Number | Required | The id value of the template you want to get.
| Name | Type | Required | Description |
|:------------|:----------|:---------|:-------------------------------|
| template_id | number | Yes | ID of the template to retrieve |
##### Example Request
``` shell
```shell
curl -u "username:username" -X GET 'http://localhost:9000/api/templates/1'
```
##### Example Response
```json
{
"data": {
@ -64,21 +75,27 @@ curl -u "username:username" -X GET 'http://localhost:9000/api/templates/1'
}
```
#### **`GET`** /api/templates/:`template_id`/preview
Gets the HTML preview of a template body.
______________________________________________________________________
#### GET /api/templates/{template_id}/preview
Retrieve the HTML preview of a template.
##### Parameters
Name | Parameter Type | Data Type | Required/Optional | Description
--------------|----------------|------------|-------------------|-----------------------------------------------------------------
`template_id` | Path Parameter | Number | Required | The id value of the template whose html preview you want to get.
| Name | Type | Required | Description |
|:------------|:----------|:---------|:------------------------------|
| template_id | number | Yes | ID of the template to preview |
##### Example Request
``` shell
```shell
curl -u "username:username" -X GET 'http://localhost:9000/api/templates/1/preview'
```
##### Example Response
``` html
```html
<p>Hi there</p>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis et elit ac elit sollicitudin condimentum non a magna.
Sed tempor mauris in facilisis vehicula. Aenean nisl urna, accumsan ac tincidunt vitae, interdum cursus massa.
@ -93,22 +110,81 @@ curl -u "username:username" -X GET 'http://localhost:9000/api/templates/1/previe
<p>Here is a link to <a href="https://listmonk.app" target="_blank">listmonk</a>.</p>
```
#### **`PUT`** /api/templates/:`template_id`/default
Sets a template to the default template.
______________________________________________________________________
#### POST /api/templates
Create a template.
##### Parameters
Name | Parameter Type | Data Type | Required/Optional | Description
--------------|----------------|-----------|-------------------|----------------------------------------------------------------------
`template_id` | Path Parameter | Number | Required | The id value of the template you want to set to the default template.
| Name | Type | Required | Description |
|:--------|:----------|:---------|:----------------------------------------------|
| name | string | Yes | Name of the template |
| type | string | Yes | Type of the template (`campaign` or `tx`) |
| subject | string | | Subject line for the template (only for `tx`) |
| body | string | Yes | HTML body of the template |
##### Example Request
``` shell
```shell
curl -u "username:password" -X POST 'http://localhost:9000/api/templates' \
-H 'Content-Type: application/json' \
-d '{
"name": "New template",
"type": "campaign",
"subject": "Your Weekly Newsletter",
"body": "<h1>Header</h1><p>Content goes here</p>"
}'
```
##### Example Response
```json
{
"data": [
{
"id": 1,
"created_at": "2020-03-14T17:36:41.288578+01:00",
"updated_at": "2020-03-14T17:36:41.288578+01:00",
"name": "Default template",
"body": "{{ template \"content\" . }}",
"type": "campaign",
"is_default": true
}
]
}
```
______________________________________________________________________
#### PUT /api/templates/{template_id}
Update a template.
> Refer to parameters from [POST /api/templates](#post-apitemplates)
______________________________________________________________________
#### PUT /api/templates/{template_id}/default
Set a template as the default.
##### Parameters
| Name | Type | Required | Description |
|:------------|:----------|:---------|:-------------------------------------|
| template_id | number | Yes | ID of the template to set as default |
##### Example Request
```shell
curl -u "username:username" -X PUT 'http://localhost:9000/api/templates/1/default'
```
##### Example Response
``` json
```json
{
"data": {
"id": 1,
@ -122,24 +198,28 @@ curl -u "username:username" -X PUT 'http://localhost:9000/api/templates/1/defaul
}
```
______________________________________________________________________
#### **`DELETE`** /api/templates/:`template_id`
Deletes a template.
#### DELETE /api/templates/{template_id}
Delete a template.
##### Parameters
Name | Parameter Type | Data Type | Required/Optional | Description
--------------|----------------|-----------|-------------------|-------------------------------------------------
`template_id` | Path Parameter | Number | Required | The id value of the template you want to delete.
| Name | Type | Required | Description |
|:------------|:----------|:---------|:-----------------------------|
| template_id | number | Yes | ID of the template to delete |
##### Example Request
``` shell
```shell
curl -u "username:username" -X DELETE 'http://localhost:9000/api/templates/35'
```
##### Example Response
```json
{
"data": true
}
```
```

View file

@ -1,30 +1,32 @@
# API / Transactional
| Method | Endpoint | Description |
|:-------|:---------|:------------|
| `POST` | /api/tx | |
| Method | Endpoint | Description |
|:-------|:---------|:-------------------------------|
| POST | /api/tx | Send transactional messages |
______________________________________________________________________
## POST /api/tx
Send a transactional message to one or multiple subscribers using a predefined transactional template.
#### POST /api/tx
Allows sending transactional messages to one or more subscribers via a preconfigured transactional template.
##### Parameters
| Name | Data Type | Optional | Description |
|:--------------------|:----------|:--------|:-------------------------------------------------------------------------------------------------------------------------------------------------|
| `subscriber_email` | String | Optional | E-mail of the subscriber. Either this or `subscriber_id` should be passed. |
| `subscriber_id` | Number | Optional | ID of the subscriber. Either this or `subscriber_email` should be passed. |
| `subscriber_emails` | []String | Optional | E-mails of the subscribers. This is an alternative to `subscriber_email` for multiple recipients. `["email1@example.com", "emailX@example.com"]` |
| `subscriber_ids` | []Number | Optional | IDs of the subscribers. This is an alternative to `subscriber_id` for multiple recipients. `[1,2,3]` |
| `template_id` | Number | Required | ID of the transactional template to use in the message. |
| `from_email` | String | Optional | Optional `from` email. eg: `Company <email@company.com>` |
| `data` | Map | Optional | Optional data in `{}` nested map. Available in the template as `{{ .Tx.Data.* }}` |
| `headers` | []Map | Optional | Optional array of mail headers. `[{"key": "value"}, {"key": "value"}]` |
| `messenger` | String | Optional | Messenger to use to send the message. Default value is `email`. |
| `content_type` | String | Optional | `html`, `markdown`, `plain` |
| Name | Type | Required | Description |
|:------------------|:----------|:---------|:---------------------------------------------------------------------------|
| subscriber_email | string | | Email of the subscriber. Can substitute with `subscriber_id`. |
| subscriber_id | number | | Subscriber's ID can substitute with `subscriber_email`. |
| subscriber_emails | string\[\] | | Multiple subscriber emails as alternative to `subscriber_email`. |
| subscriber_ids | number\[\] | | Multiple subscriber IDs as an alternative to `subscriber_id`. |
| template_id | number | Yes | ID of the transactional template to be used for the message. |
| from_email | string | | Optional sender email. |
| data | JSON | | Optional nested JSON map. Available in the template as `{{ .Tx.Data.* }}`. |
| headers | JSON\[\] | | Optional array of email headers. |
| messenger | string | | Messenger to send the message. Default is `email`. |
| content_type | string | | Email format options include `html`, `markdown`, and `plain`. |
##### Example
##### Request
```shell
curl -u "username:password" "http://localhost:9000/api/tx" -X POST \
-H 'Content-Type: application/json; charset=utf-8' \
@ -38,16 +40,19 @@ curl -u "username:password" "http://localhost:9000/api/tx" -X POST \
EOF
```
##### Response
``` json
##### Example response
```json
{
"data": true
}
```
### File Attachments
To include file attachments in a transactional message, use Content-Type `multipart/form-data`.
Use the parameters described above as a JSON object via the `data` form key and include an arbitrary number of attachments via the `file` key.
______________________________________________________________________
#### File Attachments
To include file attachments in a transactional message, use the `multipart/form-data` Content-Type. Use `data` param for the parameters described above as a JSON object. Include any number of attachments via the `file` param.
```shell
curl -u "username:password" "http://localhost:9000/api/tx" -X POST \
@ -58,4 +63,3 @@ curl -u "username:password" "http://localhost:9000/api/tx" -X POST \
-F 'file=@"/path/to/attachment.pdf"' \
-F 'file=@"/path/to/attachment2.pdf"'
```

View file

@ -2,7 +2,7 @@
Enable bounce processing in Settings -> Bounces. POP3 bounce scanning and APIs only become available once the setting is enabled.
### POP3 bounce mailbox
## POP3 bounce mailbox
Configure the bounce mailbox in Settings -> Bounces. Either the "From" e-mail that is set on a campaign (or in settings) should have a POP3 mailbox behind it to receive bounce e-mails, or you should configure a dedicated POP3 mailbox and add that address as the `Return-Path` (envelope sender) header in Settings -> SMTP -> Custom headers box. For example:
```
@ -14,22 +14,22 @@ Configure the bounce mailbox in Settings -> Bounces. Either the "From" e-mail th
Some mail servers may also return the bounce to the `Reply-To` address, which can also be added to the header settings.
### Webhook API
## Webhook API
The bounce webhook API can be used to record bounce events with custom scripting. This could be by reading a mailbox, a database, or mail server logs.
| Method | Endpoint | Description |
|--------|------------------|------------------------|
| ------ | ---------------- | ---------------------- |
| `POST` | /webhooks/bounce | Record a bounce event. |
| Name | Data type | Required/Optional | Description |
|-------------------|-----------|-------------------|--------------------------------------------------------------------------------------|
| `subscriber_uuid` | String | Optional | The UUID of the subscriber. Either this or `email` is required. |
| `email` | String | Optional | The e-mail of the subscriber. Either this or `subscriber_uuid` is required. |
| `campaign_uuid` | String | Optional | UUID of the campaign for which the bounce happened. |
| `source` | String | Required | A string indicating the source, eg: `api`, `my_script` etc. |
| `type` | String | Required | `hard` or `soft` bounce. Currently, this has no effect on how the bounce is treated. |
| `meta` | String | Optional | An optional escaped JSON string with arbitrary metadata about the bounce event. |
| Name | Type | Required | Description |
| ----------------| --------- | -----------| ------------------------------------------------------------------------------------ |
| subscriber_uuid | string | | The UUID of the subscriber. Either this or `email` is required. |
| email | string | | The e-mail of the subscriber. Either this or `subscriber_uuid` is required. |
| campaign_uuid | string | | UUID of the campaign for which the bounce happened. |
| source | string | Yes | A string indicating the source, eg: `api`, `my_script` etc. |
| type | string | Yes | `hard` or `soft` bounce. Currently, this has no effect on how the bounce is treated. |
| meta | string | | An optional escaped JSON string with arbitrary metadata about the bounce event. |
```shell
@ -39,10 +39,28 @@ curl -u 'username:password' -X POST localhost:9000/webhooks/bounce \
```
### External webhooks
## External webhooks
listmonk supports receiving bounce webhook events from the following SMTP providers.
| Endpoint | Description | More info |
|-----------------------------|------------------|-----------|
| `https://listmonk.yoursite.com/webhooks/service/ses` | Amazon (AWS) SES | You can use these [Mautic steps](https://docs.mautic.org/en/channels/emails/bounce-management#amazon-webhook) as a general guide, but use your listmonk's endpoint instead. <ul> <li>When creating the *topic* select "standard" instead of the preselected "FIFO". You can put a name and leave everything else at default.</li> <li>When creating a *subscription* choose HTTPS for "Protocol", and leave *"Enable raw message delivery"* UNCHECKED.</li> <li>On the _"SES -> verified identities"_ page, make sure to check **"[include original headers](https://github.com/knadh/listmonk/issues/720#issuecomment-1046877192)"**.</li> <li>The Mautic screenshot suggests you should turn off _email feedback forwarding_, but that's completely optional depending on whether you want want email notifications.</li></ul> |
| `https://listmonk.yoursite.com/webhooks/service/sendgrid` | Sendgrid / Twilio Signed event webhook | [More info](https://docs.sendgrid.com/for-developers/tracking-events/getting-started-event-webhook-security-features) |
| Endpoint | Description | More info |
|:----------------------------------------------------------|:---------------------------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `https://listmonk.yoursite.com/webhooks/service/ses` | Amazon (AWS) SES | You can use these [Mautic steps](https://docs.mautic.org/en/channels/emails/bounce-management#amazon-webhook) as a general guide, but use your listmonk's endpoint instead. <ul> <li>When creating the *topic* select "standard" instead of the preselected "FIFO". You can put a name and leave everything else at default.</li> <li>When creating a *subscription* choose HTTPS for "Protocol", and leave *"Enable raw message delivery"* UNCHECKED.</li> <li>On the _"SES -> verified identities"_ page, make sure to check **"[include original headers](https://github.com/knadh/listmonk/issues/720#issuecomment-1046877192)"**.</li> <li>The Mautic screenshot suggests you should turn off _email feedback forwarding_, but that's completely optional depending on whether you want want email notifications.</li></ul> |
| `https://listmonk.yoursite.com/webhooks/service/sendgrid` | Sendgrid / Twilio Signed event webhook | [More info](https://docs.sendgrid.com/for-developers/tracking-events/getting-started-event-webhook-security-features) |
| `https://listmonk.yoursite.com/webhooks/service/postmark` | Postmark webhook | [More info](https://postmarkapp.com/developer/webhooks/webhooks-overview) |
## Verification
If you're using Amazon SES you can use Amazon's test emails to make sure everything's working: [https://docs.aws.amazon.com/ses/latest/dg/send-an-email-from-console.html](https://docs.aws.amazon.com/ses/latest/dg/send-an-email-from-console.html)
```
success@simulator.amazonses.com
bounce@simulator.amazonses.com
complaint@simulator.amazonses.com
suppressionlist@simulator.amazonses.com
```
They all count as _hard_ bounces.
**Exporting bounces**: [https://github.com/knadh/listmonk/issues/863](https://github.com/knadh/listmonk/issues/863)

View file

@ -6,7 +6,7 @@ A subscriber is a recipient identified by an e-mail address and name. Subscriber
### Attributes
Attributes are arbitrary properties attached to a subscriber in addition to their e-mail and name. They are represented as a JSON map. It is not necessary for all subscribers to have the same attributes. Subscribers can be [queried and segmented](../querying-and-segmentation) into lists based on their attributes, and the attributes can be inserted into the e-mails sent to them. For example:
Attributes are arbitrary properties attached to a subscriber in addition to their e-mail and name. They are represented as a JSON map. It is not necessary for all subscribers to have the same attributes. Subscribers can be [queried and segmented](querying-and-segmentation.md) into lists based on their attributes, and the attributes can be inserted into the e-mails sent to them. For example:
```json
{
@ -35,7 +35,7 @@ A subscriber can be added to one or more lists, and each such relationship can h
### Segmentation
Segmentation is the process of filtering a large list of subscribers into a smaller group based on arbitrary conditions, primarily based on their attributes. For instance, if an e-mail needs to be sent subscribers who live in a particular city, given their city is described in their attributes, it's possible to quickly filter them out into a new list and e-mail them. [Learn more](../querying-and-segmentation).
Segmentation is the process of filtering a large list of subscribers into a smaller group based on arbitrary conditions, primarily based on their attributes. For instance, if an e-mail needs to be sent subscribers who live in a particular city, given their city is described in their attributes, it's possible to quickly filter them out into a new list and e-mail them. [Learn more](querying-and-segmentation.md).
## List
@ -53,11 +53,11 @@ A transactional message is an arbitrary message sent to a subscriber using the t
## Template
A template is a re-usable HTML design that can be used across campaigns and when sending arbitrary transactional messages. Most commonly, templates have standard header and footer areas with logos and branding elements, where campaign content is inserted in the middle. listmonk supports [Go template](https://gowebexamples.com/templates/) expressions that lets you create powerful, dynamic HTML templates. [Learn more](../templating).
A template is a re-usable HTML design that can be used across campaigns and when sending arbitrary transactional messages. Most commonly, templates have standard header and footer areas with logos and branding elements, where campaign content is inserted in the middle. listmonk supports [Go template](https://gowebexamples.com/templates/) expressions that lets you create powerful, dynamic HTML templates. [Learn more](templating.md).
## Messenger
listmonk supports multiple custom messaging backends in additional to the default SMTP e-mail backend, enabling not just e-mail campaigns, but arbitrary message campaigns such as SMS, FCM notifications etc. A *Messenger* is a web service that accepts a campaign message pushed to it as a JSON request, which the service can in turn broadcast as SMS, FCM etc. [Learn more](../messengers).
listmonk supports multiple custom messaging backends in additional to the default SMTP e-mail backend, enabling not just e-mail campaigns, but arbitrary message campaigns such as SMS, FCM notifications etc. A *Messenger* is a web service that accepts a campaign message pushed to it as a JSON request, which the service can in turn broadcast as SMS, FCM etc. [Learn more](messengers.md).
## Tracking pixel
@ -69,4 +69,4 @@ It is possible to track the clicks on every link that is sent in an e-mail. This
## Bounce
A bounce occurs when an e-mail that is sent to a recipient "bounces" back for one of many reasons including the recipient address being invalid, their mailbox being full, or the recipient's e-mail service provider marking the e-mail as spam. listmonk can automatically process such bounce e-mails that land in a configured POP mailbox, or via APIs of SMTP e-mail providers such as AWS SES and Sengrid. Based on settings, subscribers returning bounced e-mails can either be blocklisted or deleted automatically. [Learn more](../bounces).
A bounce occurs when an e-mail that is sent to a recipient "bounces" back for one of many reasons including the recipient address being invalid, their mailbox being full, or the recipient's e-mail service provider marking the e-mail as spam. listmonk can automatically process such bounce e-mails that land in a configured POP mailbox, or via APIs of SMTP e-mail providers such as AWS SES and Sengrid. Based on settings, subscribers returning bounced e-mails can either be blocklisted or deleted automatically. [Learn more](bounces.md).

View file

@ -9,7 +9,7 @@ To generate a new sample configuration file, run `--listmonk --new-config`
Variables in config.toml can also be provided as environment variables prefixed by `LISTMONK_` with periods replaced by `__` (double underscore). Example:
| **Environment variable** | Example value |
|--------------------------------|----------------|
| ------------------------------ | -------------- |
| `LISTMONK_app__address` | "0.0.0.0:9000" |
| `LISTMONK_app__admin_username` | listmonk |
| `LISTMONK_app__admin_password` | listmonk |
@ -22,7 +22,7 @@ Variables in config.toml can also be provided as environment variables prefixed
### Customizing system templates
[Read this](../templating/#system-templates)
See [system templates](templating.md#system-templates).
### HTTP routes
@ -31,7 +31,7 @@ When configuring auth proxies and web application firewalls, use this table.
#### Private admin endpoints.
| Methods | Route | Description |
|---------|--------------------|-------------------------|
| ------- | ------------------ | ----------------------- |
| `*` | `/api/*` | Admin APIs |
| `GET` | `/admin/*` | Admin UI and HTML pages |
| `POST` | `/webhooks/bounce` | Admin bounce webhook |
@ -40,7 +40,7 @@ When configuring auth proxies and web application firewalls, use this table.
#### Public endpoints to expose to the internet.
| Methods | Route | Description |
|-------------|-----------------------|-----------------------------------------------|
| ----------- | --------------------- | --------------------------------------------- |
| `GET, POST` | `/subscription/*` | HTML subscription pages |
| `GET, ` | `/link/*` | Tracked link redirection |
| `GET` | `/campaign/*` | Pixel tracking image |
@ -48,13 +48,13 @@ When configuring auth proxies and web application firewalls, use this table.
| `POST` | `/webhooks/service/*` | Bounce webhook endpoints for AWS and Sendgrid |
## Media Uploads
## Media uploads
### Filesystem
#### Using filesystem
When configuring `docker` volume mounts for using filesystem media uploads, you can follow either of two approaches. [The second option may be necessary if](https://github.com/knadh/listmonk/issues/1169#issuecomment-1674475945) your setup requires you to use `sudo` for docker commands.
After making any changes you will need to run `sudo docker-compose stop ; sudo docker-compose up`.
After making any changes you will need to run `sudo docker compose stop ; sudo docker compose up`.
And under `https://listmonk.mysite.com/admin/settings` you put `/listmonk/uploads`.
@ -97,6 +97,16 @@ To use the default `uploads` folder:
```yml
app:
volumes:
- ./listmonk/uploads:/listmonk/uploads
- ./uploads:/listmonk/uploads
```
## Time zone
To change listmonk's time zone (logs, etc.) edit `docker-compose.yml`:
```
environment:
- TZ=Etc/UTC
```
with any Timezone listed [here](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones). Then run `sudo docker-compose stop ; sudo docker-compose up` after making changes.

View file

@ -5,7 +5,7 @@ The app has two distinct components, the Go backend and the VueJS frontend. In t
### Pre-requisites
- `go`
- `nodejs` (if you are working on the frontend) and `yarn`
- Postgres database. If there is no local installation, the demo docker DB can be used for development (`docker-compose up demo-db`)
- Postgres database. If there is no local installation, the demo docker DB can be used for development (`docker compose up demo-db`)
### First time setup

View file

@ -4,7 +4,7 @@ In many environments, a mailing list manager's subscriber database is not run in
## Using APIs
The [subscriber APIs](../apis/subscribers) offers several APIs to manipulate the subscribers database, like addition, updation, and deletion. For bulk synchronisation, a CSV can be generated (and optionally zipped) and posted to the import API.
The [subscriber APIs](apis/subscribers.md) offers several APIs to manipulate the subscribers database, like addition, updation, and deletion. For bulk synchronisation, a CSV can be generated (and optionally zipped) and posted to the import API.
## Interacting directly with the DB

View file

@ -7,4 +7,4 @@ listmonk is a self-hosted, high performance mailing list and newsletter manager.
[![listmonk screenshot](https://user-images.githubusercontent.com/547147/134939475-e0391111-f762-44cb-b056-6cb0857755e3.png)](https://listmonk.app)
## Developers
listmonk is a free and open source software licensed under AGPLv3. If you are interested in contributing, check out the [GitHub repository](https://github.com/knadh/listmonk) and refer to the [developer setup](developer-setup). The backend is written in Go and the frontend is Vue with Buefy for UI.
listmonk is a free and open source software licensed under AGPLv3. If you are interested in contributing, check out the [GitHub repository](https://github.com/knadh/listmonk) and refer to the [developer setup](developer-setup.md). The backend is written in Go and the frontend is Vue with Buefy for UI.

View file

@ -2,6 +2,8 @@
listmonk requires Postgres ⩾ 12.
See the "[Tutorials](#tutorials)" section at the bottom for detailed guides.
## Binary
- Download the [latest release](https://github.com/knadh/listmonk/releases) and extract the listmonk binary.
- `./listmonk --new-config` to generate config.toml. Then, edit the file.
@ -13,14 +15,17 @@ listmonk requires Postgres ⩾ 12.
The latest image is available on DockerHub at `listmonk/listmonk:latest`
Use the sample [docker-compose.yml](https://github.com/knadh/listmonk/blob/master/docker-compose.yml) to run listmonk and Postgres DB with docker-compose as follows:
!!! note
Listmonk's docs and scripts use `docker compose`, which is compatible with the latest version of docker. If you installed docker and docker-compose from your Linux distribution, you probably have an older version and will need to use the `docker-compose` command instead, or you'll need to update docker manually. [More info](https://gist.github.com/MaximilianKohler/e5158fcfe6de80a9069926a67afcae11#docker-update).
Use the sample [docker-compose.yml](https://github.com/knadh/listmonk/blob/master/docker-compose.yml) to run listmonk and Postgres DB with `docker compose` as follows:
### Demo
#### Easy Docker install
```bash
mkdir listmonk-demo
mkdir listmonk-demo && cd listmonk-demo
sh -c "$(curl -fsSL https://raw.githubusercontent.com/knadh/listmonk/master/install-demo.sh)"
```
@ -28,7 +33,7 @@ sh -c "$(curl -fsSL https://raw.githubusercontent.com/knadh/listmonk/master/inst
```bash
wget -O docker-compose.yml https://raw.githubusercontent.com/knadh/listmonk/master/docker-compose.yml
docker-compose up -d demo-db demo-app
docker compose up -d demo-db demo-app
```
!!! warning
@ -41,7 +46,7 @@ docker-compose up -d demo-db demo-app
This setup is recommended if you want to _quickly_ setup `listmonk` in production.
```bash
mkdir listmonk
mkdir listmonk && cd listmonk
sh -c "$(curl -fsSL https://raw.githubusercontent.com/knadh/listmonk/master/install-prod.sh)"
```
@ -56,14 +61,14 @@ The above shell script performs the following actions:
#### Manual Docker install
The following workflow is recommended to setup `listmonk` manually using `docker-compose`. You are encouraged to customise the contents of `docker-compose.yml` to your needs. The overall setup looks like:
The following workflow is recommended to setup `listmonk` manually using `docker compose`. You are encouraged to customise the contents of `docker-compose.yml` to your needs. The overall setup looks like:
- `docker-compose up db` to run the Postgres DB.
- `docker-compose run --rm app ./listmonk --install` to setup the DB (or `--upgrade` to upgrade an existing DB).
- `docker compose up db` to run the Postgres DB.
- `docker compose run --rm app ./listmonk --install` to setup the DB (or `--upgrade` to upgrade an existing DB).
- Copy `config.toml.sample` to your directory and make the following changes:
- `app.address` => `0.0.0.0:9000` (Port forwarding on Docker will work only if the app is advertising on all interfaces.)
- `db.host` => `listmonk_db` (Container Name of the DB container)
- Run `docker-compose up app` and visit `http://localhost:9000`.
- Run `docker compose up app` and visit `http://localhost:9000`.
##### Mounting a custom config.toml
@ -108,12 +113,23 @@ max_lifetime = "300s"
Mount the local `config.toml` inside the container at `listmonk/config.toml`.
!!! tip
- See [configuring with environment variables](../configuration) for variables like `app.admin_password` and `db.password`
- Ensure that both `app` and `db` containers are in running. If the containers are not running, restart them `docker-compose restart app db`.
- See [configuring with environment variables](configuration.md) for variables like `app.admin_password` and `db.password`
- Ensure that both `app` and `db` containers are in running. If the containers are not running, restart them `docker compose restart app db`.
- Refer to [this tutorial](https://yasoob.me/posts/setting-up-listmonk-opensource-newsletter-mailing/) for setting up a production instance with Docker + Nginx + LetsEncrypt SSL.
!!! info
The example `docker-compose.yml` file works with Docker Engine 18.06.0+ and `docker-compose` which supports file format 3.7.
The example `docker-compose.yml` file works with Docker Engine 24.0.5+ and Docker Compose version v2.20.2+.
##### Changing the port
To change the port for listmonk:
- Ensure no other container of listmonk app is running. You can check with `docker ps | grep listmonk`.
- Change [L11](https://github.com/knadh/listmonk/blob/master/docker-compose.yml#L11) to `custom-port:9000` Eg: `3876:9000`. This will expose the port 3876 on your local network to the container's network interface on port 9000.
- For NGINX setup, if you're running NGINX on your local machine, you can proxy_pass to the `<MACHINE_IP>:3876`. You can also run NGINX as a docker container within the listmonk's container (for that you need to add a service `nginx` in the docker-compose.yml). If you do that, then proxy_pass will be set to `http://app:9000`. Docker's network will resolve the DNS for `app` and directly speak to port 9000 (which the app is exposing within its own network).
## Compiling from source
@ -129,8 +145,21 @@ The `master` branch with bleeding edge changes is periodically built and publish
## 3rd party hosting
<a href="https://railway.app/new/template/listmonk"><img src="https://camo.githubusercontent.com/081df3dd8cff37aab35044727b02b94a8e948052487a8c6253e190f5940d776d/68747470733a2f2f7261696c7761792e6170702f627574746f6e2e737667" alt="One-click deploy on Raleway" style="max-height: 32px;" /></a>
<br />
<a href="https://www.pikapods.com/pods?run=listmonk"><img src="https://www.pikapods.com/static/run-button.svg" alt="Deploy on PikaPod" /></a>
<a href ="https://github.com/paulrudy/listmonk-on-fly">Tutorial for deploying on Fly.io</a>
## Tutorials
* [Informal step-by-step on how to get started with Listmonk using **Railway**](https://github.com/knadh/listmonk/issues/120#issuecomment-1421838533)
* [Complete Listmonk setup guide. Step-by-step tutorial for installation and all basic functions. **Amazon EC2 & SES**](https://gist.github.com/MaximilianKohler/e5158fcfe6de80a9069926a67afcae11)
* [Step-by-step guide on how to install and set up Listmonk on a server (rameerez, **AWS Lightsail & docker**)](https://github.com/knadh/listmonk/issues/1208)
* [**Binary** install on Ubuntu 22.04 as a service](https://mumaritc.hashnode.dev/how-to-install-listmonk-using-binary-on-ubuntu-2204)
* [**Binary** install on Ubuntu 18.04 as a service (Apache & Plesk)](https://devgypsy.com/post/2020-08-18-installing-listmonk-newsletter-manager/)
* [**Binary and docker** on linux (techviewleo)](https://techviewleo.com/manage-mailing-list-and-newsletter-using-listmonk/)
* [**Binary** install on your PC](https://www.youtube.com/watch?v=fAOBqgR9Yfo). Discussions of limitations: [[1](https://github.com/knadh/listmonk/issues/862#issuecomment-1307328228)][[2](https://github.com/knadh/listmonk/issues/248#issuecomment-1320806990)].
* [Install Listmonk with **Docker on Rocky Linux 8** (nginx, Let's Encrypt SSL)](https://wiki.crowncloud.net/?How_to_Install_Listmonk_with_Docker_on_Rocky_Linux_8)
* [**Docker** with nginx reverse proxy, certbot SSL, and Gmail SMTP](https://www.maketecheasier.com/create-own-newsletter-with-listmonk/)
* [Install Listmonk on Self-hosting with **Pre-Configured AMI Package at AWS** by Single Click](https://meetrix.io/articles/how-to-install-llama-2-on-aws-with-pre-configured-ami-package/)
* [Tutorial for deploying on **Fly.io**](https://github.com/paulrudy/listmonk-on-fly) -- Currently [not working](https://github.com/knadh/listmonk/issues/984#issuecomment-1694545255)

View file

@ -8,7 +8,7 @@ Messengers support optional BasicAuth authentication. `Plain text` format for ca
When a campaign starts, listmonk POSTs messages in the following format to the selected messenger's endpoint. The endpoint should return a `200 OK` response in case of a successful request.
The address required to broadcast the message, for instance, a phone number or an FCM ID, is expected to be stored and relayed as [subscriber attributes](../concepts/#attributes).
The address required to broadcast the message, for instance, a phone number or an FCM ID, is expected to be stored and relayed as [subscriber attributes](concepts.md/#attributes).
```json
{

View file

@ -98,3 +98,15 @@ by accommodating for the fixed-header's height */
color: #666 !important;
text-decoration: underline;
}
.md-typeset hr {
background: #f6f6f6;
margin: 60px 0;
display: block;
}
.md-header--shadow {
box-shadow: 0 4px 3px #eee;
transition: none;
}
.md-header__topic:first-child {
font-weight: normal;
}

View file

@ -109,7 +109,7 @@ The expression `{{ template "content" . }}` should appear exactly once in every
### Example campaign body
Campaign bodies can be composed using the built-in WYSIWYG editor or as raw HTML documents. Assuming that the subscriber has a set of [attributes defined](../querying-and-segmentation#sample-attributes), this example shows how to render those values in a campaign.
Campaign bodies can be composed using the built-in WYSIWYG editor or as raw HTML documents. Assuming that the subscriber has a set of [attributes defined](querying-and-segmentation.md#sample-attributes), this example shows how to render those values in a campaign.
```
Hey, did you notice how the template showed your first name?

View file

@ -7,14 +7,54 @@ Some versions may require changes to the database. These changes or database "mi
- `./listmonk --upgrade` to upgrade an existing DB. Upgrades are idempotent and running them multiple times have no side effects.
- Run `./listmonk` and visit `http://localhost:9000`.
If you installed listmonk as a service, you will need to stop it before overwriting the binary. Something like `sudo systemctl stop listmonk` or `sudo service listmonk stop` should work. Then overwrite the binary with the new version, then run `./listmonk --upgrade, and `start` it back with the same commands.
If it's not running as a service, `pkill -9 listmonk` will stop the listmonk process.
## Docker
- `docker-compose pull` to pull the latest version from DockerHub.
- `docker-compose run --rm app ./listmonk --upgrade` to upgrade an existing DB.
- Run `docker-compose up app db` and visit `http://localhost:9000`.
- `docker compose pull` to pull the latest version from DockerHub.
- `docker compose run --rm app ./listmonk --upgrade` to upgrade an existing DB.
- Run `docker compose up app db` and visit `http://localhost:9000`.
## Railway
- Head to your dashboard, and select your Listmonk project.
- Select the GitHub deployment service.
- In the Deployment tab, head to the latest deployment, click on the three vertical dots to the right, and select "Redeploy".
- ![Railway Redeploy option](https://user-images.githubusercontent.com/55474996/226517149-6dc512d5-f862-46f7-a57d-5e55b781ff53.png)
![Railway Redeploy option](https://user-images.githubusercontent.com/55474996/226517149-6dc512d5-f862-46f7-a57d-5e55b781ff53.png)
## Downgrade
To restore a previous version, you have to restore the DB for that particular version. DBs that have been upgraded with a particular version shouldn't be used with older versions. There may be DB changes that a new version brings that are incompatible with previous versions.
**General steps:**
1. Stop listmonk.
2. Restore your pre-upgrade database.
3. If you're using `docker compose`, edit `docker-compose.yml` and change `listmonk:latest` to `listmonk:v2.4.0` _(for example)_.
4. Restart.
**Example with docker:**
1. Stop listmonk (app):
```
sudo docker stop listmonk_app
```
2. Restore your pre-upgrade db (required) _(be careful, this will wipe your existing DB)_:
```
psql -h 127.0.0.1 -p 9432 -U listmonk
drop schema public cascade;
create schema public;
\q
psql -h 127.0.0.1 -p 9432 -U listmonk -W listmonk < listmonk-preupgrade-db.sql
```
3. Edit the `docker-compose.yml`:
```
x-app-defaults: &app-defaults
restart: unless-stopped
image: listmonk/listmonk:v2.4.0
```
4. Restart:
`sudo docker compose up -d app db nginx certbot`

View file

@ -14,6 +14,8 @@ theme:
language: 'en'
feature:
tabs: true
features:
- content.code.copy
palette:
primary: "white"

View file

@ -1,32 +1,32 @@
{
"version": "v2.5.0",
"version": "v2.5.1",
"date": "2023-08-11T13:54:12Z",
"url": "https://github.com/knadh/listmonk/releases/tag/v2.5.0",
"url": "https://github.com/knadh/listmonk/releases/tag/v2.5.1",
"assets":
[
{
"name": "darwin",
"url": "https://github.com/knadh/listmonk/releases/download/v2.5.0/listmonk_2.5.0_darwin_amd64.tar.gz"
"url": "https://github.com/knadh/listmonk/releases/download/v2.5.1/listmonk_2.5.1_darwin_amd64.tar.gz"
},
{
"name": "freebsd",
"url": "https://github.com/knadh/listmonk/releases/download/v2.5.0/listmonk_2.5.0_freebsd_amd64.tar.gz"
"url": "https://github.com/knadh/listmonk/releases/download/v2.5.1/listmonk_2.5.1_freebsd_amd64.tar.gz"
},
{
"name": "linux",
"url": "https://github.com/knadh/listmonk/releases/download/v2.5.0/listmonk_2.5.0_linux_amd64.tar.gz"
"url": "https://github.com/knadh/listmonk/releases/download/v2.5.1/listmonk_2.5.1_linux_amd64.tar.gz"
},
{
"name": "netbsd",
"url": "https://github.com/knadh/listmonk/releases/download/v2.5.0/listmonk_2.5.0_netbsd_amd64.tar.gz"
"url": "https://github.com/knadh/listmonk/releases/download/v2.5.1/listmonk_2.5.1_netbsd_amd64.tar.gz"
},
{
"name": "openbsd",
"url": "https://github.com/knadh/listmonk/releases/download/v2.5.0/listmonk_2.5.0_openbsd_amd64.tar.gz"
"url": "https://github.com/knadh/listmonk/releases/download/v2.5.1/listmonk_2.5.1_openbsd_amd64.tar.gz"
},
{
"name": "windows",
"url": "https://github.com/knadh/listmonk/releases/download/v2.5.0/listmonk_2.5.0_windows_amd64.tar.gz"
"url": "https://github.com/knadh/listmonk/releases/download/v2.5.1/listmonk_2.5.1_windows_amd64.tar.gz"
}
]
}

View file

@ -2703,6 +2703,8 @@ components:
type: string
settings.bounces.enableSendgrid:
type: string
settings.bounces.enablePostmark:
type: string
settings.bounces.enableWebhooks:
type: string
settings.bounces.enabled:
@ -2721,6 +2723,12 @@ components:
type: string
settings.bounces.sendgridKey:
type: string
settings.bounces.postmarkUsername:
type: string
settings.bounces.postmarkUsernameHelp:
type: string
settings.bounces.postmarkPassword:
type: string
settings.bounces.type:
type: string
settings.bounces.username:
@ -3398,6 +3406,12 @@ components:
type: boolean
bounce.sendgrid_key:
type: string
bounce.postmark_enabled:
type: boolean
bounce.postmark_username:
type: string
bounce.postmark_password:
type: string
bounce.mailboxes:
type: array
items:

View file

@ -8,7 +8,7 @@ module.exports = {
'@vue/airbnb',
],
parserOptions: {
parser: 'babel-eslint',
parser: '@babel/eslint-parser',
},
rules: {
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',

View file

@ -234,9 +234,9 @@ describe('Subscribers', () => {
});
it('Sorts subscribers', () => {
const asc = [3, 4, 5, 6, 7, 8];
const desc = [8, 7, 6, 5, 4, 3];
const cases = ['cy-status', 'cy-email', 'cy-name', 'cy-created_at', 'cy-updated_at'];
let asc = [3, 4, 5, 6, 7, 8];
let desc = [8, 7, 6, 5, 4, 3];
let cases = ['cy-email', 'cy-name', 'cy-created_at', 'cy-updated_at'];
cases.forEach((c) => {
cy.sortTable(`thead th.${c}`, asc);
@ -244,6 +244,19 @@ describe('Subscribers', () => {
cy.sortTable(`thead th.${c}`, desc);
cy.wait(250);
});
asc = [4, 6, 8, 3, 5, 7];
desc = [7, 5, 3, 8, 6, 4];
cases = ['cy-status'];
cases.forEach((c) => {
cy.sortTable(`thead th.${c}`, asc);
cy.wait(250);
cy.sortTable(`thead th.${c}`, desc);
cy.wait(250);
});
});
});
@ -339,7 +352,7 @@ describe('Domain blocklist', () => {
cy.get('.b-tabs nav a').eq(2).click();
cy.get('textarea[name="privacy.domain_blocklist"]').clear();
cy.get('[data-cy=btn-save]').click();
cy.wait(1000);
cy.wait(3000);
// Add banned domain.
cy.request({

View file

@ -10,7 +10,7 @@
},
"dependencies": {
"@tinymce/tinymce-vue": "^3",
"axios": "^0.27.2",
"axios": "^1.6.0",
"buefy": "^0.9.10",
"c3": "^0.7.20",
"codeflask": "^1.4.1",
@ -19,7 +19,7 @@
"indent.js": "^0.3.5",
"qs": "^6.10.1",
"textversionjs": "^1.1.3",
"tinymce": "^5.10.7",
"tinymce": "^5.10.9",
"turndown": "^7.0.0",
"vue": "^2.6.12",
"vue-i18n": "^8.22.2",
@ -27,13 +27,14 @@
"vuex": "^3.6.2"
},
"devDependencies": {
"@babel/core": "^7.23.3",
"@babel/eslint-parser": "^7.23.3",
"@vue/cli-plugin-babel": "~5.0.8",
"@vue/cli-plugin-eslint": "~5.0.8",
"@vue/cli-plugin-router": "~5.0.8",
"@vue/cli-plugin-vuex": "~5.0.8",
"@vue/cli-service": "~5.0.8",
"@vue/eslint-config-airbnb": "^5.3.0",
"babel-eslint": "^10.1.0",
"cypress": "10.10.0",
"cypress-file-upload": "^5.0.2",
"eslint": "^7.27.0",

View file

@ -347,7 +347,7 @@ export default Vue.extend({
getCampaigns() {
this.$api.getCampaigns({
page: this.queryParams.page,
query: this.queryParams.query,
query: this.queryParams.query.replace(/[^\p{L}\p{N}\s]/gu, ' '),
order_by: this.queryParams.orderBy,
order: this.queryParams.order,
});

View file

@ -231,7 +231,7 @@ export default Vue.extend({
getLists() {
this.$api.queryLists({
page: this.queryParams.page,
query: this.queryParams.query,
query: this.queryParams.query.replace(/[^\p{L}\p{N}\s]/gu, ' '),
order_by: this.queryParams.orderBy,
order: this.queryParams.order,
}).then((resp) => {

View file

@ -155,6 +155,12 @@ export default Vue.extend({
hasDummy = 'captcha';
}
if (this.isDummy(form['bounce.postmark'].password)) {
form['bounce.postmark'].password = '';
} else if (this.hasDummy(form['bounce.postmark'].password)) {
hasDummy = 'postmark';
}
for (let i = 0; i < form.messengers.length; i += 1) {
// If it's the dummy UI password placeholder, ignore it.
if (this.isDummy(form.messengers[i].password)) {

View file

@ -331,9 +331,9 @@ export default Vue.extend({
this.queryParams.page = 1;
if (this.$utils.validateEmail(q)) {
this.queryParams.queryExp = `email = '${q}'`;
this.queryParams.queryExp = `email = '${q.toLowerCase()}'`;
} else {
this.queryParams.queryExp = `(name ~* '${q}' OR email ~* '${q}')`;
this.queryParams.queryExp = `(name ~* '${q}' OR email ~* '${q.toLowerCase()}')`;
}
},

View file

@ -73,6 +73,33 @@
</b-field>
</div>
</div>
<div class="columns">
<div class="column is-3">
<b-field :label="$t('settings.bounces.enablePostmark')">
<b-switch v-model="data['bounce.postmark'].enabled"
name="postmark_enabled" :native-value="true"
data-cy="btn-enable-bounce-postmark" />
</b-field>
</div>
<div class="column">
<b-field :label="$t('settings.bounces.postmarkUsername')"
:message="$t('settings.bounces.postmarkUsernameHelp')">
<b-input v-model="data['bounce.postmark'].username" type="text"
:disabled="!data['bounce.postmark'].enabled"
name="postmark_username"
data-cy="btn-enable-bounce-postmark" />
</b-field>
</div>
<div class="column">
<b-field :label="$t('settings.bounces.postmarkPassword')"
:message="$t('globals.messages.passwordChange')">
<b-input v-model="data['bounce.postmark'].password" type="password"
:disabled="!data['bounce.postmark'].enabled"
name="postmark_password"
data-cy="btn-enable-bounce-postmark" />
</b-field>
</div>
</div>
</div>
</div>

341
frontend/yarn.lock vendored
View file

@ -33,11 +33,24 @@
dependencies:
"@babel/highlight" "^7.18.6"
"@babel/code-frame@^7.22.13":
version "7.22.13"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.13.tgz#e3c1c099402598483b7a8c46a721d1038803755e"
integrity sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==
dependencies:
"@babel/highlight" "^7.22.13"
chalk "^2.4.2"
"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.20.1", "@babel/compat-data@^7.20.5":
version "7.21.0"
resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.21.0.tgz#c241dc454e5b5917e40d37e525e2f4530c399298"
integrity sha512-gMuZsmsgxk/ENC3O/fRw5QY8A9/uxQbbCEypnLIiYYc/qVJtEV7ouxC3EllIIwNzMqAQee5tanFabWsUOutS7g==
"@babel/compat-data@^7.22.9":
version "7.23.3"
resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.23.3.tgz#3febd552541e62b5e883a25eb3effd7c7379db11"
integrity sha512-BmR4bWbDIoFJmJ9z2cZ8Gmm2MXgEDgjdWgpKmKWUt54UGFJdlj31ECtbaDvCG/qVdG3AQ1SfpZEs01lUFbzLOQ==
"@babel/core@^7.12.16":
version "7.21.3"
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.21.3.tgz#cf1c877284a469da5d1ce1d1e53665253fae712e"
@ -59,6 +72,36 @@
json5 "^2.2.2"
semver "^6.3.0"
"@babel/core@^7.23.3":
version "7.23.3"
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.23.3.tgz#5ec09c8803b91f51cc887dedc2654a35852849c9"
integrity sha512-Jg+msLuNuCJDyBvFv5+OKOUjWMZgd85bKjbICd3zWrKAo+bJ49HJufi7CQE0q0uR8NGyO6xkCACScNqyjHSZew==
dependencies:
"@ampproject/remapping" "^2.2.0"
"@babel/code-frame" "^7.22.13"
"@babel/generator" "^7.23.3"
"@babel/helper-compilation-targets" "^7.22.15"
"@babel/helper-module-transforms" "^7.23.3"
"@babel/helpers" "^7.23.2"
"@babel/parser" "^7.23.3"
"@babel/template" "^7.22.15"
"@babel/traverse" "^7.23.3"
"@babel/types" "^7.23.3"
convert-source-map "^2.0.0"
debug "^4.1.0"
gensync "^1.0.0-beta.2"
json5 "^2.2.3"
semver "^6.3.1"
"@babel/eslint-parser@^7.23.3":
version "7.23.3"
resolved "https://registry.yarnpkg.com/@babel/eslint-parser/-/eslint-parser-7.23.3.tgz#7bf0db1c53b54da0c8a12627373554a0828479ca"
integrity sha512-9bTuNlyx7oSstodm1cR1bECj4fkiknsDa1YniISkJemMY3DGhJNYBECbe6QD/q54mp2J8VO66jW3/7uP//iFCw==
dependencies:
"@nicolo-ribaudo/eslint-scope-5-internals" "5.1.1-v1"
eslint-visitor-keys "^2.1.0"
semver "^6.3.1"
"@babel/generator@^7.21.3":
version "7.21.3"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.21.3.tgz#232359d0874b392df04045d72ce2fd9bb5045fce"
@ -69,6 +112,26 @@
"@jridgewell/trace-mapping" "^0.3.17"
jsesc "^2.5.1"
"@babel/generator@^7.23.0":
version "7.23.0"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.0.tgz#df5c386e2218be505b34837acbcb874d7a983420"
integrity sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==
dependencies:
"@babel/types" "^7.23.0"
"@jridgewell/gen-mapping" "^0.3.2"
"@jridgewell/trace-mapping" "^0.3.17"
jsesc "^2.5.1"
"@babel/generator@^7.23.3":
version "7.23.3"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.3.tgz#86e6e83d95903fbe7613f448613b8b319f330a8e"
integrity sha512-keeZWAV4LU3tW0qRi19HRpabC/ilM0HRBBzf9/k8FFiG4KVpiv0FIy4hHfLfFQZNhziCTPTmd59zoyv6DNISzg==
dependencies:
"@babel/types" "^7.23.3"
"@jridgewell/gen-mapping" "^0.3.2"
"@jridgewell/trace-mapping" "^0.3.17"
jsesc "^2.5.1"
"@babel/helper-annotate-as-pure@^7.18.6":
version "7.18.6"
resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz#eaa49f6f80d5a33f9a5dd2276e6d6e451be0a6bb"
@ -95,6 +158,17 @@
lru-cache "^5.1.1"
semver "^6.3.0"
"@babel/helper-compilation-targets@^7.22.15":
version "7.22.15"
resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz#0698fc44551a26cf29f18d4662d5bf545a6cfc52"
integrity sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==
dependencies:
"@babel/compat-data" "^7.22.9"
"@babel/helper-validator-option" "^7.22.15"
browserslist "^4.21.9"
lru-cache "^5.1.1"
semver "^6.3.1"
"@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.21.0":
version "7.21.0"
resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.21.0.tgz#64f49ecb0020532f19b1d014b03bccaa1ab85fb9"
@ -134,6 +208,11 @@
resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be"
integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==
"@babel/helper-environment-visitor@^7.22.20":
version "7.22.20"
resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167"
integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==
"@babel/helper-explode-assignable-expression@^7.18.6":
version "7.18.6"
resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.18.6.tgz#41f8228ef0a6f1a036b8dfdfec7ce94f9a6bc096"
@ -149,6 +228,14 @@
"@babel/template" "^7.20.7"
"@babel/types" "^7.21.0"
"@babel/helper-function-name@^7.23.0":
version "7.23.0"
resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz#1f9a3cdbd5b2698a670c30d2735f9af95ed52759"
integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==
dependencies:
"@babel/template" "^7.22.15"
"@babel/types" "^7.23.0"
"@babel/helper-hoist-variables@^7.18.6":
version "7.18.6"
resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz#d4d2c8fb4baeaa5c68b99cc8245c56554f926678"
@ -156,6 +243,13 @@
dependencies:
"@babel/types" "^7.18.6"
"@babel/helper-hoist-variables@^7.22.5":
version "7.22.5"
resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb"
integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==
dependencies:
"@babel/types" "^7.22.5"
"@babel/helper-member-expression-to-functions@^7.20.7", "@babel/helper-member-expression-to-functions@^7.21.0":
version "7.21.0"
resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.21.0.tgz#319c6a940431a133897148515877d2f3269c3ba5"
@ -170,6 +264,13 @@
dependencies:
"@babel/types" "^7.18.6"
"@babel/helper-module-imports@^7.22.15":
version "7.22.15"
resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz#16146307acdc40cc00c3b2c647713076464bdbf0"
integrity sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==
dependencies:
"@babel/types" "^7.22.15"
"@babel/helper-module-transforms@^7.18.6", "@babel/helper-module-transforms@^7.20.11", "@babel/helper-module-transforms@^7.21.2":
version "7.21.2"
resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz#160caafa4978ac8c00ac66636cb0fa37b024e2d2"
@ -184,6 +285,17 @@
"@babel/traverse" "^7.21.2"
"@babel/types" "^7.21.2"
"@babel/helper-module-transforms@^7.23.3":
version "7.23.3"
resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz#d7d12c3c5d30af5b3c0fcab2a6d5217773e2d0f1"
integrity sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==
dependencies:
"@babel/helper-environment-visitor" "^7.22.20"
"@babel/helper-module-imports" "^7.22.15"
"@babel/helper-simple-access" "^7.22.5"
"@babel/helper-split-export-declaration" "^7.22.6"
"@babel/helper-validator-identifier" "^7.22.20"
"@babel/helper-optimise-call-expression@^7.18.6":
version "7.18.6"
resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz#9369aa943ee7da47edab2cb4e838acf09d290ffe"
@ -225,6 +337,13 @@
dependencies:
"@babel/types" "^7.20.2"
"@babel/helper-simple-access@^7.22.5":
version "7.22.5"
resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz#4938357dc7d782b80ed6dbb03a0fba3d22b1d5de"
integrity sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==
dependencies:
"@babel/types" "^7.22.5"
"@babel/helper-skip-transparent-expression-wrappers@^7.20.0":
version "7.20.0"
resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz#fbe4c52f60518cab8140d77101f0e63a8a230684"
@ -239,21 +358,43 @@
dependencies:
"@babel/types" "^7.18.6"
"@babel/helper-split-export-declaration@^7.22.6":
version "7.22.6"
resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz#322c61b7310c0997fe4c323955667f18fcefb91c"
integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==
dependencies:
"@babel/types" "^7.22.5"
"@babel/helper-string-parser@^7.19.4":
version "7.19.4"
resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz#38d3acb654b4701a9b77fb0615a96f775c3a9e63"
integrity sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==
"@babel/helper-string-parser@^7.22.5":
version "7.22.5"
resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f"
integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==
"@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1":
version "7.19.1"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2"
integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==
"@babel/helper-validator-identifier@^7.22.20":
version "7.22.20"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0"
integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==
"@babel/helper-validator-option@^7.18.6":
version "7.21.0"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz#8224c7e13ace4bafdc4004da2cf064ef42673180"
integrity sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==
"@babel/helper-validator-option@^7.22.15":
version "7.22.15"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz#694c30dfa1d09a6534cdfcafbe56789d36aba040"
integrity sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==
"@babel/helper-wrap-function@^7.18.9":
version "7.20.5"
resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.20.5.tgz#75e2d84d499a0ab3b31c33bcfe59d6b8a45f62e3"
@ -273,6 +414,15 @@
"@babel/traverse" "^7.21.0"
"@babel/types" "^7.21.0"
"@babel/helpers@^7.23.2":
version "7.23.2"
resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.23.2.tgz#2832549a6e37d484286e15ba36a5330483cac767"
integrity sha512-lzchcp8SjTSVe/fPmLwtWVBFC7+Tbn8LGHDVfDp9JGxpAY5opSaEFgt8UQvrnECWOTdji2mOWMz1rOhkHscmGQ==
dependencies:
"@babel/template" "^7.22.15"
"@babel/traverse" "^7.23.2"
"@babel/types" "^7.23.0"
"@babel/highlight@^7.10.4", "@babel/highlight@^7.18.6":
version "7.18.6"
resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf"
@ -282,11 +432,30 @@
chalk "^2.0.0"
js-tokens "^4.0.0"
"@babel/parser@^7.18.4", "@babel/parser@^7.20.7", "@babel/parser@^7.21.3", "@babel/parser@^7.7.0":
"@babel/highlight@^7.22.13":
version "7.22.20"
resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.20.tgz#4ca92b71d80554b01427815e06f2df965b9c1f54"
integrity sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==
dependencies:
"@babel/helper-validator-identifier" "^7.22.20"
chalk "^2.4.2"
js-tokens "^4.0.0"
"@babel/parser@^7.18.4", "@babel/parser@^7.20.7", "@babel/parser@^7.21.3":
version "7.21.3"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.3.tgz#1d285d67a19162ff9daa358d4cb41d50c06220b3"
integrity sha512-lobG0d7aOfQRXh8AyklEAgZGvA4FShxo6xQbUrrT/cNBPUdIDojlokwJsQyCC/eKia7ifqM0yP+2DRZ4WKw2RQ==
"@babel/parser@^7.22.15", "@babel/parser@^7.23.0":
version "7.23.0"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.0.tgz#da950e622420bf96ca0d0f2909cdddac3acd8719"
integrity sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==
"@babel/parser@^7.23.3":
version "7.23.3"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.3.tgz#0ce0be31a4ca4f1884b5786057cadcb6c3be58f9"
integrity sha512-uVsWNvlVsIninV2prNz/3lHCb+5CJ+e+IUBfbjToAHODtfGYLfCFuY4AU7TskI+dAKk+njsPiBjq1gKTvZOBaw==
"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.18.6":
version "7.18.6"
resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz#da5b8f9a580acdfbe53494dba45ea389fb09a4d2"
@ -938,23 +1107,48 @@
"@babel/parser" "^7.20.7"
"@babel/types" "^7.20.7"
"@babel/traverse@^7.0.0", "@babel/traverse@^7.20.5", "@babel/traverse@^7.20.7", "@babel/traverse@^7.21.0", "@babel/traverse@^7.21.2", "@babel/traverse@^7.21.3", "@babel/traverse@^7.7.0":
version "7.21.3"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.21.3.tgz#4747c5e7903d224be71f90788b06798331896f67"
integrity sha512-XLyopNeaTancVitYZe2MlUEvgKb6YVVPXzofHgqHijCImG33b/uTurMS488ht/Hbsb2XK3U2BnSTxKVNGV3nGQ==
"@babel/template@^7.22.15":
version "7.22.15"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38"
integrity sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==
dependencies:
"@babel/code-frame" "^7.18.6"
"@babel/generator" "^7.21.3"
"@babel/helper-environment-visitor" "^7.18.9"
"@babel/helper-function-name" "^7.21.0"
"@babel/helper-hoist-variables" "^7.18.6"
"@babel/helper-split-export-declaration" "^7.18.6"
"@babel/parser" "^7.21.3"
"@babel/types" "^7.21.3"
"@babel/code-frame" "^7.22.13"
"@babel/parser" "^7.22.15"
"@babel/types" "^7.22.15"
"@babel/traverse@^7.0.0", "@babel/traverse@^7.20.5", "@babel/traverse@^7.20.7", "@babel/traverse@^7.21.0", "@babel/traverse@^7.21.2", "@babel/traverse@^7.21.3":
version "7.23.2"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.2.tgz#329c7a06735e144a506bdb2cad0268b7f46f4ad8"
integrity sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==
dependencies:
"@babel/code-frame" "^7.22.13"
"@babel/generator" "^7.23.0"
"@babel/helper-environment-visitor" "^7.22.20"
"@babel/helper-function-name" "^7.23.0"
"@babel/helper-hoist-variables" "^7.22.5"
"@babel/helper-split-export-declaration" "^7.22.6"
"@babel/parser" "^7.23.0"
"@babel/types" "^7.23.0"
debug "^4.1.0"
globals "^11.1.0"
"@babel/types@^7.0.0", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.20.5", "@babel/types@^7.20.7", "@babel/types@^7.21.0", "@babel/types@^7.21.2", "@babel/types@^7.21.3", "@babel/types@^7.4.4", "@babel/types@^7.7.0":
"@babel/traverse@^7.23.2", "@babel/traverse@^7.23.3":
version "7.23.3"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.3.tgz#26ee5f252e725aa7aca3474aa5b324eaf7908b5b"
integrity sha512-+K0yF1/9yR0oHdE0StHuEj3uTPzwwbrLGfNOndVJVV2TqA5+j3oljJUb4nmB954FLGjNem976+B+eDuLIjesiQ==
dependencies:
"@babel/code-frame" "^7.22.13"
"@babel/generator" "^7.23.3"
"@babel/helper-environment-visitor" "^7.22.20"
"@babel/helper-function-name" "^7.23.0"
"@babel/helper-hoist-variables" "^7.22.5"
"@babel/helper-split-export-declaration" "^7.22.6"
"@babel/parser" "^7.23.3"
"@babel/types" "^7.23.3"
debug "^4.1.0"
globals "^11.1.0"
"@babel/types@^7.0.0", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.20.5", "@babel/types@^7.20.7", "@babel/types@^7.21.0", "@babel/types@^7.21.2", "@babel/types@^7.21.3", "@babel/types@^7.4.4":
version "7.21.3"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.21.3.tgz#4865a5357ce40f64e3400b0f3b737dc6d4f64d05"
integrity sha512-sBGdETxC+/M4o/zKC0sl6sjWv62WFR/uzxrJ6uYyMLZOUlPnwzw0tKgVHOXxaAd5l2g8pEDM5RZ495GPQI77kg==
@ -963,6 +1157,24 @@
"@babel/helper-validator-identifier" "^7.19.1"
to-fast-properties "^2.0.0"
"@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.0":
version "7.23.0"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.0.tgz#8c1f020c9df0e737e4e247c0619f58c68458aaeb"
integrity sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==
dependencies:
"@babel/helper-string-parser" "^7.22.5"
"@babel/helper-validator-identifier" "^7.22.20"
to-fast-properties "^2.0.0"
"@babel/types@^7.23.3":
version "7.23.3"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.3.tgz#d5ea892c07f2ec371ac704420f4dcdb07b5f9598"
integrity sha512-OZnvoH2l8PK5eUvEcUyCt/sXgr/h+UWpVuBbOljwcrAgUl6lpchoQ++PHGyQy1AtYnVA6CEq3y5xeEI10brpXw==
dependencies:
"@babel/helper-string-parser" "^7.22.5"
"@babel/helper-validator-identifier" "^7.22.20"
to-fast-properties "^2.0.0"
"@colors/colors@1.5.0":
version "1.5.0"
resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9"
@ -1099,6 +1311,13 @@
resolved "https://registry.yarnpkg.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz#b2ac626d6cb9c8718ab459166d4bb405b8ffa78b"
integrity sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==
"@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1":
version "5.1.1-v1"
resolved "https://registry.yarnpkg.com/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz#dbf733a965ca47b1973177dc0bb6c889edcfb129"
integrity sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==
dependencies:
eslint-scope "5.1.1"
"@node-ipc/js-queue@2.0.3":
version "2.0.3"
resolved "https://registry.yarnpkg.com/@node-ipc/js-queue/-/js-queue-2.0.3.tgz#ac7fe33d766fa53e233ef8fedaf3443a01c5a4cd"
@ -2071,25 +2290,14 @@ aws4@^1.8.0:
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.12.0.tgz#ce1c9d143389679e253b314241ea9aa5cec980d3"
integrity sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==
axios@^0.27.2:
version "0.27.2"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.27.2.tgz#207658cc8621606e586c85db4b41a750e756d972"
integrity sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==
axios@^1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.0.tgz#f1e5292f26b2fd5c2e66876adc5b06cdbd7d2102"
integrity sha512-EZ1DYihju9pwVB+jg67ogm+Tmqc6JmhamRN6I4Zt8DfZu5lbcQGw3ozH9lFejSJgs/ibaef3A9PMXPLeefFGJg==
dependencies:
follow-redirects "^1.14.9"
follow-redirects "^1.15.0"
form-data "^4.0.0"
babel-eslint@^10.1.0:
version "10.1.0"
resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-10.1.0.tgz#6968e568a910b78fb3779cdd8b6ac2f479943232"
integrity sha512-ifWaTHQ0ce+448CYop8AdrQiBsGrnC+bMgfyKFdi6EsPLTAWG+QfyDeM6OH+FmWnKvEq5NnBMLvlBUPKQZoDSg==
dependencies:
"@babel/code-frame" "^7.0.0"
"@babel/parser" "^7.7.0"
"@babel/traverse" "^7.7.0"
"@babel/types" "^7.7.0"
eslint-visitor-keys "^1.0.0"
resolve "^1.12.0"
proxy-from-env "^1.1.0"
babel-loader@^8.2.2:
version "8.3.0"
@ -2241,6 +2449,16 @@ browserslist@^4.0.0, browserslist@^4.14.5, browserslist@^4.16.3, browserslist@^4
node-releases "^2.0.8"
update-browserslist-db "^1.0.10"
browserslist@^4.21.9:
version "4.22.1"
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.22.1.tgz#ba91958d1a59b87dab6fed8dfbcb3da5e2e9c619"
integrity sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==
dependencies:
caniuse-lite "^1.0.30001541"
electron-to-chromium "^1.4.535"
node-releases "^2.0.13"
update-browserslist-db "^1.0.13"
buefy@^0.9.10:
version "0.9.23"
resolved "https://registry.yarnpkg.com/buefy/-/buefy-0.9.23.tgz#47819ed44781adcfb805e370f84988f37291d5fe"
@ -2339,6 +2557,11 @@ caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001449, caniuse-lite@^1.0.30001464:
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001468.tgz#0101837c6a4e38e6331104c33dcfb3bdf367a4b7"
integrity sha512-zgAo8D5kbOyUcRAgSmgyuvBkjrGk5CGYG5TYgFdpQv+ywcyEpo1LOWoG8YmoflGnh+V+UsNuKYedsoYs0hzV5A==
caniuse-lite@^1.0.30001541:
version "1.0.30001561"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001561.tgz#752f21f56f96f1b1a52e97aae98c57c562d5d9da"
integrity sha512-NTt0DNoKe958Q0BE0j0c1V9jbUzhBxHIEJy7asmGrpE0yG63KTV7PLHPnK2E1O9RsQrQ081I3NLuXGS6zht3cw==
case-sensitive-paths-webpack-plugin@^2.3.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz#db64066c6422eed2e08cc14b986ca43796dbc6d4"
@ -2349,7 +2572,7 @@ caseless@~0.12.0:
resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
integrity sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==
chalk@^2.0.0, chalk@^2.1.0:
chalk@^2.0.0, chalk@^2.1.0, chalk@^2.4.2:
version "2.4.2"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
@ -2639,6 +2862,11 @@ convert-source-map@^1.7.0:
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f"
integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==
convert-source-map@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a"
integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==
cookie-signature@1.0.6:
version "1.0.6"
resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c"
@ -3366,6 +3594,11 @@ electron-to-chromium@^1.4.284:
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.333.tgz#ebb21f860f8a29923717b06ec0cb54e77ed34c04"
integrity sha512-YyE8+GKyGtPEP1/kpvqsdhD6rA/TP1DUFDN4uiU/YI52NzDxmwHkEb3qjId8hLBa5siJvG0sfC3O66501jMruQ==
electron-to-chromium@^1.4.535:
version "1.4.581"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.581.tgz#23b684c67bf56d4284e95598c05a5d266653b6d8"
integrity sha512-6uhqWBIapTJUxgPTCHH9sqdbxIMPt7oXl0VcAL1kOtlU6aECdcMncCrX5Z7sHQ/invtrC9jUQUef7+HhO8vVFw==
emoji-regex@^8.0.0:
version "8.0.0"
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
@ -3609,12 +3842,12 @@ eslint-utils@^2.1.0:
dependencies:
eslint-visitor-keys "^1.1.0"
eslint-visitor-keys@^1.0.0, eslint-visitor-keys@^1.1.0, eslint-visitor-keys@^1.3.0:
eslint-visitor-keys@^1.1.0, eslint-visitor-keys@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e"
integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==
eslint-visitor-keys@^2.0.0:
eslint-visitor-keys@^2.0.0, eslint-visitor-keys@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303"
integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==
@ -4002,10 +4235,10 @@ flatted@^3.1.0:
resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787"
integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==
follow-redirects@^1.0.0, follow-redirects@^1.14.9:
version "1.15.2"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13"
integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==
follow-redirects@^1.0.0, follow-redirects@^1.15.0:
version "1.15.3"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.3.tgz#fe2f3ef2690afce7e82ed0b44db08165b207123a"
integrity sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==
for-each@^0.3.3:
version "0.3.3"
@ -4915,7 +5148,7 @@ json5@^1.0.1, json5@^1.0.2:
dependencies:
minimist "^1.2.0"
json5@^2.1.2, json5@^2.2.2:
json5@^2.1.2, json5@^2.2.2, json5@^2.2.3:
version "2.2.3"
resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283"
integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==
@ -5353,6 +5586,11 @@ node-forge@^1:
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3"
integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==
node-releases@^2.0.13:
version "2.0.13"
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.13.tgz#d5ed1627c23e3461e819b02e57b75e4899b1c81d"
integrity sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==
node-releases@^2.0.8:
version "2.0.10"
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.10.tgz#c311ebae3b6a148c89b1813fd7c4d3c024ef537f"
@ -6035,6 +6273,11 @@ proxy-from-env@1.0.0:
resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.0.0.tgz#33c50398f70ea7eb96d21f7b817630a55791c7ee"
integrity sha512-F2JHgJQ1iqwnHDcQjVBsq3n/uoaFL+iPW/eAeL7kVxy/2RrWaN4WroKjjvbsoRtv0ftelNyC01bjRhn/bhcf4A==
proxy-from-env@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2"
integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==
pseudomap@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3"
@ -6254,7 +6497,7 @@ resolve-from@^4.0.0:
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==
resolve@^1.10.0, resolve@^1.12.0, resolve@^1.14.2, resolve@^1.20.0, resolve@^1.22.1:
resolve@^1.10.0, resolve@^1.14.2, resolve@^1.20.0, resolve@^1.22.1:
version "1.22.1"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177"
integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==
@ -6409,7 +6652,7 @@ selfsigned@^2.1.1:
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8"
integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==
semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0:
semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0, semver@^6.3.1:
version "6.3.1"
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
@ -6926,10 +7169,10 @@ thunky@^1.0.2:
resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d"
integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==
tinymce@^5.10.7:
version "5.10.7"
resolved "https://registry.yarnpkg.com/tinymce/-/tinymce-5.10.7.tgz#d89d446f1962f2a1df6b2b70018ce475ec7ffb80"
integrity sha512-9UUjaO0R7FxcFo0oxnd1lMs7H+D0Eh+dDVo5hKbVe1a+VB0nit97vOqlinj+YwgoBDt6/DSCUoWqAYlLI8BLYA==
tinymce@^5.10.9:
version "5.10.9"
resolved "https://registry.yarnpkg.com/tinymce/-/tinymce-5.10.9.tgz#1dfacb3231c71a688d90ff44a0b3f2e91b3b9edf"
integrity sha512-5bkrors87X9LhYX2xq8GgPHrIgJYHl87YNs+kBcjQ5I3CiUgzo/vFcGvT3MZQ9QHsEeYMhYO6a5CLGGffR8hMg==
tmp@~0.2.1:
version "0.2.1"
@ -7107,6 +7350,14 @@ update-browserslist-db@^1.0.10:
escalade "^3.1.1"
picocolors "^1.0.0"
update-browserslist-db@^1.0.13:
version "1.0.13"
resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz#3c5e4f5c083661bd38ef64b6328c26ed6c8248c4"
integrity sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==
dependencies:
escalade "^3.1.1"
picocolors "^1.0.0"
uri-js@^4.2.2:
version "4.4.1"
resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e"

45
go.mod
View file

@ -1,16 +1,13 @@
module github.com/knadh/listmonk
go 1.13
go 1.20
require (
github.com/Masterminds/sprig/v3 v3.2.3
github.com/disintegration/imaging v1.6.2
github.com/emersion/go-message v0.16.0
github.com/gofrs/uuid v4.0.0+incompatible
github.com/google/uuid v1.3.0 // indirect
github.com/gorilla/feeds v1.1.1
github.com/huandu/xstrings v1.4.0 // indirect
github.com/imdario/mergo v0.3.14 // indirect
github.com/jmoiron/sqlx v1.3.5
github.com/knadh/go-pop3 v0.3.0
github.com/knadh/goyesql/v2 v2.2.0
@ -26,23 +23,45 @@ require (
github.com/knadh/paginator v1.0.1
github.com/knadh/smtppool v1.0.2
github.com/knadh/stuffbin v1.1.0
github.com/kr/pretty v0.3.1 // indirect
github.com/labstack/echo/v4 v4.10.2
github.com/labstack/echo/v4 v4.11.3
github.com/lib/pq v1.10.7
github.com/mailru/easyjson v0.7.7
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
github.com/paulbellamy/ratecounter v0.2.0
github.com/rhnvrm/simples3 v0.8.3
github.com/shopspring/decimal v1.3.1 // indirect
github.com/spf13/cast v1.5.0 // indirect
github.com/spf13/pflag v1.0.5
github.com/yuin/goldmark v1.5.4
golang.org/x/crypto v0.7.0 // indirect
golang.org/x/image v0.6.0 // indirect
golang.org/x/mod v0.9.0
golang.org/x/sys v0.7.0 // indirect
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
gopkg.in/volatiletech/null.v6 v6.0.0-20170828023728-0bef4e07ae1b
)
require (
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver/v3 v3.2.0 // indirect
github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/huandu/xstrings v1.4.0 // indirect
github.com/imdario/mergo v0.3.14 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/labstack/gommon v0.4.1 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/pelletier/go-toml v1.9.5 // indirect
github.com/shopspring/decimal v1.3.1 // indirect
github.com/spf13/cast v1.5.0 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.2 // indirect
golang.org/x/crypto v0.15.0 // indirect
golang.org/x/image v0.10.0 // indirect
golang.org/x/net v0.18.0 // indirect
golang.org/x/sys v0.14.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/time v0.3.0 // indirect
)
replace github.com/imdario/mergo => github.com/imdario/mergo v0.3.8

52
go.sum
View file

@ -16,7 +16,6 @@ github.com/emersion/go-message v0.16.0/go.mod h1:pDJDgf/xeUIF+eicT6B/hPX/ZbEorKk
github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594 h1:IbFBtwoTQyw0fIM5xv1HF+Y+3ZijDR839WMulgxCcUY=
github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594/go.mod h1:aqO8z8wPrjkscevZJFVE1wXJrLpC5LtJG7fqLOsPb2U=
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
@ -28,7 +27,6 @@ github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keL
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
@ -72,18 +70,18 @@ github.com/knadh/smtppool v1.0.2 h1:qg2A2DYqrfeCxxAonVaL9xruhIP+e7xhq+TImyiLajQ=
github.com/knadh/smtppool v1.0.2/go.mod h1:3DJHouXAgPDBz0kC50HukOsdapYSwIEfJGwuip46oCA=
github.com/knadh/stuffbin v1.1.0 h1:f5S5BHzZALjuJEgTIOMC9NidEnBJM7Ze6Lu1GHR/lwU=
github.com/knadh/stuffbin v1.1.0/go.mod h1:yVCFaWaKPubSNibBsTAJ939q2ABHudJQxRWZWV5yh+4=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/labstack/echo/v4 v4.10.2 h1:n1jAhnq/elIFTHr1EYpiYtyKgx4RW9ccVgkqByZaN2M=
github.com/labstack/echo/v4 v4.10.2/go.mod h1:OEyqf2//K1DFdE57vw2DRgWY0M7s65IVQO2FzvI4J5k=
github.com/labstack/echo/v4 v4.11.3 h1:Upyu3olaqSHkCjs1EJJwQ3WId8b8b1hxbogyommKktM=
github.com/labstack/echo/v4 v4.11.3/go.mod h1:UcGuQ8V6ZNRmSweBIJkPvGfwCMIlFmiqrPqiEBfPYws=
github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8=
github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=
github.com/labstack/gommon v0.4.1 h1:gqEff0p/hTENGMABzezPoPSRtIh1Cvw0ueMOe0/dfOk=
github.com/labstack/gommon v0.4.1/go.mod h1:TyTrpPqxR5KMk8LKVtLmfMjeQ5FEkBYdxLYPw/WfrOM=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw=
@ -97,6 +95,8 @@ github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27k
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg=
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
@ -108,8 +108,6 @@ github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RR
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/paulbellamy/ratecounter v0.2.0 h1:2L/RhJq+HA8gBQImDXtLPrDXK5qAj6ozWVK/zFXVJGs=
github.com/paulbellamy/ratecounter v0.2.0/go.mod h1:Hfx1hDpSGoqxkVVpBi/IlYD7kChlfo5C6hzIHwPqfFE=
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
@ -119,7 +117,6 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rhnvrm/simples3 v0.8.3 h1:6dS0EE/hMIkaJd9gJOoXZOwtQQqI4NJyk0jvtl86n28=
github.com/rhnvrm/simples3 v0.8.3/go.mod h1:Y+3vYm2V7Y4VijFoJHHTrja6OgPrJ2cBti8dPGkC3sA=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
@ -151,12 +148,13 @@ github.com/yuin/goldmark v1.5.4/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5ta
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
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/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.6.0 h1:bR8b5okrPI3g/gyZakLZHeWxAR8Dn5CyxXv1hLH5g/4=
golang.org/x/image v0.6.0/go.mod h1:MXLdDR43H7cDJq5GEGXEVeeNhPgi+YYEQ2pC1byI1x0=
golang.org/x/image v0.10.0 h1:gXjUUtwtx5yOE0VKWq1CH4IJAClq4UGgUA3i+rpON9M=
golang.org/x/image v0.10.0/go.mod h1:jtrku+n79PfroUbvDdeUWMAI+heR786BofxrbiSF+J0=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs=
@ -167,9 +165,10 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
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/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=
@ -186,13 +185,14 @@ golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
golang.org/x/sys v0.13.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/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.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
@ -200,8 +200,11 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
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.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@ -210,13 +213,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/volatiletech/null.v6 v6.0.0-20170828023728-0bef4e07ae1b h1:P+3+n9hUbqSDkSdtusWHVPQRrpRpLiLFzlZ02xXskM0=
gopkg.in/volatiletech/null.v6 v6.0.0-20170828023728-0bef4e07ae1b/go.mod h1:0LRKfykySnChgQpG3Qpk+bkZFWazQ+MMfc5oldQCwnY=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=

View file

@ -362,6 +362,7 @@
"settings.bounces.delete": "Esborra",
"settings.bounces.enable": "Activa el processament de rebots",
"settings.bounces.enableMailbox": "Activa la bústia de rebots",
"settings.bounces.enablePostmark": "Activa Postmark",
"settings.bounces.enableSES": "Activa SES",
"settings.bounces.enableSendgrid": "Activa SendGrid",
"settings.bounces.enableWebhooks": "Activa els webhooks pels rebots",
@ -372,6 +373,9 @@
"settings.bounces.invalidScanInterval": "L'interval d'escaneig ha de ser com a mínim d'1 minut.",
"settings.bounces.name": "Rebots",
"settings.bounces.none": "Cap",
"settings.bounces.postmarkPassword": "Postmark Password",
"settings.bounces.postmarkUsername": "Postmark Username",
"settings.bounces.postmarkUsernameHelp": "Postmark allows you to enable basic authorization for webhooks. Make sure to enter the same credentials here and in your Postmark webhook settings.",
"settings.bounces.scanInterval": "Interval d'escaneig",
"settings.bounces.scanIntervalHelp": "Interval en què s'hauria d'escanejar la bústia de rebot (s per segon, m per minut).",
"settings.bounces.sendgridKey": "Clau SendGrid ",
@ -437,7 +441,7 @@
"settings.media.s3.region": "Regió",
"settings.media.s3.secret": "Secret d'accés AWS",
"settings.media.s3.uploadExpiry": "Caducitat de la càrrega",
"settings.media.s3.uploadExpiryHelp": "(Opcional) Especifica TTL (en segons) per a l'URL presignada generada. Només aplicable a contenidors privats (s, m, h, d per a segons, minuts, hores, dies).",
"settings.media.s3.uploadExpiryHelp": "(Opcional) Especifica TTL per a l'URL presignada generada. Només aplicable a contenidors privats (s, m, h, d per a segons, minuts, hores, dies).",
"settings.media.s3.url": "URL del backend S3",
"settings.media.s3.urlHelp": "Canvia només si fas servir un backend personalitzat compatible amb S3 com Minio.",
"settings.media.title": "Càrrega de mèdia",

View file

@ -362,6 +362,7 @@
"settings.bounces.delete": "Odstranit",
"settings.bounces.enable": "Povolit zpracování nedoručitelnosti",
"settings.bounces.enableMailbox": "Povolit poštovní schránku v případě nedoručitelnosti",
"settings.bounces.enablePostmark": "Povolit Postmark",
"settings.bounces.enableSES": "Povolit SES",
"settings.bounces.enableSendgrid": "Povolit SendGrid",
"settings.bounces.enableWebhooks": "Povolit webhooky v případě nedoručitelnosti",
@ -372,6 +373,9 @@
"settings.bounces.invalidScanInterval": "Interval skenování v případě nedoručitelnosti by měl být minimálně 1 minuta.",
"settings.bounces.name": "Případy nedoručitelnosti",
"settings.bounces.none": "Žádné",
"settings.bounces.postmarkPassword": "Postmark Password",
"settings.bounces.postmarkUsername": "Postmark Username",
"settings.bounces.postmarkUsernameHelp": "Postmark allows you to enable basic authorization for webhooks. Make sure to enter the same credentials here and in your Postmark webhook settings.",
"settings.bounces.scanInterval": "Interval skenování",
"settings.bounces.scanIntervalHelp": "Interval, ve kterém by se poštovní schránka v případě nedoručitelnosti měla skenovat na nedoručitelnost (s - sekundy, m - minuty).",
"settings.bounces.sendgridKey": "Klíč SendGrid",
@ -437,7 +441,7 @@
"settings.media.s3.region": "Oblast",
"settings.media.s3.secret": "Přístupový tajný údaj AWS",
"settings.media.s3.uploadExpiry": "Uplynulá platnost odeslání",
"settings.media.s3.uploadExpiryHelp": "(Volitelné) Uveďte TTL (v sekundách) pro generovanou předem přihlášenou adresu URL. Vhodné pouze pro soukromé sektory (s, m, h, d pro sekundy, minuty, hodiny, dny).",
"settings.media.s3.uploadExpiryHelp": "(Volitelné) Uveďte TTL pro generovanou předem přihlášenou adresu URL. Vhodné pouze pro soukromé sektory (s, m, h, d pro sekundy, minuty, hodiny, dny).",
"settings.media.s3.url": "Adresa URL pro S3 backend",
"settings.media.s3.urlHelp": "Lze změnit, pouze pokud se použije S3 kompatibilní backend, jako je Minio.",
"settings.media.title": "Odeslání médií",

View file

@ -362,6 +362,7 @@
"settings.bounces.delete": "Dileu",
"settings.bounces.enable": "Galluogi proses sboncio'n ôl",
"settings.bounces.enableMailbox": "Galluogi blwch post negeseuon sydd wedi sboncio'n ôl",
"settings.bounces.enablePostmark": "Galluogi Postmark",
"settings.bounces.enableSES": "Galluogi SES",
"settings.bounces.enableSendgrid": "Galluogi SendGrid",
"settings.bounces.enableWebhooks": "Galluogi bachau gwe sydd wedi sboncio'n ôl",
@ -372,6 +373,9 @@
"settings.bounces.invalidScanInterval": "Dylai'r cyfnod sganio ar gyfer negeseuon sydd wedi sboncio'n ôl bara o leiaf 1 munud",
"settings.bounces.name": "Wedi sboncio'n ôl",
"settings.bounces.none": "Dim",
"settings.bounces.postmarkPassword": "Postmark Password",
"settings.bounces.postmarkUsername": "Postmark Username",
"settings.bounces.postmarkUsernameHelp": "Postmark allows you to enable basic authorization for webhooks. Make sure to enter the same credentials here and in your Postmark webhook settings.",
"settings.bounces.scanInterval": "Cyfnod sganio",
"settings.bounces.scanIntervalHelp": "Y cyfnod ar gyfer sganio'r blwch post ar gyfer negeseuon sydd wedi sboncio'n ôl (e ar gyfer eiliad",
"settings.bounces.sendgridKey": "Allwedd SendGrid",
@ -437,7 +441,7 @@
"settings.media.s3.region": "Rhanbarth",
"settings.media.s3.secret": "Cyfrinach mynediad AWS",
"settings.media.s3.uploadExpiry": "Dyddiad dod i ben ar gyfer llwytho i fyny",
"settings.media.s3.uploadExpiryHelp": "(Dewisol) Nodwch y TTL (mewn eiliadau) ar gyfer yr URL a lofnodwyd ymlaen llaw. Dim ond yn berthnasol ar gyfer bwcedi preifat (e",
"settings.media.s3.uploadExpiryHelp": "(Dewisol) Nodwch y TTL ar gyfer yr URL a lofnodwyd ymlaen llaw. Dim ond yn berthnasol ar gyfer bwcedi preifat (e",
"settings.media.s3.url": "URL cefn ôl S3",
"settings.media.s3.urlHelp": "Dim ond ei newid os ydych yn defnyddio URL cefn ôl personol sy'n gydnaws â S3 fel Minio.",
"settings.media.title": "Cyfryngau sydd wedi'u llwytho i fyny",

View file

@ -356,26 +356,26 @@
"settings.appearance.publicName": "Öffentlich",
"settings.bounces.action": "Aktion",
"settings.bounces.blocklist": "Sperrliste",
"settings.bounces.complaint": "Complaint",
"settings.bounces.count": "Bounce Anzahl",
"settings.bounces.countHelp": "Anzahl von Bounces pro Abonnent",
"settings.bounces.delete": "Löschen",
"settings.bounces.enable": "Verarbeiten von Bounces aktivieren",
"settings.bounces.enableMailbox": "Bounce-Postfach aktivieren",
"settings.bounces.enablePostmark": "Postmark aktivieren",
"settings.bounces.enableSES": "SES aktivieren",
"settings.bounces.enableSendgrid": "SendGrid aktivieren",
"settings.bounces.enableWebhooks": "Bounce-Webhooks aktivieren",
"settings.bounces.enabled": "Aktiviert",
"settings.bounces.folder": "Ordner",
"settings.bounces.folderHelp": "Name des zu scannenden IMAP-Ordners. z.B.: Inbox.",
"settings.bounces.hard": "Hard",
"settings.bounces.invalidScanInterval": "Der Bounce Scan-Interval sollte mindestens 1 Minute betragen.",
"settings.bounces.name": "Bounces",
"settings.bounces.none": "Keine",
"settings.bounces.postmarkPassword": "Postmark Passwort",
"settings.bounces.postmarkUsername": "Postmark Benutzername",
"settings.bounces.postmarkUsernameHelp": "Postmark ermöglicht HTTP-Basic-Auth für Webhooks. Die Anmeldeinformationen müssen mit denen in den Postmark Webhook-Einstellungen übereinstimmen.",
"settings.bounces.scanInterval": "Scan-Interval",
"settings.bounces.scanIntervalHelp": "Interval mit dem das Bounce-Postfach gescannt werden soll (s for Sekunden, m für Minuten).",
"settings.bounces.sendgridKey": "SendGrid Schlüssel",
"settings.bounces.soft": "Soft",
"settings.bounces.type": "Typ",
"settings.bounces.username": "Benutzername",
"settings.confirmRestart": "Stelle sicher, dass laufende Kampagnen pausiert sind. Neustarten?",
@ -409,8 +409,8 @@
"settings.mailserver.authProtocol": "Autentifizierungsprotokoll",
"settings.mailserver.host": "Server",
"settings.mailserver.hostHelp": "SMTP Server Adresse.",
"settings.mailserver.idleTimeout": "Maximale Wartezeit",
"settings.mailserver.idleTimeoutHelp": "Wartezeit auf neue Aktivität bevor eine Verbindung geschlossen wird. (s für Sekunden, m für Minuten).",
"settings.mailserver.idleTimeout": "Maximale IDLE-Zeit",
"settings.mailserver.idleTimeoutHelp": "Wartezeit auf neue Aktivität bevor eine Verbindung geschlossen und aus dem Pool entfernt wird. (s für Sekunden, m für Minuten).",
"settings.mailserver.maxConns": "Max. Verbindungen",
"settings.mailserver.maxConnsHelp": "Maximale gleichzeitige Verbindungen zum SMTP Server",
"settings.mailserver.password": "Passwort",
@ -423,7 +423,7 @@
"settings.mailserver.tlsHelp": "Verwende STARTTLS.",
"settings.mailserver.username": "Benutzername",
"settings.mailserver.waitTimeout": "Maximale Wartezeit",
"settings.mailserver.waitTimeoutHelp": "Wartezeit auf neue Aktivität bevor eine Verbindung geschlossen wird. (s für Sekunden, m für Minuten).",
"settings.mailserver.waitTimeoutHelp": "Wartezeit auf neue Aktivität bevor eine Verbindung geschlossen und aus dem Pool entfernt wird. (s für Sekunden, m für Minuten).",
"settings.media.provider": "Anbieter",
"settings.media.s3.bucket": "Bucket",
"settings.media.s3.bucketPath": "Bucket Pfad",
@ -437,7 +437,7 @@
"settings.media.s3.region": "Region",
"settings.media.s3.secret": "AWS Access Secret",
"settings.media.s3.uploadExpiry": "Upload Ablaufdatum",
"settings.media.s3.uploadExpiryHelp": "(Optional) TTL (in Sekunden) für die generierte URL. Nur für private Buckets. (s, m, h, d für Sekunden, Minuten, Stunden, Tage).",
"settings.media.s3.uploadExpiryHelp": "(Optional) TTL für die generierte URL. Nur für private Buckets. (s, m, h, d für Sekunden, Minuten, Stunden, Tage).",
"settings.media.s3.url": "S3 Backend-URL",
"settings.media.s3.urlHelp": "Nur bei Verwendungen eines eigenen S3-kompatiblen Backends (wie Minio) ändern.",
"settings.media.title": "Medien Uploads",

580
i18n/el.json Normal file
View file

@ -0,0 +1,580 @@
{
"_.code": "el",
"_.name": "Ελληνικά (el)",
"admin.errorMarshallingConfig": "Σφάλμα κατά τη μετατροπή του config: {error}",
"analytics.count": "Πλήθος",
"analytics.fromDate": "Από",
"analytics.invalidDates": "Μή έγκυρη ημερομηνία `από` ή `έως`.",
"analytics.isUnique": "Οι μετρήσεις είναι μοναδικές ανά συνδρομητή.",
"analytics.links": "Σύνδεσμοι",
"analytics.nonUnique": "Οι μετρήσεις δεν είναι μοναδικές, καθώς η παρακολούθηση του κάθε μεμονωμένου συνδρομητή έχει απενεργοποιηθεί.",
"analytics.title": "Στατιστικά",
"analytics.toDate": "Έως",
"bounces.complaint": "Διαμαρτυρία",
"bounces.hard": "Hard",
"bounces.soft": "Soft",
"bounces.source": "Πηγή",
"bounces.unknownService": "Άγνωστη υπηρεσία.",
"bounces.view": "Προβολή των bounce",
"campaigns.addAltText": "Προσθέστε εναλλακτικό μήνυμα σε μορφή απλού κειμένου",
"campaigns.addAttachments": "Προσθέστε συνημμένα",
"campaigns.archive": "Αρχείο",
"campaigns.archiveEnable": "Δημοσίευση στο δημόσιο αρχείο",
"campaigns.archiveHelp": "Δημοσιεύστε το μήνυμα της (σε εξέλιξη, σε παύση, ολοκληρωμένης) εκστρατείας στο δημόσιο αρχείο.",
"campaigns.archiveMeta": "Μεταδεδομένα εκστρατείας",
"campaigns.archiveMetaHelp": "Εικονικά δεδομένα συνδρομητή που χρησιμοποιούνται στο δημόσιο μήνυμα, συμπεριλαμβανομένου του ονόματος, της διεύθυνσης email και οποιωνδήποτε προαιρετικών χαρακτηριστικών που χρησιμοποιούνται στο μήνυμα ή το πρότυπο της εκστρατείας.",
"campaigns.attachments": "Συνημμένα",
"campaigns.cantUpdate": "Δεν είναι δυνατή η ενημέρωση μιας εκστρατείας που βρίσκεται σε εξέλιξη ή έχει ολοκληρωθεί.",
"campaigns.clicks": "Κλικ",
"campaigns.confirmDelete": "Διαγραφή {name}",
"campaigns.confirmSchedule": "Αυτή η εκστρατεία θα ξεκινήσει αυτόματα στην προγραμματισμένη ημερομηνία και ώρα. Θέλετε να την προγραμματίσετε τώρα;",
"campaigns.confirmSwitchFormat": "Το περιεχόμενο μπορεί να χάσει τη μορφοποίησή του. Θέλετε να συνεχίσετε;",
"campaigns.content": "Περιεχόμενο",
"campaigns.contentHelp": "Περιεχόμενο εδώ",
"campaigns.continue": "Συνέχεια",
"campaigns.copyOf": "Αντίγραφο του {name}",
"campaigns.customHeadersHelp": "Πίνακας με προσαρμοσμένες κεφαλίδες που θα προστεθούν στα εξερχόμενα μηνύματα. Π.χ.: [{\"X-Custom\": \"value\"}, {\"X-Custom2\": \"value\"}]",
"campaigns.dateAndTime": "Ημερομηνία και ώρα",
"campaigns.ended": "Ολοκληρώθηκε",
"campaigns.errorSendTest": "Σφάλμα κατά την αποστολή του δοκιμαστικού μηνύματος: {error}",
"campaigns.fieldInvalidBody": "Σφάλμα κατά τη σύνταξη του περιεχομένου της εκστρατείας: {error}",
"campaigns.fieldInvalidFromEmail": "Μη έγκυρη διεύθυνση αποστολέα.",
"campaigns.fieldInvalidListIDs": "Μη έγκυρο(-α) ID λίστας.",
"campaigns.fieldInvalidMessenger": "Άγνωστος messenger {name}.",
"campaigns.fieldInvalidName": "Μη έγκυρο μήκος για το όνομα.",
"campaigns.fieldInvalidSendAt": "Η προγραμματισμένη ημερομηνία πρέπει να είναι στο μέλλον.",
"campaigns.fieldInvalidSubject": "Μη έγκυρο μήκος για το θέμα.",
"campaigns.formatHTML": "Μορφοποίηση HTML",
"campaigns.fromAddress": "Διεύθυνση αποστολέα",
"campaigns.fromAddressPlaceholder": "Όνομα που θα εμφανίζεται ως αποστολέας <noreply@yoursite.com>",
"campaigns.invalid": "Μη έγκυρη εκστρατεία",
"campaigns.invalidCustomHeaders": "Μη έγκυρες προσαρμοσμένες κεφαλίδες: {error}",
"campaigns.markdown": "Markdown",
"campaigns.needsSendAt": "Απαιτείται ημερομηνία για να προγραμματιστεί μία εκστρατεία.",
"campaigns.newCampaign": "Νέα εκστρατεία",
"campaigns.noKnownSubsToTest": "Δεν υπάρχουν συνδρομητές για δοκιμή.",
"campaigns.noOptinLists": "Δεν βρέθηκαν λίστες συγκατάθεσης για τη δημιουργία εκστρατείας.",
"campaigns.noSubs": "Δεν υπάρχουν συνδρομητές στις επιλεγμένες λίστες για τη δημιουργία της εκστρατείας.",
"campaigns.noSubsToTest": "Δεν υπάρχουν συνδρομητές για στόχευση.",
"campaigns.notFound": "Η εκστρατεία δεν βρέθηκε.",
"campaigns.onlyActiveCancel": "Μόνο ενεργές εκστρατείες μπορούν να ακυρωθούν.",
"campaigns.onlyActivePause": "Μόνο ενεργές εκστρατείες μπορούν να τεθούν σε παύση.",
"campaigns.onlyDraftAsScheduled": "Μόνο προσχέδια εκστρατειών μπορούν να προγραμματιστούν.",
"campaigns.onlyPausedDraft": "Μόνο εκστρατείες σε παύση και προσχέδια εκστρατειών μπορούν να εκκινηθούν.",
"campaigns.onlyScheduledAsDraft": "Μόνο προγραμματισμένες εκστρατείες μπορούν να αποθηκευτούν ως πρόχειρες.",
"campaigns.pause": "Παύση",
"campaigns.plainText": "Μορφή απλού κειμένου",
"campaigns.preview": "Προεπισκόπηση",
"campaigns.progress": "Πρόοδος",
"campaigns.queryPlaceholder": "Όνομα ή θέμα",
"campaigns.rateMinuteShort": "λεπτά",
"campaigns.rawHTML": "Ακατέργαστη HTML",
"campaigns.removeAltText": "Αφαίρεση εναλλακτικού μηνύματος σε μορφή απλού κειμένου",
"campaigns.richText": "Πλούσιο κείμενο",
"campaigns.schedule": "Προγραμματισμός εκστρατείας",
"campaigns.scheduled": "Προγραμματισμένη",
"campaigns.send": "Αποστολή",
"campaigns.sendLater": "Αποστολή αργότερα",
"campaigns.sendTest": "Αποστολή δοκιμαστικού μηνύματος",
"campaigns.sendTestHelp": "Πατήστε Enter μετά την πληκτρολόγηση μιας διεύθυνσης email για να προσθέσετε πολλαπλούς παραλήπτες. Οι διευθύνσεις email πρέπει να αντιστοιχούν σε υπάρχοντες συνδρομητές.",
"campaigns.sendToLists": "Λίστες για αποστολή",
"campaigns.sent": "Απεσταλμένα",
"campaigns.start": "Έναρξη εκστρατείας",
"campaigns.started": "Η εκστρατεία \"{name}\" άρχισε",
"campaigns.startedAt": "Έναρξη",
"campaigns.stats": "Στατιστικά",
"campaigns.status.cancelled": "Ακυρώθηκε",
"campaigns.status.draft": "Προσχέδιο",
"campaigns.status.finished": "Ολοκληρώθηκε",
"campaigns.status.paused": "Σε παύση",
"campaigns.status.running": "Εκτελείται",
"campaigns.status.scheduled": "Προγραμματίστηκε",
"campaigns.statusChanged": "Η εκστρατεία \"{name}\" έχει την κατάσταση {status}",
"campaigns.subject": "Θέμα",
"campaigns.testEmails": "Διευθύνσεις e-mail",
"campaigns.testSent": "Το δοκιμαστικό μήνυμα στάλθηκε",
"campaigns.timestamps": "Χρονοσήματα",
"campaigns.trackLink": "Σύνδεσμος παρακολούθησης",
"campaigns.views": "Προβολές",
"dashboard.campaignViews": "Προβολές εκστρατειών",
"dashboard.linkClicks": "Κλικ συνδέσμων",
"dashboard.messagesSent": "Απεσταλμένα μυνήματα",
"dashboard.orphanSubs": "\"Ορφανοί\" συνδρομητές",
"email.data.info": "Ένα αντίγραφο όλων των δεδομένων που έχουν καταγραφεί για εσάς είναι συνημμένο ως αρχείο σε μορφή JSON. Μπορεί να προβληθεί με έναν επεξεργαστή κειμένου.",
"email.data.title": "Τα δεδομένα σας",
"email.optin.confirmSub": "Επιβεβαίωση συνδρομής",
"email.optin.confirmSubHelp": "Επιβεβαιώστε την εγγραφή σας κάνοντας κλικ στο κουμπί παρακάτω.",
"email.optin.confirmSubInfo": "Έχετε προστεθεί στις παρακάτω λίστες:",
"email.optin.confirmSubTitle": "Επιβεβαιώστε την εγγραφή",
"email.optin.confirmSubWelcome": "Γειά σας",
"email.optin.privateList": "Προσωπική λίστα",
"email.status.campaignReason": "Λόγος",
"email.status.campaignSent": "Απεστάλη",
"email.status.campaignUpdateTitle": "Ενημέρωση εκστρατείας",
"email.status.importFile": "Αρχείο",
"email.status.importRecords": "Εγγραφές",
"email.status.importTitle": "Εισαγωγή ενημέρωσης",
"email.status.status": "Κατάσταση",
"email.unsub": "Διαγραφή",
"email.unsubHelp": "Δεν θέλετε να λαμβάνετε αυτά τα email;",
"email.viewInBrowser": "Προβολή στον browser",
"forms.formHTML": "Φόρμα HTML",
"forms.formHTMLHelp": "Χρησιμοποιήστε την παρακάτω HTML για να εμφανίσετε ένα φόρμα εγγραφής σε μια εξωτερική ιστοσελίδα. Η φόρμα θα πρέπει να περιλαμβάνει το πεδίο διεύθυνσης email και ένα ή περισσότερα πεδία `l` (το UUID λίστας). Το πεδίο για το όνομα είναι προαιρετικό.",
"forms.noPublicLists": "Δεν υπάρχουν δημόσιες λίστες για τη δημιουργία φόρμας.",
"forms.publicLists": "Δημόσιες λίστες",
"forms.publicSubPage": "Δημόσια σελίδα εγγραφής",
"forms.selectHelp": "Επιλέξτε λίστες που θέλετε να προστεθούν στη φόρμα.",
"forms.title": "Φόρμες",
"globals.buttons.add": "Προσθήκη",
"globals.buttons.addNew": "Προσθήκη νέου",
"globals.buttons.back": "Πίσω",
"globals.buttons.cancel": "Ακύρωση",
"globals.buttons.clear": "Καθαρισμός",
"globals.buttons.clearAll": "Καθαρισμός όλων",
"globals.buttons.clone": "Κλωνοποίηση",
"globals.buttons.close": "Κλείσιμο",
"globals.buttons.continue": "Συνέχεια",
"globals.buttons.delete": "Διαγραφή",
"globals.buttons.deleteAll": "Διαγραφή όλων",
"globals.buttons.edit": "Επεξεργασία",
"globals.buttons.enabled": "Ενεργοποιημένο",
"globals.buttons.insert": "Εισαγωγή",
"globals.buttons.learnMore": "Μάθετε περισσότερα",
"globals.buttons.more": "Περισσότερα",
"globals.buttons.new": "Νέο",
"globals.buttons.ok": "Εντάξει",
"globals.buttons.remove": "Αφαίρεση",
"globals.buttons.save": "Αποθήκευση",
"globals.buttons.saveChanges": "Αποθήκευση αλλαγών",
"globals.days.0": "Κυρ",
"globals.days.1": "Κυρ",
"globals.days.2": "Δευ",
"globals.days.3": "Τρι",
"globals.days.4": "Τετ",
"globals.days.5": "Πεμ",
"globals.days.6": "Παρ",
"globals.days.7": "Σαβ",
"globals.fields.createdAt": "Δημιουργήθηκε",
"globals.fields.description": "Περιγραφή",
"globals.fields.id": "ID",
"globals.fields.name": "Όνομα",
"globals.fields.status": "Κατάσταση",
"globals.fields.type": "Τύπος",
"globals.fields.updatedAt": "Ενημερώθηκε",
"globals.fields.uuid": "UUID",
"globals.messages.confirm": "Σίγουρα;",
"globals.messages.confirmDiscard": "Απόρριψη αλλαγών;",
"globals.messages.created": "Το \"{name}\" δημιουργήθηκε",
"globals.messages.deleted": "Το \"{name}\" διαγράφηκε",
"globals.messages.deletedCount": "Το {name} ({num}) διαγράφηκε",
"globals.messages.done": "Ολοκληρώθηκε",
"globals.messages.emptyState": "Δεν υπάρχει τίποτα εδώ",
"globals.messages.errorCreating": "Σφάλμα δημιουργίας του {name}: {error}",
"globals.messages.errorDeleting": "Σφάλμα διαγραφής του {name}: {error}",
"globals.messages.errorFetching": "Σφάλμα ανάκτησης του {name}: {error}",
"globals.messages.errorInvalidIDs": "Ένα ή περισσότερα ID δεν είναι έγκυρα: {error}",
"globals.messages.errorUUID": "Σφάλμα δημιουργίας UUID: {error}",
"globals.messages.errorUpdating": "Σφάλμα ενημέρωσης του {name}: {error}",
"globals.messages.internalError": "Εσωτερικό σφάλμα διακομιστή",
"globals.messages.invalidData": "Μη έγκυρα δεδομένα",
"globals.messages.invalidFields": "Μη έγκυρα πεδία: {name}",
"globals.messages.invalidID": "Μυ έγκυρο/-α ID",
"globals.messages.invalidUUID": "Μυ έγκυρο/-α UUID",
"globals.messages.missingFields": "Λείπουν πεδία: {name}",
"globals.messages.notFound": "Το {name} δεν βρέθηκε",
"globals.messages.passwordChange": "Εισάγετε νέο περιεχόμενο για αλλαγή",
"globals.messages.passwordChangeFull": "Εκκαθάριση και επανεισαγωγή του συνθηματικού στο '{name}'.",
"globals.messages.updated": "Το \"{name}\" ενημερώθηκε",
"globals.months.1": "Ιαν",
"globals.months.10": "Οκτ",
"globals.months.11": "Νοε",
"globals.months.12": "Δεκ",
"globals.months.2": "Φεβ",
"globals.months.3": "Μαρ",
"globals.months.4": "Απρ",
"globals.months.5": "Μάι",
"globals.months.6": "Ιουν",
"globals.months.7": "Ιουλ",
"globals.months.8": "Αυγ",
"globals.months.9": "Σεπ",
"globals.states.off": "Απενεργοποιημένο",
"globals.terms.all": "Όλα",
"globals.terms.analytics": "Στατιστικά",
"globals.terms.bounce": "Bounce | Bounce",
"globals.terms.bounces": "Bounce",
"globals.terms.campaign": "Εκστρατεία | Εκστρατείες",
"globals.terms.campaigns": "Εκστρατείες",
"globals.terms.dashboard": "Επισκόπηση",
"globals.terms.day": "Ημέρα | Ημέρες",
"globals.terms.hour": "'Ωρα | Ώρες",
"globals.terms.list": "Λίστα | Λίστες",
"globals.terms.lists": "Λίστες",
"globals.terms.media": "Πολυμέσο | Πολυμέσα",
"globals.terms.messenger": "Αγγελιαφόρος | Αγγελιαφόροι",
"globals.terms.messengers": "Αγγελιαφόροι",
"globals.terms.minute": "Λεπτό | Λεπτά",
"globals.terms.month": "Μήνας | Μήνες",
"globals.terms.none": "Κανένα",
"globals.terms.second": "Δευτερόλεπτο | Δευτερόλεπτα",
"globals.terms.settings": "Ρυθμίσεις",
"globals.terms.subscriber": "Συνδρομητής | Συνδρομητές",
"globals.terms.subscribers": "Συνδρομητές",
"globals.terms.subscriptions": "Συνδρομή | Συνδρομές",
"globals.terms.tag": "Ετικέτα | Ετικέτες",
"globals.terms.tags": "Ετικέτες",
"globals.terms.template": "Προσχέδιο | Προσχέδια",
"globals.terms.templates": "Προσχέδια",
"globals.terms.tx": "Συναλλακτική | Συναλλακτικές",
"globals.terms.year": "Έτος | Έτη",
"import.alreadyRunning": "Μια εισαγωγή εκτελείται ήδη. Περιμένετε να ολοκληρωθεί ή σταματήστε την πριν προσπαθήσετε ξανά.",
"import.blocklist": "Λίστα αποκλεισμού",
"import.csvDelim": "Διαχωριστικό πεδίων CSV",
"import.csvDelimHelp": "Το κόμμα είναι το προεπιλεγμένο διαχωριστικό.",
"import.csvExample": "Παράδειγμα CSV",
"import.csvFile": "Αρχείο CSV ή ZIP",
"import.csvFileHelp": "Κάντε κλικ ή σύρετε ένα αρχείο CSV ή ZIP εδώ",
"import.errorCopyingFile": "Σφάλμα αντιγραφής αρχείου: {error}",
"import.errorProcessingZIP": "Σφάλμα επεξεργασίας αρχείου ZIP: {error}",
"import.errorStarting": "Σφάλμα κατά την έναρξη της εισαγωγής: {error}",
"import.importDone": "Ολοκληρώθηκε",
"import.importStarted": "Η εισαγωγή ολοκληρώθηκε",
"import.instructions": "Οδηγίες",
"import.instructionsHelp": "Ανεβάστε ένα αρχείο CSV ή ένα αρχείο ZIP με ένα μόνο αρχείο CSV για μαζική εισαγωγή συνδρομητών. Το αρχείο CSV θα πρέπει να έχει τις ακόλουθες επικεφαλίδες με τα ακριβή ονόματα των στηλών. attributes (προαιρετικό) θα πρέπει να είναι ένα έγκυρο αλφαριθμητικό JSON με double-escaped quotes.",
"import.invalidDelim": "Ο διαχωριστής θα πρέπει να είναι ένας μόνο χαρακτήρας.",
"import.invalidFile": "Μη έγκυρο αρχείο: {error}",
"import.invalidMode": "Μη έγκυρος τρόπος λειτουργίας",
"import.invalidParams": "Μη έγκυρες παράμετροι: {error}",
"import.invalidSubStatus": "Μη έγκυρη κατάσταση εγγραφής",
"import.listSubHelp": "Λίστες προς εγγραφή.",
"import.mode": "Τρόπος λειτουργίας",
"import.overwrite": "Αντικατάσταση;",
"import.overwriteHelp": "Αντικατάσταση ονόματος, χαρακτηριστικών, κατάστασης εγγραφής των υφιστάμενων συνδρομητών;",
"import.recordsCount": "{num} / {total} εγγραφές",
"import.stopImport": "Διακοπή εισαγωγής",
"import.subscribe": "Εγγραφή",
"import.title": "Εισαγωγή συνδρομητών",
"import.upload": "Μεταφόρτωση",
"lists.confirmDelete": "Σίγουρα; Αυτό δεν διαγράφει τους συνδρομητές.",
"lists.confirmSub": "Επιβεβαίωση εγγραφής(-ών) στο {name}",
"lists.invalidName": "Μη έγκυρο όνομα",
"lists.newList": "Νέα λίστα",
"lists.optin": "Συγκατάθεση",
"lists.optinHelp": "Η διπλή συγκατάθεση στέλνει ένα e-mail στον συνδρομητή ζητώντας επιβεβαίωση. Στις λίστες διπλής συγκατάθεσης, οι εκστρατείες αποστέλλονται μόνο σε επιβεβαιωμένους συνδρομητές.",
"lists.optinTo": "Συγκατάθεση για το {name}",
"lists.optins.double": "Διπλή συγκατάθεση",
"lists.optins.single": "Μονή συγκατάθεση",
"lists.sendCampaign": "Αποστολή εκστρατείας",
"lists.sendOptinCampaign": "Αποστολή εκστρατείας συγκατάθεσης",
"lists.type": "Τύπος",
"lists.typeHelp": "Οι δημόσιες λίστες είναι ανοιχτές στον κόσμο για εγγραφή και τα ονόματά τους μπορεί να εμφανίζονται σε δημόσιες σελίδες, όπως η σελίδα διαχείρισης εγγραφών.",
"lists.types.private": "Ιδιωτική",
"lists.types.public": "Δημόσια",
"logs.title": "Αρχεία καταγραφής",
"maintenance.help": "Ορισμένες ενέργειες ενδέχεται να χρειαστούν λίγο χρόνο για να ολοκληρωθούν, ανάλογα με τον όγκο των δεδομένων.",
"maintenance.maintenance.unconfirmedOptins": "Ανεπιβεβαίωτες συνδρομές συγκατάθεσης",
"maintenance.olderThan": "Παλαιότερο από",
"maintenance.orphanHelp": "\"Ορφανά\" = συνδρομητές χωρίς λίστα",
"maintenance.title": "Συντήρηση",
"maintenance.unconfirmedSubs": "Ανεπιβεβαίωτες συνδρομές παλαιότερες από {name} ημέρες.",
"media.errorReadingFile": "Σφάλμα ανάγνωσης αρχείου: {error}",
"media.errorResizing": "Σφάλμα αλλαγής μεγέθους εικόνας: {error}",
"media.errorSavingThumbnail": "Σφάλμα αποθήκευσης μικρογραφίας: {error}",
"media.errorUploading": "Σφάλμα μεταφόρτωσης αρχείου: {error}",
"media.invalidFile": "Μη έγκυρο αρχείο: {error}",
"media.title": "Πολυμέσα",
"media.unsupportedFileType": "Μη υποστηριζόμενος τύπος αρχείου ({type})",
"media.upload": "Μεταφόρτωση",
"media.uploadHelp": "Κάντε κλικ ή σύρετε μία ή περισσότερες εικόνες εδώ",
"media.uploadImage": "Μεταφόρτωση εικόνας",
"menu.allCampaigns": "Όλες οι εκστρατείες",
"menu.allLists": "Όλες οι λίστες",
"menu.allSubscribers": "Όλοι οι συνδρομητές",
"menu.dashboard": "Επισκόπηση",
"menu.forms": "Φόρμες",
"menu.import": "Εισαγωγή",
"menu.logs": "Αρχεία καταγραφής",
"menu.maintenance": "Συντήρηση",
"menu.media": "Πολυμέσα",
"menu.newCampaign": "Δημιουργία νέας",
"menu.settings": "Ρυθμίσεις",
"public.archiveEmpty": "Δεν υπάρχουν ακόμα αρχειοθετημένα μηνύματα.",
"public.archiveTitle": "Αρχείο λίστας αλληλογραφίας",
"public.blocklisted": "Μόνιμη διαγραφή.",
"public.campaignNotFound": "Το μήνυμα ηλεκτρονικού ταχυδρομείου δεν βρέθηκε.",
"public.confirmOptinSubTitle": "Επιβεβαίωση εγγραφής",
"public.confirmSub": "Επιβεβαίωση εγγραφής",
"public.confirmSubInfo": "Έχετε προστεθεί στις ακόλουθες λίστες:",
"public.confirmSubTitle": "Επιβεβαίωση",
"public.dataRemoved": "Οι εγγραφές σας και όλα τα σχετικά δεδομένα έχουν αφαιρεθεί.",
"public.dataRemovedTitle": "Τα δεδομένα έχουν αφαιρεθεί",
"public.dataSent": "Τα δεδομένα σας έχουν αποσταλεί με ηλεκτρονικό ταχυδρομείο ως συνημμένο αρχείο.",
"public.dataSentTitle": "Τα δεδομένα έχουν αποσταλεί με ηλεκτρονικό ταχυδρομείο",
"public.errorFetchingCampaign": "Σφάλμα ανάκτησης μηνύματος ηλεκτρονικού ταχυδρομείου.",
"public.errorFetchingEmail": "Το μήνυμα ηλεκτρονικού ταχυδρομείου δεν βρέθηκε",
"public.errorFetchingLists": "Σφάλμα ανάκτησης λιστών. Επαναλάβετε την προσπάθεια.",
"public.errorProcessingRequest": "Σφάλμα επεξεργασίας αίτησης. Επαναλάβετε την προσπάθεια.",
"public.errorTitle": "Σφάλμα",
"public.invalidCaptcha": "Μη έγκυρο CAPTCHA.",
"public.invalidFeature": "Αυτή η λειτουργία δεν είναι διαθέσιμη.",
"public.invalidLink": "Μη έγκυρος σύνδεσμος",
"public.managePrefs": "Διαχείριση προτιμήσεων",
"public.managePrefsUnsub": "Αποεπιλέξτε τις λίστες για να διαγραφείτε από αυτές.",
"public.noListsAvailable": "Δεν υπάρχουν διαθέσιμες λίστες για εγγραφή.",
"public.noListsSelected": "Δεν έχουν επιλεγεί έγκυρες λίστες για εγγραφή.",
"public.noSubInfo": "Δεν υπάρχουν συνδρομές προς επιβεβαίωση.",
"public.noSubTitle": "Δεν υπάρχουν εγγραφές",
"public.notFoundTitle": "Δεν βρέθηκε",
"public.poweredBy": "Βασίζεται στο",
"public.prefsSaved": "Οι προτιμήσεις σας έχουν αποθηκευτεί.",
"public.privacyConfirmWipe": "Είστε σίγουροι ότι θέλετε να διαγράψετε μόνιμα όλα τα δεδομένα των εγγραφών σας;",
"public.privacyExport": "Εξαγωγή των δεδομένων σας",
"public.privacyExportHelp": "Ένα αντίγραφο των δεδομένων σας θα σας αποσταλεί με ηλεκτρονικό ταχυδρομείο.",
"public.privacyTitle": "Ιδιωτικότητα και δεδομένα",
"public.privacyWipe": "Διαγράψτε τα δεδομένα σας",
"public.privacyWipeHelp": "Διαγράψτε μόνιμα όλες τις εγγραφές σας και τα σχετικά δεδομένα.",
"public.sub": "Εγγραφή",
"public.subConfirmed": "Έγινε εγγραφή.",
"public.subConfirmedTitle": "Επιβεβαιώθηκε",
"public.subName": "Όνομα (προαιρετικό)",
"public.subNotFound": "Η εγγραφή δεν βρέθηκε.",
"public.subOptinPending": "Σας έχει σταλεί e-mail για να επιβεβαιώσετε την εγγραφή σας.",
"public.subPrivateList": "Ιδιωτική λίστα",
"public.subTitle": "Εγγραφή",
"public.unsub": "Διαγραφή",
"public.unsubFull": "Διαγραφή από όλα τα μελλοντικά μηνύματα ηλεκτρονικού ταχυδρομείου.",
"public.unsubHelp": "Θέλετε να διαγραφείτε από αυτή τη λίστα αλληλογραφίας;",
"public.unsubTitle": "Διαγραφή",
"public.unsubbedInfo": "Έχετε διαγραφεί επιτυχώς.",
"public.unsubbedTitle": "Μη εγγεγραμμένος",
"public.unsubscribeTitle": "Διαγραφή από τη λίστα αλληλογραφίας",
"settings.appearance.adminHelp": "Προσαρμοσμένη CSS για την εφαρμογή στο περιβάλλον διαχείρισης.",
"settings.appearance.adminName": "Διαχείριση",
"settings.appearance.customCSS": "Προσαρμοσμένο CSS",
"settings.appearance.customJS": "Προσαρμοσμένη JavaScript",
"settings.appearance.name": "Εμφάνιση",
"settings.appearance.publicHelp": "Προσαρμοσμένο CSS και JavaScript για την εφαρμογή στις δημόσιες σελίδες.",
"settings.appearance.publicName": "Δημόσια",
"settings.bounces.action": "Δράση",
"settings.bounces.blocklist": "Λίστα αποκλεισμού",
"settings.bounces.count": "Πλήθος bounce",
"settings.bounces.countHelp": "Αριθμός bounce ανά συνδρομητή",
"settings.bounces.enable": "Ενεργοποίηση επεξεργασίας bounce",
"settings.bounces.enableMailbox": "Ενεργοποίηση γραμματοκιβωτίου για τα bounce",
"settings.bounces.enablePostmark": "Ενεργοποίηση Postmark",
"settings.bounces.enableSES": "Ενεργοποίηση SES",
"settings.bounces.enableSendgrid": "Ενεργοποίηση SendGrid",
"settings.bounces.enableWebhooks": "Ενεργοποίηση webhooks για τα bounce",
"settings.bounces.enabled": "Ενεργοποιημένο",
"settings.bounces.folder": "Φάκελος",
"settings.bounces.folderHelp": "Όνομα του φακέλου IMAP προς περιοδική σάρωση. Π.χ.: Εισερχόμενα.",
"settings.bounces.invalidScanInterval": "Το διάστημα σάρωσης για αναγνώριση των bounce πρέπει να είναι τουλάχιστον 1 λεπτό.",
"settings.bounces.name": "Bounce",
"settings.bounces.none": "Κανένα",
"settings.bounces.postmarkPassword": "Κωδικός πρόσβασης Postmark",
"settings.bounces.postmarkUsername": "Όνομα χρήστη Postmark",
"settings.bounces.postmarkUsernameHelp": "Η υπηρεσία Postmark σας επιτρέπει να ενεργοποιήσετε τη βασική εξουσιοδότηση για τα webhooks. Βεβαιωθείτε ότι έχετε εισάγει τα ίδια διαπιστευτήρια εδώ και στις ρυθμίσεις Postmark webhook.",
"settings.bounces.scanInterval": "Χρονικό διάστημα σάρωσης",
"settings.bounces.scanIntervalHelp": "Διάστημα στο οποίο το γραμματοκιβώτιο των bounce θα πρέπει να σαρώνεται για αναπηδήσεις (s για το δευτερόλεπτο, m για το λεπτό).",
"settings.bounces.sendgridKey": "Κλειδί πρόσβασης SendGrid",
"settings.bounces.type": "Τύπος",
"settings.bounces.username": "Όνομα χρήστη",
"settings.confirmRestart": "Βεβαιωθείτε ότι οι τρέχουσες καμπάνιες είναι σε παύση. Επανεκκίνηση;",
"settings.duplicateMessengerName": "Διπλό όνομα messenger: {name}",
"settings.errorEncoding": "Σφάλμα κωδικοποίησης ρυθμίσεων: {error}",
"settings.errorNoSMTP": "Θα πρέπει να είναι ενεργοποιημένο τουλάχιστον ένα μπλοκ SMTP",
"settings.general.adminNotifEmails": "Ηλεκτρονικά μηνύματα ειδοποίησης διαχειριστή",
"settings.general.adminNotifEmailsHelp": "Λίστα με διαχωρισμό με κόμμα των διευθύνσεων e-mail στις οποίες θα πρέπει να αποστέλλονται ειδοποιήσεις του διαχειριστή, όπως ενημερώσεις εισαγωγής, ολοκλήρωση εκστρατείας, αποτυχία κ.λπ.",
"settings.general.checkUpdates": "Έλεγχος για ενημερώσεις",
"settings.general.checkUpdatesHelp": "Να γίνεται περιοδικός έλεγχος για νέες κυκλοφορίες εφαρμογών και ειδοποίηση.",
"settings.general.enablePublicArchive": "Ενεργοποίηση δημόσιου αρχείου λίστας αλληλογραφίας",
"settings.general.enablePublicArchiveHelp": "Να δημοσιεύονται εκστρατείες για τις οποίες έχει ενεργοποιηθεί η αρχειοθέτηση στον δημόσιο ιστότοπο.",
"settings.general.enablePublicArchiveRSSContent": "Εμφάνιση πλήρους περιεχομένου στο RSS feed",
"settings.general.enablePublicArchiveRSSContentHelp": "Εμφάνιση πλήρους περιεχομένου e-mail στο RSS feed. Εάν απενεργοποιηθεί, εμφανίζονται μόνο τίτλοι και σύνδεσμοι.",
"settings.general.enablePublicSubPage": "Ενεργοποίηση δημόσιας σελίδας εγγραφής",
"settings.general.enablePublicSubPageHelp": "Εμφάνιση μιας δημόσιας σελίδας εγγραφής με όλες τις δημόσιες λίστες για να εγγραφούν οι χρήστες.",
"settings.general.faviconURL": "URL του favicon",
"settings.general.faviconURLHelp": "(Προαιρετικό) Πλήρης διεύθυνση URL για το στατικό favicon που θα εμφανίζεται σε προβολή προς τον χρήστη, όπως στη σελίδα διαγραφής.",
"settings.general.fromEmail": "Προεπιλεγμένη διεύθυνση αποστολέα",
"settings.general.fromEmailHelp": "Προεπιλεγμένη διεύθυνση αποστολέα που θα εμφανίζεται στα εξερχόμενα μηνύματα ηλεκτρονικού ταχυδρομείου της εκστρατείας. Αυτό μπορεί να αλλάξει ανά εκστρατεία.",
"settings.general.language": "Γλώσσα",
"settings.general.logoURL": "URL του λογότυπου",
"settings.general.logoURLHelp": "(Προαιρετικό) Πλήρης διεύθυνση URL για το στατικό λογότυπο που θα εμφανίζεται σε προβολή που αφορά τον χρήστη, όπως η σελίδα διαγραφής.",
"settings.general.name": "Γενικά",
"settings.general.rootURL": "Ριζικό URL",
"settings.general.rootURLHelp": "Δημόσια URL της εγκατάστασης (χωρίς τελικό \"/\").",
"settings.general.sendOptinConfirm": "Αποστολή επιβεβαίωσης συγκατάθεσης",
"settings.general.sendOptinConfirmHelp": "Στείλτε ένα e-mail επιβεβαίωσης συγκατάθεσης όταν οι συνδρομητές εγγράφονται μέσω της δημόσιας φόρμας ή όταν προστίθενται από τον διαχειριστή.",
"settings.general.siteName": "Όνομα του ιστότοπου",
"settings.invalidMessengerName": "Μη έγκυρο όνομα messenger.",
"settings.mailserver.authProtocol": "Πρωτόκολλο ταυτοποίησης",
"settings.mailserver.host": "Διακομιστής",
"settings.mailserver.hostHelp": "Διεύθυνση του διακομιστή SMTP.",
"settings.mailserver.idleTimeout": "Χρονικό όριο αδράνειας",
"settings.mailserver.idleTimeoutHelp": "Χρόνος αναμονής για νέα δραστηριότητα σε μια σύνδεση πριν από το κλείσιμό της και την αφαίρεσή της από τη δεξαμενή (s για το δευτερόλεπτο, m για το λεπτό).",
"settings.mailserver.maxConns": "Μέγιστες συνδέσεις",
"settings.mailserver.maxConnsHelp": "Μέγιστες ταυτόχρονες συνδέσεις στο διακομιστή.",
"settings.mailserver.password": "Κωδικός πρόσβασης",
"settings.mailserver.passwordHelp": "Enter για να το αλλάξετε",
"settings.mailserver.port": "Θύρα",
"settings.mailserver.portHelp": "Η θύρα του διακομιστή SMTP.",
"settings.mailserver.skipTLS": "Παράλειψη επαλήθευσης TLS",
"settings.mailserver.skipTLSHelp": "Παράλειψη ελέγχου ονόματος διακομιστη στο πιστοποιητικό TLS.",
"settings.mailserver.tls": "TLS",
"settings.mailserver.tlsHelp": "Κρυπτογράφηση TLS/SSL. Το STARTTLS χρησιμοποιείται συχνά.",
"settings.mailserver.username": "Όνομα χρήστη",
"settings.mailserver.waitTimeout": "Χρονικό όριο αναμονής",
"settings.mailserver.waitTimeoutHelp": "Χρόνος αναμονής για νέα δραστηριότητα σε μια σύνδεση πριν από το κλείσιμό της και την αφαίρεσή της από τη δεξαμενή (s για το δευτερόλεπτο, m για το λεπτό).",
"settings.media.provider": "Πάροχος",
"settings.media.s3.bucket": "Bucket",
"settings.media.s3.bucketPath": "Διαδρομή του bucket",
"settings.media.s3.bucketPathHelp": "Διαδρομή μέσα στο bucket για να ανεβάσετε αρχεία. Η προεπιλογή είναι /",
"settings.media.s3.bucketType": "Τύπος bucket",
"settings.media.s3.bucketTypePrivate": "Ιδιωτικό",
"settings.media.s3.bucketTypePublic": "Δημόσιο",
"settings.media.s3.key": "Κλειδί πρόσβασης AWS",
"settings.media.s3.publicURL": "Προσαρμοσμένη δημόσια URL (προαιρετική)",
"settings.media.s3.publicURLHelp": "Προσαρμοσμένο S3 domain που θα χρησιμοποιείται για συνδέσμους εικόνων αντί για την προεπιλεγμένη URL του S3 backend.",
"settings.media.s3.region": "Περιοχή (Region)",
"settings.media.s3.secret": "Μυστικό πρόσβασης AWS",
"settings.media.s3.uploadExpiry": "Χρονικό διάστημα λήξης μεταφόρτωσης",
"settings.media.s3.uploadExpiryHelp": "(Προαιρετικό) Καθορίστε τη λήξη για τη δημιουργούμενη προεπιλεγμένη URL. Ισχύει μόνο για ιδιωτικούς κάδους (s, m, h, d για δευτερόλεπτα, λεπτά, ώρες, ημέρες).",
"settings.media.s3.url": "URL του S3 backend",
"settings.media.s3.urlHelp": "Αλλάξτε το μόνο αν χρησιμοποιείτε ένα προσαρμοσμένο backend συμβατό με το S3, όπως το Minio.",
"settings.media.title": "Μεταφορτώσεις πολυμέσων",
"settings.media.upload.extensions": "Επιτρεπόμενες επεκτάσεις αρχείων",
"settings.media.upload.extensionsHelp": "Προσθέστε * για να επιτρέψετε όλες τις επεκτάσεις",
"settings.media.upload.path": "Διαδρομή μεταφόρτωσης",
"settings.media.upload.pathHelp": "Διαδρομή προς τον φάκελο όπου θα μεταφορτωθούν τα πολυμέσα.",
"settings.media.upload.uri": "URI μεταφόρτωσης",
"settings.media.upload.uriHelp": "URI μεταφόρτωσης που είναι ορατό στον έξω κόσμο. Τα πολυμέσα που μεταφορτώνονται στο upload_path θα είναι δημόσια προσβάσιμα στο {root_url}, για παράδειγμα στο https://listmonk.yoursite.com/uploads.",
"settings.messengers.maxConns": "Μέγιστες συνδέσεις",
"settings.messengers.maxConnsHelp": "Μέγιστες ταυτόχρονες συνδέσεις στο διακομιστή.",
"settings.messengers.messageSaved": "Οι ρυθμίσεις αποθηκεύτηκαν. Επαναφόρτωση εφαρμογής…",
"settings.messengers.name": "Αγγελιαφόροι",
"settings.messengers.nameHelp": "Π.χ.: my-sms. Αλφαριημητικό με παύλες.",
"settings.messengers.password": "Κωδικός πρόσβασης",
"settings.messengers.retries": "Επαναληπτικές προσπάθειες",
"settings.messengers.retriesHelp": "Αριθμός επαναληπτικών προσπαθειών όταν ένα μήνυμα αποτυγχάνει.",
"settings.messengers.skipTLSHelp": "Παράλειψη ελέγχου ονόματος διακομιστή στο πιστοποιητικό TLS.",
"settings.messengers.timeout": "Χρονικό όριο αδράνειας",
"settings.messengers.timeoutHelp": "Χρόνος αναμονής για νέα δραστηριότητα σε μια σύνδεση πριν από το κλείσιμό της και την αφαίρεσή της από τη δεξαμενή (s για το δευτερόλεπτο, m για το λεπτό).",
"settings.messengers.url": "URL",
"settings.messengers.urlHelp": "Ριζικό URL του διακομιστή Postback.",
"settings.messengers.username": "Όνομα χρήστη",
"settings.needsRestart": "Οι ρυθμίσεις άλλαξαν. Διακόψτε όλες τις τρέχουσες καμπάνιες και επανεκκινήστε την εφαρμογή",
"settings.performance.batchSize": "Μέγεθος παρτίδας",
"settings.performance.batchSizeHelp": "Ο αριθμός των συνδρομητών που θα αντληθούν από τη βάση δεδομένων σε κάθε επανάληψη. Κάθε επανάληψη αντλεί συνδρομητές από τη βάση δεδομένων, στέλνει μηνύματα σε αυτούς και στη συνέχεια μεταβαίνει στην επόμενη επανάληψη για να αντλήσει την επόμενη παρτίδα. Αυτός ο αριθμός θα πρέπει ιδανικά να είναι υψηλότερος από τη μέγιστη επιτεύξιμη απόδοση (παραλληλισμός * ρυθμός μηνυμάτων).",
"settings.performance.concurrency": "Παραλληλισμός",
"settings.performance.concurrencyHelp": "Μέγιστος αριθμός νημάτων που θα προσπαθήσει να στείλει μηνύματα ταυτόχρονα.",
"settings.performance.maxErrThreshold": "Μέγιστο όριο σφάλματος",
"settings.performance.maxErrThresholdHelp": "Ο αριθμός των σφαλμάτων (π.χ.: υπέρβαση χρονικού ορίου του διακομιστή SMTP κατά την αποστολή μηνυμάτων) που πρέπει να ανέχεται μια εκστρατεία που εκτελείται πριν διακοπεί για χειροκίνητη διερεύνηση ή παρέμβαση. Ορίστε την τιμή 0 για να μην γίνεται ποτέ παύση.",
"settings.performance.messageRate": "Ρυθμός μηνυμάτων",
"settings.performance.messageRateHelp": "Μέγιστος αριθμός μηνυμάτων που πρέπει να αποστέλλονται ανά δευτερόλεπτο ανά νήμα παράλληλης επεξεργασίας μέσα σε ένα δευτερόλεπτο. Εάν παραλληλισμός = 10 και ρυθμός μηνυμάτων = 10, τότε μπορούν να αποστέλλονται έως και 10x10=100 μηνύματα κάθε δευτερόλεπτο. Αυτό, μαζί με τον παραλληλισμό, θα πρέπει να ρυθμιστεί ώστε τα μηνύματα που αποστέλλονται επιτυχώς ανά δευτερόλεπτο να είναι κάτω από τα όρια ρυθμού των διακομιστών μηνυμάτων, αν αυτά υπάρχουν.",
"settings.performance.name": "Επιδόσεις",
"settings.performance.slidingWindow": "Ενεργοποίηση ορίου ολισθαίνοντος παραθύρου",
"settings.performance.slidingWindowDuration": "Διάρκεια",
"settings.performance.slidingWindowDurationHelp": "Διάρκεια της περιόδου του ολισθαίνοντος παραθύρου (m για το λεπτό, h για την ώρα).",
"settings.performance.slidingWindowHelp": "Περιορισμός του συνολικού αριθμού των μηνυμάτων που αποστέλλονται σε δεδομένη περίοδο. Με την επίτευξη αυτού του ορίου, η αποστολή μηνυμάτων εμποδίζεται μέχρι να εκκαθαριστεί το χρονικό παράθυρο.",
"settings.performance.slidingWindowRate": "Μέγιστα μηνύματα",
"settings.performance.slidingWindowRateHelp": "Μέγιστος αριθμός μηνυμάτων προς αποστολή εντός της διάρκειας του παραθύρου.",
"settings.privacy.allowBlocklist": "Να επιτρέπεται ο αποκλεισμος (blocklisting)",
"settings.privacy.allowBlocklistHelp": "Να επιτρέπεται στους συνδρομητές να διαγραφούν από όλες τις λίστες αλληλογραφίας και να αυτοχαρακτηριστούν ως αποκλεισμένοι;",
"settings.privacy.allowExport": "Να επιτρέπεται η εξαγωγή",
"settings.privacy.allowExportHelp": "Να επιτρέπεται στους συνδρομητές να εξάγουν τα δεδομένα που έχουν συλλεχθεί γι' αυτούς;",
"settings.privacy.allowPrefs": "Να επιτρέπονται αλλαγές προτιμήσεων",
"settings.privacy.allowPrefsHelp": "Να επιτρέπεται στους συνδρομητές να αλλάξουν τις προτιμήσεις τους, όπως τα ονόματά τους και τις συνδρομές σε πολλαπλές λίστες.",
"settings.privacy.allowWipe": "Να επιτρέπεται η ολική εκκαθάριση",
"settings.privacy.allowWipeHelp": "Να επιτρέπεται στους συνδρομητές να διαγράφουν τους εαυτούς τους, συμπεριλαμβανομένων των εγγραφών τους και όλων των άλλων δεδομένων από τη βάση δεδομένων. Οι προβολές εκστρατειών και τα κλικ σε συνδέσμους διαγράφονται επίσης, ενώ οι καταγραφές του πλήθους των προβολές και των κλικ παραμένουν (χωρίς να συνδέεται με αυτά κανένας συνδρομητής), ώστε να μην επηρεάζονται τα στατιστικά και τα αναλυτικά στοιχεία.",
"settings.privacy.domainBlocklist": "Λίστα αποκλεισμένων domain",
"settings.privacy.domainBlocklistHelp": "Οι διευθύνσεις ηλεκτρονικού ταχυδρομείου σε αυτά τα domain δεν μπορούν να εγγραφούν. Εισάγετε ένα domain ανά γραμμή, π.χ.: somesite.com",
"settings.privacy.individualSubTracking": "Παρακολούθηση μεμονωμένων συνδρομητών",
"settings.privacy.individualSubTrackingHelp": "Παρακολουθήστε τις προβολές και τα κλικ σε επίπεδο συνδρομητή. Όταν είναι απενεργοποιημένη, η παρακολούθηση προβολών και κλικ συνεχίζεται χωρίς να συνδέεται με μεμονωμένους συνδρομητές.",
"settings.privacy.listUnsubHeader": "Να περιλαμβάνεται η κεφαλίδα `List-Unsubscribe`",
"settings.privacy.listUnsubHeaderHelp": "Να συμπεριλαμβάνονται επικεφαλίδες διαγραφής που επιτρέπουν σε χρήστες προγραμμάτων ηλεκτρονικού ταχυδρομείου να διαγραφούν από τη λίστα με ένα μόνο κλικ.",
"settings.privacy.name": "Ιδιωτικότητα",
"settings.privacy.recordOptinIP": "Καταγραφή διεύθυνσης IP με τη συγκατάθεση",
"settings.privacy.recordOptinIPHelp": "Καταγράψτε τη διεύθυνση IP της διπλής συγκατάθεσης στα χαρακτηριστικά των συνδρομητών.",
"settings.restart": "Επανεκίννηση",
"settings.security.captchaKey": "SiteKey του hCaptcha.com",
"settings.security.captchaKeyHelp": "Επισκεφθείτε το www.hcaptcha.com για να λάβετε το κλειδί και το μυστικό.",
"settings.security.captchaSecret": "Μυστικό (secret) του hCaptcha.com",
"settings.security.enableCaptcha": "Ενεργοποίηση CAPTCHA",
"settings.security.enableCaptchaHelp": "Ενεργοποιήστε το CAPTCHA στη δημόσια φόρμα εγγραφής.",
"settings.security.name": "Ασφάλεια",
"settings.smtp.customHeaders": "Προσαρμοσμένες επικεφαλίδες",
"settings.smtp.customHeadersHelp": "Προαιρετικός πίνακας κεφαλίδων e-mail που πρέπει να περιλαμβάνονται σε όλα τα μηνύματα που αποστέλλονται από αυτόν τον διακομιστή. π.χ.: [{\"X-Custom\": \"value\"}, {\"X-Custom2\": \"value\"}]",
"settings.smtp.enabled": "Ενεργοποιημένο",
"settings.smtp.heloHost": "Όνομα διακομιστή για την εντολή HELO",
"settings.smtp.heloHostHelp": "Προαιρετικό. Ορισμένοι διακομιστές SMTP απαιτούν ένα FQDN στο όνομα κεντρικού υπολογιστή. Από προεπιλογή, οι εντολές HELLO ακολουθούνται από `localhost`. Ορίστε το εάν πρέπει να χρησιμοποιηθεί ένα προσαρμοσμένο όνομα κεντρικού υπολογιστή.",
"settings.smtp.name": "SMTP",
"settings.smtp.retries": "Επαναληπτικές προσπάθειες",
"settings.smtp.retriesHelp": "Αριθμός επαναληπτικών προσπαθειών όταν ένα μήνυμα αποτυγχάνει.",
"settings.smtp.sendTest": "Αποστολή δοκιμαστικού e-mail",
"settings.smtp.setCustomHeaders": "Ορισμός προσαρμοσμένων κεφαλίδων",
"settings.smtp.testConnection": "Δοκιμή σύνδεσης",
"settings.smtp.testEnterEmail": "Εισάγετε ξανά τον κωδικό πρόσβασης για δοκιμή",
"settings.smtp.toEmail": "Στο e-mail",
"settings.title": "Ρυθμίσεις",
"settings.updateAvailable": "Μια νέα ενημέρωση {version} είναι διαθέσιμη.",
"subscribers.advancedQuery": "Για προχωρημένους",
"subscribers.advancedQueryHelp": "Μερική έκφραση SQL για την αναζήτηση χαρακτηριστικών συνδρομητών",
"subscribers.attribs": "Χαρακτηριστικά",
"subscribers.attribsHelp": "Τα χαρακτηριστικά ορίζονται ως JSON map, για παράδειγμα:",
"subscribers.blocklistedHelp": "Οι αποκλεισμένοι συνδρομητές δεν θα λάβουν ποτέ κανένα μήνυμα ηλεκτρονικού ταχυδρομείου.",
"subscribers.confirmBlocklist": "Να αποκλειστούν {αριθμός} συνδρομητές;",
"subscribers.confirmDelete": "Να διαγραφούν {αριθμός} συνδρομητές;",
"subscribers.confirmExport": "Να γίνει εξαγωγή {αριθμός} συνδρομητών;",
"subscribers.domainBlocklisted": "Το domain είναι αποκλεισμένο.",
"subscribers.downloadData": "Λήψη δεδομένων",
"subscribers.email": "Διεύθυνση e-mail",
"subscribers.emailExists": "Το e-mail υπάρχει ήδη.",
"subscribers.errorBlocklisting": "Σφάλμα αποκλεισμού συνδρομητών: {error}",
"subscribers.errorNoIDs": "Δεν δόθηκαν ID.",
"subscribers.errorNoListsGiven": "Δεν δόθηκαν λίστες.",
"subscribers.errorPreparingQuery": "Σφάλμα προετοιμασίας ερωτήματος συνδρομητή: {error}",
"subscribers.errorSendingOptin": "Σφάλμα αποστολής e-mail συγκατάθεσης.",
"subscribers.export": "Εξαγωγή",
"subscribers.invalidAction": "Μη έγκυρη δράση.",
"subscribers.invalidEmail": "Μη έγκυρο e-mail.",
"subscribers.invalidJSON": "Μη έγκυρο JSON στα χαρακτηριστικά.",
"subscribers.invalidName": "Μη έγκυρο όνομα.",
"subscribers.listChangeApplied": "Η μεταβολή της λίστας εφαρμόστηκε.",
"subscribers.lists": "Λίστες",
"subscribers.listsHelp": "Οι λίστες από τις οποίες οι ίδιοι οι συνδρομητές έχουν διαγραφεί δεν μπορούν να διαγραφούν.",
"subscribers.listsPlaceholder": "Λίστες προς εγγραφή",
"subscribers.manageLists": "Διαχείριση λιστών",
"subscribers.markUnsubscribed": "Χαρακτηρίστε ως μη εγγεγραμμένο",
"subscribers.newSubscriber": "Νέος συνδρομητής",
"subscribers.numSelected": "{αριθμός} επιλεγμένοι συνδρομητές",
"subscribers.optinSubject": "Επιβεβαίωση εγγραφής",
"subscribers.preconfirm": "Προεπιβεβαίωση εγγραφών",
"subscribers.preconfirmHelp": "Να μην αποσταλούν e-mail συγκατάθεσης, και να χαρακτηριστούν όλες οι εγγραφές στη λίστα ως \"εγγεγραμμένες\".",
"subscribers.query": "Ερώτημα",
"subscribers.queryPlaceholder": "E-mail ή όνομα",
"subscribers.reset": "Επαναφορά",
"subscribers.selectAll": "Επιλέξτε όλα τα {num}",
"subscribers.sendOptinConfirm": "Αποστολή επιβεβαίωσης συγκατάθεσης",
"subscribers.sentOptinConfirm": "Η επιβεβαίωση συγκατάθεσης απεστάλη",
"subscribers.status.blocklisted": "Αποκλεισμένο",
"subscribers.status.confirmed": "Επιβεβαιωμένο",
"subscribers.status.enabled": "Ενεργοποιημένο",
"subscribers.status.subscribed": "Εγγεγραμμένο",
"subscribers.status.unconfirmed": "Ανεπιβεβαίωτο",
"subscribers.status.unsubscribed": "Μη εγγεγραμμένο",
"subscribers.subscribersDeleted": "{αριθμός} συνδρομητής(-ές) διαγράφηκε(-αν)",
"templates.cantDeleteDefault": "Δεν είναι δυνατή η διαγραφή ανύπαρκτου ή προεπιλεγμένου προτύπου",
"templates.default": "Προεπιλεγμένο",
"templates.dummyName": "Εικονική εκστρατεία",
"templates.dummySubject": "Θέμα εικονικής καμπάνιας",
"templates.errorCompiling": "Σφάλμα σύνταξης προτύπου: {error}",
"templates.errorRendering": "Σφάλμα απεικόνισης μηνύματος: {error}",
"templates.fieldInvalidName": "Μη έγκυρο μήκος για το όνομα.",
"templates.makeDefault": "Ορισμός ως προεπιλεγμένο",
"templates.newTemplate": "Νέο πρότυπο",
"templates.placeholderHelp": "Το προσωρινό {placeholder} θα πρέπει να εμφανίζεται ακριβώς μία φορά στο πρότυπο.",
"templates.preview": "Προεπισκόπηση",
"templates.rawHTML": "Ακατέργαστη HTML",
"templates.subject": "Θέμα",
"users.login": "Σύνδεση",
"users.logout": "Αποσύνδεση"
}

View file

@ -360,6 +360,7 @@
"settings.bounces.countHelp": "Number of bounces per subscriber",
"settings.bounces.enable": "Enable bounce processing",
"settings.bounces.enableMailbox": "Enable bounce mailbox",
"settings.bounces.enablePostmark": "Enable Postmark",
"settings.bounces.enableSES": "Enable SES",
"settings.bounces.enableSendgrid": "Enable SendGrid",
"settings.bounces.enableWebhooks": "Enable bounce webhooks",
@ -369,6 +370,9 @@
"settings.bounces.invalidScanInterval": "Bounce scan interval should be minimum 1 minute.",
"settings.bounces.name": "Bounces",
"settings.bounces.none": "None",
"settings.bounces.postmarkPassword": "Postmark Password",
"settings.bounces.postmarkUsername": "Postmark Username",
"settings.bounces.postmarkUsernameHelp": "Postmark allows you to enable basic authorization for webhooks. Make sure to enter the same credentials here and in your Postmark webhook settings.",
"settings.bounces.scanInterval": "Scan interval",
"settings.bounces.scanIntervalHelp": "Interval at which the bounce mailbox should be scanned for bounces (s for second, m for minute).",
"settings.bounces.sendgridKey": "SendGrid Key",
@ -433,7 +437,7 @@
"settings.media.s3.region": "Region",
"settings.media.s3.secret": "AWS access secret",
"settings.media.s3.uploadExpiry": "Upload expiry",
"settings.media.s3.uploadExpiryHelp": "(Optional) Specify TTL (in seconds) for the generated presigned URL. Only applicable for private buckets (s, m, h, d for seconds, minutes, hours, days).",
"settings.media.s3.uploadExpiryHelp": "(Optional) Specify expiry for the generated presigned URL. Only applicable for private buckets (s, m, h, d for seconds, minutes, hours, days).",
"settings.media.s3.url": "S3 backend URL",
"settings.media.s3.urlHelp": "Only change if using a custom S3 compatible backend like Minio.",
"settings.media.title": "Media uploads",

View file

@ -363,6 +363,7 @@
"settings.bounces.delete": "Eliminar",
"settings.bounces.enable": "Activar el procesamiento de rebotes",
"settings.bounces.enableMailbox": "Activar el buzón de rebotes",
"settings.bounces.enablePostmark": "Activar Postmark",
"settings.bounces.enableSES": "Activar SES",
"settings.bounces.enableSendgrid": "Activar SendGrid",
"settings.bounces.enableWebhooks": "Activar webhooks de rebotes",
@ -373,6 +374,9 @@
"settings.bounces.invalidScanInterval": "El intervalo mínimo de escanéo de los rebotes debería de ser 1 minuto.",
"settings.bounces.name": "Rebotes",
"settings.bounces.none": "Ninguno",
"settings.bounces.postmarkPassword": "Postmark Password",
"settings.bounces.postmarkUsername": "Postmark Username",
"settings.bounces.postmarkUsernameHelp": "Postmark allows you to enable basic authorization for webhooks. Make sure to enter the same credentials here and in your Postmark webhook settings.",
"settings.bounces.scanInterval": "Intervalo de escaneo",
"settings.bounces.scanIntervalHelp": "Intervalo en el que el buzón de rebotes debería ser escaneado para encontrar nuevos rebotes (s para segundos, m para minutos).",
"settings.bounces.sendgridKey": "Clave para SendGrid",
@ -438,7 +442,7 @@
"settings.media.s3.region": "Región",
"settings.media.s3.secret": "Secreto de acceso a S3 (secret access key)",
"settings.media.s3.uploadExpiry": "Expiración de la carga",
"settings.media.s3.uploadExpiryHelp": "(Opcional) TTL específico (en segundos) para la URL pre firmada generada. Solo es aplicable para buckets/contenedores privados (s, m, h, d para segundos, minutos, horas, días)",
"settings.media.s3.uploadExpiryHelp": "(Opcional) TTL específico para la URL pre firmada generada. Solo es aplicable para buckets/contenedores privados (s, m, h, d para segundos, minutos, horas, días)",
"settings.media.s3.url": "URL de API de S3",
"settings.media.s3.urlHelp": "Cambiar únicamente si se utiliza un servicio S3 personalizado (por ejemplo MinIO).",
"settings.media.title": "Cargas multimedia",

View file

@ -363,6 +363,7 @@
"settings.bounces.delete": "Delete",
"settings.bounces.enable": "Ota käyttöön bounce-käsittely",
"settings.bounces.enableMailbox": "Ota käyttöön bounce-postilaatikko",
"settings.bounces.enablePostmark": "Ota käyttöön Postmark",
"settings.bounces.enableSES": "Ota käyttöön SES",
"settings.bounces.enableSendgrid": "Ota käyttöön SendGrid",
"settings.bounces.enableWebhooks": "Ota käyttöön palautusten webhookit",
@ -373,6 +374,9 @@
"settings.bounces.invalidScanInterval": "Palautusten skannausintervallin pitää olla vähintään 1 minuutti.",
"settings.bounces.name": "Palautukset",
"settings.bounces.none": "Ei mitään",
"settings.bounces.postmarkPassword": "Postmark Password",
"settings.bounces.postmarkUsername": "Postmark Username",
"settings.bounces.postmarkUsernameHelp": "Postmark allows you to enable basic authorization for webhooks. Make sure to enter the same credentials here and in your Postmark webhook settings.",
"settings.bounces.scanInterval": "Skannausintervalli",
"settings.bounces.scanIntervalHelp": "Aika, jonka välein bounce-postilaatikko tarkistetaan bounce-palautusten varalta (s sekunteja, m minuutteja).",
"settings.bounces.sendgridKey": "SendGrid-avain",
@ -438,7 +442,7 @@
"settings.media.s3.region": "Alue",
"settings.media.s3.secret": "AWS-käyttösalaisuus",
"settings.media.s3.uploadExpiry": "Latauksen vanheneminen",
"settings.media.s3.uploadExpiryHelp": "(Valinnainen) Määritä TTL-arvo (sekunteina) luodulle ennalta määritettävälle URL-osoitteelle. Sovelletaan vain yksityisiin säilöihin (s, m, h, d sekunteina, minuutteina, tunteina, päivinä).",
"settings.media.s3.uploadExpiryHelp": "(Valinnainen) Määritä TTL-arvo luodulle ennalta määritettävälle URL-osoitteelle. Sovelletaan vain yksityisiin säilöihin (s, m, h, d sekunteina, minuutteina, tunteina, päivinä).",
"settings.media.s3.url": "S3-avaruuden URL-osoite",
"settings.media.s3.urlHelp": "Voit muuttaa vain, jos käytät mukautettua S3-yhteensopivaa taustajärjestelmää, kuten Minio.",
"settings.media.title": "Median lataukset",

View file

@ -363,6 +363,7 @@
"settings.bounces.delete": "Effacer",
"settings.bounces.enable": "Activer le traitement des rebonds",
"settings.bounces.enableMailbox": "Activer la boîte aux lettres de rebond",
"settings.bounces.enablePostmark": "Activer Postmark",
"settings.bounces.enableSES": "Activer SES",
"settings.bounces.enableSendgrid": "Activer SendGrid",
"settings.bounces.enableWebhooks": "Activez les 'webhooks' de rebond",
@ -373,6 +374,9 @@
"settings.bounces.invalidScanInterval": "L'intervalle de 'scan' des rebonds doit être d'au moins 1 minute.",
"settings.bounces.name": "Rebonds",
"settings.bounces.none": "Aucun",
"settings.bounces.postmarkPassword": "Mot de passe Postmark",
"settings.bounces.postmarkUsername": "Nom d'utilisateur Postmark",
"settings.bounces.postmarkUsernameHelp": "Postmark vous permet d'activer l'autorisation basique pour les webhooks. Prenez soin de rentrer les mêmes identifiants ici ainsi que dans les paramètres de webhook Postmark.",
"settings.bounces.scanInterval": "Interval de 'scan'",
"settings.bounces.scanIntervalHelp": "Intervalle auquel la boîte aux lettres de rebond doit être analysée pour les rebonds (s pour seconde, m pour minute).",
"settings.bounces.sendgridKey": "Clés de SendGrid",
@ -438,7 +442,7 @@
"settings.media.s3.region": "Région",
"settings.media.s3.secret": "Mot de passe d'accès AWS",
"settings.media.s3.uploadExpiry": "Durée de validité",
"settings.media.s3.uploadExpiryHelp": "(Facultatif) Spécifiez la durée de validité (en secondes) pour l'URL prédéfinie générée. Uniquement applicable pour les compartiments privés (s, m, h, d pour les secondes, minutes, heures, jours).",
"settings.media.s3.uploadExpiryHelp": "(Facultatif) Spécifiez la durée de validité pour l'URL prédéfinie générée. Uniquement applicable pour les compartiments privés (s, m, h, d pour les secondes, minutes, heures, jours).",
"settings.media.s3.url": "URL du 'backend' S3",
"settings.media.s3.urlHelp": "Ne changez que si vous utilisez un 'backend' personnalisé compatible S3 comme Minio.",
"settings.media.title": "Mise en ligne de fichiers",

580
i18n/he.json Normal file
View file

@ -0,0 +1,580 @@
{
"_.code": "he",
"_.name": "עברית (he)",
"admin.errorMarshallingConfig": "שגיאה בארגון תצורה: {error}",
"analytics.count": "כמות",
"analytics.fromDate": "מ",
"analytics.invalidDates": "טווח תאריכים לא חוקי.",
"analytics.isUnique": "הספירות הן ייחודיות לכל מנוי.",
"analytics.links": "קישורים",
"analytics.nonUnique": "הספירות אינן ייחודיות מאחר ומעקב אישי של המנויים מושבת.",
"analytics.title": "סטטיסטיקות",
"analytics.toDate": "ל",
"bounces.complaint": "תלונה",
"bounces.hard": "קשה",
"bounces.soft": "עדין",
"bounces.source": "מקור",
"bounces.unknownService": "שרות לא ידוע.",
"bounces.view": "צפה בהקפצות",
"campaigns.addAltText": "הוספת טקסט פשוט",
"campaigns.addAttachments": "הוסף קבצים",
"campaigns.archive": "ארכיון",
"campaigns.archiveEnable": "פרסם לארכיון ציבורי",
"campaigns.archiveHelp": "פרסם (פועל, מושהה, הושלם) את הודעת הקמפיין בארכיון הציבורי.",
"campaigns.archiveMeta": "מטא-נתונים של קמפיין",
"campaigns.archiveMetaHelp": "נתוני חבוי של המנויים לשימוש בהודעה ציבורית כולל שם, דואר אלקטרוני, וכל מאפיינים אופציונליים שבשימוש בהודעת הקמפיין או התבנית.",
"campaigns.attachments": "קבצים מצורפים",
"campaigns.cantUpdate": "לא ניתן לעדכן קמפיין בריצה או שהושלם.",
"campaigns.clicks": "לחיצות",
"campaigns.confirmDelete": "מחק את {name}",
"campaigns.confirmSchedule": "הקמפיין יתחיל באופן אוטומטי בתאריך ובשעה המתוכננים. לתזמן כעת?",
"campaigns.confirmSwitchFormat": "התוכן עלול לאבד את העיצוב, להמשיך?",
"campaigns.content": "תוכן",
"campaigns.contentHelp": "תוכן כאן",
"campaigns.continue": "המשך",
"campaigns.copyOf": "עותק של {name}",
"campaigns.customHeadersHelp": "מערך כותרות מותאמות אישית לצירוף להודעות. דוגמא: [{\"X-Custom\": \"value\"}, {\"X-Custom2\": \"value\"}]",
"campaigns.dateAndTime": "תאריך ושעה",
"campaigns.ended": "הסתיים",
"campaigns.errorSendTest": "שגיאה בשליחת הבדיקה: {error}",
"campaigns.fieldInvalidBody": "שגיאה בקימפול גוף הקמפיין: {error}",
"campaigns.fieldInvalidFromEmail": "`from_email` לא חוקי.",
"campaigns.fieldInvalidListIDs": "מזהי רשימה לא חוקיים.",
"campaigns.fieldInvalidMessenger": "שולח לא ידוע {name}.",
"campaigns.fieldInvalidName": "אורך שם לא חוקי.",
"campaigns.fieldInvalidSendAt": "התאריך המתוכנן צריך להיות בעתיד.",
"campaigns.fieldInvalidSubject": "אורך נושא לא חוקי.",
"campaigns.formatHTML": "עיצוב HTML",
"campaigns.fromAddress": "מכתובת",
"campaigns.fromAddressPlaceholder": "השם שלך <noreply@yoursite.com>",
"campaigns.invalid": "קמפיין לא חוקי",
"campaigns.invalidCustomHeaders": "כותרות מותאמות אישית לא חוקיות: {error}",
"campaigns.markdown": "Markdown",
"campaigns.needsSendAt": "יש לבחור תאריך תזמון לקמפיין.",
"campaigns.newCampaign": "קמפיין חדש",
"campaigns.noKnownSubsToTest": "אין מנויים ידועים לבדיקה.",
"campaigns.noOptinLists": "לא נמצאו רשימות פעילות ליצירת קמפיין.",
"campaigns.noSubs": "אין מנויים ברשימות שנבחרו עבור יצירת הקמפיין.",
"campaigns.noSubsToTest": "אין מנויים לשיוך.",
"campaigns.notFound": "קמפיין לא נמצא.",
"campaigns.onlyActiveCancel": "ניתן לבטל רק קמפיינים פעילים.",
"campaigns.onlyActivePause": "ניתן להשהות רק קמפיינים פעילים.",
"campaigns.onlyDraftAsScheduled": "ניתן לתזמן רק טיוטה של קמפיינים.",
"campaigns.onlyPausedDraft": "אפשר להתחיל רק קמפיינים מושהים וטיוטה.",
"campaigns.onlyScheduledAsDraft": "ניתן לשמור סקירות רקודות כטיוטה.",
"campaigns.pause": "עצור",
"campaigns.plainText": "טקסט רגיל",
"campaigns.preview": "תצוגה מקדימה",
"campaigns.progress": "בתהליך",
"campaigns.queryPlaceholder": "שם או נושא",
"campaigns.rateMinuteShort": "מינימום",
"campaigns.rawHTML": "HTML גולמי",
"campaigns.removeAltText": "הסר הודעת טקסט פשוט",
"campaigns.richText": "טקסט עשיר",
"campaigns.schedule": "תזמון קמפיין",
"campaigns.scheduled": "מתוזמן",
"campaigns.send": "שלח",
"campaigns.sendLater": "שלח מאוחר יותר",
"campaigns.sendTest": "שלח הודעת בדיקה",
"campaigns.sendTestHelp": "לחץ על Enter לאחר שתקלוד כתובת דואר אלקטרוני על מנת להוסיף מקבלים מרובים. הכתובות חייבות להיות שייכות למנויים קיימים.",
"campaigns.sendToLists": "רשימות לשליחה",
"campaigns.sent": "נשלח",
"campaigns.start": "התחל קמפיין",
"campaigns.started": "\"{name}\" התחיל",
"campaigns.startedAt": "התחיל",
"campaigns.stats": "סטטיסטיקות",
"campaigns.status.cancelled": "בוטל",
"campaigns.status.draft": "טיוטה",
"campaigns.status.finished": "הושלם",
"campaigns.status.paused": "מושהה",
"campaigns.status.running": "רץ",
"campaigns.status.scheduled": "מתוזמן",
"campaigns.statusChanged": "\"{name}\" {status}",
"campaigns.subject": "נושא",
"campaigns.testEmails": "כתובות אימייל",
"campaigns.testSent": "הודעת בדיקה נשלחה",
"campaigns.timestamps": "חותמות זמן",
"campaigns.trackLink": "קישור מעקב",
"campaigns.views": "צפיות",
"dashboard.campaignViews": "צפיות בקמפיין",
"dashboard.linkClicks": "לחיצות על קישורים",
"dashboard.messagesSent": "הודעות שנשלחו",
"dashboard.orphanSubs": "יתומים",
"email.data.info": "עותק של כל הנתונים הרשומים עליך מוצורף כקובץ בפורמט JSON. ניתן להציגו בעורך טקסט.",
"email.data.title": "הנתונים שלך",
"email.optin.confirmSub": "אשר רישום",
"email.optin.confirmSubHelp": "אשר את המינוי שלך על ידי לחיצה על הכפתור למטה.",
"email.optin.confirmSubInfo": "נוספת בהצלחה לרשימת הבאות:",
"email.optin.confirmSubTitle": "אישור רישום",
"email.optin.confirmSubWelcome": "היי",
"email.optin.privateList": "רשימה פרטית",
"email.status.campaignReason": "סיבה",
"email.status.campaignSent": "נשלח",
"email.status.campaignUpdateTitle": "עדכון קמפיין",
"email.status.importFile": "קובץ",
"email.status.importRecords": "רשומות",
"email.status.importTitle": "ייבוא עדכון",
"email.status.status": "סטטוס",
"email.unsub": "ביטול רישום",
"email.unsubHelp": "לא רוצה לקבל את המיילים האלו?",
"email.viewInBrowser": "הצג בדפדפן",
"forms.formHTML": "טופס HTML",
"forms.formHTMLHelp": "השתמש ב-HTML הבא כדי להציג טופס רישום בדף אינטרנט חיצוני. הטופס צריך להכיל שדה דואר אלקטרוני ושדות `l` יחידים או רבים (UUID) בשימוש ברשימות. שדה השם הוא אופציונלי.",
"forms.noPublicLists": "אין רשימות ציבוריות ליצירת טפסים.",
"forms.publicLists": "רשימות ציבוריות",
"forms.publicSubPage": "דף הרשמה ציבורי",
"forms.selectHelp": "בחר רשימות להוספה לטופס.",
"forms.title": "טפסים",
"globals.buttons.add": "הוסף",
"globals.buttons.addNew": "הוסף חדש",
"globals.buttons.back": "חזור",
"globals.buttons.cancel": "ביטול",
"globals.buttons.clear": "ניקוי",
"globals.buttons.clearAll": "נקה הכל",
"globals.buttons.clone": "שכפול",
"globals.buttons.close": "סגור",
"globals.buttons.continue": "המשך",
"globals.buttons.delete": "מחיקה",
"globals.buttons.deleteAll": "מחק הכל",
"globals.buttons.edit": "עריכה",
"globals.buttons.enabled": "מופעל",
"globals.buttons.insert": "להכניס",
"globals.buttons.learnMore": "למד עוד",
"globals.buttons.more": "עוד",
"globals.buttons.new": "חדש",
"globals.buttons.ok": "אישור",
"globals.buttons.remove": "הסרה",
"globals.buttons.save": "שמירה",
"globals.buttons.saveChanges": "שמור שינויים",
"globals.days.0": "ראשון",
"globals.days.1": "ראשון",
"globals.days.2": "שני",
"globals.days.3": "שלישי",
"globals.days.4": "רביעי",
"globals.days.5": "חמישי",
"globals.days.6": "שישי",
"globals.days.7": "שבת",
"globals.fields.createdAt": "נוצר",
"globals.fields.description": "תיאור",
"globals.fields.id": "מזהה",
"globals.fields.name": "שם",
"globals.fields.status": "סטטוס",
"globals.fields.type": "סוג",
"globals.fields.updatedAt": "עודכן",
"globals.fields.uuid": "UUID",
"globals.messages.confirm": "האם אתה בטוח?",
"globals.messages.confirmDiscard": "לבטל את השינויים?",
"globals.messages.created": "\"{name}\" נוצר",
"globals.messages.deleted": "\"{name}\" נמחק",
"globals.messages.deletedCount": "{name} ({num}) נמחקו",
"globals.messages.done": "הושלם",
"globals.messages.emptyState": "אין פה כלום..",
"globals.messages.errorCreating": "שגיאה ביצירת {name}: {error}",
"globals.messages.errorDeleting": "שגיאה במחיקת {name}: {error}",
"globals.messages.errorFetching": "שגיאה באחזור {name}: {error}",
"globals.messages.errorInvalidIDs": "מזהה אחד יותר שגוי: {error}",
"globals.messages.errorUUID": "שגיאה ביצירת UUID: {error}",
"globals.messages.errorUpdating": "שגיאה בעדכון {name}: {error}",
"globals.messages.internalError": "שגיאת שרת כללית",
"globals.messages.invalidData": "נתונים לא חוקיים",
"globals.messages.invalidFields": "שדות לא חוקיים: {name}",
"globals.messages.invalidID": "מזהים לא חוקיים",
"globals.messages.invalidUUID": "UUID לא תקין (ים)",
"globals.messages.missingFields": "חסרים שדות: {name}",
"globals.messages.notFound": "{name} לא נמצא",
"globals.messages.passwordChange": "הזן ערך לשינוי",
"globals.messages.passwordChangeFull": "נא לנקות ולהזין שוב את הסיסמה המלאה ב־'{name}'.",
"globals.messages.updated": "\"{name}\" עודכן",
"globals.months.1": "ינואר",
"globals.months.10": "אוקטובר",
"globals.months.11": "נובמבר",
"globals.months.12": "דצמבר",
"globals.months.2": "פברואר",
"globals.months.3": "מרץ",
"globals.months.4": "אפריל",
"globals.months.5": "מאי",
"globals.months.6": "יוני",
"globals.months.7": "יולי",
"globals.months.8": "אוגוסט",
"globals.months.9": "ספטמבר",
"globals.states.off": "כבוי",
"globals.terms.all": "הכל",
"globals.terms.analytics": "סטטיסטיקות",
"globals.terms.bounce": "להקפיץ | קופץ",
"globals.terms.bounces": "קופץ",
"globals.terms.campaign": "קמפיין | קמפיינים",
"globals.terms.campaigns": "קמפיינים",
"globals.terms.dashboard": "לוח בקרה",
"globals.terms.day": "יום | ימים",
"globals.terms.hour": "שעה | שעות",
"globals.terms.list": "רשימה | רשימות",
"globals.terms.lists": "רשימות",
"globals.terms.media": "מדיה | מדיה",
"globals.terms.messenger": "שולח | שולחים",
"globals.terms.messengers": "שולחים",
"globals.terms.minute": "דקה | דקות",
"globals.terms.month": "חודש | חודשים",
"globals.terms.none": "אף אחד",
"globals.terms.second": "שניה | שניות",
"globals.terms.settings": "הגדרות",
"globals.terms.subscriber": "מנוי | מנויים",
"globals.terms.subscribers": "רשומים",
"globals.terms.subscriptions": "מנוי | מנויים",
"globals.terms.tag": "תגית | תגיות",
"globals.terms.tags": "תגיות",
"globals.terms.template": "תבנית | תבניות",
"globals.terms.templates": "תבניות",
"globals.terms.tx": "עסקה | עסקה",
"globals.terms.year": "שנה | שנים",
"import.alreadyRunning": "היבוא כבר פועל. יש להמתין שיסתיים או לעצור אותו לפני שינוי נוסף.",
"import.blocklist": "חסום רשימה",
"import.csvDelim": "CSV מפריד",
"import.csvDelimHelp": "מפריד ברירת מחדל, פסיק.",
"import.csvExample": "דוגמא לCSV",
"import.csvFile": "קובץ CSV או ZIP",
"import.csvFileHelp": "לחץ או גרור לכאן קובץ CSV או ZIP",
"import.errorCopyingFile": "שגיאה בהעתקת קובץ: {error}",
"import.errorProcessingZIP": "שגיאה בעיבוד קובץ ZIP: {error}",
"import.errorStarting": "שגיאה בהתחלת הייבוא: {error}",
"import.importDone": "הושלם",
"import.importStarted": "הייבוא התחיל",
"import.instructions": "הוראות",
"import.instructionsHelp": "ניתן לטעון קובץ CSV או קובץ ZIP שמכיל תוכן CSV אחד ליבוא בצורה כוללת מנויים. הקובץ CSV יכול לכלול את הכותרות הבאות עם שמות העמודות המדויקים. המאפיינים (אופציונלי) צריכים להיות במבנה JSON חוקי עם הצורך בדפיסות גרשיים מופרדות.",
"import.invalidDelim": "המפריד צריך להיות תו בודד.",
"import.invalidFile": "קובץ לא חוקי: {error}",
"import.invalidMode": "מצב לא חוקי",
"import.invalidParams": "פרמטרים לא חוקיים: {error}",
"import.invalidSubStatus": "סטטוס מנוי לא חוקי.",
"import.listSubHelp": "רשימות לרישום.",
"import.mode": "מצב",
"import.overwrite": "להחליף?",
"import.overwriteHelp": "לדרוס שמות, מאפיינים, ומצבי מינוי של המנויים הקיימים?",
"import.recordsCount": "{num} / {total} רשומות",
"import.stopImport": "עצור ייבוא",
"import.subscribe": "הירשם",
"import.title": "ייבוא מנויים",
"import.upload": "העלאה",
"lists.confirmDelete": "האם אתה בטוח? זה לא מוחק את המנויים.",
"lists.confirmSub": "אשר את המנויים עבור {name}",
"lists.invalidName": "שם לא חוקי",
"lists.newList": "רשימה חדשה",
"lists.optin": "רישום",
"lists.optinHelp": "הרישום הכפול משלח למנוי שאלה לאימות. ברשימות של הרישום הכפול, קמפיינים נשלחים רק למנויים שאומתו.",
"lists.optinTo": "הצטרפות ל {name}",
"lists.optins.double": "הצטרפות כפולה",
"lists.optins.single": "רישום יחיד",
"lists.sendCampaign": "שלח קמפיין",
"lists.sendOptinCampaign": "שליחת קמפיין רישום",
"lists.type": "סוג",
"lists.typeHelp": "הרשימות הציבוריות פתוחות לכל הגורם והן יכולות להופיע בעמודים ציבוריים כמו עמוד ניהול מינויים.",
"lists.types.private": "פרטי",
"lists.types.public": "ציבואי",
"logs.title": "לוגים",
"maintenance.help": "קיימות פעולות שעלולות לדרוש זמן להשלמתן בהתאם לכמות הנתונים.",
"maintenance.maintenance.unconfirmedOptins": "מנויים שלא אומתו",
"maintenance.olderThan": "ישן מ",
"maintenance.orphanHelp": "היתומים = מנויים ללא רשימות",
"maintenance.title": "תחזוקה",
"maintenance.unconfirmedSubs": "מינויים לא מאושרים לפני יותר מ-{name} ימים.",
"media.errorReadingFile": "שגיאה בקריאת הקובץ: {error}",
"media.errorResizing": "שגיאה בשינוי גודל התמונה: {error}",
"media.errorSavingThumbnail": "שגיאה בשמירת התמונה הקטנה: {error}",
"media.errorUploading": "שגיאה בהעלאת הקובץ: {error}",
"media.invalidFile": "קובץ לא חוקי: {error}",
"media.title": "מדיה",
"media.unsupportedFileType": "סוג קובץ לא נתמך ({type})",
"media.upload": "העלאה",
"media.uploadHelp": "לחץ או גרור לכאן תמונות",
"media.uploadImage": "העלה תמונה",
"menu.allCampaigns": "כל הקמפיינים",
"menu.allLists": "כל הרשימות",
"menu.allSubscribers": "כל הרשומים",
"menu.dashboard": "לוח בקרה",
"menu.forms": "טפסים",
"menu.import": "ייבוא",
"menu.logs": "לוגים",
"menu.maintenance": "תחזוקה",
"menu.media": "מדיה",
"menu.newCampaign": "צור חדש",
"menu.settings": "הגדרות",
"public.archiveEmpty": "אין הודעות בארכיון.",
"public.archiveTitle": "ארכיון רשימת תפוצה",
"public.blocklisted": "יצא מרשימת התפוטרים לצמיתות.",
"public.campaignNotFound": "ההודעה לא נמצאה.",
"public.confirmOptinSubTitle": "אשר מינוי",
"public.confirmSub": "אשר מינוי",
"public.confirmSubInfo": "נוספת לרשימות הבאות:",
"public.confirmSubTitle": "אישור",
"public.dataRemoved": "המינויים שלך וכל הנתונים המשוייכים הוסרו.",
"public.dataRemovedTitle": "נתונים הוסרו",
"public.dataSent": "הנתונים שלך נשלחו אליך כעת לאימייל כקובץ מצורך.",
"public.dataSentTitle": "הנתונים נשלחו לאימייל.",
"public.errorFetchingCampaign": "שגיאה באחזור הודעת האימייל.",
"public.errorFetchingEmail": "הודעת האימייל לא נמצאה.",
"public.errorFetchingLists": "שגיאה באחזור הרשימות, נא לנסות שוב.",
"public.errorProcessingRequest": "שגיאה בעיבוד הבקשה, נא לנסות שוב.",
"public.errorTitle": "שגיאה",
"public.invalidCaptcha": "קאפצ׳ה לא חוקי.",
"public.invalidFeature": "תכונה זו אינה זמינה.",
"public.invalidLink": "קישור לא חוקי",
"public.managePrefs": "ניהול העדפות",
"public.managePrefsUnsub": "בטל את הסימון של רשימות המינוי המעוניינות להתפטר מהן.",
"public.noListsAvailable": "אין רשימות זמינות למינוי.",
"public.noListsSelected": "לא נבחרו רשימות תקינות למינוי.",
"public.noSubInfo": "אין מידע לאימות מנויים.",
"public.noSubTitle": "אין מנויים",
"public.notFoundTitle": "לא נמצא",
"public.poweredBy": "מופעל ע״י",
"public.prefsSaved": "ההעדפות שלך נשמרו.",
"public.privacyConfirmWipe": "האם אתה בטוח שתרצה למחוק את כל נתוני המינוי לצמיתות?",
"public.privacyExport": "ייצא את הנתונים שלך",
"public.privacyExportHelp": "עותק של הנתונים שלך יישלח לך בדואר אלקטרוני.",
"public.privacyTitle": "פרטיות ונתונים",
"public.privacyWipe": "מחיקת הנתונים שלך",
"public.privacyWipeHelp": "מחק את המינויים שלך ואת כל הנתונים המולוות להם לצמיתות.",
"public.sub": "רישום",
"public.subConfirmed": "נרשמת בהצלחה.",
"public.subConfirmedTitle": "מאושר",
"public.subName": "שם (לא חובה)",
"public.subNotFound": "המינוי לא נמצא.",
"public.subOptinPending": "נשלחה לך הודעת דואר אלקטרוני על מנת לאמת את המינוי שלך/יך.",
"public.subPrivateList": "רשימה פרטית",
"public.subTitle": "רישום",
"public.unsub": "ביטול רישום",
"public.unsubFull": "עצור את ההרשמה לכל דואר אלקטרוני עתידי.",
"public.unsubHelp": "האם ברצונך להפסיק את הרישום לרשימת התפוצה הזו?",
"public.unsubTitle": "הפסק את ההרשמה",
"public.unsubbedInfo": "בצעת הפסקת ההרשמה בהצלחה.",
"public.unsubbedTitle": "הרשמתך בוטלה",
"public.unsubscribeTitle": "הרשמה לרשימת דיוור",
"settings.appearance.adminHelp": "CSS מותאם אישית שייחל לממשק הניהול.",
"settings.appearance.adminName": "ניהול",
"settings.appearance.customCSS": "CSS מותאם",
"settings.appearance.customJS": "JavaScript מותאם",
"settings.appearance.name": "עיצוב",
"settings.appearance.publicHelp": "CSS ו־JavaScript מותאמים אישית שייחלו לעמודים הציבוריים.",
"settings.appearance.publicName": "ציבורי",
"settings.bounces.action": "פעולה",
"settings.bounces.blocklist": "רשימה שחורה",
"settings.bounces.count": "ספירת השטחות",
"settings.bounces.countHelp": "מספר השטחות למנוי",
"settings.bounces.enable": "הפעלת תהליך החזרת הודעות שטחות",
"settings.bounces.enableMailbox": "הפעלת תיבת הודעות שטחות",
"settings.bounces.enablePostmark": "הפעלת Postmark",
"settings.bounces.enableSES": "הפעלת SES",
"settings.bounces.enableSendgrid": "הפעלת SendGrid",
"settings.bounces.enableWebhooks": "הפעלת Webhooks השטחות",
"settings.bounces.enabled": "מופעל",
"settings.bounces.folder": "תיקייה",
"settings.bounces.folderHelp": "שם התיקייה של שורת הכתובת החדשה שמתקשרת עם שימוש. לדוגמה: Inbox.",
"settings.bounces.invalidScanInterval": "מרווח הסריקה לשטחות צריך להיות מינימום של דקה אחת.",
"settings.bounces.name": "השטחות",
"settings.bounces.none": "אין",
"settings.bounces.postmarkPassword": "סיסמת Postmark",
"settings.bounces.postmarkUsername": "שם משתמש ה־Postmark",
"settings.bounces.postmarkUsernameHelp": "Postmark מאפשר לך להפעיל הפרמה בסיסית לכבות הפקת מידע. מומלץ להזין את אותם פרטים כאן ובהגדרות הגרורה של הפרמה שלך ב־Postmark.",
"settings.bounces.scanInterval": "מרווח הסריקה",
"settings.bounces.scanIntervalHelp": "המרווח שבו תיקיית ההודעות שטחות יוסרת כדי לבדוק ולשחזר (s לשנייה, m לדקה).",
"settings.bounces.sendgridKey": "מפתח SendGrid",
"settings.bounces.type": "סוג",
"settings.bounces.username": "שם משתמש",
"settings.confirmRestart": "נא להשהות את כל הקמפיינים הפעילים לפני הפעלה מחדש?",
"settings.duplicateMessengerName": "תושבת שם מורה כפול: {name}",
"settings.errorEncoding": "שגיאה בהצפנת ההגדרות: {error}",
"settings.errorNoSMTP": "יש להפעיל לפחות בלוקSMTP אחת",
"settings.general.adminNotifEmails": "דואר אלקטרוני של התראות מנהל",
"settings.general.adminNotifEmailsHelp": "רשימת הודעות אלקטרוניות מופרדות בפסיקים שבין כתובות דואר אלקטרוני הולכות למנהל כגון חדשות עדכונים בהטמעות, הודעות קמפיין שהסתיימו, כשלים ועוד.",
"settings.general.checkUpdates": "בדוק עדכונים",
"settings.general.checkUpdatesHelp": "בדיקות תקופתיות עבור גרסות אפליקציה חדשות והתראות גרסה.",
"settings.general.enablePublicArchive": "הפעלת הארכיון הציבורי של רשימות התפוצה",
"settings.general.enablePublicArchiveHelp": "פרסם קמפיינים בהם מופעל הארכיון על האתר הציבורי.",
"settings.general.enablePublicArchiveRSSContent": "הצג תוכן מלא בפיד ה־RSS",
"settings.general.enablePublicArchiveRSSContentHelp": "הצג תוכן מלא של הדואר האלקטרוני ב־RSS. אם הופעל, רק האלמנטים של הכותרת והקישור יוצגו.",
"settings.general.enablePublicSubPage": "הפעלת הדף הציבורי לרישום",
"settings.general.enablePublicSubPageHelp": "הצג דף רישום ציבורי עם כל הרשימות הציבוריות כדי שאנשים יוכלו להירשם.",
"settings.general.faviconURL": "קישור אירוע בינלאומי (Favicon)",
"settings.general.faviconURLHelp": "(אופציונלי) URL מלא לקישור אירוע בינלאומי (Favicon) הסטטי שיתצוגן בתצוגה למשתמשים כמו עמוד ההפסקה מהתפוצה.",
"settings.general.fromEmail": "דואר אלקטרוני ברירת מחדל עבור מאין השולח",
"settings.general.fromEmailHelp": "דואר אלקטרוני ברירת מחדל עבור מאין השולח המוצג על הודעות הקמפיין היוצאות. ניתן לשנות זאת בקמפיין.",
"settings.general.language": "שפה",
"settings.general.logoURL": "קישור ללוגו (סמל התוכנה)",
"settings.general.logoURLHelp": "(אופציונלי) URL מלא ללוגו הסטטי שסמל התוכנה והופצת ההפסקה יוצג אותו למשתמשים כמו עמוד ההפסקה מהתפוצה.",
"settings.general.name": "כללי",
"settings.general.rootURL": "URL ראשי",
"settings.general.rootURLHelp": "כתובת האתר הציבורית של ההתקנה (ללא סלש מאחרי הסיומת).",
"settings.general.sendOptinConfirm": "שליחת אישור הרישום",
"settings.general.sendOptinConfirmHelp": "שליחת הודעת אישור הרישום דרך הטופס הציבורי או דרך הוספתה על ידי המנהל.",
"settings.general.siteName": "שם אתר",
"settings.invalidMessengerName": "שם מסיר פצליי.",
"settings.mailserver.authProtocol": "פרוטוקול אימות",
"settings.mailserver.host": "מארח",
"settings.mailserver.hostHelp": "כתובת שרת SMTP",
"settings.mailserver.idleTimeout": "זמן אי פעילות",
"settings.mailserver.idleTimeoutHelp": "זמן המתנה לפענוח פעילות נוספת בחיבור לפני סגירתו והסרתו מהקופסה (s לשנייה, m לדקה).",
"settings.mailserver.maxConns": "מקסימום חיבורים",
"settings.mailserver.maxConnsHelp": "למספר החיבורים המרובים הממוקדים לשרת.",
"settings.mailserver.password": "סיסמא",
"settings.mailserver.passwordHelp": "יש להזין כדי לשנות",
"settings.mailserver.port": "פורט",
"settings.mailserver.portHelp": "יציאת השרת SMTP.",
"settings.mailserver.skipTLS": "דלג על אימות TLS",
"settings.mailserver.skipTLSHelp": "דלג על הבדיקה של השרת בזמן התקשורת SSL.",
"settings.mailserver.tls": "TLS",
"settings.mailserver.tlsHelp": "הצפנת ה- TLS/SSL. STARTTLS בשימוש נרחב.",
"settings.mailserver.username": "שם משתמש",
"settings.mailserver.waitTimeout": "זמן המתנה",
"settings.mailserver.waitTimeoutHelp": "זמן המתנה לפענוח פעילות נוספת בחיבור לפני סגירתו והסרתו מהקופסה (s לשנייה, m לדקה).",
"settings.media.provider": "ספק",
"settings.media.s3.bucket": "דלור סלון",
"settings.media.s3.bucketPath": "נתיב דלור סלון",
"settings.media.s3.bucketPathHelp": "נתיב בתוך דלור הסלון אליו יעלו קבצים. ברירת המחדל היא /",
"settings.media.s3.bucketType": "סוג דלור סלון",
"settings.media.s3.bucketTypePrivate": "פרטי",
"settings.media.s3.bucketTypePublic": "ציבורי",
"settings.media.s3.key": "מפתח גישה של AWS",
"settings.media.s3.publicURL": "כתובת URL ציבורית מותאמת אישית (אופציונלי)",
"settings.media.s3.publicURLHelp": "תחום S3 מותאם אישית לשימוש בקישורים של תמונה במקום כתובת S3 המשתנה ברירת המחדל.",
"settings.media.s3.region": "איזור",
"settings.media.s3.secret": "סוד כניסה AWS",
"settings.media.s3.uploadExpiry": "תפוגת עלייה",
"settings.media.s3.uploadExpiryHelp": "(Optional) Specify TTL (in seconds) for the generated presigned URL. Only applicable for private buckets (s, m, h, d for seconds, minutes, hours, days).",
"settings.media.s3.url": "כתובת תשתית S3",
"settings.media.s3.urlHelp": "אפשר לשנות רק אם משתמשים בתשתית S3 הזרה מותאמת אישית כמו Minio.",
"settings.media.title": "העלאת קבצים",
"settings.media.upload.extensions": "סיומת קובץ מאושרת",
"settings.media.upload.extensionsHelp": "הוסף * להרשות כל הסיומות",
"settings.media.upload.path": "נתיב העלאה",
"settings.media.upload.pathHelp": "נתיב הספרייה שבה יועלו הקבצים.",
"settings.media.upload.uri": "URI העלאה",
"settings.media.upload.uriHelp": "URI העלאה הגלוי לעולם החיצוני. התקיות המעולות לתוך upload_path יהיו גלויות באופן ציבורי תחת {root_url}, לדוגמה, https://listmonk.example.com/uploads.",
"settings.messengers.maxConns": "מקסימום בקשות מקבילות",
"settings.messengers.maxConnsHelp": "מספר חיבורים מקבילים רבים ביותר לשרת.",
"settings.messengers.messageSaved": "הגדרות נשמרו. מרענן את אפליקציה...",
"settings.messengers.name": "שליחים",
"settings.messengers.nameHelp": "לדוגמה: sms שלי. אלפאנומרי / מקף.",
"settings.messengers.password": "סיסמא",
"settings.messengers.retries": "ניסיונות повторы",
"settings.messengers.retriesHelp": "מספר הניסיונות בכשל הודעה.",
"settings.messengers.skipTLSHelp": "דלג על הבדיקה של שמות המארחים בתעודת התקנות HTTPS.",
"settings.messengers.timeout": "זמן אי פעילות",
"settings.messengers.timeoutHelp": "זמן המתנה לפענוח פעילות נוספת בחיבור לפני סגירתו והסרתו מהקופסה (s לשנייה, m לדקה).",
"settings.messengers.url": "כתובת (URL)",
"settings.messengers.urlHelp": "כתובת URL ריבות השליחה.",
"settings.messengers.username": "שם משתמש",
"settings.needsRestart": "השינויים בהגדרות יחדו עם השהיית קמפיינים נכונים חדשים והפעל את אפליקציית ההפעלה.",
"settings.performance.batchSize": "מס יחידות בפסה",
"settings.performance.batchSizeHelp": "מס המנויים לשימוש מגרסת מסד הנתונים בשלב יחיד בלבד. שלב במסד הנתונים מושלם כולל מנויים מהמסד, שליחת הודעות אליהם והמשכת השלב המוסכמת לשלב הבא למשל מנויים נוספים ממסד הנתונים. הערך המומלץ מעלה מכותרת הרמות הנישפות המרבית (תנועה * קצב הודעות).",
"settings.performance.concurrency": "דרגת תוחלת",
"settings.performance.concurrencyHelp": "שלב הפועל ביותר המטפלים מזמן אחד שירבים לשלח הודעות בתקופה יחידה.",
"settings.performance.maxErrThreshold": "רמת ה-שגיא המרבית",
"settings.performance.maxErrThresholdHelp": "מספר השגיאות (יכולות להיות: תקיעות בפעילות SMTP במשך הזמן שנמצאים) שההפעלה המתקיימת נותנת להן עד לסיום כדי שתתפוס עבודה או תערוך ידנית. הגדרת 0 מבטלת את ההשהיה לעניין.",
"settings.performance.messageRate": "צורת הודעה",
"settings.performance.messageRateHelp": "מספר הודעות מירבי היוצאות לשניה לפועל הבודד בפעם, בנקודה בתוך שניה. אם ביצועים אוטומטיים קיימים עם סייונים בקיבול הטכנולוגי המקצועי, במידה בהישג יעיל מספר הודעות, הודעות executived במהירות סופית שלא הומצאו מעגל הגבול נכשל.",
"settings.performance.name": "ביצועים",
"settings.performance.slidingWindow": "הפעלת הגבלת חלון המסגת",
"settings.performance.slidingWindowDuration": "זמן",
"settings.performance.slidingWindowDurationHelp": "משך התקופה שבה יחידות המסגת פעילות (m לדקה, h לשעה).",
"settings.performance.slidingWindowHelp": "הגבל את כמות ההודעות הפועלות בזמן מוגבל. בהגעה לגבול, ההודעות יעצרו משליחה עד לניקוי התקופה.",
"settings.performance.slidingWindowRate": "מספר כותרות מקסימלי",
"settings.performance.slidingWindowRateHelp": "הגבלת מספר ההודעות שנשלחות בתאוריה בזמן מינון התקופה.",
"settings.privacy.allowBlocklist": "אישור שמירת אפשורית ל-Blocklisting",
"settings.privacy.allowBlocklistHelp": "ניתן למנויים להפסיק את כל קבלת הדואר האלקטרוני ולמסמן את עצמם כבלקות מתפוצת?",
"settings.privacy.allowExport": "אישור בחידוש",
"settings.privacy.allowExportHelp": "ניתן למנויים ליצור ייצוא של הנתונים שנאספו עליהם?",
"settings.privacy.allowPrefs": "אישור שינויים של בחירות",
"settings.privacy.allowPrefsHelp": "ניתן למנויים לשתף פעולה בשינוי בחירות כמו שמותיהם ורישומי המנויים הרבים.",
"settings.privacy.allowWipe": "אישור מחיקה",
"settings.privacy.allowWipeHelp": "ניתן למנויים למחוק את עצמם כולל מינויים וכל הנתונים הקשורים להם ממסד הנתונים. תוספות חישוב גם מסירות הודעות וחיצונית בזמו שנשארו (ללא subscriber משוייך אליהם) בזמן מדידת נתונים כדי שלא יתפקעו נתונים וניתוחים.",
"settings.privacy.domainBlocklist": "רשימת החסימה",
"settings.privacy.domainBlocklistHelp": "כתובות דואר אלקטרוני באמצעות שמן נאסר על הרשות להרשים. שמות התחומים יבשים על כל שורה. לדוגמה: somesite.com",
"settings.privacy.individualSubTracking": "מעקב אישי של המנויים",
"settings.privacy.individualSubTrackingHelp": "רישום תצורה יחידה למוניטים השליחים ולחיצה. בתיבת סימונים שיגורה, המודולים ימשיכו כאב צמיחה גבול תצורה יחידה.",
"settings.privacy.listUnsubHeader": "כלול את הכותרת 'הרשם לרשימה' ב־'List-Unsubscribe'",
"settings.privacy.listUnsubHeaderHelp": "כותרות המערכת שמאפשרות ללקוחות הדואר האלקטרוני ללחוץ לביטול הרישום.",
"settings.privacy.name": "פרטיות",
"settings.privacy.recordOptinIP": "תצורת דין רישום IP הפעילה",
"settings.privacy.recordOptinIPHelp": "תיחום כתובת ה־IP של רישום הפעילה החזקה במאפייני המנוי.",
"settings.restart": "הפעלה מחדש",
"settings.security.captchaKey": "מפתח אתר של hCaptcha.com",
"settings.security.captchaKeyHelp": "אין להתרשם הפעלה על מנת לקבל את מפתח המקוד והסוד שלך.",
"settings.security.captchaSecret": "סוד מאיש הגזיון",
"settings.security.enableCaptcha": "הפעל קאפצ׳ה",
"settings.security.enableCaptchaHelp": "הפעלת CAPTCHA על טופס ההרשמה הציבורי.",
"settings.security.name": "אבטחה",
"settings.smtp.customHeaders": "כותרות מותאמות אישית",
"settings.smtp.customHeadersHelp": "מערך אופציונלי של כותרות הדואר האלקטרוני הנרשמות בכל הודעה הנשלחת מתוך השרת הזה. לדוגמה: [{\"X-Custom\": \"ערך\"}, {\"X-Custom2\": \"ערך\"}]",
"settings.smtp.enabled": "מופעל",
"settings.smtp.heloHost": "שם מארח HELO",
"settings.smtp.heloHostHelp": "אופציונלי. חלק מהשרתים בשימוש החייבים רשומת שמות ממשלה בשם המארח. הדיוק של MH גולל HELO משומש.",
"settings.smtp.name": "SMTP",
"settings.smtp.retries": "ניסיונות повторы",
"settings.smtp.retriesHelp": "מספר הניסיונות בכשל הודעה.",
"settings.smtp.sendTest": "שלח אימייל",
"settings.smtp.setCustomHeaders": "ערך כותרות מותאם אישית",
"settings.smtp.testConnection": "בדוק חיבור",
"settings.smtp.testEnterEmail": "הזן סיסמא לבדיקה",
"settings.smtp.toEmail": "לכתובת",
"settings.title": "הגדרות",
"settings.updateAvailable": "עדכון חדש {version} זמין.",
"subscribers.advancedQuery": "מתקדם",
"subscribers.advancedQueryHelp": "הביטוי הדו־לשוני הוא להשתמש בביטוי SQL חלקיאָני לחיפוש אחריות במאפיינים בעלי חיפוש מתקדם.",
"subscribers.attribs": "מאפיינים",
"subscribers.attribsHelp": "האטריביוטים מוגדרים כמפתח JSON, לדוגמה:",
"subscribers.blocklistedHelp": "מנויים מהות מעוניינים באימייל שום גבול?",
"subscribers.confirmBlocklist": "שמירה ל- {num} מנויים ברשימה השחורה?",
"subscribers.confirmDelete": "מחיקה של {num} מנויים?",
"subscribers.confirmExport": "ייצוא של {num} מנויים?",
"subscribers.domainBlocklisted": "שם התחום של האימייל ניכר ברשימה השחורה.",
"subscribers.downloadData": "הורדת נתונים",
"subscribers.email": "כתובת אימייל",
"subscribers.emailExists": "כתובת האימייל קיימת.",
"subscribers.errorBlocklisting": "שגיאה בשמירת מנויים ברשימה השחורה: {error}",
"subscribers.errorNoIDs": "לא ניתנו מזהה.",
"subscribers.errorNoListsGiven": "לא ניתנו רשימות.",
"subscribers.errorPreparingQuery": "אירעה שגיאה בהכנת השאילתה של המנויים: {error}",
"subscribers.errorSendingOptin": "אירעה שגיאה בשליחת האישור של הרישום.",
"subscribers.export": "ייצוא",
"subscribers.invalidAction": "פעולה לא חוקית.",
"subscribers.invalidEmail": "אימייל לא חוקי.",
"subscribers.invalidJSON": "JSON לא תקין במאפיינים.",
"subscribers.invalidName": "שם לא חוקי.",
"subscribers.listChangeApplied": "השינוי הוחל ברשימה.",
"subscribers.lists": "רשימות",
"subscribers.listsHelp": "לא ניתן להסיר רשימות שממדו את עצמם.",
"subscribers.listsPlaceholder": "רשימות לרישום",
"subscribers.manageLists": "ניהול רשימות",
"subscribers.markUnsubscribed": "סמן כלא מנוי",
"subscribers.newSubscriber": "מנוי חדש",
"subscribers.numSelected": "נבחרו {num} מנויים",
"subscribers.optinSubject": "אישור הרשמה",
"subscribers.preconfirm": "אשר מנויים מראש",
"subscribers.preconfirmHelp": "אל תשלח הודעת אימייל לאישור ההצטרפות וסמן את כל המנויים כ׳רשומים׳.",
"subscribers.query": "שאילתה",
"subscribers.queryPlaceholder": "כתובת אימייל או שם",
"subscribers.reset": "איפוס",
"subscribers.selectAll": "בחר הכל {num}",
"subscribers.sendOptinConfirm": "שלח אישור הצטרפות",
"subscribers.sentOptinConfirm": "אישור הצטרפות נשלח",
"subscribers.status.blocklisted": "ברשימת החסימה",
"subscribers.status.confirmed": "מאושר",
"subscribers.status.enabled": "מופעל",
"subscribers.status.subscribed": "נרשם",
"subscribers.status.unconfirmed": "לא מאושר",
"subscribers.status.unsubscribed": "לא נרשם",
"subscribers.subscribersDeleted": "{num} רשומים נמחקו",
"templates.cantDeleteDefault": "לא ניתן למחוק תבנית לא קיימת או ברירת מחדל",
"templates.default": "ברירת מחדל",
"templates.dummyName": "קמפיין דמה",
"templates.dummySubject": "נושא קמפיין דמה",
"templates.errorCompiling": "שגיאה בהידור התבנית: {error}",
"templates.errorRendering": "שגיאה בהצגת הודעה: {error}",
"templates.fieldInvalidName": "אורך לא חוקי עבור שם.",
"templates.makeDefault": "הגדר כברירת מחדל",
"templates.newTemplate": "תבנית חדשה",
"templates.placeholderHelp": "התו מילוי תחבירי {placeholder} יש להופיע פעם יחידה בתבנית.",
"templates.preview": "תצוגה מקדימה",
"templates.rawHTML": "HTML גולמי",
"templates.subject": "נושא",
"users.login": "התחברות",
"users.logout": "התנתקות"
}

View file

@ -362,6 +362,7 @@
"settings.bounces.delete": "Törlés",
"settings.bounces.enable": "Visszapattanások feldolgozása",
"settings.bounces.enableMailbox": "Visszapattanó postafiók",
"settings.bounces.enablePostmark": "Postmark",
"settings.bounces.enableSES": "SES",
"settings.bounces.enableSendgrid": "SendGrid",
"settings.bounces.enableWebhooks": "Visszapattanó webhook",
@ -372,6 +373,9 @@
"settings.bounces.invalidScanInterval": "Az ellenőrzés gyakorisága 1 percnél nagyobb kell legyen.",
"settings.bounces.name": "Visszapattanók",
"settings.bounces.none": "Nincs",
"settings.bounces.postmarkPassword": "Postmark Password",
"settings.bounces.postmarkUsername": "Postmark Username",
"settings.bounces.postmarkUsernameHelp": "Postmark allows you to enable basic authorization for webhooks. Make sure to enter the same credentials here and in your Postmark webhook settings.",
"settings.bounces.scanInterval": "Ellenőrzés gyakorisága",
"settings.bounces.scanIntervalHelp": "A visszapattanó e-mailek ellenőrzésének gyakorisága. (s: másodperc, m: perc)",
"settings.bounces.sendgridKey": "Kulcs",

View file

@ -363,6 +363,7 @@
"settings.bounces.delete": "Cancella",
"settings.bounces.enable": "Abilita il processamento dei rimbalzi",
"settings.bounces.enableMailbox": "Abilita la cassella di posta per i rimbalzi",
"settings.bounces.enablePostmark": "Attiva Postmark",
"settings.bounces.enableSES": "Attiva SES",
"settings.bounces.enableSendgrid": "Attiva SendGrid",
"settings.bounces.enableWebhooks": "Attiva bounce webhooks",
@ -373,6 +374,9 @@
"settings.bounces.invalidScanInterval": "L'intervallo di scansione dei rimbalzi deve essere di almeno 1 minuto.",
"settings.bounces.name": "Rimbalzi",
"settings.bounces.none": "Nessuno",
"settings.bounces.postmarkPassword": "Postmark Password",
"settings.bounces.postmarkUsername": "Postmark Username",
"settings.bounces.postmarkUsernameHelp": "Postmark allows you to enable basic authorization for webhooks. Make sure to enter the same credentials here and in your Postmark webhook settings.",
"settings.bounces.scanInterval": "Intervallo di scansione",
"settings.bounces.scanIntervalHelp": "Intervallo con cui la mailbox di rimbalzo deve essere scansionata per i rimbalzi (s per secondo, m per minuto).",
"settings.bounces.sendgridKey": "Chiave SendGrid",
@ -438,7 +442,7 @@
"settings.media.s3.region": "Regione",
"settings.media.s3.secret": "Segreto d'acceso AWS",
"settings.media.s3.uploadExpiry": "Caricamento scaduto",
"settings.media.s3.uploadExpiryHelp": "(Facoltativo) Specifica il TTL (in secondi) per l'URL predefinito generato. Applicabile solo per i buckets privati (s, m, h, d per i secondi, minuti, ore e giorni).",
"settings.media.s3.uploadExpiryHelp": "(Facoltativo) Specifica il TTL per l'URL predefinito generato. Applicabile solo per i buckets privati (s, m, h, d per i secondi, minuti, ore e giorni).",
"settings.media.s3.url": "URL backend S3",
"settings.media.s3.urlHelp": "Modifficare soltanto se stai utilizando uno backend compatibile con S3, ad essempio Minio.",
"settings.media.title": "Caricamento dei media",

View file

@ -363,6 +363,7 @@
"settings.bounces.delete": "削除",
"settings.bounces.enable": "バウンス処理を有効にする",
"settings.bounces.enableMailbox": "バウンスメールボックスを有効にする",
"settings.bounces.enablePostmark": "Postmarkを有効にする",
"settings.bounces.enableSES": "SESを有効にする",
"settings.bounces.enableSendgrid": "SendGridを有効にする",
"settings.bounces.enableWebhooks": "バウンスウェブフックを有効にする",
@ -373,6 +374,9 @@
"settings.bounces.invalidScanInterval": "バウンススキャン間隔は最低1分。",
"settings.bounces.name": "バウンス",
"settings.bounces.none": "なし",
"settings.bounces.postmarkPassword": "Postmark Password",
"settings.bounces.postmarkUsername": "Postmark Username",
"settings.bounces.postmarkUsernameHelp": "Postmark allows you to enable basic authorization for webhooks. Make sure to enter the same credentials here and in your Postmark webhook settings.",
"settings.bounces.scanInterval": "スキャン間隔",
"settings.bounces.scanIntervalHelp": "バウンスメールボックスのバウンスをスキャンする間隔 (秒はs,分はm).",
"settings.bounces.sendgridKey": "SendGridキー",
@ -438,7 +442,7 @@
"settings.media.s3.region": "地域",
"settings.media.s3.secret": "AWS シークレットアクセス",
"settings.media.s3.uploadExpiry": "アップロードの有効期限",
"settings.media.s3.uploadExpiryHelp": "(任意) 生成されたプリサインURLのTTLを(秒で)特定。プライベートバケットのみ適用可能。 (秒はs, 分はm, 時間はh, 日はd).",
"settings.media.s3.uploadExpiryHelp": "(任意) 生成されたプリサインURLのTTLを 特定。プライベートバケットのみ適用可能。 (秒はs, 分はm, 時間はh, 日はd).",
"settings.media.s3.url": "S3バックエンドURL",
"settings.media.s3.urlHelp": "MinioのようなS3互換のカスタムバックエンドを使用する場合のみ変更。",
"settings.media.title": "メディアアップロード",

View file

@ -362,6 +362,7 @@
"settings.bounces.delete": "നീക്കം ചെയ്യുക",
"settings.bounces.enable": "ബൗൺസ് പ്രോസസ്സിംഗ് പ്രവർത്തനക്ഷമമാക്കുക",
"settings.bounces.enableMailbox": "ബൗൺസ് മെയിൽബോക്സ് പ്രവർത്തനക്ഷമമാക്കുക",
"settings.bounces.enablePostmark": "Postmark പ്രവർത്തനക്ഷമമാക്കുക",
"settings.bounces.enableSES": "SES പ്രവർത്തനക്ഷമമാക്കുക",
"settings.bounces.enableSendgrid": "SendGrid പ്രവർത്തനക്ഷമമാക്കുക",
"settings.bounces.enableWebhooks": "ബൗൺസ് വെബ്‌ഹുക്കുകൾ പ്രവർത്തനക്ഷമമാക്കുക",
@ -372,6 +373,9 @@
"settings.bounces.invalidScanInterval": "ബൗൺസ് സ്കാൻ ചെയ്യാനുള്ള ഏറ്റവും കുറഞ്ഞ ഇടവേള 1 മിനിറ്റായിരിക്കണം.",
"settings.bounces.name": "ബൗൺസുകൾ",
"settings.bounces.none": "ഒന്നുമില്ല",
"settings.bounces.postmarkPassword": "Postmark Password",
"settings.bounces.postmarkUsername": "Postmark Username",
"settings.bounces.postmarkUsernameHelp": "Postmark allows you to enable basic authorization for webhooks. Make sure to enter the same credentials here and in your Postmark webhook settings.",
"settings.bounces.scanInterval": "സ്കാൻ ചെയ്യാനുള്ള ഇടവേള",
"settings.bounces.scanIntervalHelp": "ബൗൺസ് മെയിൽബോക്‌സ് സ്‌കാൻ ചെയ്യേണ്ട ഇടവേള (സെക്കൻഡിന് s, മിനിറ്റിന് m).",
"settings.bounces.sendgridKey": "SendGrid കീ",

View file

@ -362,6 +362,7 @@
"settings.bounces.delete": "Verwijder",
"settings.bounces.enable": "Bounce processing inschakelen",
"settings.bounces.enableMailbox": "Bounce mailbox inschakelen",
"settings.bounces.enablePostmark": "Postmark inschakelen",
"settings.bounces.enableSES": "SES inschakelen",
"settings.bounces.enableSendgrid": "SendGrid inschakelen",
"settings.bounces.enableWebhooks": "Bounce webhooks inschakelen",
@ -372,6 +373,9 @@
"settings.bounces.invalidScanInterval": "Bounce scan interval moet minstens 1 minuut zijn.",
"settings.bounces.name": "Bounces",
"settings.bounces.none": "Geen",
"settings.bounces.postmarkPassword": "Postmark Password",
"settings.bounces.postmarkUsername": "Postmark Username",
"settings.bounces.postmarkUsernameHelp": "Postmark allows you to enable basic authorization for webhooks. Make sure to enter the same credentials here and in your Postmark webhook settings.",
"settings.bounces.scanInterval": "Scaninterval",
"settings.bounces.scanIntervalHelp": "Interval waarin de bounce mailbox gescanned moet worden voor bounces (s voor seconden, m voor minuten).",
"settings.bounces.sendgridKey": "SendGrid sleutel",
@ -437,7 +441,7 @@
"settings.media.s3.region": "Regio",
"settings.media.s3.secret": "AWS access secret",
"settings.media.s3.uploadExpiry": "Upload vervaldatum",
"settings.media.s3.uploadExpiryHelp": "(Optioneel) TTL (in seconden) voor de gegenereerde, getekende URL. Enkel van toepassing voor privébuckets (s, m, h, d voor seconden, minuten, uren, dagen).",
"settings.media.s3.uploadExpiryHelp": "(Optioneel) TTL voor de gegenereerde, getekende URL. Enkel van toepassing voor privébuckets (s, m, h, d voor seconden, minuten, uren, dagen).",
"settings.media.s3.url": "S3 backend URL",
"settings.media.s3.urlHelp": "Enkel veranderen als je een custom S3-compatibele backend gebruikt zoals Minio.",
"settings.media.title": "Media uploads",

View file

@ -362,6 +362,7 @@
"settings.bounces.delete": "Usuń",
"settings.bounces.enable": "Włącz procesowanie odbić",
"settings.bounces.enableMailbox": "Włącz skrzynkę pocztową z odbiciami",
"settings.bounces.enablePostmark": "Włącz Postmark",
"settings.bounces.enableSES": "Włącz SES",
"settings.bounces.enableSendgrid": "Włącz SendGrid",
"settings.bounces.enableWebhooks": "Włącz webhooki odbić",
@ -372,6 +373,9 @@
"settings.bounces.invalidScanInterval": "Interwał czasu powinien być minimum 1 minuta.",
"settings.bounces.name": "Odbicia",
"settings.bounces.none": "Brak",
"settings.bounces.postmarkPassword": "Postmark Password",
"settings.bounces.postmarkUsername": "Postmark Username",
"settings.bounces.postmarkUsernameHelp": "Postmark allows you to enable basic authorization for webhooks. Make sure to enter the same credentials here and in your Postmark webhook settings.",
"settings.bounces.scanInterval": "Interwał skanowania",
"settings.bounces.scanIntervalHelp": "Interwał czasu przeszukiwania skrzynki w poszkukiwaniu odbić (s dla sekund, m dla minut).",
"settings.bounces.sendgridKey": "Klucz SendGrid",
@ -437,7 +441,7 @@
"settings.media.s3.region": "Region",
"settings.media.s3.secret": "Sekret dostępu AWS",
"settings.media.s3.uploadExpiry": "Wygaśnięcie przesyłania",
"settings.media.s3.uploadExpiryHelp": "(Opcjonalne) Zdefiniuj TTL (w sekundach) dla wygenerowanego podpisanego URL. Tylko dla prywatnych komór (bucketów) (s, m, h, d dla sekund, minut, godzin, dni).",
"settings.media.s3.uploadExpiryHelp": "(Opcjonalne) Zdefiniuj TTL dla wygenerowanego podpisanego URL. Tylko dla prywatnych komór (bucketów) (s, m, h, d dla sekund, minut, godzin, dni).",
"settings.media.s3.url": "Adres URL dla S3 backend",
"settings.media.s3.urlHelp": "Zmień tylko, jeśli używasz niestandardowego backendu kompatybilnego z S3, takiego jak Minio.",
"settings.media.title": "Wysyłka mediów",

View file

@ -362,6 +362,7 @@
"settings.bounces.delete": "Deletar",
"settings.bounces.enable": "Ativar processamento de bounce",
"settings.bounces.enableMailbox": "Ativar caixa de email de bounce",
"settings.bounces.enablePostmark": "Ativar Postmark",
"settings.bounces.enableSES": "Ativar SES",
"settings.bounces.enableSendgrid": "Ativar SendGrid",
"settings.bounces.enableWebhooks": "Ativar webhooks bounce",
@ -372,6 +373,9 @@
"settings.bounces.invalidScanInterval": "Intervalo de escaneamento de Bounce deve ser no mínimo 1 minuto.",
"settings.bounces.name": "Rejeições",
"settings.bounces.none": "Nenhuma",
"settings.bounces.postmarkPassword": "Postmark Password",
"settings.bounces.postmarkUsername": "Postmark Username",
"settings.bounces.postmarkUsernameHelp": "Postmark allows you to enable basic authorization for webhooks. Make sure to enter the same credentials here and in your Postmark webhook settings.",
"settings.bounces.scanInterval": "Intervalo de Escaneamento",
"settings.bounces.scanIntervalHelp": "Intervalo no qual a caixa de emails de bounce deve ser escaneada por bounces (s para segundo, m para minuto).",
"settings.bounces.sendgridKey": "Key SendGrid",
@ -437,7 +441,7 @@
"settings.media.s3.region": "Região",
"settings.media.s3.secret": "Segredo de acesso AWS",
"settings.media.s3.uploadExpiry": "Expiração do arquivo enviado",
"settings.media.s3.uploadExpiryHelp": "(Opcional) Especificar TTL (em segundos) para a URL pré-assinada gerada. Apenas aplicável para buckets privados (s, m, h, d para segundos, minutos, horas e dias).",
"settings.media.s3.uploadExpiryHelp": "(Opcional) Especificar TTL para a URL pré-assinada gerada. Apenas aplicável para buckets privados (s, m, h, d para segundos, minutos, horas e dias).",
"settings.media.s3.url": "URL backend do S3",
"settings.media.s3.urlHelp": "Altere apenas se usar um backnd customizado compatível com S3, como o Minio.",
"settings.media.title": "Envios de mídias",

View file

@ -362,6 +362,7 @@
"settings.bounces.delete": "Eliminar",
"settings.bounces.enable": "Ligar processamento de bounces",
"settings.bounces.enableMailbox": "Ligar caixa de correio de bounces",
"settings.bounces.enablePostmark": "Ligar Postmark",
"settings.bounces.enableSES": "Ligar SES",
"settings.bounces.enableSendgrid": "Ligar SendGrid",
"settings.bounces.enableWebhooks": "Ligar webhooks de bounces",
@ -372,6 +373,9 @@
"settings.bounces.invalidScanInterval": "Intervalo de procura de bounces deve ser, no mínimo, 1 minuto.",
"settings.bounces.name": "Rejeições",
"settings.bounces.none": "Nenhum",
"settings.bounces.postmarkPassword": "Postmark Password",
"settings.bounces.postmarkUsername": "Postmark Username",
"settings.bounces.postmarkUsernameHelp": "Postmark allows you to enable basic authorization for webhooks. Make sure to enter the same credentials here and in your Postmark webhook settings.",
"settings.bounces.scanInterval": "Intervalo de procura",
"settings.bounces.scanIntervalHelp": "Intervalo de procura de bounces na caixa de correio de bounces (s para segundos, m para minutos).",
"settings.bounces.sendgridKey": "Chave do SendGrid",
@ -437,7 +441,7 @@
"settings.media.s3.region": "Região",
"settings.media.s3.secret": "Segredo de acesso AWS",
"settings.media.s3.uploadExpiry": "Validade do upload",
"settings.media.s3.uploadExpiryHelp": "(Opcional) Especifica TTL (em segundos) para o URL pré-assinado gerado. Apenas aplicável a buckets privados (s, m, h, d para segundos, minutos, horas e dias).",
"settings.media.s3.uploadExpiryHelp": "(Opcional) Especifica TTL para o URL pré-assinado gerado. Apenas aplicável a buckets privados (s, m, h, d para segundos, minutos, horas e dias).",
"settings.media.s3.url": "URL do backend S3",
"settings.media.s3.urlHelp": "Apenas alterar quando um backend customizado compatível com S3, como Minio, está em uso.",
"settings.media.title": "Upload de mídia",

View file

@ -363,6 +363,7 @@
"settings.bounces.delete": "settings.bounces.delete",
"settings.bounces.enable": "Activați procesarea săririi",
"settings.bounces.enableMailbox": "Activați cutia poștală de respingere",
"settings.bounces.enablePostmark": "Activați Postmark",
"settings.bounces.enableSES": "Activați SES",
"settings.bounces.enableSendgrid": "Activați SendGrid",
"settings.bounces.enableWebhooks": "Activați webhooks bounce",
@ -373,6 +374,9 @@
"settings.bounces.invalidScanInterval": "Intervalul de scanare a săririi ar trebui să fie de minim 1 minut.",
"settings.bounces.name": "Neachitate",
"settings.bounces.none": "Nimic",
"settings.bounces.postmarkPassword": "Postmark Password",
"settings.bounces.postmarkUsername": "Postmark Username",
"settings.bounces.postmarkUsernameHelp": "Postmark allows you to enable basic authorization for webhooks. Make sure to enter the same credentials here and in your Postmark webhook settings.",
"settings.bounces.scanInterval": "Interval de scanare",
"settings.bounces.scanIntervalHelp": "Interval la care căsuța poștală de respingeri trebuie scanată pentru respingeri (s pentru secunde, m pentru minut).",
"settings.bounces.sendgridKey": "SendGrid cheie",
@ -438,7 +442,7 @@
"settings.media.s3.region": "Regiune",
"settings.media.s3.secret": "Secret de acces AWS",
"settings.media.s3.uploadExpiry": "Încărcarea expirării",
"settings.media.s3.uploadExpiryHelp": "(Opțional) Specifică TTL (în secunde) pentru adresa URL presemnată generată. Se aplică numai pentru bucketurile private (s, m, h, d pentru secunde, minute, ore, zile).",
"settings.media.s3.uploadExpiryHelp": "(Opțional) Specifică TTL pentru adresa URL presemnată generată. Se aplică numai pentru bucketurile private (s, m, h, d pentru secunde, minute, ore, zile).",
"settings.media.s3.url": "S3 backend URL-ul",
"settings.media.s3.urlHelp": "Schimbă numai dacă folosești un backend personalizat compatibil S3, cum ar fi Minio.",
"settings.media.title": "Încărcări media",

View file

@ -362,6 +362,7 @@
"settings.bounces.delete": "Удалить",
"settings.bounces.enable": "Включить обработку отказов",
"settings.bounces.enableMailbox": "Включить почтовый ящик с отскоком",
"settings.bounces.enablePostmark": "Включить Postmark",
"settings.bounces.enableSES": "Включить SES",
"settings.bounces.enableSendgrid": "Включить SendGrid",
"settings.bounces.enableWebhooks": "Включить веб-крючки отскока",
@ -372,6 +373,9 @@
"settings.bounces.invalidScanInterval": "Интервал сканирования скачков должен составлять минимум 1 минуту.",
"settings.bounces.name": "Отскоки",
"settings.bounces.none": "Нет",
"settings.bounces.postmarkPassword": "Postmark Password",
"settings.bounces.postmarkUsername": "Postmark Username",
"settings.bounces.postmarkUsernameHelp": "Postmark allows you to enable basic authorization for webhooks. Make sure to enter the same credentials here and in your Postmark webhook settings.",
"settings.bounces.scanInterval": "Интервал сканирования",
"settings.bounces.scanIntervalHelp": "Интервал, с которым почтовый ящик должен сканироваться на наличие отказов (с - секунда, м - минута).",
"settings.bounces.sendgridKey": "Ключ SendGrid",
@ -437,7 +441,7 @@
"settings.media.s3.region": "Регион",
"settings.media.s3.secret": "Секретаня фраза AWS",
"settings.media.s3.uploadExpiry": "Срок жизни выгрузки",
"settings.media.s3.uploadExpiryHelp": "(Необязательно) Укажите TTL (в секундах) сгенерированного подписанного URL. Применимо только для приватных bucket (s, m, h, d соответствует секундам, минутам, часам и дням).",
"settings.media.s3.uploadExpiryHelp": "(Необязательно) Укажите TTL сгенерированного подписанного URL. Применимо только для приватных bucket (s, m, h, d соответствует секундам, минутам, часам и дням).",
"settings.media.s3.url": "URL бэкенда S3",
"settings.media.s3.urlHelp": "Измените только в случае использования бэкенда, совместимого с S3, например, Minio.",
"settings.media.title": "Выгрузки медиа",

View file

@ -362,6 +362,7 @@
"settings.bounces.delete": "Delete",
"settings.bounces.enable": "Enable bounce processing",
"settings.bounces.enableMailbox": "Enable bounce mailbox",
"settings.bounces.enablePostmark": "Enable Postmark",
"settings.bounces.enableSES": "Enable SES",
"settings.bounces.enableSendgrid": "Enable SendGrid",
"settings.bounces.enableWebhooks": "Enable bounce webhooks",
@ -372,6 +373,9 @@
"settings.bounces.invalidScanInterval": "Bounce scan interval should be minimum 1 minute.",
"settings.bounces.name": "Bounces",
"settings.bounces.none": "None",
"settings.bounces.postmarkPassword": "Postmark Password",
"settings.bounces.postmarkUsername": "Postmark Username",
"settings.bounces.postmarkUsernameHelp": "Postmark allows you to enable basic authorization for webhooks. Make sure to enter the same credentials here and in your Postmark webhook settings.",
"settings.bounces.scanInterval": "Scan interval",
"settings.bounces.scanIntervalHelp": "Interval at which the bounce mailbox should be scanned for bounces (s for second, m for minute).",
"settings.bounces.sendgridKey": "SendGrid Key",
@ -437,7 +441,7 @@
"settings.media.s3.region": "Region",
"settings.media.s3.secret": "AWS access secret",
"settings.media.s3.uploadExpiry": "Upload expiry",
"settings.media.s3.uploadExpiryHelp": "(Optional) Specify TTL (in seconds) for the generated presigned URL. Only applicable for private buckets (s, m, h, d for seconds, minutes, hours, days).",
"settings.media.s3.uploadExpiryHelp": "(Optional) Specify TTL for the generated presigned URL. Only applicable for private buckets (s, m, h, d for seconds, minutes, hours, days).",
"settings.media.s3.url": "S3 backend URL",
"settings.media.s3.urlHelp": "Only change if using a custom S3 compatible backend like Minio.",
"settings.media.title": "Media uploads",

View file

@ -1,5 +1,5 @@
{
"_.code": "sk-sk",
"_.code": "sk",
"_.name": "slovenčina (sk)",
"admin.errorMarshallingConfig": "Chyba konfigurácie zaradenia: {error}",
"analytics.count": "Počet",
@ -362,6 +362,7 @@
"settings.bounces.delete": "Odstrániť",
"settings.bounces.enable": "Zapnúť spracovanie nedoručiteľných",
"settings.bounces.enableMailbox": "Povoliť poštovú schránku pre nedoručiteľných",
"settings.bounces.enablePostmark": "Zapnúť Postmark",
"settings.bounces.enableSES": "Zapnúť SES",
"settings.bounces.enableSendgrid": "Zapnúť SendGrid",
"settings.bounces.enableWebhooks": "Zapnúť webhooky pre nedoručiteľné",
@ -372,6 +373,9 @@
"settings.bounces.invalidScanInterval": "Interval kontroly nedoručiteľných by mal byť minimálne 1 minúta.",
"settings.bounces.name": "Nedoručiteľné",
"settings.bounces.none": "Žiadne",
"settings.bounces.postmarkPassword": "Postmark Password",
"settings.bounces.postmarkUsername": "Postmark Username",
"settings.bounces.postmarkUsernameHelp": "Postmark allows you to enable basic authorization for webhooks. Make sure to enter the same credentials here and in your Postmark webhook settings.",
"settings.bounces.scanInterval": "Interval kontroly",
"settings.bounces.scanIntervalHelp": "Interval, v ktorom by se poštová schránka nedoručiteľných mala kontrolovať na nové správy (s - sekundy, m - minúty).",
"settings.bounces.sendgridKey": "Kľúč SendGrid",
@ -437,7 +441,7 @@
"settings.media.s3.region": "Región",
"settings.media.s3.secret": "Prístupové tajomstvo AWS",
"settings.media.s3.uploadExpiry": "Expirácia nahrávania",
"settings.media.s3.uploadExpiryHelp": "(Voliteľné) Uveďte TTL (v sekundách) pre generovanú vopred podpísanú adresu URL. Vhodné len pre súkromné buckety (s, m, h, d pre sekundy, minúty, hodiny, dni).",
"settings.media.s3.uploadExpiryHelp": "(Voliteľné) Uveďte TTL pre generovanú vopred podpísanú adresu URL. Vhodné len pre súkromné buckety (s, m, h, d pre sekundy, minúty, hodiny, dni).",
"settings.media.s3.url": "Adresa URL pre S3 backend",
"settings.media.s3.urlHelp": "Dá sa nastaviť ak používateľ S3 kompatibilný backend ako napr. Minio.",
"settings.media.title": "Nahrávanie médií",

View file

@ -363,6 +363,7 @@
"settings.bounces.delete": "Sil",
"settings.bounces.enable": "Sıçrama işlemeyi etkinleştirin",
"settings.bounces.enableMailbox": "Geri dönen posta kutusunu etkinleştirin",
"settings.bounces.enablePostmark": "Postmark'i etkinleştirin",
"settings.bounces.enableSES": "SES'i etkinleştirin",
"settings.bounces.enableSendgrid": "SendGrid'i etkinleştirin",
"settings.bounces.enableWebhooks": "Sıçrama web kancalarını etkinleştirin",
@ -373,6 +374,9 @@
"settings.bounces.invalidScanInterval": "Sıçrama tarama aralığı en az 1 dakika olmalıdır.",
"settings.bounces.name": "Sıçramalar",
"settings.bounces.none": "Hiçbiri",
"settings.bounces.postmarkPassword": "Postmark Password",
"settings.bounces.postmarkUsername": "Postmark Username",
"settings.bounces.postmarkUsernameHelp": "Postmark allows you to enable basic authorization for webhooks. Make sure to enter the same credentials here and in your Postmark webhook settings.",
"settings.bounces.scanInterval": "Tarama aralığı",
"settings.bounces.scanIntervalHelp": "Sıçrama posta kutusunun sıçramalar için taranması gereken aralık (saniye için s, dakika için m).",
"settings.bounces.sendgridKey": "SendGrid Anahtarı",
@ -438,7 +442,7 @@
"settings.media.s3.region": "Bölge",
"settings.media.s3.secret": "AWS erişim parolası(secret)",
"settings.media.s3.uploadExpiry": "Yükleme sona erme",
"settings.media.s3.uploadExpiryHelp": "(İsteğe bağlı) Oluşturulan önceden imzalanmış URL için TTL'yi (saniye cinsinden) belirtin. Yalnızca özel paketler için geçerlidir (saniye, dakika, saat, gün için s, m, h, d).",
"settings.media.s3.uploadExpiryHelp": "(İsteğe bağlı) Oluşturulan önceden imzalanmış URL için TTL'yi belirtin. Yalnızca özel paketler için geçerlidir (saniye, dakika, saat, gün için s, m, h, d).",
"settings.media.s3.url": "S3 arka uç URL'si",
"settings.media.s3.urlHelp": "Yalnızca Minio gibi özel bir S3 uyumlu arka uç kullanıyorsanız değiştirin.",
"settings.media.title": "Medya yüklemeleri",

View file

@ -363,6 +363,7 @@
"settings.bounces.delete": "Xóa",
"settings.bounces.enable": "Bật xử lý số trang không truy cập",
"settings.bounces.enableMailbox": "Bật hộp thư bị trả lại",
"settings.bounces.enablePostmark": "Bật Postmark",
"settings.bounces.enableSES": "Bật SES",
"settings.bounces.enableSendgrid": "Bật SendGrid",
"settings.bounces.enableWebhooks": "Bật webhook bị trả lại",
@ -373,6 +374,9 @@
"settings.bounces.invalidScanInterval": "Khoảng thời gian quét bị trả lại phải tối thiểu là 1 phút.",
"settings.bounces.name": "Bị trả lại",
"settings.bounces.none": "Không có",
"settings.bounces.postmarkPassword": "Postmark Password",
"settings.bounces.postmarkUsername": "Postmark Username",
"settings.bounces.postmarkUsernameHelp": "Postmark allows you to enable basic authorization for webhooks. Make sure to enter the same credentials here and in your Postmark webhook settings.",
"settings.bounces.scanInterval": "Khoảng thời gian quét",
"settings.bounces.scanIntervalHelp": "Khoảng thời gian mà hộp thư trả lại sẽ được quét để tìm thư trả lại (s cho giây, m cho phút).",
"settings.bounces.sendgridKey": "Khóa SendGrid",
@ -438,7 +442,7 @@
"settings.media.s3.region": "Vùng",
"settings.media.s3.secret": "AWS truy cập bí mật",
"settings.media.s3.uploadExpiry": "Hết hạn tải lên",
"settings.media.s3.uploadExpiryHelp": "(Tùy chọn) Chỉ định TTL (tính bằng giây) cho URL được chỉ định trước đã tạo. Chỉ áp dụng cho các nhóm riêng (s, m, h, d cho giây, phút, giờ, ngày).",
"settings.media.s3.uploadExpiryHelp": "(Tùy chọn) Chỉ định TTL cho URL được chỉ định trước đã tạo. Chỉ áp dụng cho các nhóm riêng (s, m, h, d cho giây, phút, giờ, ngày).",
"settings.media.s3.url": "URL phụ trợ S3",
"settings.media.s3.urlHelp": "Chỉ thay đổi nếu sử dụng chương trình phụ trợ tương thích S3 tùy chỉnh như Minio.",
"settings.media.title": "Tải lên phương tiện",

View file

@ -362,6 +362,7 @@
"settings.bounces.delete": "删除",
"settings.bounces.enable": "启用退回处理",
"settings.bounces.enableMailbox": "启用退回邮箱",
"settings.bounces.enablePostmark": "启用Postmark",
"settings.bounces.enableSES": "启用SES",
"settings.bounces.enableSendgrid": "启用SendGrid",
"settings.bounces.enableWebhooks": "启用反弹webhooks",
@ -372,6 +373,9 @@
"settings.bounces.invalidScanInterval": "反弹扫描间隔应至少为 1 分钟。",
"settings.bounces.name": "反弹",
"settings.bounces.none": "无",
"settings.bounces.postmarkPassword": "Postmark Password",
"settings.bounces.postmarkUsername": "Postmark Username",
"settings.bounces.postmarkUsernameHelp": "Postmark allows you to enable basic authorization for webhooks. Make sure to enter the same credentials here and in your Postmark webhook settings.",
"settings.bounces.scanInterval": "扫描间隔",
"settings.bounces.scanIntervalHelp": "应扫描退回邮箱以查找退回邮件的时间间隔s 表示秒m 表示分钟)。",
"settings.bounces.sendgridKey": "SendGrid键",
@ -437,7 +441,7 @@
"settings.media.s3.region": "地区",
"settings.media.s3.secret": "AWS访问密钥",
"settings.media.s3.uploadExpiry": "上传到期",
"settings.media.s3.uploadExpiryHelp": "可选为生成的预签名UR 指定TTL(以秒为单位)。仅适用于私有存储桶s、m、h、d 表示秒、分钟、小时、天)。",
"settings.media.s3.uploadExpiryHelp": "可选为生成的预签名UR 指定TTL。仅适用于私有存储桶s、m、h、d 表示秒、分钟、小时、天)。",
"settings.media.s3.url": "S3后端URL",
"settings.media.s3.urlHelp": "只有在使用像Minio这样的自定义S3兼容后端时才进行更改。",
"settings.media.title": "媒体上传",

View file

@ -363,6 +363,7 @@
"settings.bounces.delete": "刪除",
"settings.bounces.enable": "啟用退回處理",
"settings.bounces.enableMailbox": "啟用退回郵箱",
"settings.bounces.enablePostmark": "啟用Postmark",
"settings.bounces.enableSES": "啟用SES",
"settings.bounces.enableSendgrid": "啟用SendGrid",
"settings.bounces.enableWebhooks": "啟用反彈webhooks",
@ -373,6 +374,9 @@
"settings.bounces.invalidScanInterval": "反彈掃描間隔應至少為1 分鐘。",
"settings.bounces.name": "反彈",
"settings.bounces.none": "無",
"settings.bounces.postmarkPassword": "Postmark Password",
"settings.bounces.postmarkUsername": "Postmark Username",
"settings.bounces.postmarkUsernameHelp": "Postmark allows you to enable basic authorization for webhooks. Make sure to enter the same credentials here and in your Postmark webhook settings.",
"settings.bounces.scanInterval": "掃描間隔",
"settings.bounces.scanIntervalHelp": "應掃描退回郵箱以查找退回郵件的時間間隔s 表示秒m 表示分鐘)。",
"settings.bounces.sendgridKey": "SendGrid鍵",
@ -438,7 +442,7 @@
"settings.media.s3.region": "地區",
"settings.media.s3.secret": "AWS訪問密鑰",
"settings.media.s3.uploadExpiry": "上傳到期",
"settings.media.s3.uploadExpiryHelp": "可選為生成的預簽名UR 指定TTL(以秒為單位)。僅適用於私有存儲桶s、m、h、d 表示秒、分鐘、小時、天)。",
"settings.media.s3.uploadExpiryHelp": "可選為生成的預簽名UR 指定TTL。僅適用於私有存儲桶s、m、h、d 表示秒、分鐘、小時、天)。",
"settings.media.s3.url": "S3後端URL",
"settings.media.s3.urlHelp": "只有在使用像Minio這樣的自定義S3兼容後端時才進行更改。",
"settings.media.title": "媒體上傳",

View file

@ -1,17 +0,0 @@
export async function defineConfig(env) {
const { default: pluginJson } = await env.$import(
'https://cdn.jsdelivr.net/gh/samuelstroschein/inlang-plugin-json@2.3.1/dist/index.js'
);
const { default: standardLintRules } = await env.$import(
'https://cdn.jsdelivr.net/gh/inlang/standard-lint-rules@2/dist/index.js'
);
return {
referenceLanguage: 'en',
plugins: [pluginJson({
pathPattern: './i18n/{language}.json',
variableReferencePattern: ["{", "}"]
}), standardLintRules()]
};
}

View file

@ -1,7 +1,7 @@
#!/usr/bin/env sh
set -eu
# Listmonk demo setup using `docker-compose`.
# Listmonk demo setup using `docker compose`.
# See https://listmonk.app/docs/installation/ for detailed installation steps.
check_dependencies() {
@ -15,15 +15,17 @@ check_dependencies() {
exit 1
fi
if ! command -v docker-compose > /dev/null; then
echo "docker-compose is not installed."
# Check for "docker compose" functionality.
if ! docker compose version > /dev/null 2>&1; then
echo "'docker compose' functionality is not available. Please update to a newer version of Docker. See https://docs.docker.com/engine/install/ for more details."
exit 1
fi
}
setup_containers() {
curl -o docker-compose.yml https://raw.githubusercontent.com/knadh/listmonk/master/docker-compose.yml
docker-compose up -d demo-db demo-app
# Use "docker compose" instead of "docker-compose"
docker compose up -d demo-db demo-app
}
show_output(){

25
install-prod.sh Normal file → Executable file
View file

@ -1,7 +1,7 @@
#!/usr/bin/env sh
set -eu
# Listmonk production setup using `docker-compose`.
# Listmonk production setup using `docker compose`.
# See https://listmonk.app/docs/installation/ for detailed installation steps.
printf '\n'
@ -10,6 +10,7 @@ RED="$(tput setaf 1 2>/dev/null || printf '')"
BLUE="$(tput setaf 4 2>/dev/null || printf '')"
GREEN="$(tput setaf 2 2>/dev/null || printf '')"
NO_COLOR="$(tput sgr0 2>/dev/null || printf '')"
SED_INPLACE=$(if [[ "$(uname)" == "Darwin" ]]; then echo "-i ''"; else echo "-i"; fi)
info() {
printf '%s\n' "${BLUE}> ${NO_COLOR} $*"
@ -38,8 +39,9 @@ check_dependencies() {
exit 1
fi
if ! exists docker-compose; then
error "docker-compose is not installed."
# Check for "docker compose" functionality.
if ! docker compose version > /dev/null 2>&1; then
echo "'docker compose' functionality is not available. Please update to a newer version of Docker. See https://docs.docker.com/engine/install/ for more details."
exit 1
fi
}
@ -47,7 +49,7 @@ check_dependencies() {
check_existing_db_volume() {
info "checking for an existing docker db volume"
if docker volume inspect listmonk_listmonk-data >/dev/null 2>&1; then
error "listmonk-data volume already exists. Please use docker-compose down -v to remove old volumes for a fresh setup of PostgreSQL."
error "listmonk-data volume already exists. Please use docker compose down -v to remove old volumes for a fresh setup of PostgreSQL."
exit 1
fi
}
@ -96,27 +98,27 @@ modify_config(){
info "modifying config.toml"
# Replace `db.host=localhost` with `db.host=db` in config file.
sed -i "s/host = \"localhost\"/host = \"listmonk_db\"/g" config.toml
sed $SED_INPLACE "s/host = \"localhost\"/host = \"listmonk_db\"/g" config.toml
# Replace `db.password=listmonk` with `db.password={{db_password}}` in config file.
# Note that `password` is wrapped with `\b`. This ensures that `admin_password` doesn't match this pattern instead.
sed -i "s/\bpassword\b = \"listmonk\"/password = \"$db_password\"/g" config.toml
sed $SED_INPLACE "s/\bpassword\b = \"listmonk\"/password = \"$db_password\"/g" config.toml
# Replace `app.address=localhost:9000` with `app.address=0.0.0.0:9000` in config file.
sed -i "s/address = \"localhost:9000\"/address = \"0.0.0.0:9000\"/g" config.toml
sed $SED_INPLACE "s/address = \"localhost:9000\"/address = \"0.0.0.0:9000\"/g" config.toml
info "modifying docker-compose.yml"
sed -i "s/POSTGRES_PASSWORD=listmonk/POSTGRES_PASSWORD=$db_password/g" docker-compose.yml
sed $SED_INPLACE "s/POSTGRES_PASSWORD=listmonk/POSTGRES_PASSWORD=$db_password/g" docker-compose.yml
}
run_migrations(){
info "running migrations"
docker-compose up -d db
docker compose up -d db
while ! is_healthy listmonk_db; do sleep 3; done
docker-compose run --rm app ./listmonk --install
docker compose run --rm app ./listmonk --install
}
start_services(){
info "starting app"
docker-compose up -d app db
docker compose up -d app db
}
show_output(){
@ -130,7 +132,6 @@ show_output(){
fi
}
check_dependencies
check_existing_db_volume
get_config

View file

@ -33,6 +33,11 @@ type Opt struct {
SESEnabled bool `json:"ses_enabled"`
SendgridEnabled bool `json:"sendgrid_enabled"`
SendgridKey string `json:"sendgrid_key"`
Postmark struct {
Enabled bool
Username string
Password string
}
RecordBounceCB func(models.Bounce) error
}
@ -43,6 +48,7 @@ type Manager struct {
mailbox Mailbox
SES *webhooks.SES
Sendgrid *webhooks.Sendgrid
Postmark *webhooks.Postmark
queries *Queries
opt Opt
log *log.Logger
@ -77,6 +83,7 @@ func New(opt Opt, q *Queries, lo *log.Logger) (*Manager, error) {
if opt.SESEnabled {
m.SES = webhooks.NewSES()
}
if opt.SendgridEnabled {
sg, err := webhooks.NewSendgrid(opt.SendgridKey)
if err != nil {
@ -85,6 +92,10 @@ func New(opt Opt, q *Queries, lo *log.Logger) (*Manager, error) {
m.Sendgrid = sg
}
}
if opt.Postmark.Enabled {
m.Postmark = webhooks.NewPostmark(opt.Postmark.Username, opt.Postmark.Password)
}
}
return m, nil

View file

@ -0,0 +1,118 @@
package webhooks
import (
"crypto/subtle"
"encoding/json"
"fmt"
"strings"
"time"
"github.com/knadh/listmonk/models"
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
)
type postmarkNotif struct {
RecordType string `json:"RecordType"`
MessageStream string `json:"MessageStream"`
ID int `json:"ID"`
Type string `json:"Type"`
TypeCode int `json:"TypeCode"`
Name string `json:"Name"`
Tag string `json:"Tag"`
MessageID string `json:"MessageID"`
Metadata map[string]string `json:"Metadata"`
ServerID int `json:"ServerID"`
Description string `json:"Description"`
Details string `json:"Details"`
Email string `json:"Email"`
From string `json:"From"`
BouncedAt time.Time `json:"BouncedAt"` // "2019-11-05T16:33:54.9070259Z"
DumpAvailable bool `json:"DumpAvailable"`
Inactive bool `json:"Inactive"`
CanActivate bool `json:"CanActivate"`
Subject string `json:"Subject"`
Content string `json:"Content"`
}
// Postmark handles webhook notifications (mainly bounce notifications).
type Postmark struct {
authHandler echo.HandlerFunc
}
func NewPostmark(username, password string) *Postmark {
return &Postmark{
authHandler: middleware.BasicAuth(makePostmarkAuthHandler(username, password))(func(c echo.Context) error {
return nil
}),
}
}
// ProcessBounce processes Postmark bounce notifications and returns one object.
func (p *Postmark) ProcessBounce(b []byte, c echo.Context) ([]models.Bounce, error) {
// Do basicauth.
if err := p.authHandler(c); err != nil {
return nil, err
}
var n postmarkNotif
if err := json.Unmarshal(b, &n); err != nil {
return nil, fmt.Errorf("error unmarshalling postmark notification: %v", err)
}
// Ignore non-bounce messages.
if n.RecordType != "Bounce" {
return nil, nil
}
supportedBounceType := true
typ := models.BounceTypeHard
switch n.Type {
case "HardBounce", "BadEmailAddress", "ManuallyDeactivated":
typ = models.BounceTypeHard
case "SoftBounce", "Transient", "DnsError", "SpamNotification", "VirusNotification", "DMARCPolicy":
typ = models.BounceTypeSoft
case "SpamComplaint":
typ = models.BounceTypeComplaint
default:
supportedBounceType = false
}
if !supportedBounceType {
return nil, fmt.Errorf("unsupported bounce type: %v", n.Type)
}
// Look for the campaign ID in headers.
campUUID := ""
if v, ok := n.Metadata["X-Listmonk-Campaign"]; ok {
campUUID = v
}
return []models.Bounce{{
Email: strings.ToLower(n.Email),
CampaignUUID: campUUID,
Type: typ,
Source: "postmark",
Meta: json.RawMessage(b),
CreatedAt: n.BouncedAt,
}}, nil
}
func makePostmarkAuthHandler(cfgUser, cfgPassword string) func(username, password string, c echo.Context) (bool, error) {
var (
u = []byte(cfgUser)
p = []byte(cfgPassword)
)
return func(username, password string, c echo.Context) (bool, error) {
if len(u) == 0 || len(p) == 0 {
return true, nil
}
if subtle.ConstantTimeCompare([]byte(username), u) == 1 && subtle.ConstantTimeCompare([]byte(password), p) == 1 {
return true, nil
}
return false, nil
}
}

View file

@ -8,7 +8,7 @@ import (
"encoding/pem"
"errors"
"fmt"
"io/ioutil"
"io"
"net/http"
"net/url"
"regexp"
@ -49,7 +49,10 @@ type sesMail struct {
EventType string `json:"eventType"`
NotifType string `json:"notificationType"`
Bounce struct {
BounceType string `json:"bounceType"`
BounceType string `json:"bounceType"`
BouncedRecipients []struct {
Status string `json:"status"`
} `json:"bouncedRecipients"`
} `json:"bounce"`
Mail struct {
Timestamp sesTimestamp `json:"timestamp"`
@ -132,6 +135,12 @@ func (s *SES) ProcessBounce(b []byte) (models.Bounce, error) {
if m.Bounce.BounceType == "Permanent" {
typ = models.BounceTypeHard
}
if m.Bounce.BounceType == "Transient" && len(m.Bounce.BouncedRecipients) > 0 {
// "Invalid domain" bounce.
if m.Bounce.BouncedRecipients[0].Status == "5.4.4" {
typ = models.BounceTypeHard
}
}
if m.NotifType == "Complaint" {
typ = models.BounceTypeComplaint
}
@ -230,7 +239,7 @@ func (s *SES) getCert(certURL string) (*x509.Certificate, error) {
return nil, fmt.Errorf("invalid SNS certificate URL: %v", u.Host)
}
body, err := ioutil.ReadAll(resp.Body)
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}

View file

@ -3,7 +3,7 @@ package captcha
import (
"encoding/json"
"fmt"
"io/ioutil"
"io"
"net/http"
"net/url"
"strings"
@ -58,7 +58,7 @@ func (c *Captcha) Verify(token string) (error, bool) {
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
body, err := io.ReadAll(resp.Body)
if err != nil {
return err, false
}

View file

@ -24,7 +24,7 @@ const (
// QueryCampaigns retrieves paginated campaigns optionally filtering them by the given arbitrary
// query expression. It also returns the total number of records in the DB.
func (c *Core) QueryCampaigns(searchStr string, statuses []string, orderBy, order string, offset, limit int) (models.Campaigns, int, error) {
queryStr, stmt := makeSearchQuery(searchStr, orderBy, order, c.q.QueryCampaigns)
queryStr, stmt := makeSearchQuery(searchStr, orderBy, order, c.q.QueryCampaigns, campQuerySortFields)
if statuses == nil {
statuses = []string{}

View file

@ -57,9 +57,11 @@ type Opt struct {
}
var (
regexFullTextQuery = regexp.MustCompile(`\s+`)
regexpSpaces = regexp.MustCompile(`[\s]+`)
querySortFields = []string{"name", "status", "created_at", "updated_at"}
regexFullTextQuery = regexp.MustCompile(`\s+`)
regexpSpaces = regexp.MustCompile(`[\s]+`)
campQuerySortFields = []string{"name", "status", "created_at", "updated_at"}
subQuerySortFields = []string{"email", "status", "name", "created_at", "updated_at"}
listQuerySortFields = []string{"name", "status", "created_at", "updated_at", "subscriber_count"}
)
// New returns a new instance of the core.
@ -88,7 +90,7 @@ func pqErrMsg(err error) string {
// makeSearchQuery cleans an optional search string and prepares the
// query SQL statement (string interpolated) and returns the
// search query string along with the SQL expression.
func makeSearchQuery(searchStr, orderBy, order, query string) (string, string) {
func makeSearchQuery(searchStr, orderBy, order, query string, querySortFields []string) (string, string) {
if searchStr != "" {
searchStr = `%` + string(regexFullTextQuery.ReplaceAll([]byte(searchStr), []byte("&"))) + `%`
}

View file

@ -39,7 +39,7 @@ func (c *Core) GetLists(typ string) ([]models.List, error) {
func (c *Core) QueryLists(searchStr, orderBy, order string, offset, limit int) ([]models.List, int, error) {
out := []models.List{}
queryStr, stmt := makeSearchQuery(searchStr, orderBy, order, c.q.QueryLists)
queryStr, stmt := makeSearchQuery(searchStr, orderBy, order, c.q.QueryLists, listQuerySortFields)
if err := c.db.Select(&out, stmt, 0, "", queryStr, offset, limit); err != nil {
c.log.Printf("error fetching lists: %v", err)
@ -75,7 +75,7 @@ func (c *Core) GetList(id int, uuid string) (models.List, error) {
}
var res []models.List
queryStr, stmt := makeSearchQuery("", "", "", c.q.QueryLists)
queryStr, stmt := makeSearchQuery("", "", "", c.q.QueryLists, nil)
if err := c.db.Select(&res, stmt, id, uu, queryStr, 0, 1); err != nil {
c.log.Printf("error fetching lists: %v", err)
return models.List{}, echo.NewHTTPError(http.StatusInternalServerError,

View file

@ -14,10 +14,6 @@ import (
"github.com/lib/pq"
)
var (
subQuerySortFields = []string{"email", "name", "created_at", "updated_at"}
)
// GetSubscriber fetches a subscriber by one of the given params.
func (c *Core) GetSubscriber(id int, uuid, email string) (models.Subscriber, error) {
var uu interface{}

View file

@ -4,7 +4,6 @@ import (
"crypto/rand"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"regexp"
@ -67,7 +66,7 @@ func (c *Client) GetURL(name string) string {
// GetBlob accepts a URL, reads the file, and returns the blob.
func (c *Client) GetBlob(url string) ([]byte, error) {
b, err := ioutil.ReadFile(filepath.Join(getDir(c.opts.UploadPath), filepath.Base(url)))
b, err := os.ReadFile(filepath.Join(getDir(c.opts.UploadPath), filepath.Base(url)))
return b, err
}

View file

@ -3,7 +3,7 @@ package s3
import (
"fmt"
"io"
"io/ioutil"
"net/url"
"path/filepath"
"strings"
"time"
@ -40,6 +40,11 @@ func NewS3Store(opt Opt) (media.Store, error) {
}
opt.URL = strings.TrimRight(opt.URL, "/")
// Default (and max S3 expiry) is 7 days.
if opt.Expiry.Seconds() < 1 {
opt.Expiry = time.Duration(167) * time.Hour
}
if opt.AccessKey == "" && opt.SecretKey == "" {
// fallback to IAM role if no access key/secret key is provided.
cl, _ = simples3.NewUsingIAM(opt.Region)
@ -102,16 +107,22 @@ func (c *Client) GetURL(name string) string {
}
// GetBlob reads a file from S3 and returns the raw bytes.
func (c *Client) GetBlob(url string) ([]byte, error) {
func (c *Client) GetBlob(uurl string) ([]byte, error) {
if p, err := url.Parse(uurl); err != nil {
uurl = filepath.Base(uurl)
} else {
uurl = filepath.Base(p.Path)
}
file, err := c.s3.FileDownload(simples3.DownloadInput{
Bucket: c.opts.Bucket,
ObjectKey: c.makeBucketPath(filepath.Base(url)),
ObjectKey: c.makeBucketPath(filepath.Base(uurl)),
})
if err != nil {
return nil, err
}
b, err := ioutil.ReadAll(file)
b, err := io.ReadAll(file)
if err != nil {
return nil, err
}

View file

@ -5,7 +5,6 @@ import (
"encoding/base64"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/textproto"
"time"
@ -129,6 +128,7 @@ func (p *Postback) Push(m models.Message) error {
copy(a.Content, f.Content)
files = append(files, a)
}
pb.Attachments = files
}
b, err := pb.MarshalJSON()
@ -196,7 +196,7 @@ func (p *Postback) exec(method, rURL string, reqBody []byte, headers http.Header
}
defer func() {
// Drain and close the body to let the Transport reuse the connection
io.Copy(ioutil.Discard, r.Body)
io.Copy(io.Discard, r.Body)
r.Body.Close()
}()

View file

@ -0,0 +1,22 @@
package migrations
import (
"github.com/jmoiron/sqlx"
"github.com/knadh/koanf/v2"
"github.com/knadh/stuffbin"
)
// V2_6_0 performs the DB migrations.
func V2_6_0(db *sqlx.DB, fs stuffbin.FileSystem, ko *koanf.Koanf) error {
// Insert new preference settings.
if _, err := db.Exec(`INSERT INTO settings (key, value) VALUES ('bounce.postmark', '{"enabled": false, "username": "", "password": ""}') ON CONFLICT DO NOTHING;`); err != nil {
return err
}
// Fix incorrect "d" (day) time prefix in S3 expiry settings.
if _, err := db.Exec(`UPDATE settings SET value = '"167h"' WHERE key = 'upload.s3.expiry' AND value = '"14d"'`); err != nil {
return err
}
return nil
}

View file

@ -15,7 +15,6 @@ import (
"errors"
"fmt"
"io"
"io/ioutil"
"log"
"net/mail"
"os"
@ -377,7 +376,7 @@ func (s *Session) ExtractZIP(srcPath string, maxCSVs int) (string, []string, err
defer z.Close()
// Create a temporary directory to extract the files.
dir, err := ioutil.TempDir("", "listmonk")
dir, err := os.MkdirTemp("", "listmonk")
if err != nil {
s.log.Printf("error creating temporary directory for extracting ZIP: %v", err)
return "", nil, err

View file

@ -92,7 +92,12 @@ type Settings struct {
SESEnabled bool `json:"bounce.ses_enabled"`
SendgridEnabled bool `json:"bounce.sendgrid_enabled"`
SendgridKey string `json:"bounce.sendgrid_key"`
BounceBoxes []struct {
BouncePostmark struct {
Enabled bool `json:"enabled"`
Username string `json:"username"`
Password string `json:"password"`
} `json:"bounce.postmark"`
BounceBoxes []struct {
UUID string `json:"uuid"`
Enabled bool `json:"enabled"`
Type string `json:"type"`

19
project.inlang.json Normal file
View file

@ -0,0 +1,19 @@
{
"$schema":"https://inlang.com/schema/project-settings",
"sourceLanguageTag": "en",
"languageTags": ["ca", "cs-cz", "cy", "de", "en", "es", "fi", "fr", "hu", "it", "jp", "ml", "nl", "pl", "pt-BR", "pt", "ro", "ru", "se", "sk", "tr", "vi", "zh-CN", "zh-TW"],
"modules": [
"https://cdn.jsdelivr.net/npm/@inlang/plugin-json@4/dist/index.js",
"https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-empty-pattern@1/dist/index.js",
"https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-identical-pattern@1/dist/index.js",
"https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-without-source@1/dist/index.js",
"https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-missing-translation@1/dist/index.js"
],
"plugin.inlang.json": {
"pathPattern": "./i18n/{languageTag}.json",
"variableReferencePattern": [
"{",
"}"
]
}
}

View file

@ -178,6 +178,7 @@ INSERT INTO subscriber_lists (subscriber_id, list_id, status)
WHEN $4='blocklisted' THEN 'unsubscribed'::subscription_status
-- When subscriber is edited from the admin form, retain the status. Otherwise, a blocklisted
-- subscriber when being re-enabled, their subscription statuses change.
WHEN subscriber_lists.status = 'confirmed' THEN 'confirmed'
WHEN $9 = TRUE THEN subscriber_lists.status
ELSE $8::subscription_status
END
@ -415,15 +416,16 @@ WITH ls AS (
WHEN $3 != '' THEN to_tsvector(name) @@ to_tsquery ($3)
ELSE true
END
ORDER BY %order%
OFFSET $4 LIMIT (CASE WHEN $5 < 1 THEN NULL ELSE $5 END)
),
counts AS (
SELECT list_id, JSON_OBJECT_AGG(status, subscriber_count) AS subscriber_statuses FROM (
SELECT COUNT(*) as subscriber_count, list_id, status FROM subscriber_lists
SELECT list_id, JSON_OBJECT_AGG(status, num) AS subscriber_statuses, SUM(num) AS subscriber_count
FROM (
SELECT list_id, status, COUNT(*) as num
FROM subscriber_lists
WHERE ($1 = 0 OR list_id = $1)
GROUP BY list_id, status
) row GROUP BY list_id
) AS subquery GROUP BY list_id
)
SELECT ls.*, subscriber_statuses FROM ls
LEFT JOIN counts ON (counts.list_id = ls.id) ORDER BY %order%;
@ -1082,4 +1084,4 @@ DELETE FROM bounces WHERE subscriber_id = (SELECT id FROM sub);
-- name: get-db-info
SELECT JSON_BUILD_OBJECT('version', (SELECT VERSION()),
'size_mb', (SELECT ROUND(pg_database_size('listmonk')/(1024^2)))) AS info;
'size_mb', (SELECT ROUND(pg_database_size((SELECT CURRENT_DATABASE()))/(1024^2)))) AS info;

View file

@ -244,7 +244,7 @@ INSERT INTO settings (key, value) VALUES
('upload.s3.bucket_domain', '""'),
('upload.s3.bucket_path', '"/"'),
('upload.s3.bucket_type', '"public"'),
('upload.s3.expiry', '"14d"'),
('upload.s3.expiry', '"167h"'),
('smtp',
'[{"enabled":true, "host":"smtp.yoursite.com","port":25,"auth_protocol":"cram","username":"username","password":"password","hello_hostname":"","max_conns":10,"idle_timeout":"15s","wait_timeout":"5s","max_msg_retries":2,"tls_type":"STARTTLS","tls_skip_verify":false,"email_headers":[]},
{"enabled":false, "host":"smtp.gmail.com","port":465,"auth_protocol":"login","username":"username@gmail.com","password":"password","hello_hostname":"","max_conns":10,"idle_timeout":"15s","wait_timeout":"5s","max_msg_retries":2,"tls_type":"TLS","tls_skip_verify":false,"email_headers":[]}]'),
@ -255,6 +255,7 @@ INSERT INTO settings (key, value) VALUES
('bounce.ses_enabled', 'false'),
('bounce.sendgrid_enabled', 'false'),
('bounce.sendgrid_key', '""'),
('bounce.postmark', '{"enabled": false, "username": "", "password": ""}'),
('bounce.mailboxes',
'[{"enabled":false, "type": "pop", "host":"pop.yoursite.com","port":995,"auth_protocol":"userpass","username":"username","password":"password","return_path": "bounce@listmonk.yoursite.com","scan_interval":"15m","tls_enabled":true,"tls_skip_verify":false}]'),
('appearance.admin.custom_css', '""'),

View file

@ -90,7 +90,7 @@
</div>
<div class="footer">
<p>{{ L.T "public.poweredBy" }} <a href="https://listmonk.app" target="_blank">listmonk</a></p>
<p>{{ L.T "public.poweredBy" }} <a href="https://listmonk.app" target="_blank" rel="noreferrer">listmonk</a></p>
</div>
<div class="gutter">&nbsp;</div>
</body>

View file

@ -92,7 +92,7 @@
</div>
<div class="footer" style="text-align: center;font-size: 12px;color: #888;">
<p>{{ L.T "public.poweredBy" }} <a href="https://listmonk.app" target="_blank" style="color: #888;">listmonk</a></p>
<p>{{ L.T "public.poweredBy" }} <a href="https://listmonk.app" target="_blank" rel="noreferrer" style="color: #888;">listmonk</a></p>
</div>
</body>
</html>

View file

@ -97,7 +97,7 @@
<a href="{{ UnsubscribeURL }}" style="color: #888;">{{ L.T "email.unsub" }}</a>
<a href="{{ MessageURL }}" style="color: #888;">{{ L.T "email.viewInBrowser" }}</a>
</p>
<p>{{ L.T "public.poweredBy" }} <a href="https://listmonk.app" target="_blank" style="color: #888;">listmonk</a></p>
<p>{{ L.T "public.poweredBy" }} <a href="https://listmonk.app" target="_blank" rel="noreferrer" style="color: #888;">listmonk</a></p>
</div>
<div class="gutter" style="padding: 30px;">&nbsp;{{ TrackView }}</div>
</body>

View file

@ -102,7 +102,7 @@
</div>
<div class="footer" style="text-align: center;font-size: 12px;color: #888;">
<p>{{ L.T "public.poweredBy" }} <a href="https://listmonk.app" target="_blank" style="color: #888;">listmonk</a></p>
<p>{{ L.T "public.poweredBy" }} <a href="https://listmonk.app" target="_blank" rel="noreferrer" style="color: #888;">listmonk</a></p>
</div>
</body>
</html>

View file

@ -16,7 +16,7 @@
<p>
<a href="{{ .OptinURL }}" class="button">{{ L.Ts "email.optin.confirmSub" }}</a>
</p>
<a href="{{ .UnsubURL }}">{{ L.T "email.unsub" }}</a>
<a href="{{ .UnsubURL }}?manage=true">{{ L.T "email.unsub" }}</a>
{{ template "footer" }}
{{ end }}

View file

@ -41,7 +41,7 @@
</div>
<footer class="container">
{{ L.T "public.poweredBy" }} <a target="_blank" href="https://listmonk.app">listmonk</a>
{{ L.T "public.poweredBy" }} <a target="_blank" rel="noreferrer" href="https://listmonk.app">listmonk</a>
</footer>
</body>
</html>