Update pre-commit (#1138)

* Update pre-commit

* Upgrade djlint, remove flake8 and add pylint

* Reformat with new djlint version

* Run pre-commit on CI

* Use only python3.10 on CI

* Reformat files with pre-commit

* Run pre-commit against all files

* Reformat

* Added global excludes

* Added pre-commit to the contributing file

* Set python 3.9 as default

* Set language version to python3

Co-authored-by: Adrià Casajús <adria.casajus@proton.ch>
Co-authored-by: Carlos Quintana <carlos.quintana@proton.ch>
This commit is contained in:
Adrià Casajús 2022-07-04 16:01:04 +02:00 committed by GitHub
parent e2f9ea4ae1
commit 046748c443
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
46 changed files with 1188 additions and 808 deletions

View File

@ -7,12 +7,12 @@ assignees: ''
---
Please note that this is only for bug report.
Please note that this is only for bug report.
For help on your account, please reach out to us at hi[at]simplelogin.io. Please make sure to check out [our FAQ](https://simplelogin.io/faq/) that contains frequently asked questions.
For feature request, you can use our [forum](https://github.com/simple-login/app/discussions/categories/feature-request).
For feature request, you can use our [forum](https://github.com/simple-login/app/discussions/categories/feature-request).
For self-hosted question/issue, please ask in [self-hosted forum](https://github.com/simple-login/app/discussions/categories/self-hosting-question)

View File

@ -1,6 +1,6 @@
name: Run tests & Publish to Docker Registry
on:
on:
push:
jobs:
@ -9,7 +9,7 @@ jobs:
strategy:
max-parallel: 4
matrix:
python-version: [3.7, "3.10"]
python-version: ["3.9", "3.10"]
# service containers to run with `postgres-job`
services:
@ -75,9 +75,7 @@ jobs:
- name: Check formatting & linting
run: |
poetry run black --check .
poetry run flake8
poetry run djlint --check templates
poetry run pre-commit run --all-files
- name: Run db migration
run: |
@ -214,4 +212,4 @@ jobs:
release_name: ${{ github.ref }}
body: ${{ steps.build_changelog.outputs.changelog }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@ -1,10 +1,27 @@
exclude: "(migrations|static/node_modules|static/assets|static/vendor)"
default_language_version:
python: python3
repos:
- repo: https://github.com/psf/black
rev: 22.1.0
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.2.0
hooks:
- id: black
language_version: python3.7
- repo: https://github.com/pycqa/flake8
rev: 4.0.1
- id: check-yaml
- id: trailing-whitespace
- repo: https://github.com/psf/black
rev: 22.3.0
hooks:
- id: flake8
- id: black
- repo: https://gitlab.com/pycqa/flake8
rev: 3.9.2
hooks:
- id: flake8
- repo: https://github.com/Riverside-Healthcare/djLint
rev: v1.3.0
hooks:
- id: djlint-jinja
files: '.*\.html'
entry: djlint --reformat
- repo: https://github.com/PyCQA/pylint
rev: v2.14.4
hooks:
- id: pylint

227
.pylintrc Normal file
View File

@ -0,0 +1,227 @@
[MASTER]
extension-pkg-allow-list=re2
fail-under=7.0
ignore=CVS
ignore-paths=migrations
ignore-patterns=^\.#
jobs=0
[MESSAGES CONTROL]
disable=missing-function-docstring,
missing-module-docstring,
duplicate-code,
#import-error,
missing-class-docstring,
useless-object-inheritance,
use-dict-literal,
logging-format-interpolation,
consider-using-f-string,
unnecessary-comprehension,
inconsistent-return-statements,
wrong-import-order,
line-too-long,
invalid-name,
global-statement,
no-else-return,
unspecified-encoding,
logging-fstring-interpolation,
too-few-public-methods,
bare-except,
fixme,
unnecessary-pass,
f-string-without-interpolation,
super-init-not-called,
unused-argument,
ungrouped-imports,
too-many-locals,
consider-using-with,
too-many-statements,
consider-using-set-comprehension,
unidiomatic-typecheck,
useless-else-on-loop,
too-many-return-statements,
broad-except,
protected-access,
consider-using-enumerate,
too-many-nested-blocks,
too-many-branches,
simplifiable-if-expression,
possibly-unused-variable,
pointless-string-statement,
wrong-import-position,
redefined-outer-name,
raise-missing-from,
logging-too-few-args,
redefined-builtin,
too-many-arguments,
import-outside-toplevel,
redefined-argument-from-local,
logging-too-many-args,
too-many-instance-attributes,
unreachable,
no-name-in-module,
no-member,
consider-using-ternary,
too-many-lines,
arguments-differ,
too-many-public-methods,
unused-variable,
consider-using-dict-items,
consider-using-in,
reimported,
too-many-boolean-expressions,
cyclic-import,
not-callable, # (paddle_utils.py) verifier.verify cannot be called (although it can)
abstract-method, # (models.py)
[BASIC]
# Naming style matching correct argument names.
argument-naming-style=snake_case
# Regular expression matching correct argument names. Overrides argument-
# naming-style. If left empty, argument names will be checked with the set
# naming style.
#argument-rgx=
# Naming style matching correct attribute names.
attr-naming-style=snake_case
# Regular expression matching correct attribute names. Overrides attr-naming-
# style. If left empty, attribute names will be checked with the set naming
# style.
#attr-rgx=
# Bad variable names which should always be refused, separated by a comma.
bad-names=foo,
bar,
baz,
toto,
tutu,
tata
# Bad variable names regexes, separated by a comma. If names match any regex,
# they will always be refused
bad-names-rgxs=
# Naming style matching correct class attribute names.
class-attribute-naming-style=any
# Regular expression matching correct class attribute names. Overrides class-
# attribute-naming-style. If left empty, class attribute names will be checked
# with the set naming style.
#class-attribute-rgx=
# Naming style matching correct class constant names.
class-const-naming-style=UPPER_CASE
# Regular expression matching correct class constant names. Overrides class-
# const-naming-style. If left empty, class constant names will be checked with
# the set naming style.
#class-const-rgx=
# Naming style matching correct class names.
class-naming-style=PascalCase
# Regular expression matching correct class names. Overrides class-naming-
# style. If left empty, class names will be checked with the set naming style.
#class-rgx=
# Naming style matching correct constant names.
const-naming-style=UPPER_CASE
# Regular expression matching correct constant names. Overrides const-naming-
# style. If left empty, constant names will be checked with the set naming
# style.
#const-rgx=
# Minimum line length for functions/classes that require docstrings, shorter
# ones are exempt.
docstring-min-length=-1
# Naming style matching correct function names.
function-naming-style=snake_case
# Regular expression matching correct function names. Overrides function-
# naming-style. If left empty, function names will be checked with the set
# naming style.
#function-rgx=
# Good variable names which should always be accepted, separated by a comma.
good-names=i,
j,
k,
ex,
Run,
_
# Good variable names regexes, separated by a comma. If names match any regex,
# they will always be accepted
good-names-rgxs=
# Include a hint for the correct naming format with invalid-name.
include-naming-hint=no
# Naming style matching correct inline iteration names.
inlinevar-naming-style=any
# Regular expression matching correct inline iteration names. Overrides
# inlinevar-naming-style. If left empty, inline iteration names will be checked
# with the set naming style.
#inlinevar-rgx=
# Naming style matching correct method names.
method-naming-style=snake_case
# Regular expression matching correct method names. Overrides method-naming-
# style. If left empty, method names will be checked with the set naming style.
#method-rgx=
# Naming style matching correct module names.
module-naming-style=snake_case
# Regular expression matching correct module names. Overrides module-naming-
# style. If left empty, module names will be checked with the set naming style.
#module-rgx=
# Colon-delimited sets of names that determine each other's naming style when
# the name regexes allow several styles.
name-group=
# Regular expression which should only match function or class names that do
# not require a docstring.
no-docstring-rgx=^_
# List of decorators that produce properties, such as abc.abstractproperty. Add
# to this list to register other decorators that produce valid properties.
# These decorators are taken in consideration only for invalid-name.
property-classes=abc.abstractproperty
# Regular expression matching correct type variable names. If left empty, type
# variable names will be checked with the set naming style.
#typevar-rgx=
# Naming style matching correct variable names.
variable-naming-style=snake_case
# Regular expression matching correct variable names. Overrides variable-
# naming-style. If left empty, variable names will be checked with the set
# naming style.
#variable-rgx=
[STRING]
# This flag controls whether inconsistent-quotes generates a warning when the
# character used as a quote delimiter is used inconsistently within a module.
check-quote-consistency=no
# This flag controls whether the implicit-str-concat should generate a warning
# on implicit string concatenation in sequences defined over several lines.
check-str-concat-over-line-jumps=no
[FORMAT]
max-line-length=88
single-line-if-stmt=yes

View File

@ -117,7 +117,7 @@ Add SUPPORT_NAME param to set a support email name.
## [1.0.1] - 2020-01-28
Simplify config file.
Simplify config file.
## [1.0.0] - 2020-01-22

View File

@ -1,9 +1,9 @@
Thanks for taking the time to contribute! 🎉👍
Before working on a new feature, please get in touch with us at dev[at]simplelogin.io to avoid duplication.
We can also discuss the best way to implement it.
Before working on a new feature, please get in touch with us at dev[at]simplelogin.io to avoid duplication.
We can also discuss the best way to implement it.
The project uses Flask, Python3.7+ and requires Postgres 12+ as dependency.
The project uses Flask, Python3.7+ and requires Postgres 12+ as dependency.
## General Architecture
@ -43,13 +43,23 @@ You also need to install `gpg` tool, on Mac it can be done with:
brew install gnupg
```
If you see the `pyre2` package in the error message, you might need to install its dependencies with `brew`.
If you see the `pyre2` package in the error message, you might need to install its dependencies with `brew`.
More info on https://github.com/andreasvc/pyre2
```bash
brew install -s re2 pybind11
```
## Linting and static analysis
We use pre-commit to run all our linting and static analysis checks. Please run
```bash
poetry run pre-commit install
```
To install it in your development environment.
## Run tests
```bash

View File

@ -29,12 +29,12 @@
---
Your email address is your **online identity**. When you use the same email address everywhere, you can be easily tracked.
More information on https://simplelogin.io
Your email address is your **online identity**. When you use the same email address everywhere, you can be easily tracked.
More information on https://simplelogin.io
This README contains instructions on how to self host SimpleLogin.
Once you have your own SimpleLogin instance running, you can change the `API URL` in SimpleLogin's Chrome/Firefox extension, Android/iOS app to your server.
Once you have your own SimpleLogin instance running, you can change the `API URL` in SimpleLogin's Chrome/Firefox extension, Android/iOS app to your server.
SimpleLogin roadmap is at https://github.com/simple-login/app/projects/1 and our forum at https://github.com/simple-login/app/discussions, feel free to submit new ideas or vote on features.
@ -374,10 +374,10 @@ sudo systemctl restart postfix
To run SimpleLogin, you need a config file at `$(pwd)/simplelogin.env`. Below is an example that you can use right away, make sure to
- replace `mydomain.com` by your domain,
- set `FLASK_SECRET` to a secret string,
- set `FLASK_SECRET` to a secret string,
- update 'myuser' and 'mypassword' with your database credentials used in previous step.
All possible parameters can be found in [config example](example.env). Some are optional and are commented out by default.
All possible parameters can be found in [config example](example.env). Some are optional and are commented out by default.
Some have "dummy" values, fill them up if you want to enable these features (Paddle, AWS, etc).
```.env

View File

@ -2,13 +2,13 @@
## Supported Versions
We only add security updates to the latest MAJOR.MINOR version of the project. No security updates are backported to previous versions.
We only add security updates to the latest MAJOR.MINOR version of the project. No security updates are backported to previous versions.
If you want be up to date on security patches, make sure your SimpleLogin image is up to date.
## Reporting a Vulnerability
If you've found a security vulnerability, you can disclose it responsibly by sending a summary to security@simplelogin.io.
We will review the potential threat and fix it as fast as we can.
If you've found a security vulnerability, you can disclose it responsibly by sending a summary to security@simplelogin.io.
We will review the potential threat and fix it as fast as we can.
We are incredibly thankful for people who disclose vulnerabilities, unfortunately we do not have a bounty program in place yet.

View File

@ -239,7 +239,7 @@ Input:
}
```
Output:
Output:
- 200 with ```{"ok": true}``` if sudo mode has been enabled.
- 403 with ```{"error": "Some error"}``` if there is an error.
@ -308,7 +308,7 @@ Input:
Output: a json with the following field:
- can_create: boolean. Whether user can create new alias
- suffixes: list of alias suffix that user can use.
- suffixes: list of alias suffix that user can use.
Each item is a dictionary with `suffix`, `signed-suffix`, `is_custom`, `is_premium` as keys.
The `signed-suffix` is necessary to avoid request tampering.
- prefix_suggestion: string. Suggestion for the `alias prefix`. Usually this is the website name extracted
@ -389,8 +389,8 @@ Input:
- `page_id` in query. Used for the pagination. The endpoint returns maximum 20 aliases for each page. `page_id` starts
at 0.
- (Optional) `pinned` in query. If set, only pinned aliases are returned.
- (Optional) `disabled` in query. If set, only disabled aliases are returned.
- (Optional) `enabled` in query. If set, only enabled aliases are returned.
- (Optional) `disabled` in query. If set, only disabled aliases are returned.
- (Optional) `enabled` in query. If set, only enabled aliases are returned.
Please note `pinned`, `disabled`, `enabled` are exclusive, i.e. only one can be present.
- (Optional) query: included in request body. Some frameworks might prevent GET request having a non-empty body, in this
case this endpoint also supports POST.

View File

@ -1,8 +1,8 @@
# TODO
`local_data/`: contain files used only locally. In deployment, these files should be replaced.
- jwtRS256.key: generated using
- jwtRS256.key: generated using
```bash
ssh-keygen -t rsa -b 4096 -m PEM -f jwtRS256.key
# Don't add passphrase

View File

@ -1,18 +1,18 @@
Some email services like Gmail, Protonmail, etc don't have a strict SPF record (`-all`) to support the "classic" email forwarding
that is usually used for group mailing list. In this scenario, an email is sent to a group is forwarded as-is,
Some email services like Gmail, Protonmail, etc don't have a strict SPF record (`-all`) to support the "classic" email forwarding
that is usually used for group mailing list. In this scenario, an email is sent to a group is forwarded as-is,
breaking therefore the SPF.
A malicious hacker could use this security fail to impersonate your alias via the reverse-alias. This rarely happens
as the reverse-alias is generated randomly and is unique for each sender.
However if you want to prevent this kind of attack, you can enforce the SPF policy even if your mailbox uses a "soft" policy.
1) Install `postfix-pcre`
```bash
apt install -y postfix-pcre
```
```
2) Add `/etc/postfix/body_checks.pcre` file with the following content
```

View File

@ -1,4 +1,4 @@
In case your Postfix server is on another server, it's recommended to enable TLS on Postfix submission to
In case your Postfix server is on another server, it's recommended to enable TLS on Postfix submission to
secure the connection between SimpleLogin email handler and Postfix.
This can be enabled by adding those lines at the end of `/etc/postfix/master.cf`
@ -11,5 +11,5 @@ submission inet n - y - - smtpd
-o smtpd_tls_auth_only=yes
```
Make sure to set the `POSTFIX_SUBMISSION_TLS` variable to `true` in the SimpleLogin `simplelogin.env` file.
Make sure to set the `POSTFIX_SUBMISSION_TLS` variable to `true` in the SimpleLogin `simplelogin.env` file.

View File

@ -2,7 +2,7 @@ Contribution from https://github.com/havedill/
## Integrating with Amazon SES
If you're self hosting, here is the method I used to route emails through Amazon's SES service.
If you're self hosting, here is the method I used to route emails through Amazon's SES service.
For me, when hosting on AWS the public IP is widely blacklisted for abuse. If you have an SES account, you are whitelisted, use TLS, and amazon creates the DKIM records.
@ -57,11 +57,11 @@ Also make sure that Postfix is able to authenticate successfully by installing t
sudo apt install libsasl2-modules
```
Then restart postfix
Then restart postfix
```bash
sudo systemctl restart postfix
```
```
and you should see the mail in `/var/log/mail.log` and in your alias emails routed through Amazons servers!

View File

@ -27,12 +27,12 @@ smtpd_tls_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem
smtpd_tls_key_file=/etc/ssl/private/ssl-cert-snakeoil.key
```
with
with
```
smtpd_tls_cert_file = /etc/letsencrypt/live/app.mydomain.com/fullchain.pem
smtpd_tls_key_file = /etc/letsencrypt/live/app.mydomain.com/privkey.pem
```
```
Make sure to replace app.mydomain.com with your own domain.

View File

@ -8,23 +8,23 @@ This can either mean:
2) The `sl-app` container can't connect to Postfix (run on the host)
### A.1
To test 1), running `swaks --to your-mailbox@mail.com` should send you an email.
Make sure to replace `your-mailbox@mail.com` by your mailbox address.
To test 1), running `swaks --to your-mailbox@mail.com` should send you an email.
Make sure to replace `your-mailbox@mail.com` by your mailbox address.
`swaks` can be installed with `sudo apt install swaks` on Debian-based OS.
### A.2
Once 1) works, we can test the 2) by
Once 1) works, we can test the 2) by
a) first connecting to the container by `docker exec -it sl-app bash`
b) then run the following commands
```bash
apt update
apt install telnet -y
telnet 10.0.0.1 25
```
If the `telnet 10.0.0.1 25` doesn't work, it means Postfix can't be reached from the docker container.
If the `telnet 10.0.0.1 25` doesn't work, it means Postfix can't be reached from the docker container.
This means an issue with the Docker network.
You can then try `telnet 172.17.0.1 25` as `172.17.0.1` is *usually* the host IP address. If this works, then you can set
@ -52,8 +52,8 @@ And `postmap -q not-exist.com pgsql:/etc/postfix/pgsql-relay-domains.cf` should
And `postmap -q not-exist.com pgsql:/etc/postfix/pgsql-transport-maps.cf` should return nothing.
### B.2
For 2), you can check in the `sl-email` log by running `docker logs sl-email` and if the incoming email doesn't appear there,
then it means Postfix can't connect to the `sl-email` container. Please run through the self-hosting instructions and
For 2), you can check in the `sl-email` log by running `docker logs sl-email` and if the incoming email doesn't appear there,
then it means Postfix can't connect to the `sl-email` container. Please run through the self-hosting instructions and
make sure no step is missed.
### B.3

View File

@ -2,7 +2,7 @@ SimpleLogin needs to have the following ports open:
- 22: so you SSH into the server
- 25: to receive the incoming emails
- 80 and optionally 443 for SimpleLogin webapp
If you use `UFW` Firewall, you could run the following commands to open these ports:
```bash

View File

@ -193,7 +193,7 @@ sudo docker run -d \
--restart always \
--network="sl-network" \
simplelogin/app:3.4.0 python email_handler.py
# Run the job runner
docker run -d \
--name sl-job-runner \
@ -205,6 +205,6 @@ docker run -d \
--restart always \
--network="sl-network" \
simplelogin/app:3.4.0 python job_runner.py
```

View File

@ -26,7 +26,7 @@ def upgrade():
session = Session(bind=bind)
session.execute("""
ALTER TABLE alias ADD COLUMN ts_vector tsvector GENERATED ALWAYS
ALTER TABLE alias ADD COLUMN ts_vector tsvector GENERATED ALWAYS
AS (to_tsvector('english', note)) STORED;
""")

View File

@ -1,7 +1,7 @@
"""empty message
Revision ID: 5e549314e1e2
Revises:
Revises:
Create Date: 2019-06-23 16:02:14.692075
"""

309
poetry.lock generated
View File

@ -66,14 +66,6 @@ python-dateutil = "*"
python-editor = ">=0.3"
SQLAlchemy = ">=1.1.0"
[[package]]
name = "appdirs"
version = "1.4.4"
description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
category = "dev"
optional = false
python-versions = "*"
[[package]]
name = "appnope"
version = "0.1.0"
@ -93,6 +85,20 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
[package.dependencies]
python-dateutil = ">=2.7.0"
[[package]]
name = "astroid"
version = "2.11.6"
description = "An abstract syntax tree for Python with inference support."
category = "dev"
optional = false
python-versions = ">=3.6.2"
[package.dependencies]
lazy-object-proxy = ">=1.4.0"
typed-ast = {version = ">=1.4.0,<2.0", markers = "implementation_name == \"cpython\" and python_version < \"3.8\""}
typing-extensions = {version = ">=3.10", markers = "python_version < \"3.10\""}
wrapt = ">=1.11,<2"
[[package]]
name = "async-timeout"
version = "3.0.1"
@ -142,6 +148,21 @@ category = "main"
optional = false
python-versions = "*"
[[package]]
name = "backports.entry-points-selectable"
version = "1.1.1"
description = "Compatibility shim providing selectable entry points for older implementations"
category = "dev"
optional = false
python-versions = ">=2.7"
[package.dependencies]
importlib-metadata = {version = "*", markers = "python_version < \"3.8\""}
[package.extras]
docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"]
testing = ["pytest", "pytest-flake8", "pytest-cov", "pytest-black (>=0.3.7)", "pytest-mypy", "pytest-checkdocs (>=2.4)", "pytest-enabler (>=1.0.1)"]
[[package]]
name = "bcrypt"
version = "3.2.0"
@ -378,6 +399,17 @@ wrapt = ">=1.10,<2"
[package.extras]
dev = ["tox", "bump2version (<1)", "sphinx (<2)", "importlib-metadata (<3)", "importlib-resources (<4)", "configparser (<5)", "sphinxcontrib-websupport (<2)", "zipp (<2)", "PyTest (<5)", "PyTest-Cov (<2.6)", "pytest", "pytest-cov"]
[[package]]
name = "dill"
version = "0.3.5.1"
description = "serialize all of python"
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, !=3.6.*"
[package.extras]
graph = ["objgraph (>=1.7.2)"]
[[package]]
name = "distlib"
version = "0.3.1"
@ -388,23 +420,26 @@ python-versions = "*"
[[package]]
name = "djlint"
version = "0.7.3"
version = "1.3.0"
description = "HTML Template Linter and Formatter"
category = "dev"
optional = false
python-versions = ">=3.6.2,<4.0.0"
python-versions = ">=3.7,<4.0"
[package.dependencies]
click = ">=8.0.1,<9.0.0"
colorama = ">=0.4.4,<0.5.0"
html-tag-names = ">=0.1.2,<0.2.0"
html-void-elements = ">=0.1.0,<0.2.0"
importlib-metadata = ">=4.11.0,<5.0.0"
pathspec = ">=0.9.0,<0.10.0"
PyYAML = ">=6.0,<7.0"
regex = ">=2022.1.18,<2023.0.0"
tomlkit = ">=0.8.0,<0.9.0"
tomli = {version = ">=2.0.1,<3.0.0", markers = "python_version < \"3.11\""}
tqdm = ">=4.62.2,<5.0.0"
[package.extras]
test = ["coverage (>=6.2,<7.0)", "pytest (>=6.2.5,<7.0.0)", "pytest-cov (>=3.0.0,<4.0.0)"]
test = ["coverage (>=6.3.1,<7.0.0)", "pytest (>=7.0.1,<8.0.0)", "pytest-cov (>=3.0.0,<4.0.0)"]
[[package]]
name = "dkimpy"
@ -469,35 +504,6 @@ category = "main"
optional = false
python-versions = "*"
[[package]]
name = "flake8"
version = "4.0.1"
description = "the modular source code checker: pep8 pyflakes and co"
category = "dev"
optional = false
python-versions = ">=3.6"
[package.dependencies]
importlib-metadata = {version = "<4.3", markers = "python_version < \"3.8\""}
mccabe = ">=0.6.0,<0.7.0"
pycodestyle = ">=2.8.0,<2.9.0"
pyflakes = ">=2.4.0,<2.5.0"
[[package]]
name = "flake8-bugbear"
version = "22.1.11"
description = "A plugin for flake8 finding likely bugs and design problems in your program. Contains warnings that don't belong in pyflakes and pycodestyle."
category = "dev"
optional = false
python-versions = ">=3.6"
[package.dependencies]
attrs = ">=19.2.0"
flake8 = ">=3.0.0"
[package.extras]
dev = ["coverage", "hypothesis", "hypothesmith (>=0.2)", "pre-commit"]
[[package]]
name = "flanker"
version = "0.9.11"
@ -816,6 +822,22 @@ gevent = ["gevent (>=0.13)"]
setproctitle = ["setproctitle"]
tornado = ["tornado (>=0.2)"]
[[package]]
name = "html-tag-names"
version = "0.1.2"
description = "List of known HTML tag names"
category = "dev"
optional = false
python-versions = ">=3.7,<4.0"
[[package]]
name = "html-void-elements"
version = "0.1.0"
description = "List of HTML void tag names."
category = "dev"
optional = false
python-versions = ">=3.7,<4.0"
[[package]]
name = "httplib2"
version = "0.18.1"
@ -856,18 +878,20 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
[[package]]
name = "importlib-metadata"
version = "1.7.0"
version = "4.12.0"
description = "Read metadata from Python packages"
category = "main"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
python-versions = ">=3.7"
[package.dependencies]
typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""}
zipp = ">=0.5"
[package.extras]
docs = ["sphinx", "rst.linker"]
testing = ["packaging", "pep517", "importlib-resources (>=1.3)"]
docs = ["sphinx", "jaraco.packaging (>=9)", "rst.linker (>=1.9)"]
perf = ["ipython"]
testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.3)", "packaging", "pyfakefs", "flufl.flake8", "pytest-perf (>=0.9.2)", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)", "importlib-resources (>=1.3)"]
[[package]]
name = "iniconfig"
@ -917,6 +941,20 @@ category = "main"
optional = false
python-versions = "*"
[[package]]
name = "isort"
version = "5.10.1"
description = "A Python utility / library to sort Python imports."
category = "dev"
optional = false
python-versions = ">=3.6.1,<4.0"
[package.extras]
pipfile_deprecated_finder = ["pipreqs", "requirementslib"]
requirements_deprecated_finder = ["pipreqs", "pip-api"]
colors = ["colorama (>=0.4.3,<0.5.0)"]
plugins = ["setuptools"]
[[package]]
name = "itsdangerous"
version = "1.1.0"
@ -973,6 +1011,14 @@ python-versions = "*"
[package.dependencies]
cryptography = ">=2.3"
[[package]]
name = "lazy-object-proxy"
version = "1.7.1"
description = "A fast and thorough lazy object proxy."
category = "dev"
optional = false
python-versions = ">=3.6"
[[package]]
name = "limits"
version = "1.5.1"
@ -1020,11 +1066,11 @@ traitlets = "*"
[[package]]
name = "mccabe"
version = "0.6.1"
version = "0.7.0"
description = "McCabe checker, plugin for flake8"
category = "dev"
optional = false
python-versions = "*"
python-versions = ">=3.6"
[[package]]
name = "memory-profiler"
@ -1283,14 +1329,6 @@ python-versions = "*"
[package.dependencies]
pyasn1 = ">=0.4.6,<0.5.0"
[[package]]
name = "pycodestyle"
version = "2.8.0"
description = "Python style guide checker"
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
[[package]]
name = "pycparser"
version = "2.20"
@ -1307,14 +1345,6 @@ category = "main"
optional = false
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
[[package]]
name = "pyflakes"
version = "2.4.0"
description = "passive checker of Python programs"
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
[[package]]
name = "pygments"
version = "2.7.4"
@ -1337,6 +1367,29 @@ dev = ["sphinx", "sphinx-rtd-theme", "zope.interface", "cryptography (>=3.3.1)",
docs = ["sphinx", "sphinx-rtd-theme", "zope.interface"]
tests = ["pytest (>=6.0.0,<7.0.0)", "coverage[toml] (==5.0.4)"]
[[package]]
name = "pylint"
version = "2.14.4"
description = "python code static checker"
category = "dev"
optional = false
python-versions = ">=3.7.2"
[package.dependencies]
astroid = ">=2.11.6,<=2.12.0-dev0"
colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""}
dill = ">=0.2"
isort = ">=4.2.5,<6"
mccabe = ">=0.6,<0.8"
platformdirs = ">=2.2.0"
tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
tomlkit = ">=0.10.1"
typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""}
[package.extras]
spelling = ["pyenchant (>=3.2,<4.0)"]
testutils = ["gitpython (>3)"]
[[package]]
name = "pyopenssl"
version = "19.1.0"
@ -1731,7 +1784,7 @@ python-versions = "*"
[[package]]
name = "tomli"
version = "2.0.0"
version = "2.0.1"
description = "A lil' TOML parser"
category = "dev"
optional = false
@ -1739,7 +1792,7 @@ python-versions = ">=3.7"
[[package]]
name = "tomlkit"
version = "0.8.0"
version = "0.11.0"
description = "Style preserving TOML library"
category = "dev"
optional = false
@ -1836,22 +1889,23 @@ socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
[[package]]
name = "virtualenv"
version = "20.0.31"
version = "20.8.1"
description = "Virtual Python Environment builder"
category = "dev"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7"
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
[package.dependencies]
appdirs = ">=1.4.3,<2"
"backports.entry-points-selectable" = ">=1.0.4"
distlib = ">=0.3.1,<1"
filelock = ">=3.0.0,<4"
importlib-metadata = {version = ">=0.12,<2", markers = "python_version < \"3.8\""}
importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""}
platformdirs = ">=2,<3"
six = ">=1.9.0,<2"
[package.extras]
docs = ["proselint (>=0.10.2)", "sphinx (>=3)", "sphinx-argparse (>=0.2.5)", "sphinx-rtd-theme (>=0.4.3)", "towncrier (>=19.9.0rc1)"]
testing = ["coverage (>=5)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", "pytest (>=4)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.1)", "pytest-mock (>=2)", "pytest-randomly (>=1)", "pytest-timeout (>=1)", "pytest-xdist (>=1.31.0)", "packaging (>=20.0)", "xonsh (>=0.9.16)"]
testing = ["coverage (>=4)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", "pytest (>=4)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.1)", "pytest-mock (>=2)", "pytest-randomly (>=1)", "pytest-timeout (>=1)", "packaging (>=20.0)"]
[[package]]
name = "watchtower"
@ -2004,8 +2058,8 @@ testing = ["coverage (>=5.0.3)", "zope.event", "zope.testing"]
[metadata]
lock-version = "1.1"
python-versions = "^3.7"
content-hash = "29d95a850f0a87a38aabb8f5eddba072316860dae26943fd690898ea9bac2b02"
python-versions = "^3.7.2"
content-hash = "04190874ee0655ddf7d67e024a61009c5f9c92439ff0af8dfce3341580269edb"
[metadata.files]
aiohttp = [
@ -2038,10 +2092,6 @@ alembic = [
{file = "alembic-1.4.3-py2.py3-none-any.whl", hash = "sha256:4e02ed2aa796bd179965041afa092c55b51fb077de19d61835673cc80672c01c"},
{file = "alembic-1.4.3.tar.gz", hash = "sha256:5334f32314fb2a56d86b4c4dd1ae34b08c03cae4cb888bc699942104d66bc245"},
]
appdirs = [
{file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"},
{file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"},
]
appnope = [
{file = "appnope-0.1.0-py2.py3-none-any.whl", hash = "sha256:5b26757dc6f79a3b7dc9fab95359328d5747fcb2409d331ea66d0272b90ab2a0"},
{file = "appnope-0.1.0.tar.gz", hash = "sha256:8b995ffe925347a2138d7ac0fe77155e4311a0ea6d6da4f5128fe4b3cbe5ed71"},
@ -2050,6 +2100,10 @@ arrow = [
{file = "arrow-0.16.0-py2.py3-none-any.whl", hash = "sha256:98184d8dd3e5d30b96c2df4596526f7de679ccb467f358b82b0f686436f3a6b8"},
{file = "arrow-0.16.0.tar.gz", hash = "sha256:92aac856ea5175c804f7ccb96aca4d714d936f1c867ba59d747a8096ec30e90a"},
]
astroid = [
{file = "astroid-2.11.6-py3-none-any.whl", hash = "sha256:ba33a82a9a9c06a5ceed98180c5aab16e29c285b828d94696bf32d6015ea82a9"},
{file = "astroid-2.11.6.tar.gz", hash = "sha256:4f933d0bf5e408b03a6feb5d23793740c27e07340605f236496cd6ce552043d6"},
]
async-timeout = [
{file = "async-timeout-3.0.1.tar.gz", hash = "sha256:0c3c816a028d47f659d6ff5c745cb2acf1f966da1fe5c19c77a70282b25f4c5f"},
{file = "async_timeout-3.0.1-py3-none-any.whl", hash = "sha256:4291ca197d287d274d0b6cb5d6f8f8f82d434ed288f962539ff18cc9012f9ea3"},
@ -2069,6 +2123,10 @@ backcall = [
{file = "backcall-0.2.0-py2.py3-none-any.whl", hash = "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255"},
{file = "backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"},
]
"backports.entry-points-selectable" = [
{file = "backports.entry_points_selectable-1.1.1-py2.py3-none-any.whl", hash = "sha256:7fceed9532a7aa2bd888654a7314f864a3c16a4e710b34a58cfc0f08114c663b"},
{file = "backports.entry_points_selectable-1.1.1.tar.gz", hash = "sha256:914b21a479fde881635f7af5adc7f6e38d6b274be32269070c53b698c60d5386"},
]
bcrypt = [
{file = "bcrypt-3.2.0-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:b589229207630484aefe5899122fb938a5b017b0f4349f769b8c13e78d99a8fd"},
{file = "bcrypt-3.2.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:c95d4cbebffafcdd28bd28bb4e25b31c50f6da605c81ffd9ad8a3d1b2ab7b1b6"},
@ -2269,13 +2327,17 @@ deprecated = [
{file = "Deprecated-1.2.13-py2.py3-none-any.whl", hash = "sha256:64756e3e14c8c5eea9795d93c524551432a0be75629f8f29e67ab8caf076c76d"},
{file = "Deprecated-1.2.13.tar.gz", hash = "sha256:43ac5335da90c31c24ba028af536a91d41d53f9e6901ddb021bcc572ce44e38d"},
]
dill = [
{file = "dill-0.3.5.1-py2.py3-none-any.whl", hash = "sha256:33501d03270bbe410c72639b350e941882a8b0fd55357580fbc873fba0c59302"},
{file = "dill-0.3.5.1.tar.gz", hash = "sha256:d75e41f3eff1eee599d738e76ba8f4ad98ea229db8b085318aa2b3333a208c86"},
]
distlib = [
{file = "distlib-0.3.1-py2.py3-none-any.whl", hash = "sha256:8c09de2c67b3e7deef7184574fc060ab8a793e7adbb183d942c389c8b13c52fb"},
{file = "distlib-0.3.1.zip", hash = "sha256:edf6116872c863e1aa9d5bb7cb5e05a022c519a4594dc703843343a9ddd9bff1"},
]
djlint = [
{file = "djlint-0.7.3-py3-none-any.whl", hash = "sha256:714ed457e022047149c8bff57d5be00ce30f8846b60e866791c66d27e7d11e7f"},
{file = "djlint-0.7.3.tar.gz", hash = "sha256:68aad9ddfef883cc9d9e0d177387b74840af5ca12dcce6e4629eb7075c97dc05"},
{file = "djlint-1.3.0-py3-none-any.whl", hash = "sha256:0c986bf542cdac3025d431a5b15e6c3977f652f2e76e408dbb5e7aaab6b73d99"},
{file = "djlint-1.3.0.tar.gz", hash = "sha256:b2d8e6c0a14f88da165296f0da05795d15299b7ab0a9093d670ce9ffd867bc79"},
]
dkimpy = [
{file = "dkimpy-1.0.5.tar.gz", hash = "sha256:9a2420bf09af686736773153fca32a02ae11ecbe24b540c26104628959f91121"},
@ -2296,14 +2358,6 @@ filelock = [
{file = "filelock-3.0.12-py3-none-any.whl", hash = "sha256:929b7d63ec5b7d6b71b0fa5ac14e030b3f70b75747cef1b10da9b879fef15836"},
{file = "filelock-3.0.12.tar.gz", hash = "sha256:18d82244ee114f543149c66a6e0c14e9c4f8a1044b5cdaadd0f82159d6a6ff59"},
]
flake8 = [
{file = "flake8-4.0.1-py2.py3-none-any.whl", hash = "sha256:479b1304f72536a55948cb40a32dce8bb0ffe3501e26eaf292c7e60eb5e0428d"},
{file = "flake8-4.0.1.tar.gz", hash = "sha256:806e034dda44114815e23c16ef92f95c91e4c71100ff52813adf7132a6ad870d"},
]
flake8-bugbear = [
{file = "flake8-bugbear-22.1.11.tar.gz", hash = "sha256:4c2a4136bd4ecb8bf02d5159af302ffc067642784c9d0488b33ce4610da825ee"},
{file = "flake8_bugbear-22.1.11-py3-none-any.whl", hash = "sha256:ce7ae44aaaf67ef192b8a6de94a5ac617144e1675ad0654fdea556f48dc18d9b"},
]
flanker = [
{file = "flanker-0.9.11.tar.gz", hash = "sha256:974418e5b498fd3bcb3859c22e22d26495257f9cf98b744c17f2335aca86e001"},
]
@ -2474,6 +2528,14 @@ gunicorn = [
{file = "gunicorn-20.0.4-py2.py3-none-any.whl", hash = "sha256:cd4a810dd51bf497552cf3f863b575dabd73d6ad6a91075b65936b151cbf4f9c"},
{file = "gunicorn-20.0.4.tar.gz", hash = "sha256:1904bb2b8a43658807108d59c3f3d56c2b6121a701161de0ddf9ad140073c626"},
]
html-tag-names = [
{file = "html-tag-names-0.1.2.tar.gz", hash = "sha256:04924aca48770f36b5a41c27e4d917062507be05118acb0ba869c97389084297"},
{file = "html_tag_names-0.1.2-py3-none-any.whl", hash = "sha256:eeb69ef21078486b615241f0393a72b41352c5219ee648e7c61f5632d26f0420"},
]
html-void-elements = [
{file = "html-void-elements-0.1.0.tar.gz", hash = "sha256:931b88f84cd606fee0b582c28fcd00e41d7149421fb673e1e1abd2f0c4f231f0"},
{file = "html_void_elements-0.1.0-py3-none-any.whl", hash = "sha256:784cf39db03cdeb017320d9301009f8f3480f9d7b254d0974272e80e0cb5e0d2"},
]
httplib2 = [
{file = "httplib2-0.18.1-py3-none-any.whl", hash = "sha256:ca2914b015b6247791c4866782fa6042f495b94401a0f0bd3e1d6e0ba2236782"},
{file = "httplib2-0.18.1.tar.gz", hash = "sha256:8af66c1c52c7ffe1aa5dc4bcd7c769885254b0756e6e69f953c7f0ab49a70ba3"},
@ -2491,8 +2553,8 @@ idna = [
{file = "idna-2.10.tar.gz", hash = "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6"},
]
importlib-metadata = [
{file = "importlib_metadata-1.7.0-py2.py3-none-any.whl", hash = "sha256:dc15b2969b4ce36305c51eebe62d418ac7791e9a157911d58bfb1f9ccd8e2070"},
{file = "importlib_metadata-1.7.0.tar.gz", hash = "sha256:90bb658cdbbf6d1735b6341ce708fc7024a3e14e99ffdc5783edea9f9b077f83"},
{file = "importlib_metadata-4.12.0-py3-none-any.whl", hash = "sha256:7401a975809ea1fdc658c3aa4f78cc2195a0e019c5cbc4c06122884e9ae80c23"},
{file = "importlib_metadata-4.12.0.tar.gz", hash = "sha256:637245b8bab2b6502fcbc752cc4b7a6f6243bb02b31c5c26156ad103d3d45670"},
]
iniconfig = [
{file = "iniconfig-1.0.1-py3-none-any.whl", hash = "sha256:80cf40c597eb564e86346103f609d74efce0f6b4d4f30ec8ce9e2c26411ba437"},
@ -2506,6 +2568,10 @@ ipython-genutils = [
{file = "ipython_genutils-0.2.0-py2.py3-none-any.whl", hash = "sha256:72dd37233799e619666c9f639a9da83c34013a73e8bbc79a7a6348d93c61fab8"},
{file = "ipython_genutils-0.2.0.tar.gz", hash = "sha256:eb2e116e75ecef9d4d228fdc66af54269afa26ab4463042e33785b887c628ba8"},
]
isort = [
{file = "isort-5.10.1-py3-none-any.whl", hash = "sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7"},
{file = "isort-5.10.1.tar.gz", hash = "sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951"},
]
itsdangerous = [
{file = "itsdangerous-1.1.0-py2.py3-none-any.whl", hash = "sha256:b12271b2047cb23eeb98c8b5622e2e5c5e9abd9784a153e9d8ef9cb4dd09d749"},
{file = "itsdangerous-1.1.0.tar.gz", hash = "sha256:321b033d07f2a4136d3ec762eac9f16a10ccd60f53c0c91af90217ace7ba1f19"},
@ -2526,6 +2592,45 @@ jwcrypto = [
{file = "jwcrypto-0.8-py2.py3-none-any.whl", hash = "sha256:16e17faa4dce36551ade3a3ccb06236a61e5924ea1db163c9be9827acf935a82"},
{file = "jwcrypto-0.8.tar.gz", hash = "sha256:b7fee2635bbefdf145399392f5be26ad54161c8271c66b5fe107b4b452f06c24"},
]
lazy-object-proxy = [
{file = "lazy-object-proxy-1.7.1.tar.gz", hash = "sha256:d609c75b986def706743cdebe5e47553f4a5a1da9c5ff66d76013ef396b5a8a4"},
{file = "lazy_object_proxy-1.7.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bb8c5fd1684d60a9902c60ebe276da1f2281a318ca16c1d0a96db28f62e9166b"},
{file = "lazy_object_proxy-1.7.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a57d51ed2997e97f3b8e3500c984db50a554bb5db56c50b5dab1b41339b37e36"},
{file = "lazy_object_proxy-1.7.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd45683c3caddf83abbb1249b653a266e7069a09f486daa8863fb0e7496a9fdb"},
{file = "lazy_object_proxy-1.7.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:8561da8b3dd22d696244d6d0d5330618c993a215070f473b699e00cf1f3f6443"},
{file = "lazy_object_proxy-1.7.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fccdf7c2c5821a8cbd0a9440a456f5050492f2270bd54e94360cac663398739b"},
{file = "lazy_object_proxy-1.7.1-cp310-cp310-win32.whl", hash = "sha256:898322f8d078f2654d275124a8dd19b079080ae977033b713f677afcfc88e2b9"},
{file = "lazy_object_proxy-1.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:85b232e791f2229a4f55840ed54706110c80c0a210d076eee093f2b2e33e1bfd"},
{file = "lazy_object_proxy-1.7.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:46ff647e76f106bb444b4533bb4153c7370cdf52efc62ccfc1a28bdb3cc95442"},
{file = "lazy_object_proxy-1.7.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:12f3bb77efe1367b2515f8cb4790a11cffae889148ad33adad07b9b55e0ab22c"},
{file = "lazy_object_proxy-1.7.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c19814163728941bb871240d45c4c30d33b8a2e85972c44d4e63dd7107faba44"},
{file = "lazy_object_proxy-1.7.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:e40f2013d96d30217a51eeb1db28c9ac41e9d0ee915ef9d00da639c5b63f01a1"},
{file = "lazy_object_proxy-1.7.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:2052837718516a94940867e16b1bb10edb069ab475c3ad84fd1e1a6dd2c0fcfc"},
{file = "lazy_object_proxy-1.7.1-cp36-cp36m-win32.whl", hash = "sha256:6a24357267aa976abab660b1d47a34aaf07259a0c3859a34e536f1ee6e76b5bb"},
{file = "lazy_object_proxy-1.7.1-cp36-cp36m-win_amd64.whl", hash = "sha256:6aff3fe5de0831867092e017cf67e2750c6a1c7d88d84d2481bd84a2e019ec35"},
{file = "lazy_object_proxy-1.7.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6a6e94c7b02641d1311228a102607ecd576f70734dc3d5e22610111aeacba8a0"},
{file = "lazy_object_proxy-1.7.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4ce15276a1a14549d7e81c243b887293904ad2d94ad767f42df91e75fd7b5b6"},
{file = "lazy_object_proxy-1.7.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e368b7f7eac182a59ff1f81d5f3802161932a41dc1b1cc45c1f757dc876b5d2c"},
{file = "lazy_object_proxy-1.7.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:6ecbb350991d6434e1388bee761ece3260e5228952b1f0c46ffc800eb313ff42"},
{file = "lazy_object_proxy-1.7.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:553b0f0d8dbf21890dd66edd771f9b1b5f51bd912fa5f26de4449bfc5af5e029"},
{file = "lazy_object_proxy-1.7.1-cp37-cp37m-win32.whl", hash = "sha256:c7a683c37a8a24f6428c28c561c80d5f4fd316ddcf0c7cab999b15ab3f5c5c69"},
{file = "lazy_object_proxy-1.7.1-cp37-cp37m-win_amd64.whl", hash = "sha256:df2631f9d67259dc9620d831384ed7732a198eb434eadf69aea95ad18c587a28"},
{file = "lazy_object_proxy-1.7.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:07fa44286cda977bd4803b656ffc1c9b7e3bc7dff7d34263446aec8f8c96f88a"},
{file = "lazy_object_proxy-1.7.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4dca6244e4121c74cc20542c2ca39e5c4a5027c81d112bfb893cf0790f96f57e"},
{file = "lazy_object_proxy-1.7.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:91ba172fc5b03978764d1df5144b4ba4ab13290d7bab7a50f12d8117f8630c38"},
{file = "lazy_object_proxy-1.7.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:043651b6cb706eee4f91854da4a089816a6606c1428fd391573ef8cb642ae4f7"},
{file = "lazy_object_proxy-1.7.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b9e89b87c707dd769c4ea91f7a31538888aad05c116a59820f28d59b3ebfe25a"},
{file = "lazy_object_proxy-1.7.1-cp38-cp38-win32.whl", hash = "sha256:9d166602b525bf54ac994cf833c385bfcc341b364e3ee71e3bf5a1336e677b55"},
{file = "lazy_object_proxy-1.7.1-cp38-cp38-win_amd64.whl", hash = "sha256:8f3953eb575b45480db6568306893f0bd9d8dfeeebd46812aa09ca9579595148"},
{file = "lazy_object_proxy-1.7.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dd7ed7429dbb6c494aa9bc4e09d94b778a3579be699f9d67da7e6804c422d3de"},
{file = "lazy_object_proxy-1.7.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70ed0c2b380eb6248abdef3cd425fc52f0abd92d2b07ce26359fcbc399f636ad"},
{file = "lazy_object_proxy-1.7.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7096a5e0c1115ec82641afbdd70451a144558ea5cf564a896294e346eb611be1"},
{file = "lazy_object_proxy-1.7.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f769457a639403073968d118bc70110e7dce294688009f5c24ab78800ae56dc8"},
{file = "lazy_object_proxy-1.7.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:39b0e26725c5023757fc1ab2a89ef9d7ab23b84f9251e28f9cc114d5b59c1b09"},
{file = "lazy_object_proxy-1.7.1-cp39-cp39-win32.whl", hash = "sha256:2130db8ed69a48a3440103d4a520b89d8a9405f1b06e2cc81640509e8bf6548f"},
{file = "lazy_object_proxy-1.7.1-cp39-cp39-win_amd64.whl", hash = "sha256:677ea950bef409b47e51e733283544ac3d660b709cfce7b187f5ace137960d61"},
{file = "lazy_object_proxy-1.7.1-pp37.pp38-none-any.whl", hash = "sha256:d66906d5785da8e0be7360912e99c9188b70f52c422f9fc18223347235691a84"},
]
limits = [
{file = "limits-1.5.1-py2-none-any.whl", hash = "sha256:0e5f8b10f18dd809eb2342f5046eb9aa5e4e69a0258567b5f4aa270647d438b3"},
{file = "limits-1.5.1.tar.gz", hash = "sha256:f0c3319f032c4bfad68438ed1325c0fac86dac64582c7c25cddc87a0b658fa20"},
@ -2593,8 +2698,8 @@ matplotlib-inline = [
{file = "matplotlib_inline-0.1.3-py3-none-any.whl", hash = "sha256:aed605ba3b72462d64d475a21a9296f400a19c4f74a31b59103d2a99ffd5aa5c"},
]
mccabe = [
{file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"},
{file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"},
{file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"},
{file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"},
]
memory-profiler = [
{file = "memory_profiler-0.57.0.tar.gz", hash = "sha256:23b196f91ea9ac9996e30bfab1e82fecc30a4a1d24870e81d1e81625f786a2c3"},
@ -2825,10 +2930,6 @@ pyasn1-modules = [
{file = "pyasn1_modules-0.2.8-py3.6.egg", hash = "sha256:cbac4bc38d117f2a49aeedec4407d23e8866ea4ac27ff2cf7fb3e5b570df19e0"},
{file = "pyasn1_modules-0.2.8-py3.7.egg", hash = "sha256:c29a5e5cc7a3f05926aff34e097e84f8589cd790ce0ed41b67aed6857b26aafd"},
]
pycodestyle = [
{file = "pycodestyle-2.8.0-py2.py3-none-any.whl", hash = "sha256:720f8b39dde8b293825e7ff02c475f3077124006db4f440dcbc9a20b76548a20"},
{file = "pycodestyle-2.8.0.tar.gz", hash = "sha256:eddd5847ef438ea1c7870ca7eb78a9d47ce0cdb4851a5523949f2601d0cbbe7f"},
]
pycparser = [
{file = "pycparser-2.20-py2.py3-none-any.whl", hash = "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705"},
{file = "pycparser-2.20.tar.gz", hash = "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0"},
@ -2870,10 +2971,6 @@ pycryptodome = [
{file = "pycryptodome-3.9.8-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:9f62d21bc693f3d7d444f17ed2ad7a913b4c37c15cd807895d013c39c0517dfd"},
{file = "pycryptodome-3.9.8.tar.gz", hash = "sha256:0e24171cf01021bc5dc17d6a9d4f33a048f09d62cc3f62541e95ef104588bda4"},
]
pyflakes = [
{file = "pyflakes-2.4.0-py2.py3-none-any.whl", hash = "sha256:3bb3a3f256f4b7968c9c788781e4ff07dce46bdf12339dcda61053375426ee2e"},
{file = "pyflakes-2.4.0.tar.gz", hash = "sha256:05a85c2872edf37a4ed30b0cce2f6093e1d0581f8c19d7393122da7e25b2b24c"},
]
pygments = [
{file = "Pygments-2.7.4-py3-none-any.whl", hash = "sha256:bc9591213a8f0e0ca1a5e68a479b4887fdc3e75d0774e5c71c31920c427de435"},
{file = "Pygments-2.7.4.tar.gz", hash = "sha256:df49d09b498e83c1a73128295860250b0b7edd4c723a32e9bc0d295c7c2ec337"},
@ -2882,6 +2979,10 @@ pyjwt = [
{file = "PyJWT-2.4.0-py3-none-any.whl", hash = "sha256:72d1d253f32dbd4f5c88eaf1fdc62f3a19f676ccbadb9dbc5d07e951b2b26daf"},
{file = "PyJWT-2.4.0.tar.gz", hash = "sha256:d42908208c699b3b973cbeb01a969ba6a96c821eefb1c5bfe4c390c01d67abba"},
]
pylint = [
{file = "pylint-2.14.4-py3-none-any.whl", hash = "sha256:89b61867db16eefb7b3c5b84afc94081edaf11544189e2b238154677529ad69f"},
{file = "pylint-2.14.4.tar.gz", hash = "sha256:47705453aa9dce520e123a7d51843d5f0032cbfa06870f89f00927aa1f735a4a"},
]
pyopenssl = [
{file = "pyOpenSSL-19.1.0-py2.py3-none-any.whl", hash = "sha256:621880965a720b8ece2f1b2f54ea2071966ab00e2970ad2ce11d596102063504"},
{file = "pyOpenSSL-19.1.0.tar.gz", hash = "sha256:9a24494b2602aaf402be5c9e30a0b82d4a5c67528fe8fb475e3f3bc00dd69507"},
@ -3240,12 +3341,12 @@ toml = [
{file = "toml-0.10.1.tar.gz", hash = "sha256:926b612be1e5ce0634a2ca03470f95169cf16f939018233a670519cb4ac58b0f"},
]
tomli = [
{file = "tomli-2.0.0-py3-none-any.whl", hash = "sha256:b5bde28da1fed24b9bd1d4d2b8cba62300bfb4ec9a6187a957e8ddb9434c5224"},
{file = "tomli-2.0.0.tar.gz", hash = "sha256:c292c34f58502a1eb2bbb9f5bbc9a5ebc37bee10ffb8c2d6bbdfa8eb13cc14e1"},
{file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
{file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
]
tomlkit = [
{file = "tomlkit-0.8.0-py3-none-any.whl", hash = "sha256:b824e3466f1d475b2b5f1c392954c6cb7ea04d64354ff7300dc7c14257dc85db"},
{file = "tomlkit-0.8.0.tar.gz", hash = "sha256:29e84a855712dfe0e88a48f6d05c21118dbafb283bb2eed614d46f80deb8e9a1"},
{file = "tomlkit-0.11.0-py3-none-any.whl", hash = "sha256:0f4050db66fd445b885778900ce4dd9aea8c90c4721141fde0d6ade893820ef1"},
{file = "tomlkit-0.11.0.tar.gz", hash = "sha256:71ceb10c0eefd8b8f11fe34e8a51ad07812cb1dc3de23247425fbc9ddc47b9dd"},
]
tqdm = [
{file = "tqdm-4.64.0-py2.py3-none-any.whl", hash = "sha256:74a2cdefe14d11442cedf3ba4e21a3b84ff9a2dbdc6cfae2c34addb2a14a5ea6"},
@ -3302,8 +3403,8 @@ urllib3 = [
{file = "urllib3-1.25.10.tar.gz", hash = "sha256:91056c15fa70756691db97756772bb1eb9678fa585d9184f24534b100dc60f4a"},
]
virtualenv = [
{file = "virtualenv-20.0.31-py2.py3-none-any.whl", hash = "sha256:e0305af10299a7fb0d69393d8f04cb2965dda9351140d11ac8db4e5e3970451b"},
{file = "virtualenv-20.0.31.tar.gz", hash = "sha256:43add625c53c596d38f971a465553f6318decc39d98512bc100fa1b1e839c8dc"},
{file = "virtualenv-20.8.1-py2.py3-none-any.whl", hash = "sha256:10062e34c204b5e4ec5f62e6ef2473f8ba76513a9a617e873f1f8fb4a519d300"},
{file = "virtualenv-20.8.1.tar.gz", hash = "sha256:bcc17f0b3a29670dd777d6f0755a4c04f28815395bca279cdcb213b97199a6b8"},
]
watchtower = [
{file = "watchtower-0.8.0-py2.py3-none-any.whl", hash = "sha256:d6704b258494bddc3e9ddda286ef5067e4b09ab4287aea4289afdd035cc4742b"},

View File

@ -53,7 +53,7 @@ packages = [
include = ["templates/*", "templates/**/*"]
[tool.poetry.dependencies]
python = "^3.7"
python = "^3.7.2"
flask = "^1.1.2"
flask_login = "^0.5.0"
wtforms = "^2.3.3"
@ -116,9 +116,8 @@ pytest = "^7.0.0"
pytest-cov = "^3.0.0"
pre-commit = "^2.17.0"
black = "^22.1.0"
flake8 = "^4.0.1"
flake8-bugbear = "^22.1.11"
djlint = "0.7.3"
djlint = "^1.3.0"
pylint = "^2.14.4"
[build-system]
requires = ["poetry>=0.12"]

View File

@ -1,7 +1,7 @@
#!/bin/sh
export DB_URI=postgresql://myuser:mypassword@localhost:15432/simplelogin
echo 'drop schema public cascade; create schema public;' | psql $DB_URI
echo 'drop schema public cascade; create schema public;' | psql $DB_URI
poetry run alembic upgrade head
poetry run flask dummy-data

View File

@ -1,6 +1,6 @@
#!/bin/sh
export DB_URI=postgresql://myuser:mypassword@localhost:15432/test
echo 'drop schema public cascade; create schema public;' | psql $DB_URI
echo 'drop schema public cascade; create schema public;' | psql $DB_URI
poetry run alembic upgrade head

View File

@ -7,7 +7,7 @@ function confirmDeleteAlias() {
let message = `Maybe you want to disable the alias instead? Please note once deleted, it <b>can't</b> be restored.`;
if (aliasDomainTrashUrl !== undefined) {
message = `Maybe you want to disable the alias instead? When it's deleted, it's moved to the domain
message = `Maybe you want to disable the alias instead? When it's deleted, it's moved to the domain
<a href="${aliasDomainTrashUrl}">trash</a>`;
}

View File

@ -6,9 +6,7 @@
<div class="card">
<div class="card-body p-6 text-center">
<h1 class="h4">An email to validate your email is on its way.</h1>
<p>
Please check your inbox/spam folder.
</p>
<p>Please check your inbox/spam folder.</p>
</div>
</div>
{% endblock %}

View File

@ -29,9 +29,7 @@
style="border: 1px solid"
class="my-2 img-fluid"/>
</p>
<p>
This might seem like "magic" but trust us, only the first time is a bit awkward.
</p>
<p>This might seem like "magic" but trust us, only the first time is a bit awkward.</p>
<p>
{% if alias.mailbox_id %}
{% if alias.mailboxes | length == 1 %}

View File

@ -10,9 +10,7 @@
<p>
You are invited to become the owner of the alias <b>{{ alias.email }}</b>
</p>
<p>
Please choose the mailbox(es) that owns this alias 👇
</p>
<p>Please choose the mailbox(es) that owns this alias 👇</p>
<form method="post" class="mt-2">
<select data-width="100%" class="mailbox-select" multiple name="mailbox_ids">
{% for mailbox in mailboxes %}

View File

@ -51,9 +51,7 @@
</form>
{% endif %}
{% endif %}
<p class="mt-5">
This person can then confirm the reception and become the owner of the alias.
</p>
<p class="mt-5">This person can then confirm the reception and become the owner of the alias.</p>
<div class="alert alert-danger">After the confirmation, you can no longer use this alias.</div>
</div>
</div>

View File

@ -131,5 +131,5 @@
that.closest("form").submit();
})
</script>
</script>
{% endblock %}

View File

@ -109,9 +109,7 @@
<div id="debug-zone">
<hr />
<h3>Debug Zone</h3>
<p>
You can test whether an alias will be automatically created given the rules above
</p>
<p>You can test whether an alias will be automatically created given the rules above</p>
<div class="alert alert-info">No worries, no alias will be created during the test :)</div>
<form method="post" action="#debug-zone">
<input type="hidden" name="form-name" value="test-auto-create-rule">

View File

@ -7,12 +7,8 @@
<div class="card">
<div class="card-body">
<h1 class="h2">Entering Sudo Mode</h1>
<p>
The next page contains security related setting.
</p>
<p>
Please enter your account password so that we can ensure it's you.
</p>
<p>The next page contains security related setting.</p>
<p>Please enter your account password so that we can ensure it's you.</p>
<form method="post">
{{ password_check_form.csrf_token }}
<div class="font-weight-bold mt-5">Password</div>

View File

@ -7,9 +7,7 @@
<div class="card">
<div class="card-body">
<h1 class="h2">Extend Subscription</h1>
<p>
Your subscription is expired on {{ coinbase_subscription.end_at.format("YYYY-MM-DD") }}
</p>
<p>Your subscription is expired on {{ coinbase_subscription.end_at.format("YYYY-MM-DD") }}</p>
<div>
<a class="buy-with-crypto"
data-custom="{{ current_user.id }}"

View File

@ -11,9 +11,7 @@
<div class="card">
<div class="card-body">
<h1 class="h2">Manage Your Security Key</h1>
<p>
Unlink all keys will also disable WebAuthn 2FA.
</p>
<p>Unlink all keys will also disable WebAuthn 2FA.</p>
<form id="formManageKey" method="post">
{{ fido_manage_form.csrf_token }}
{{ fido_manage_form.credential_id(class="form-control", placeholder="") }}

View File

@ -13,9 +13,7 @@
<div class="card">
<div class="card-body">
<h1 class="h2 text-center">Register Your Security Key</h1>
<p class="text-center">
Follow your browser's steps to register your security key with SimpleLogin
</p>
<p class="text-center">Follow your browser's steps to register your security key with SimpleLogin</p>
<form id="formRegisterKey" method="post">
{{ fido_token_form.csrf_token }}
{{ fido_token_form.sk_assertion(class="form-control", placeholder="") }}

View File

@ -26,441 +26,491 @@
display: none;
}
</style>
</style>
{% endblock %}
{% block title %}Alias{% endblock %}
{% block default_content %}
<!-- Global Stats -->
<div class="row">
<div class="col-12 col-md-6 col-lg-3">
<div class="card">
<div class="card-body">
<div class="d-flex align-items-center">
<div class="subheader">Aliases</div>
<div class="text-muted" style="order: 2; margin-left: auto; font-size: .8rem">All time</div>
</div>
<div class="h1 m-0">{{ stats.nb_alias }}</div>
</div>
</div>
</div>
<div class="col-12 col-md-6 col-lg-3">
<div class="card">
<div class="card-body">
<div class="d-flex align-items-center">
<div class="subheader">Forwards</div>
<div class="text-muted" style="order: 2; margin-left: auto; font-size: .8rem">Last 14 days</div>
</div>
<div class="h1 m-0">{{ stats.nb_forward }}</div>
</div>
</div>
</div>
<div class="col-12 col-md-6 col-lg-3">
<div class="card">
<div class="card-body">
<div class="d-flex align-items-center">
<div class="subheader">Replies/Sent</div>
<div class="text-muted" style="order: 2; margin-left: auto; font-size: .8rem">Last 14 days</div>
</div>
<div class="h1 m-0">{{ stats.nb_reply }}</div>
</div>
</div>
</div>
<div class="col-12 col-md-6 col-lg-3">
<div class="card">
<div class="card-body">
<div class="d-flex align-items-center">
<div class="subheader">Blocks</div>
<div class="text-muted" style="order: 2; margin-left: auto; font-size: .8rem">Last 14 days</div>
</div>
<div class="h1 m-0">{{ stats.nb_block }}</div>
</div>
</div>
</div>
</div>
<!-- END Global Stats -->
<!-- Controls: buttons & search -->
<div id="filter-app">
<div class="row mb-3">
<div class="col d-flex">
<div>
<div class="btn-group" role="group">
<form method="post">
<input type="hidden" name="form-name" value="create-custom-email">
<button data-toggle="tooltip" title="Create a custom alias" class="btn btn-primary mr-2">
<i class="fa fa-plus"></i> New Custom Alias
</button>
</form>
<div class="btn-group" role="group">
<form method="post">
<input type="hidden" name="form-name" value="create-random-email">
<button data-toggle="tooltip" title="Create a totally random alias" class="btn btn-success">
<i class="fa fa-random"></i> Random Alias
</button>
</form>
<button id="btnGroupDrop1" type="button" class="btn btn-success dropdown-toggle btn-group-border-left" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
</button>
<div class="dropdown-menu dropdown-menu-right border-left" aria-labelledby="btnGroupDrop1">
<div>
<form method="post">
<input type="hidden" name="form-name" value="create-random-email">
<input type="hidden" name="generator_scheme" value="{{ AliasGeneratorEnum.word.value }}">
<button class="dropdown-item">By Random Words</button>
</form>
</div>
<div>
<form method="post">
<input type="hidden" name="form-name" value="create-random-email">
<input type="hidden" name="generator_scheme" value="{{ AliasGeneratorEnum.uuid.value }}">
<button class="dropdown-item">By UUID</button>
</form>
</div>
</div>
</div>
</div>
</div>
<div style="margin-left: auto">
<div class="btn-group">
<a v-if="!showFilter" @click="toggleFilter()" class="btn btn-outline-secondary">
<i class="fe fe-chevrons-down"></i> Filters
</a>
</div>
</div>
</div>
</div>
<div class="row mb-2" v-if="showFilter" id="filter-control">
<!-- Filter Control -->
<div class="col d-flex">
<form method="get" class="form-inline">
<select name="sort" onchange="this.form.submit()" class="form-control mr-3 shadow">
<option value="" {% if sort == "" %} selected{% endif %}>
Sort by most recent activity
</option>
<option value="old2new" {% if sort == "old2new" %} selected{% endif %}>
Alias Old-Recent
</option>
<option value="new2old" {% if sort == "new2old" %} selected{% endif %}>
Alias Recent-Old
</option>
<option value="a2z" {% if sort == "a2z" %} selected{% endif %}>
Alias A-Z
</option>
<option value="z2a" {% if sort == "z2a" %} selected{% endif %}>
Alias Z-A
</option>
</select>
<select name="filter" onchange="this.form.submit()" class="form-control mr-3 shadow" style="max-width: 200px">
<option value="" {% if filter == "" %} selected{% endif %}>
All Aliases
</option>
<option value="pinned" {% if filter == "pinned" %} selected{% endif %}>
Pinned Aliases
</option>
<option value="enabled" {% if filter == "enabled" %} selected{% endif %}>
Only Enabled Aliases
</option>
<option value="disabled" {% if filter == "disabled" %} selected{% endif %}>
Only Disabled Aliases
</option>
<option value="hibp" {% if filter == "hibp" %} selected{% endif %}>
Only Aliases Found In Data Breaches
</option>
{% for mailbox in current_user.mailboxes() %}
<option value="mailbox:{{ mailbox.id }}" {% if filter == "mailbox:" ~ mailbox.id %}
selected {% endif %}>
{{ mailbox.email }}'s aliases
</option>
{% endfor %}
{% for directory in current_user.directories %}
<option value="directory:{{ directory.id }}" {% if filter == "directory:" ~ directory.id %}
selected {% endif %}>
Directory <b>{{ directory.name }}</b> aliases
</option>
{% endfor %}
</select>
<input type="search" name="query" placeholder="Enter to search for alias" class="form-control shadow mr-2" style="max-width: 15em" value="{{ query }}">
</form>
<div style="margin-left: auto">
{% if query or sort or filter %}
<a href="{{ url_for('dashboard.index') }}" class="btn btn-outline-secondary">Reset</a>
{% endif %}
<a v-if="showFilter" @click="toggleFilter()" class="btn btn-outline-secondary">
<i class="fe fe-chevrons-up"></i>
</a>
</div>
</div>
</div>
</div>
<!-- END Controls: buttons & search -->
<!-- Alias list -->
<div class="row">
{% for alias_info in alias_infos %}
{% set alias = alias_info.alias %}
<div class="col-12 col-lg-6" id="alias-container-{{ alias.id }}">
<div class="card p-4 shadow-sm {% if alias.id == highlight_alias_id %} highlight-row{% endif %} " {% if highlight_alias_id and alias.id != highlight_alias_id %} style="opacity: 0.6"{% endif %}>
<div class="row">
<div class="col-8">
<span class="{% if alias.id == highlight_alias_id %} highlighted{% endif %} clipboard cursor mb-0" {% if loop.index ==1 %}
data-intro="This is your first <em>alias</em>.
<br />
<br />
Emails sent to an alias are <em>forwarded</em> to your <em>real</em> email address.
<br />
<br />
" data-step="2"
{% endif %}
{% if alias.enabled %}data-toggle="tooltip" title="Click to copy" data-clipboard-text="{{ alias.email }}"{% endif %}
>
<span class="font-weight-bold">{{ alias.email }}</span>
</span>
{% if alias.automatic_creation %}
<span class="fa fa-inbox" data-toggle="tooltip" title="This alias was automatically generated because of an incoming email"></span>
{% endif %}
{% if alias.pinned %}
<span class="fa fa-thumb-tack" data-toggle="tooltip" title="This alias is pinned"></span>
{% endif %}
{% if alias.hibp_breaches | length > 0 %}
<a href="https://haveibeenpwned.com/account/{{ alias.email }}">
<span class="fa fa-warning text-danger" data-toggle="tooltip" title="This alias was found in {{ alias.hibp_breaches | length }} data breaches. Check haveibeenpwned.com for more information."></span>
</a>
{% endif %}
{% if alias.custom_domain and not alias.custom_domain.verified %}
<span class="fa fa-warning text-warning" data-toggle="tooltip" title="Alias can't receive emails as its domain doesn't have MX records set up."></span>
{% endif %}
</div>
<div class="col text-right">
<label class="custom-switch cursor" data-toggle="tooltip" {% if alias.enabled %}
title="Disable alias - stop receiving emails sent to this alias" {% else %} title="Enable alias - start receiving emails sent to this alias" {% endif %} {% if loop.index ==1 %}
data-intro="By <em>disabling</em> an alias, emails sent to this alias will <em>not</em> be forwarded to your inbox.
<br />
<br />
" data-step="3"
{% endif %}
style="padding-left: 0px">
<input type="checkbox" class="enable-disable-alias custom-switch-input" data-alias="{{ alias.id }}" data-alias-email="{{ alias.email }}" {{ "checked" if alias.enabled else "" }}>
<span class="custom-switch-indicator"></span>
</label>
</div>
</div>
<!-- Email Activity -->
<div class="row mb-2">
<div class="col">
<div style="font-size: 12px">
{% if alias_info.latest_email_log != None %}
{% set email_log = alias_info.latest_email_log %}
{% set contact = alias_info.latest_contact %}
{% if email_log.is_reply %}
{{ contact.website_email }}
<i class="fa fa-reply mr-2" data-toggle="tooltip" title="Email reply/sent from alias"></i>
{{ email_log.created_at | dt }}
{% elif email_log.bounced %}
<span class="text-danger">
{{ contact.website_email }}
<i class="fa fa-warning mr-2" data-toggle="tooltip" title="Email bounced and cannot be forwarded to your mailbox"></i>
{{ email_log.created_at | dt }}
</span>
{% elif email_log.blocked %}
{{ contact.website_email }}
<i class="fa fa-ban mr-2 text-danger" data-toggle="tooltip" title="Email blocked"></i>
{{ email_log.created_at | dt }}
{% else %}
{{ contact.website_email }}
<i class="fa fa-paper-plane mr-2" data-toggle="tooltip" title="Email sent to alias"></i>
{{ email_log.created_at | dt }}
{% include 'partials/toggle_contact.html' %}
{% endif %}
{% else %}
No emails received/sent in the last 14 days. Created {{ alias.created_at | dt }}.
{% endif %}
</div>
</div>
</div>
<!-- END Email Activity -->
<div class="small-text mt-1">
Alias description
</div>
<div class="d-flex mb-2">
<div class="flex-grow-1 mr-2">
<textarea id="note-{{ alias.id }}" name="note" class="form-control" style="font-size: 12px" rows="2" placeholder="e.g. where the alias is used or why is it created">{{ alias.note or "" }}</textarea>
</div>
<div>
<a data-alias="{{ alias.id }}"
class="save-note btn btn-sm btn-outline-success w-100">
Save
</a>
</div>
</div>
<!-- Send Email && More button -->
<div class="row">
<div class="col">
<a href="{{ url_for('dashboard.alias_contact_manager', alias_id=alias.id) }}" id="send-email-{{ alias.id }}" {% if loop.index ==1 %}
data-intro="Not only alias can receive emails, it can <em>send</em> emails too!
<br />
<br />
You can add a new <em>contact</em> for your alias here.
<br />
<br />
If you need to reply to an email, just hit 'Reply': the response will come from your alias and your real email address stays <em>hidden</em>." data-step="4"
{% endif %}
class="btn btn-sm btn-outline-primary
{% if not alias.enabled %}disabled{% endif %}
" data-toggle="tooltip" title="Add new contact, manage your existing contacts">
Contacts&nbsp; &nbsp;<i class="fe fe-send"></i>
</a>
</div>
{% if not current_user.expand_alias_info %}
<div class="col text-right">
<a class="btn btn-sm collapsed"
data-toggle="collapse"
href="#alias-{{ alias.id }}"
role="button"
aria-expanded="false">
<span class="if-collapsed">
More <i class="fe fe-chevron-down"></i>
</span>
<span class="if-not-collapsed">Less
<i class="fe fe-chevron-up"></i>
</span>
</a>
</div>
{% endif %}
</div>
<!-- END Send Email && More button -->
<!-- Collapse section -->
<div class="{% if not current_user.expand_alias_info %} collapse{% endif %} mt-2"
id="alias-{{ alias.id }}">
{% if alias_info.latest_email_log != None %}
<div style="font-size: 12px">
Alias created {{ alias.created_at | dt }}
</div>
{% endif %}
<span class="alias-activity">{{ alias_info.nb_forward }}</span> forwards,
<span class="alias-activity">{{ alias_info.nb_blocked }}</span> blocks,
<span class="alias-activity">{{ alias_info.nb_reply }}</span> sents
in the last 14 days
<a href="{{ url_for('dashboard.alias_log', alias_id=alias.id) }}"
class="btn btn-sm btn-link">
See All &nbsp;
</a>
{% if mailboxes|length > 1 %}
<div class="small-text">
Current mailbox
</div>
<div class="d-flex">
<div class="flex-grow-1 mr-2">
<select required
id="mailbox-{{ alias.id }}"
data-width="100%"
class="mailbox-select"
multiple
name="mailbox">
{% for mailbox in mailboxes %}
<option value="{{ mailbox.id }}" {% if alias_info.contain_mailbox(mailbox.id) %}
selected {% endif %}>
{{ mailbox.email }}
</option>
{% endfor %}
</select>
<!-- Global Stats -->
<div class="row">
<div class="col-12 col-md-6 col-lg-3">
<div class="card">
<div class="card-body">
<div class="d-flex align-items-center">
<div class="subheader">Aliases</div>
<div class="text-muted"
style="order: 2; margin-left: auto; font-size: .8rem">All time</div>
</div>
<div class="h1 m-0">{{ stats.nb_alias }}</div>
</div>
</div>
<div>
<a data-alias="{{ alias.id }}"
class="save-mailbox btn btn-sm btn-outline-info w-100">
Update
</div>
<div class="col-12 col-md-6 col-lg-3">
<div class="card">
<div class="card-body">
<div class="d-flex align-items-center">
<div class="subheader">Forwards</div>
<div class="text-muted"
style="order: 2; margin-left: auto; font-size: .8rem">Last 14 days</div>
</div>
<div class="h1 m-0">{{ stats.nb_forward }}</div>
</div>
</div>
</div>
<div class="col-12 col-md-6 col-lg-3">
<div class="card">
<div class="card-body">
<div class="d-flex align-items-center">
<div class="subheader">Replies/Sent</div>
<div class="text-muted"
style="order: 2; margin-left: auto; font-size: .8rem">Last 14 days</div>
</div>
<div class="h1 m-0">{{ stats.nb_reply }}</div>
</div>
</div>
</div>
<div class="col-12 col-md-6 col-lg-3">
<div class="card">
<div class="card-body">
<div class="d-flex align-items-center">
<div class="subheader">Blocks</div>
<div class="text-muted"
style="order: 2; margin-left: auto; font-size: .8rem">Last 14 days</div>
</div>
<div class="h1 m-0">{{ stats.nb_block }}</div>
</div>
</div>
</div>
</div>
<!-- END Global Stats -->
<!-- Controls: buttons & search -->
<div id="filter-app">
<div class="row mb-3">
<div class="col d-flex">
<div>
<div class="btn-group" role="group">
<form method="post">
<input type="hidden" name="form-name" value="create-custom-email">
<button data-toggle="tooltip"
title="Create a custom alias"
class="btn btn-primary mr-2">
<i class="fa fa-plus"></i> New Custom Alias
</button>
</form>
<div class="btn-group" role="group">
<form method="post">
<input type="hidden" name="form-name" value="create-random-email">
<button data-toggle="tooltip"
title="Create a totally random alias"
class="btn btn-success">
<i class="fa fa-random"></i> Random Alias
</button>
</form>
<button id="btnGroupDrop1"
type="button"
class="btn btn-success dropdown-toggle btn-group-border-left"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false">
</button>
<div class="dropdown-menu dropdown-menu-right border-left"
aria-labelledby="btnGroupDrop1">
<div>
<form method="post">
<input type="hidden" name="form-name" value="create-random-email">
<input type="hidden"
name="generator_scheme"
value="{{ AliasGeneratorEnum.word.value }}">
<button class="dropdown-item">By Random Words</button>
</form>
</div>
<div>
<form method="post">
<input type="hidden" name="form-name" value="create-random-email">
<input type="hidden"
name="generator_scheme"
value="{{ AliasGeneratorEnum.uuid.value }}">
<button class="dropdown-item">By UUID</button>
</form>
</div>
</div>
</div>
</div>
</div>
<div style="margin-left: auto">
<div class="btn-group">
<a v-if="!showFilter"
@click="toggleFilter()"
class="btn btn-outline-secondary">
<i class="fe fe-chevrons-down"></i> Filters
</a>
</div>
</div>
</div>
</div>
<div class="row mb-2" v-if="showFilter" id="filter-control">
<!-- Filter Control -->
<div class="col d-flex">
<form method="get" class="form-inline">
<select name="sort"
onchange="this.form.submit()"
class="form-control mr-3 shadow">
<option value="" {% if sort == "" %} selected{% endif %}>
Sort by most recent activity
</option>
<option value="old2new" {% if sort == "old2new" %} selected{% endif %}>
Alias Old-Recent
</option>
<option value="new2old" {% if sort == "new2old" %} selected{% endif %}>
Alias Recent-Old
</option>
<option value="a2z" {% if sort == "a2z" %} selected{% endif %}>
Alias A-Z
</option>
<option value="z2a" {% if sort == "z2a" %} selected{% endif %}>
Alias Z-A
</option>
</select>
<select name="filter"
onchange="this.form.submit()"
class="form-control mr-3 shadow"
style="max-width: 200px">
<option value="" {% if filter == "" %} selected{% endif %}>
All Aliases
</option>
<option value="pinned" {% if filter == "pinned" %} selected{% endif %}>
Pinned Aliases
</option>
<option value="enabled" {% if filter == "enabled" %} selected{% endif %}>
Only Enabled Aliases
</option>
<option value="disabled" {% if filter == "disabled" %} selected{% endif %}>
Only Disabled Aliases
</option>
<option value="hibp" {% if filter == "hibp" %} selected{% endif %}>
Only Aliases Found In Data Breaches
</option>
{% for mailbox in current_user.mailboxes() %}
<option value="mailbox:{{ mailbox.id }}" {% if filter == "mailbox:" ~ mailbox.id %}
selected {% endif %}>
{{ mailbox.email }}'s aliases
</option>
{% endfor %}
{% for directory in current_user.directories %}
<option value="directory:{{ directory.id }}" {% if filter == "directory:" ~ directory.id %}
selected {% endif %}>
Directory <b>{{ directory.name }}</b> aliases
</option>
{% endfor %}
</select>
<input type="search"
name="query"
placeholder="Enter to search for alias"
class="form-control shadow mr-2"
style="max-width: 15em"
value="{{ query }}">
</form>
<div style="margin-left: auto">
{% if query or sort or filter %}
<a href="{{ url_for('dashboard.index') }}"
class="btn btn-outline-secondary">Reset</a>
{% endif %}
<a v-if="showFilter"
@click="toggleFilter()"
class="btn btn-outline-secondary">
<i class="fe fe-chevrons-up"></i>
</a>
</div>
</div>
</div>
</div>
<!-- END Controls: buttons & search -->
<!-- Alias list -->
<div class="row">
{% for alias_info in alias_infos %}
{% set alias = alias_info.alias %}
<div class="col-12 col-lg-6" id="alias-container-{{ alias.id }}">
<div class="card p-4 shadow-sm {% if alias.id == highlight_alias_id %} highlight-row{% endif %} "
{% if highlight_alias_id and alias.id != highlight_alias_id %} style="opacity: 0.6"{% endif %}>
<div class="row">
<div class="col-8">
<span class="{% if alias.id == highlight_alias_id %} highlighted{% endif %} clipboard cursor mb-0" {% if loop.index ==1 %}
data-intro="This is your first <em>alias</em>.
<br />
<br />
Emails sent to an alias are <em>forwarded</em> to your <em>real</em> email address.
<br />
<br />
" data-step="2"
{% endif %}
{% if alias.enabled %}data-toggle="tooltip" title="Click to copy" data-clipboard-text="{{ alias.email }}"{% endif %}
>
<span class="font-weight-bold">{{ alias.email }}</span>
</span>
{% if alias.automatic_creation %}
<span class="fa fa-inbox"
data-toggle="tooltip"
title="This alias was automatically generated because of an incoming email"></span>
{% endif %}
{% if alias.pinned %}
<span class="fa fa-thumb-tack"
data-toggle="tooltip"
title="This alias is pinned"></span>
{% endif %}
{% if alias.hibp_breaches | length > 0 %}
<a href="https://haveibeenpwned.com/account/{{ alias.email }}">
<span class="fa fa-warning text-danger"
data-toggle="tooltip"
title="This alias was found in {{ alias.hibp_breaches | length }} data breaches. Check haveibeenpwned.com for more information."></span>
</a>
{% endif %}
{% if alias.custom_domain and not alias.custom_domain.verified %}
<span class="fa fa-warning text-warning"
data-toggle="tooltip"
title="Alias can't receive emails as its domain doesn't have MX records set up."></span>
{% endif %}
</div>
<div class="col text-right">
<label class="custom-switch cursor" data-toggle="tooltip" {% if alias.enabled %}
title="Disable alias - stop receiving emails sent to this alias" {% else %} title="Enable alias - start receiving emails sent to this alias" {% endif %} {% if loop.index ==1 %}
data-intro="By <em>disabling</em> an alias, emails sent to this alias will <em>not</em> be forwarded to your inbox.
<br />
<br />
" data-step="3"
{% endif %}
style="padding-left: 0px">
<input type="checkbox" class="enable-disable-alias custom-switch-input" data-alias="{{ alias.id }}" data-alias-email="{{ alias.email }}" {{ "checked" if alias.enabled else "" }}>
<span class="custom-switch-indicator"></span>
</label>
</div>
</div>
<!-- Email Activity -->
<div class="row mb-2">
<div class="col">
<div style="font-size: 12px">
{% if alias_info.latest_email_log != None %}
{% set email_log = alias_info.latest_email_log %}
{% set contact = alias_info.latest_contact %}
{% if email_log.is_reply %}
{{ contact.website_email }}
<i class="fa fa-reply mr-2"
data-toggle="tooltip"
title="Email reply/sent from alias"></i>
{{ email_log.created_at | dt }}
{% elif email_log.bounced %}
<span class="text-danger">
{{ contact.website_email }}
<i class="fa fa-warning mr-2"
data-toggle="tooltip"
title="Email bounced and cannot be forwarded to your mailbox"></i>
{{ email_log.created_at | dt }}
</span>
{% elif email_log.blocked %}
{{ contact.website_email }}
<i class="fa fa-ban mr-2 text-danger"
data-toggle="tooltip"
title="Email blocked"></i>
{{ email_log.created_at | dt }}
{% else %}
{{ contact.website_email }}
<i class="fa fa-paper-plane mr-2"
data-toggle="tooltip"
title="Email sent to alias"></i>
{{ email_log.created_at | dt }}
{% include 'partials/toggle_contact.html' %}
{% endif %}
{% else %}
No emails received/sent in the last 14 days. Created {{ alias.created_at | dt }}.
{% endif %}
</div>
</div>
</div>
<!-- END Email Activity -->
<div class="small-text mt-1">
Alias description
</div>
<div class="d-flex mb-2">
<div class="flex-grow-1 mr-2">
<textarea id="note-{{ alias.id }}" name="note" class="form-control" style="font-size: 12px" rows="2" placeholder="e.g. where the alias is used or why is it created">{{ alias.note or "" }}</textarea>
</div>
<div>
<a data-alias="{{ alias.id }}"
class="save-note btn btn-sm btn-outline-success w-100">
Save
</a>
</div>
</div>
<!-- Send Email && More button -->
<div class="row">
<div class="col">
<a href="{{ url_for('dashboard.alias_contact_manager', alias_id=alias.id) }}" id="send-email-{{ alias.id }}" {% if loop.index ==1 %}
data-intro="Not only alias can receive emails, it can <em>send</em> emails too!
<br />
<br />
You can add a new <em>contact</em> for your alias here.
<br />
<br />
If you need to reply to an email, just hit 'Reply': the response will come from your alias and your real email address stays <em>hidden</em>." data-step="4"
{% endif %}
class="btn btn-sm btn-outline-primary
{% if not alias.enabled %}disabled{% endif %}
" data-toggle="tooltip" title="Add new contact, manage your existing contacts">
Contacts&nbsp; &nbsp;<i class="fe fe-send"></i>
</a>
</div>
</div>
{% elif alias_info.mailbox != None and alias_info.mailbox.email != current_user.email %}
<div class="small-text">
Owned by <b>{{ alias_info.mailbox.email }}</b> mailbox
</div>
{% endif %}
<div class="small-text mt-2"
data-toogle="tooltip"
title="When sending an email from this alias, the email will have 'Display Name <{{ alias.email }}>' as sender.">
Display name
<i class="fe fe-help-circle"></i>
</div>
<div class="d-flex">
<div class="flex-grow-1 mr-2">
<input id="alias-name-{{ alias.id }}"
value="{{ alias.name or '' }}"
class="form-control"
placeholder="{{ alias.custom_domain.name or "Alias name" }}">
</div>
<div>
<a data-alias="{{ alias.id }}"
class="save-alias-name btn btn-sm btn-outline-primary w-100">
Save
</a>
</div>
</div>
{% if alias.mailbox_support_pgp() %}
{% if not current_user.expand_alias_info %}
<div class="small-text mt-2"
data-toogle="tooltip"
title="You can decide to turn off the PGP for an alias. This can be useful if the sender already encrypts the emails">
PGP
<i class="fe fe-help-circle"></i>
<div class="col text-right">
<a class="btn btn-sm collapsed"
data-toggle="collapse"
href="#alias-{{ alias.id }}"
role="button"
aria-expanded="false">
<span class="if-collapsed">
More <i class="fe fe-chevron-down"></i>
</span>
<span class="if-not-collapsed">Less
<i class="fe fe-chevron-up"></i>
</span>
</a>
</div>
{% endif %}
</div>
<div>
<label class="custom-switch cursor pl-0">
<input type="checkbox" class="enable-disable-pgp custom-switch-input" data-alias="{{ alias.id }}" data-alias-email="{{ alias.email }}" {{ "checked" if alias.pgp_enabled() else "" }}>
<span class="custom-switch-indicator"></span>
</label>
</div>
{% endif %}
<div class="small-text mt-2"
data-toogle="tooltip"
title="it's always pinned on top">
Pin this alias
<i class="fe fe-help-circle"></i>
</div>
<div>
<label class="custom-switch cursor pl-0">
<input type="checkbox" class="pin-alias custom-switch-input" data-alias="{{ alias.id }}" data-alias-email="{{ alias.email }}" {{ "checked" if alias.pinned else "" }}>
<span class="custom-switch-indicator"></span>
</label>
</div>
<div>
<div class="btn-group float-right" role="group" aria-label="Basic example">
<a href="{{ url_for('dashboard.alias_transfer_send_route', alias_id=alias.id) }}"
<!-- END Send Email && More button -->
<!-- Collapse section -->
<div class="{% if not current_user.expand_alias_info %} collapse{% endif %} mt-2"
id="alias-{{ alias.id }}">
{% if alias_info.latest_email_log != None %}
<div style="font-size: 12px">
Alias created {{ alias.created_at | dt }}
</div>
{% endif %}
<span class="alias-activity">{{ alias_info.nb_forward }}</span> forwards,
<span class="alias-activity">{{ alias_info.nb_blocked }}</span> blocks,
<span class="alias-activity">{{ alias_info.nb_reply }}</span> sents
in the last 14 days
<a href="{{ url_for('dashboard.alias_log', alias_id=alias.id) }}"
class="btn btn-sm btn-link">
Transfer
<i class="ml-0 dropdown-icon fe fe-share-2 text-primary"></i>
See All &nbsp;
</a>
<form method="post">
<input type="hidden" name="form-name" value="delete-alias">
<input type="hidden" name="alias-id" value="{{ alias.id }}">
<input type="hidden" name="alias" class="alias" value="{{ alias.email }}">
<span class="btn btn-link btn-sm float-right text-danger"
onclick="confirmDeleteAlias.call(this)"
{% if alias.custom_domain %} data-custom-domain-trash-url="{{ alias.custom_domain.get_trash_url() }}"{% endif %}
data-alias="{{ alias.id }}"
data-alias-email="{{ alias.email }}">
Delete&nbsp; &nbsp;<i class="dropdown-icon fe fe-trash-2 text-danger"></i>
</span>
</form>
{% if mailboxes|length > 1 %}
<div class="small-text">
Current mailbox
</div>
<div class="d-flex">
<div class="flex-grow-1 mr-2">
<select required
id="mailbox-{{ alias.id }}"
data-width="100%"
class="mailbox-select"
multiple
name="mailbox">
{% for mailbox in mailboxes %}
<option value="{{ mailbox.id }}" {% if alias_info.contain_mailbox(mailbox.id) %}
selected {% endif %}>
{{ mailbox.email }}
</option>
{% endfor %}
</select>
</div>
<div>
<a data-alias="{{ alias.id }}"
class="save-mailbox btn btn-sm btn-outline-info w-100">
Update
</a>
</div>
</div>
{% elif alias_info.mailbox != None and alias_info.mailbox.email != current_user.email %}
<div class="small-text">
Owned by <b>{{ alias_info.mailbox.email }}</b> mailbox
</div>
{% endif %}
<div class="small-text mt-2"
data-toogle="tooltip"
title="When sending an email from this alias, the email will have 'Display Name <{{ alias.email }}>' as sender.">
Display name
<i class="fe fe-help-circle"></i>
</div>
<div class="d-flex">
<div class="flex-grow-1 mr-2">
<input id="alias-name-{{ alias.id }}"
value="{{ alias.name or '' }}"
class="form-control"
placeholder="{{ alias.custom_domain.name or "Alias name" }}">
</div>
<div>
<a data-alias="{{ alias.id }}"
class="save-alias-name btn btn-sm btn-outline-primary w-100">
Save
</a>
</div>
</div>
{% if alias.mailbox_support_pgp() %}
<div class="small-text mt-2"
data-toogle="tooltip"
title="You can decide to turn off the PGP for an alias. This can be useful if the sender already encrypts the emails">
PGP
<i class="fe fe-help-circle"></i>
</div>
<div>
<label class="custom-switch cursor pl-0">
<input type="checkbox" class="enable-disable-pgp custom-switch-input" data-alias="{{ alias.id }}" data-alias-email="{{ alias.email }}" {{ "checked" if alias.pgp_enabled() else "" }}>
<span class="custom-switch-indicator"></span>
</label>
</div>
{% endif %}
<div class="small-text mt-2"
data-toogle="tooltip"
title="it's always pinned on top">
Pin this alias
<i class="fe fe-help-circle"></i>
</div>
<div>
<label class="custom-switch cursor pl-0">
<input type="checkbox" class="pin-alias custom-switch-input" data-alias="{{ alias.id }}" data-alias-email="{{ alias.email }}" {{ "checked" if alias.pinned else "" }}>
<span class="custom-switch-indicator"></span>
</label>
</div>
<div>
<div class="btn-group float-right" role="group" aria-label="Basic example">
<a href="{{ url_for('dashboard.alias_transfer_send_route', alias_id=alias.id) }}"
class="btn btn-sm btn-link">
Transfer
<i class="ml-0 dropdown-icon fe fe-share-2 text-primary"></i>
</a>
<form method="post">
<input type="hidden" name="form-name" value="delete-alias">
<input type="hidden" name="alias-id" value="{{ alias.id }}">
<input type="hidden" name="alias" class="alias" value="{{ alias.email }}">
<span class="btn btn-link btn-sm float-right text-danger"
onclick="confirmDeleteAlias.call(this)"
{% if alias.custom_domain %} data-custom-domain-trash-url="{{ alias.custom_domain.get_trash_url() }}"{% endif %}
data-alias="{{ alias.id }}"
data-alias-email="{{ alias.email }}">
Delete&nbsp; &nbsp;<i class="dropdown-icon fe fe-trash-2 text-danger"></i>
</span>
</form>
</div>
</div>
</div>
<!-- END Collapse section -->
</div>
</div>
<!-- END Collapse section -->
</div>
</div>
{% endfor %}
</div>
<!-- END Alias list -->

View File

@ -229,5 +229,5 @@
$(this).closest("form").submit();
});
enableDragDropForPGPKeys('#pgp-public-key');
</script>
{% endblock %}
</script>
{% endblock %}

View File

@ -113,9 +113,7 @@ Upgrade your SimpleLogin account
<i class="fe fe-chevron-up"></i>
</span>
</a>
<p class="mt-2 small">
Starts at $2.5/month (billed yearly)
</p>
<p class="mt-2 small">Starts at $2.5/month (billed yearly)</p>
</div>
</div>
{% endif %}

View File

@ -100,5 +100,5 @@
}
$(this).next('.custom-file-label').html(files.join(', '));
});
</script>
</script>
{% endblock %}

View File

@ -11,9 +11,7 @@
You are about to block the alias
<a href="mailto:{{ alias }}">{{ alias }}</a>
</p>
<p>
After this, you will stop receiving all emails sent to this alias, please confirm.
</p>
<p>After this, you will stop receiving all emails sent to this alias, please confirm.</p>
<form method="post">
<button class="btn btn-warning">Confirm</button>
</form>

View File

@ -2,18 +2,14 @@
<p style="font-size: 16px;
line-height: 1.625;
color: #51545E;
margin: .4em 0 1.1875em;">
{{ text }}
</p>
margin: .4em 0 1.1875em;">{{ text }}</p>
{% endmacro %}
<!-- To be used instead of render_text, much better! -->
{% macro text() %}
<p style="font-size: 16px;
line-height: 1.625;
color: #51545E;
margin: .4em 0 1.1875em;">
{{ caller() }}
</p>
margin: .4em 0 1.1875em;">{{ caller() }}</p>
{% endmacro %}
{% macro render_button(button_text, link) %}
<!-- Action -->

View File

@ -13,9 +13,7 @@
alt="SimpleLogin logo">
</a>
<!-- End Logo -->
<p class="small text-white">
SimpleLogin is an open-source email alias solution to protect your email address.
</p>
<p class="small text-white">SimpleLogin is an open-source email alias solution to protect your email address.</p>
<p class="small text-white">
SimpleLogin is the product of SimpleLogin SAS, registered in France under the SIREN number 884302134.
</p>

View File

@ -26,159 +26,172 @@
background-color: #0ff;
border: 3px solid #88d748;
}
</style>
</style>
{% endblock %}
{% block single_content %}
<form class="card" method="post" data-parsley-validate style="max-width: 40rem; margin: auto; border-radius: 2%">
{% if not client.approved %}
<form class="card"
method="post"
data-parsley-validate
style="max-width: 40rem; margin: auto; border-radius: 2%">
{% if not client.approved %}
<div class="alert alert-warning" style="border-bottom: 3px solid;">
<b>{{ client.name }}</b> is in Dev Mode and isn't approved (yet) by SimpleLogin. <b>Please make sure you trust {{ client.name }} before proceeding.</b>
</div>
{% endif %}
<div class="card-body p-6">
<!-- User has already authorized this client -->
{% if client_user %}
<div class="alert alert-warning" style="border-bottom: 3px solid;">
<b>{{ client.name }}</b> is in Dev Mode and isn't approved (yet) by SimpleLogin. <b>Please make sure you trust {{ client.name }} before proceeding.</b>
</div>
{% endif %}
<div class="card-body p-6">
<!-- User has already authorized this client -->
{% if client_user %}
<div class="card-title">
You have already authorized <b>{{ client.name }}</b>.
</div>
<hr />
<div class="mb-4">
<b>{{ client.name }}</b> has access to the following info:
</div>
<div>
{% for scope in client.get_scopes() %}
<div class="card-title">
You have already authorized <b>{{ client.name }}</b>.
</div>
<hr />
<div class="mb-4">
<b>{{ client.name }}</b> has access to the following info:
</div>
<div>
{% for scope in client.get_scopes() %}
<div>
{% if scope == Scope.AVATAR_URL and user_info[scope.value] %}
<div>
{% if scope == Scope.AVATAR_URL and user_info[scope.value] %}
avatar:
<img src="{{ user_info[scope.value] }}" class="avatar">
{% elif scope == Scope.EMAIL %}
{{ scope.value }}:
<a href="mailto:{{ user_info[scope.value] }}">{{ user_info[scope.value] }}</a>
{% elif scope == Scope.NAME %}
{{ scope.value }}: <b>{{ user_info[scope.value] }}</b>
{% endif %}
</div>
{% endfor %}
</div>
{% else %}
{% if client.icon_id %}
avatar:
<img src="{{ user_info[scope.value] }}" class="avatar">
{% elif scope == Scope.EMAIL %}
{{ scope.value }}:
<a href="mailto:{{ user_info[scope.value] }}">{{ user_info[scope.value] }}</a>
{% elif scope == Scope.NAME %}
{{ scope.value }}: <b>{{ user_info[scope.value] }}</b>
{% endif %}
</div>
{% endfor %}
</div>
{% else %}
{% if client.icon_id %}
<div class="text-center">
<img src="{{ client.get_icon_url() }}" class="small-client-icon">
</div>
{% endif %}
<div class="text-center" style="font-size: larger">
<b>{{ client.name }}</b> will receive the following info
</div>
<hr />
<div class="row mt-4 md-4">
<div class="col-md-3 text-left">
<label style="padding-top: .5rem">Email</label>
</div>
<div class="col-md-9">
<select class="form-control" name="suggested-email">
<option selected value="{{ suggested_email }}">
{{ suggested_email }}
</option>
<option value="{{ current_user.email }}">
{{ current_user.email }} (Personal Email)
</option>
{% for email in other_emails %}
<div class="text-center">
<img src="{{ client.get_icon_url() }}" class="small-client-icon">
</div>
{% endif %}
<div class="text-center" style="font-size: larger">
<b>{{ client.name }}</b> will receive the following info
</div>
<hr />
<div class="row mt-4 md-4">
<div class="col-md-3 text-left">
<label style="padding-top: .5rem">Email</label>
</div>
<div class="col-md-9">
<select class="form-control" name="suggested-email">
<option selected value="{{ suggested_email }}">
{{ suggested_email }}
</option>
<option value="{{ current_user.email }}">
{{ current_user.email }} (Personal Email)
</option>
{% for email in other_emails %}
<option value="{{ email }}">
{{ email }}
</option>
{% endfor %}
</select>
{% if current_user.can_create_new_alias() %}
<option value="{{ email }}">
{{ email }}
</option>
{% endfor %}
</select>
{% if current_user.can_create_new_alias() %}
<div class="mt-2 mb-2">OR</div>
<div class="row mb-2">
<div class="col-sm-6 pr-1 mb-1" style="min-width: 5em">
<input name="prefix" class="form-control" type="text" maxlength="40" data-parsley-pattern="[0-9a-z-_.]{1,}" data-parsley-trigger="change" data-parsley-error-message="Only lowercase letters, dots, numbers, dashes (-) and underscores (_) are currently supported." placeholder="Alias prefix, for example newsletter.com-123_xyz" autofocus>
</div>
<div class="col-sm-6" style="padding-left: 5px">
<select class="form-control" name="suffix">
{% for suffix in suffixes %}
<div class="mt-2 mb-2">OR</div>
<div class="row mb-2">
<div class="col-sm-6 pr-1 mb-1" style="min-width: 5em">
<input name="prefix"
class="form-control"
type="text"
maxlength="40"
data-parsley-pattern="[0-9a-z-_.]{1,}"
data-parsley-trigger="change"
data-parsley-error-message="Only lowercase letters, dots, numbers, dashes (-) and underscores (_) are currently supported."
placeholder="Alias prefix, for example newsletter.com-123_xyz"
autofocus>
</div>
<div class="col-sm-6" style="padding-left: 5px">
<select class="form-control" name="suffix">
{% for suffix in suffixes %}
<option value="{{ suffix.signed_suffix }}">
{% if suffix.is_custom %}
<option value="{{ suffix.signed_suffix }}">
{% if suffix.is_custom %}
{{ suffix.suffix }} (your domain)
{% else %}
{{ suffix.suffix }}
{% endif %}
</option>
{% endfor %}
</select>
</div>
</div>
{% endif %}
</div>
</div>
<div class="row mt-4 md-4">
<div class="col-md-3 text-left">
<label style="padding-top: .5rem">Name</label>
</div>
<div class="col-md-9">
<select class="form-control" name="suggested-name">
<option selected value="{{ suggested_name }}">
{{ suggested_name }}
</option>
{% for name in other_names %}
{{ suffix.suffix }} (your domain)
{% else %}
{{ suffix.suffix }}
{% endif %}
</option>
{% endfor %}
</select>
</div>
</div>
{% endif %}
</div>
</div>
<div class="row mt-4 md-4">
<div class="col-md-3 text-left">
<label style="padding-top: .5rem">Name</label>
</div>
<div class="col-md-9">
<select class="form-control" name="suggested-name">
<option selected value="{{ suggested_name }}">
{{ suggested_name }}
</option>
{% for name in other_names %}
<option value="{{ name }}">
{{ name }}
</option>
{% endfor %}
</select>
<div class="mt-2">OR</div>
<div class="mt-2">
<input class="form-control" name="custom-name">
</div>
</div>
</div>
{% if current_user.profile_picture_id %}
<option value="{{ name }}">
{{ name }}
</option>
{% endfor %}
</select>
<div class="mt-2">OR</div>
<div class="mt-2">
<input class="form-control" name="custom-name">
</div>
</div>
</div>
{% if current_user.profile_picture_id %}
<div class="row mt-4 md-4">
<div class="col-md-3 text-left">
<label style="padding-top: 2rem">Avatar</label>
</div>
<div class="col-md-9" style="display: flex; align-items: center">
<label>
<input type="radio" name="avatar-choice" value="real" checked>
<img src="{{ current_user.profile_picture_url() }}" class="small-profile-picture">
</label>
<div style="margin: 0 1rem">OR</div>
<label>
<input type="radio" name="avatar-choice" value="default">
<img src="{{ url_for('static', filename='default-avatar.png') }}" class="small-profile-picture">
</label>
</div>
</div>
{% endif %}
{% endif %}
{% if client_user %}
<div class="row mt-4 md-4">
<div class="col-md-3 text-left">
<label style="padding-top: 2rem">Avatar</label>
</div>
<div class="col-md-9" style="display: flex; align-items: center">
<label>
<input type="radio" name="avatar-choice" value="real" checked>
<img src="{{ current_user.profile_picture_url() }}"
class="small-profile-picture">
</label>
<div style="margin: 0 1rem">OR</div>
<label>
<input type="radio" name="avatar-choice" value="default">
<img src="{{ url_for('static', filename='default-avatar.png') }}"
class="small-profile-picture">
</label>
</div>
</div>
{% endif %}
{% endif %}
{% if client_user %}
<div class="form-footer">
<div class="btn-group" role="group" aria-label="Basic example">
<button type="submit" name="button" value="allow" class="btn btn-success">Allow</button>
<button type="submit" name="button" value="deny" class="btn btn-light">Cancel</button>
</div>
</div>
{% else %}
<div class="form-footer">
<div class="btn-group btn-block" role="group" aria-label="Basic example">
<button type="submit" name="button" value="allow" class="btn btn-success">Allow</button>
<button type="submit" name="button" value="deny" class="btn btn-light">Deny</button>
</div>
</div>
{% endif %}
</div>
</form>
<div class="form-footer">
<div class="btn-group" role="group" aria-label="Basic example">
<button type="submit" name="button" value="allow" class="btn btn-success">Allow</button>
<button type="submit" name="button" value="deny" class="btn btn-light">Cancel</button>
</div>
</div>
{% else %}
<div class="form-footer">
<div class="btn-group btn-block" role="group" aria-label="Basic example">
<button type="submit" name="button" value="allow" class="btn btn-success">Allow</button>
<button type="submit" name="button" value="deny" class="btn btn-light">Deny</button>
</div>
</div>
{% endif %}
</div>
</form>
{% endblock %}

View File

@ -18,9 +18,7 @@
</div>
<div class="px-8" style="width:675px;margin:auto;">
<div class="my-6">
<p style="font-size: 1.2em;">
Quickly create aliases everywhere using our {{ browser_name }} extension
</p>
<p style="font-size: 1.2em;">Quickly create aliases everywhere using our {{ browser_name }} extension</p>
<a class="px-4 py-3 mt-4 text-decoration-none text-center"
style="background:black;
color:white;
@ -32,9 +30,7 @@
</div>
<hr style="margin-top: 75px">
<div style="margin-top: 75px">
<p style="font-size: 1.2em;">
Or install the extension later and start discovering our features on our web app
</p>
<p style="font-size: 1.2em;">Or install the extension later and start discovering our features on our web app</p>
<a class="px-4 py-3 mt-4 text-decoration-none text-center"
style="background:white;
color:black;

View File

@ -53,8 +53,7 @@
</p>
<p id="extension-version-text"
class="font-weight-light"
style="display:none;">
</p>
style="display:none;"></p>
</div>
</div>
<script type="text/javascript">

View File

@ -1,7 +1,7 @@
X-SimpleLogin-Client-IP: 40.92.66.13
Received-SPF: Pass (mailfrom) identity=mailfrom; client-ip=40.92.66.13;
helo=eur01-ve1-obe.outbound.protection.outlook.com;
envelope-from=staff@hotmail.com; receiver=<UNKNOWN>
envelope-from=staff@hotmail.com; receiver=<UNKNOWN>
Received: from EUR01-VE1-obe.outbound.protection.outlook.com
(mail-oln040092066013.outbound.protection.outlook.com [40.92.66.13])
(using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits))
@ -42,30 +42,30 @@ Date: Mon, 9 May 2022 13:10:08 +0000
From: <staff@hotmail.com>
Subject: complaint about message from 176.119.200.162
To: {{ postmaster }}
MIME-Version: 1.0
MIME-Version: 1.0
Content-Type: multipart/mixed;
boundary="31A9507D-D0B3-4DCD-AFBB-413468892CFE"
X-IncomingHeaderCount: 6
Message-ID:
Message-ID:
<1d63d9ee-8f3e-4876-955c-1807db5ad138@AM6EUR05FT047.eop-eur05.prod.protection.outlook.com>
X-EOPAttributedMessage: 0
X-MS-PublicTrafficType: Email
X-MS-Office365-Filtering-Correlation-Id: 44e9ec0b-6c5d-4cea-6417-08da31bd7000
X-MS-TrafficTypeDiagnostic: AM0PR02MB4563:EE_
X-Microsoft-Antispam: BCL:0;
X-Microsoft-Antispam-Message-Info:
X-Microsoft-Antispam-Message-Info:
lK5xD4UZS47NfR0tHc3wEp4HHOifZ4SDBb8aKx7H/vEW8Rg8rXXH12G4lWdpzr8qTsCmvzuhj5x6IAumOKQ8lWLj5Lp3jyml91wVnwCtUnk5cTXpQwDZd9QMgtEW07GoLdWjkbShAhLRDf+9Y4DxidHCacOAYxcNX42wo3vYZOEHDzVRUxSmY0c7Km60pDtiYzEk+P9AoE2YKYG2rDwDx0vgoLgqFspGqQ+2OeHD2ZAEyATHR/sQy6tf5S2d4wA3HcHrwrGMlz/4d9VbT5h9a5cqj9S59wpuc6g8nyYhmK3AHJkB5nXmpBZBihTw5X/Qh5PZqUYwPxkwpq3WlaEuXvzaKFiwJFvtuRGX+mEioClCxiwPROb7sI9ZHWPw48AHysF+whYGBfleRy4c2SuW6e1D5uewGry+lXVljxg7qKo=
X-OriginatorOrg: sct-15-20-4755-11-msonline-outlook-ab7de.templateTenant
X-MS-Exchange-CrossTenant-OriginalArrivalTime: 09 May 2022 13:11:32.0875
(UTC)
X-MS-Exchange-CrossTenant-Network-Message-Id:
X-MS-Exchange-CrossTenant-Network-Message-Id:
44e9ec0b-6c5d-4cea-6417-08da31bd7000
X-MS-Exchange-CrossTenant-Id: 84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa
X-MS-Exchange-CrossTenant-AuthSource:
X-MS-Exchange-CrossTenant-AuthSource:
AM6EUR05FT047.eop-eur05.prod.protection.outlook.com
X-MS-Exchange-CrossTenant-AuthAs: Anonymous
X-MS-Exchange-CrossTenant-FromEntityHeader: Internet
X-MS-Exchange-CrossTenant-RMS-PersistedConsumerOrg:
X-MS-Exchange-CrossTenant-RMS-PersistedConsumerOrg:
00000000-0000-0000-0000-000000000000
X-MS-Exchange-Transport-CrossTenantHeadersStamped: AM0PR02MB4563
X-Spamd-Result: default: False [-1.75 / 13.00];
@ -126,7 +126,7 @@ Received: from mail-200162.simplelogin.co (176.119.200.162) by
BN8NAM11FT053.mail.protection.outlook.com (10.13.177.209) with Microsoft SMTP
Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id
15.20.5227.15 via Frontend Transport; Mon, 9 May 2022 04:30:44 +0000
X-IncomingTopHeaderMarker:
X-IncomingTopHeaderMarker:
OriginalChecksum:5EBD8C309CA888838EDC898C63E28E1EC00EF74772276A54C08DA83D658756F4;UpperCasedChecksum:E102374CD208D4ACB2034F1A17F76DA6345BD176395C6D4EADEC3B47BFF41ECC;SizeAsReceived:1262;Count:15
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=simplelogin.co;
s=dkim; t=1652070640; h=From:To:Subject:Message-ID:Date;
@ -135,7 +135,7 @@ DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=simplelogin.co;
5NybKJmB6B6KL5hl5LG3KzCdaWfe3dAAhD4e2gIU80dal596dlzluyvLR1k+6rdM4JvlGq
OVWLR42Oj4anrnOqLCUkL44ILIhLpAE=
Date: Mon, 9 May 2022 00:30:38 -0400 (EDT)
Message-ID:
Message-ID:
<10627474.1041327707.1652070638478.JavaMail.cloud@p2-mta-0301.p2.messagegears.net>
Subject: Original Subject
Content-Type: multipart/mixed;
@ -156,13 +156,13 @@ X-MS-Exchange-Organization-ExpirationStartTime: 09 May 2022 04:30:45.1195
X-MS-Exchange-Organization-ExpirationStartTimeReason: OriginalSubmit
X-MS-Exchange-Organization-ExpirationInterval: 1:00:00:00.0000000
X-MS-Exchange-Organization-ExpirationIntervalReason: OriginalSubmit
X-MS-Exchange-Organization-Network-Message-Id:
X-MS-Exchange-Organization-Network-Message-Id:
ede92e41-5acb-4474-c5be-08da3174af2b
X-EOPAttributedMessage: 0
X-EOPTenantAttributedMessage: 84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa:0
X-MS-Exchange-Organization-MessageDirectionality: Incoming
X-MS-PublicTrafficType: Email
X-MS-Exchange-Organization-AuthSource:
X-MS-Exchange-Organization-AuthSource:
BN8NAM11FT053.eop-nam11.prod.protection.outlook.com
X-MS-Exchange-Organization-AuthAs: Anonymous
X-MS-UserLastLogonTime: 5/9/2022 3:30:52 AM
@ -177,24 +177,24 @@ X-MS-Exchange-Organization-SCL: 1
X-Microsoft-Antispam: BCL:0;
X-MS-Exchange-CrossTenant-OriginalArrivalTime: 09 May 2022 04:30:44.9945
(UTC)
X-MS-Exchange-CrossTenant-Network-Message-Id:
X-MS-Exchange-CrossTenant-Network-Message-Id:
ede92e41-5acb-4474-c5be-08da3174af2b
X-MS-Exchange-CrossTenant-Id: 84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa
X-MS-Exchange-CrossTenant-AuthSource:
X-MS-Exchange-CrossTenant-AuthSource:
BN8NAM11FT053.eop-nam11.prod.protection.outlook.com
X-MS-Exchange-CrossTenant-AuthAs: Anonymous
X-MS-Exchange-CrossTenant-FromEntityHeader: Internet
X-MS-Exchange-CrossTenant-RMS-PersistedConsumerOrg:
X-MS-Exchange-CrossTenant-RMS-PersistedConsumerOrg:
00000000-0000-0000-0000-000000000000
X-MS-Exchange-Transport-CrossTenantHeadersStamped: SJ0PR11MB4958
X-MS-Exchange-Transport-EndToEndLatency: 00:00:03.3271765
X-MS-Exchange-Processed-By-BccFoldering: 15.20.5227.023
X-Microsoft-Antispam-Mailbox-Delivery:
X-Microsoft-Antispam-Mailbox-Delivery:
abwl:0;wl:0;pcwl:0;kl:0;iwl:0;ijl:0;dwl:0;dkl:0;rwl:0;ucf:0;jmr:0;ex:0;auth:1;dest:I;ENG:(5062000285)(90000117)(90005022)(91005020)(91035115)(5061607266)(5061608174)(9050020)(9100338)(2008001134)(2008000189)(2008120399)(2008019284)(2008021020)(8390246)(8377080)(8386120)(4810004)(4910013)(9910022)(9510006)(10110021)(9320005);
X-Message-Info:
X-Message-Info:
5vMbyqxGkdcvoPRAk5ACFywqndfpuBMcVz6K/12RtMALmdfGi+GpgO+lXQe3PiGwHtV5wXFRStQwg29XySZZo6tOyvshTSJ1uafhX53S93r5MaqDxJrR0UNGr2VYdKiAm1jYIYQm84v/mEbSAGjjBwEgS1PHlzM72I96JadXzfV9Fmsd5pHlfoLxEqXe6hBJAAQS99CcpwPDnaVA9UZUHA==
X-Message-Delivery: Vj0xLjE7dXM9MDtsPTA7YT0wO0Q9MTtHRD0xO1NDTD0tMQ==
X-Microsoft-Antispam-Message-Info:
X-Microsoft-Antispam-Message-Info:
=?utf-8?B?VjZIQkpKR05oRUo1Vzc0YTBDUW52S0lsYkJSMGRzY0hJMnRMOWdyRGowcGpk?=
=?utf-8?B?SUJLSDRPaStzakpJUHlaWVFnNWpBSGRsZ1Z4aEFmaXJOR1ZMUWxTTnQ1SXg1?=
=?utf-8?B?anhFNTJ5RGU2YjRiTWhWK3FvWXBJU29YSWdqM3VvUkZpY21aaW5lSkJ5WWph?=

View File

@ -1,7 +1,7 @@
X-SimpleLogin-Client-IP: 66.163.186.21
Received-SPF: None (mailfrom) identity=mailfrom; client-ip=66.163.186.21;
helo=sonic326-46.consmr.mail.ne1.yahoo.com;
envelope-from=feedback@arf.mail.yahoo.com; receiver=<UNKNOWN>
envelope-from=feedback@arf.mail.yahoo.com; receiver=<UNKNOWN>
Received: from sonic326-46.consmr.mail.ne1.yahoo.com
(sonic326-46.consmr.mail.ne1.yahoo.com [66.163.186.21])
(using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits)
@ -24,7 +24,7 @@ Received: from sonic.gate.mail.ne1.yahoo.com by
Date: Sun, 8 May 2022 13:31:28 +0000 (UTC)
From: Yahoo! Mail AntiSpam Feedback <feedback@arf.mail.yahoo.com>
To: {{ postmaster }}
Message-ID:
Message-ID:
<1486688083.18136997.1652016688605@chakraconsumer2.asd.mail.ne1.yahoo.com>
Subject: Original subject
MIME-Version: 1.0
@ -87,7 +87,7 @@ Content-Disposition: inline
Received: from 10.217.151.74
by atlas316.free.mail.ne1.yahoo.com with HTTPS;
Sun, 8 May 2022 11:12:34 +0000
Return-Path:
Return-Path:
<{{ return_path }}>
X-Originating-Ip: [176.129.238.160]
Received-SPF: pass (domain of simplelogin.co designates 176.119.200.160 as
@ -131,7 +131,7 @@ DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=simplelogin.co;
jQ8bqMZhCsN/xVpkMqJdNJefdkj3dP4=
MIME-Version: 1.0
Date: Sun, 8 May 2022 04:11:42 -0700
Message-ID:
Message-ID:
<CAKGh96GHg2kuwvm4biQ-PF-4-8SPZ6JyPj-=GpoYZ6njctoRtg@mail.gmail.com>
Subject: MF
Content-Type: multipart/alternative; boundary="0000000000006dd95f05de7e2a70"