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 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) 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 name: Run tests & Publish to Docker Registry
on: on:
push: push:
jobs: jobs:
@ -9,7 +9,7 @@ jobs:
strategy: strategy:
max-parallel: 4 max-parallel: 4
matrix: matrix:
python-version: [3.7, "3.10"] python-version: ["3.9", "3.10"]
# service containers to run with `postgres-job` # service containers to run with `postgres-job`
services: services:
@ -75,9 +75,7 @@ jobs:
- name: Check formatting & linting - name: Check formatting & linting
run: | run: |
poetry run black --check . poetry run pre-commit run --all-files
poetry run flake8
poetry run djlint --check templates
- name: Run db migration - name: Run db migration
run: | run: |
@ -214,4 +212,4 @@ jobs:
release_name: ${{ github.ref }} release_name: ${{ github.ref }}
body: ${{ steps.build_changelog.outputs.changelog }} body: ${{ steps.build_changelog.outputs.changelog }}
env: 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: repos:
- repo: https://github.com/psf/black - repo: https://github.com/pre-commit/pre-commit-hooks
rev: 22.1.0 rev: v4.2.0
hooks: hooks:
- id: black - id: check-yaml
language_version: python3.7 - id: trailing-whitespace
- repo: https://github.com/pycqa/flake8 - repo: https://github.com/psf/black
rev: 4.0.1 rev: 22.3.0
hooks: 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 ## [1.0.1] - 2020-01-28
Simplify config file. Simplify config file.
## [1.0.0] - 2020-01-22 ## [1.0.0] - 2020-01-22

View File

@ -1,9 +1,9 @@
Thanks for taking the time to contribute! 🎉👍 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. 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. 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 ## General Architecture
@ -43,13 +43,23 @@ You also need to install `gpg` tool, on Mac it can be done with:
brew install gnupg 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 More info on https://github.com/andreasvc/pyre2
```bash ```bash
brew install -s re2 pybind11 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 ## Run tests
```bash ```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. 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 More information on https://simplelogin.io
This README contains instructions on how to self host SimpleLogin. 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. 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 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, - 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. - 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). Some have "dummy" values, fill them up if you want to enable these features (Paddle, AWS, etc).
```.env ```.env

View File

@ -2,13 +2,13 @@
## Supported Versions ## 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. If you want be up to date on security patches, make sure your SimpleLogin image is up to date.
## Reporting a Vulnerability ## Reporting a Vulnerability
If you've found a security vulnerability, you can disclose it responsibly by sending a summary to security@simplelogin.io. 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 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. 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. - 200 with ```{"ok": true}``` if sudo mode has been enabled.
- 403 with ```{"error": "Some error"}``` if there is an error. - 403 with ```{"error": "Some error"}``` if there is an error.
@ -308,7 +308,7 @@ Input:
Output: a json with the following field: Output: a json with the following field:
- can_create: boolean. Whether user can create new alias - 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. Each item is a dictionary with `suffix`, `signed-suffix`, `is_custom`, `is_premium` as keys.
The `signed-suffix` is necessary to avoid request tampering. The `signed-suffix` is necessary to avoid request tampering.
- prefix_suggestion: string. Suggestion for the `alias prefix`. Usually this is the website name extracted - 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 - `page_id` in query. Used for the pagination. The endpoint returns maximum 20 aliases for each page. `page_id` starts
at 0. at 0.
- (Optional) `pinned` in query. If set, only pinned aliases are returned. - (Optional) `pinned` in query. If set, only pinned aliases are returned.
- (Optional) `disabled` in query. If set, only disabled 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) `enabled` in query. If set, only enabled aliases are returned.
Please note `pinned`, `disabled`, `enabled` are exclusive, i.e. only one can be present. 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 - (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. case this endpoint also supports POST.

View File

@ -1,8 +1,8 @@
# TODO # TODO
`local_data/`: contain files used only locally. In deployment, these files should be replaced. `local_data/`: contain files used only locally. In deployment, these files should be replaced.
- jwtRS256.key: generated using - jwtRS256.key: generated using
```bash ```bash
ssh-keygen -t rsa -b 4096 -m PEM -f jwtRS256.key ssh-keygen -t rsa -b 4096 -m PEM -f jwtRS256.key
# Don't add passphrase # 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 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, 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. breaking therefore the SPF.
A malicious hacker could use this security fail to impersonate your alias via the reverse-alias. This rarely happens 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. 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. 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` 1) Install `postfix-pcre`
```bash ```bash
apt install -y postfix-pcre apt install -y postfix-pcre
``` ```
2) Add `/etc/postfix/body_checks.pcre` file with the following content 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. 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` 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 -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 ## 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. 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 sudo apt install libsasl2-modules
``` ```
Then restart postfix Then restart postfix
```bash ```bash
sudo systemctl restart postfix sudo systemctl restart postfix
``` ```
and you should see the mail in `/var/log/mail.log` and in your alias emails routed through Amazons servers! 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 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_cert_file = /etc/letsencrypt/live/app.mydomain.com/fullchain.pem
smtpd_tls_key_file = /etc/letsencrypt/live/app.mydomain.com/privkey.pem smtpd_tls_key_file = /etc/letsencrypt/live/app.mydomain.com/privkey.pem
``` ```
Make sure to replace app.mydomain.com with your own domain. 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) 2) The `sl-app` container can't connect to Postfix (run on the host)
### A.1 ### A.1
To test 1), running `swaks --to your-mailbox@mail.com` should send you an email. 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. 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. `swaks` can be installed with `sudo apt install swaks` on Debian-based OS.
### A.2 ### 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` a) first connecting to the container by `docker exec -it sl-app bash`
b) then run the following commands b) then run the following commands
```bash ```bash
apt update apt update
apt install telnet -y apt install telnet -y
telnet 10.0.0.1 25 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. 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 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. And `postmap -q not-exist.com pgsql:/etc/postfix/pgsql-transport-maps.cf` should return nothing.
### B.2 ### 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, 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 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. make sure no step is missed.
### B.3 ### B.3

View File

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

View File

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

View File

@ -26,7 +26,7 @@ def upgrade():
session = Session(bind=bind) session = Session(bind=bind)
session.execute(""" 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; AS (to_tsvector('english', note)) STORED;
""") """)

View File

@ -1,7 +1,7 @@
"""empty message """empty message
Revision ID: 5e549314e1e2 Revision ID: 5e549314e1e2
Revises: Revises:
Create Date: 2019-06-23 16:02:14.692075 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" python-editor = ">=0.3"
SQLAlchemy = ">=1.1.0" 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]] [[package]]
name = "appnope" name = "appnope"
version = "0.1.0" version = "0.1.0"
@ -93,6 +85,20 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
[package.dependencies] [package.dependencies]
python-dateutil = ">=2.7.0" 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]] [[package]]
name = "async-timeout" name = "async-timeout"
version = "3.0.1" version = "3.0.1"
@ -142,6 +148,21 @@ category = "main"
optional = false optional = false
python-versions = "*" 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]] [[package]]
name = "bcrypt" name = "bcrypt"
version = "3.2.0" version = "3.2.0"
@ -378,6 +399,17 @@ wrapt = ">=1.10,<2"
[package.extras] [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"] 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]] [[package]]
name = "distlib" name = "distlib"
version = "0.3.1" version = "0.3.1"
@ -388,23 +420,26 @@ python-versions = "*"
[[package]] [[package]]
name = "djlint" name = "djlint"
version = "0.7.3" version = "1.3.0"
description = "HTML Template Linter and Formatter" description = "HTML Template Linter and Formatter"
category = "dev" category = "dev"
optional = false optional = false
python-versions = ">=3.6.2,<4.0.0" python-versions = ">=3.7,<4.0"
[package.dependencies] [package.dependencies]
click = ">=8.0.1,<9.0.0" click = ">=8.0.1,<9.0.0"
colorama = ">=0.4.4,<0.5.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" pathspec = ">=0.9.0,<0.10.0"
PyYAML = ">=6.0,<7.0" PyYAML = ">=6.0,<7.0"
regex = ">=2022.1.18,<2023.0.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" tqdm = ">=4.62.2,<5.0.0"
[package.extras] [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]] [[package]]
name = "dkimpy" name = "dkimpy"
@ -469,35 +504,6 @@ category = "main"
optional = false optional = false
python-versions = "*" 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]] [[package]]
name = "flanker" name = "flanker"
version = "0.9.11" version = "0.9.11"
@ -816,6 +822,22 @@ gevent = ["gevent (>=0.13)"]
setproctitle = ["setproctitle"] setproctitle = ["setproctitle"]
tornado = ["tornado (>=0.2)"] 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]] [[package]]
name = "httplib2" name = "httplib2"
version = "0.18.1" version = "0.18.1"
@ -856,18 +878,20 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
[[package]] [[package]]
name = "importlib-metadata" name = "importlib-metadata"
version = "1.7.0" version = "4.12.0"
description = "Read metadata from Python packages" description = "Read metadata from Python packages"
category = "main" category = "main"
optional = false optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" python-versions = ">=3.7"
[package.dependencies] [package.dependencies]
typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""}
zipp = ">=0.5" zipp = ">=0.5"
[package.extras] [package.extras]
docs = ["sphinx", "rst.linker"] docs = ["sphinx", "jaraco.packaging (>=9)", "rst.linker (>=1.9)"]
testing = ["packaging", "pep517", "importlib-resources (>=1.3)"] 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]] [[package]]
name = "iniconfig" name = "iniconfig"
@ -917,6 +941,20 @@ category = "main"
optional = false optional = false
python-versions = "*" 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]] [[package]]
name = "itsdangerous" name = "itsdangerous"
version = "1.1.0" version = "1.1.0"
@ -973,6 +1011,14 @@ python-versions = "*"
[package.dependencies] [package.dependencies]
cryptography = ">=2.3" 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]] [[package]]
name = "limits" name = "limits"
version = "1.5.1" version = "1.5.1"
@ -1020,11 +1066,11 @@ traitlets = "*"
[[package]] [[package]]
name = "mccabe" name = "mccabe"
version = "0.6.1" version = "0.7.0"
description = "McCabe checker, plugin for flake8" description = "McCabe checker, plugin for flake8"
category = "dev" category = "dev"
optional = false optional = false
python-versions = "*" python-versions = ">=3.6"
[[package]] [[package]]
name = "memory-profiler" name = "memory-profiler"
@ -1283,14 +1329,6 @@ python-versions = "*"
[package.dependencies] [package.dependencies]
pyasn1 = ">=0.4.6,<0.5.0" 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]] [[package]]
name = "pycparser" name = "pycparser"
version = "2.20" version = "2.20"
@ -1307,14 +1345,6 @@ category = "main"
optional = false optional = false
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 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]] [[package]]
name = "pygments" name = "pygments"
version = "2.7.4" 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"] docs = ["sphinx", "sphinx-rtd-theme", "zope.interface"]
tests = ["pytest (>=6.0.0,<7.0.0)", "coverage[toml] (==5.0.4)"] 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]] [[package]]
name = "pyopenssl" name = "pyopenssl"
version = "19.1.0" version = "19.1.0"
@ -1731,7 +1784,7 @@ python-versions = "*"
[[package]] [[package]]
name = "tomli" name = "tomli"
version = "2.0.0" version = "2.0.1"
description = "A lil' TOML parser" description = "A lil' TOML parser"
category = "dev" category = "dev"
optional = false optional = false
@ -1739,7 +1792,7 @@ python-versions = ">=3.7"
[[package]] [[package]]
name = "tomlkit" name = "tomlkit"
version = "0.8.0" version = "0.11.0"
description = "Style preserving TOML library" description = "Style preserving TOML library"
category = "dev" category = "dev"
optional = false optional = false
@ -1836,22 +1889,23 @@ socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
[[package]] [[package]]
name = "virtualenv" name = "virtualenv"
version = "20.0.31" version = "20.8.1"
description = "Virtual Python Environment builder" description = "Virtual Python Environment builder"
category = "dev" category = "dev"
optional = false 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] [package.dependencies]
appdirs = ">=1.4.3,<2" "backports.entry-points-selectable" = ">=1.0.4"
distlib = ">=0.3.1,<1" distlib = ">=0.3.1,<1"
filelock = ">=3.0.0,<4" 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" six = ">=1.9.0,<2"
[package.extras] [package.extras]
docs = ["proselint (>=0.10.2)", "sphinx (>=3)", "sphinx-argparse (>=0.2.5)", "sphinx-rtd-theme (>=0.4.3)", "towncrier (>=19.9.0rc1)"] 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]] [[package]]
name = "watchtower" name = "watchtower"
@ -2004,8 +2058,8 @@ testing = ["coverage (>=5.0.3)", "zope.event", "zope.testing"]
[metadata] [metadata]
lock-version = "1.1" lock-version = "1.1"
python-versions = "^3.7" python-versions = "^3.7.2"
content-hash = "29d95a850f0a87a38aabb8f5eddba072316860dae26943fd690898ea9bac2b02" content-hash = "04190874ee0655ddf7d67e024a61009c5f9c92439ff0af8dfce3341580269edb"
[metadata.files] [metadata.files]
aiohttp = [ aiohttp = [
@ -2038,10 +2092,6 @@ alembic = [
{file = "alembic-1.4.3-py2.py3-none-any.whl", hash = "sha256:4e02ed2aa796bd179965041afa092c55b51fb077de19d61835673cc80672c01c"}, {file = "alembic-1.4.3-py2.py3-none-any.whl", hash = "sha256:4e02ed2aa796bd179965041afa092c55b51fb077de19d61835673cc80672c01c"},
{file = "alembic-1.4.3.tar.gz", hash = "sha256:5334f32314fb2a56d86b4c4dd1ae34b08c03cae4cb888bc699942104d66bc245"}, {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 = [ appnope = [
{file = "appnope-0.1.0-py2.py3-none-any.whl", hash = "sha256:5b26757dc6f79a3b7dc9fab95359328d5747fcb2409d331ea66d0272b90ab2a0"}, {file = "appnope-0.1.0-py2.py3-none-any.whl", hash = "sha256:5b26757dc6f79a3b7dc9fab95359328d5747fcb2409d331ea66d0272b90ab2a0"},
{file = "appnope-0.1.0.tar.gz", hash = "sha256:8b995ffe925347a2138d7ac0fe77155e4311a0ea6d6da4f5128fe4b3cbe5ed71"}, {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-py2.py3-none-any.whl", hash = "sha256:98184d8dd3e5d30b96c2df4596526f7de679ccb467f358b82b0f686436f3a6b8"},
{file = "arrow-0.16.0.tar.gz", hash = "sha256:92aac856ea5175c804f7ccb96aca4d714d936f1c867ba59d747a8096ec30e90a"}, {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 = [ async-timeout = [
{file = "async-timeout-3.0.1.tar.gz", hash = "sha256:0c3c816a028d47f659d6ff5c745cb2acf1f966da1fe5c19c77a70282b25f4c5f"}, {file = "async-timeout-3.0.1.tar.gz", hash = "sha256:0c3c816a028d47f659d6ff5c745cb2acf1f966da1fe5c19c77a70282b25f4c5f"},
{file = "async_timeout-3.0.1-py3-none-any.whl", hash = "sha256:4291ca197d287d274d0b6cb5d6f8f8f82d434ed288f962539ff18cc9012f9ea3"}, {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-py2.py3-none-any.whl", hash = "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255"},
{file = "backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"}, {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 = [ 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_10_universal2.whl", hash = "sha256:b589229207630484aefe5899122fb938a5b017b0f4349f769b8c13e78d99a8fd"},
{file = "bcrypt-3.2.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:c95d4cbebffafcdd28bd28bb4e25b31c50f6da605c81ffd9ad8a3d1b2ab7b1b6"}, {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-py2.py3-none-any.whl", hash = "sha256:64756e3e14c8c5eea9795d93c524551432a0be75629f8f29e67ab8caf076c76d"},
{file = "Deprecated-1.2.13.tar.gz", hash = "sha256:43ac5335da90c31c24ba028af536a91d41d53f9e6901ddb021bcc572ce44e38d"}, {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 = [ distlib = [
{file = "distlib-0.3.1-py2.py3-none-any.whl", hash = "sha256:8c09de2c67b3e7deef7184574fc060ab8a793e7adbb183d942c389c8b13c52fb"}, {file = "distlib-0.3.1-py2.py3-none-any.whl", hash = "sha256:8c09de2c67b3e7deef7184574fc060ab8a793e7adbb183d942c389c8b13c52fb"},
{file = "distlib-0.3.1.zip", hash = "sha256:edf6116872c863e1aa9d5bb7cb5e05a022c519a4594dc703843343a9ddd9bff1"}, {file = "distlib-0.3.1.zip", hash = "sha256:edf6116872c863e1aa9d5bb7cb5e05a022c519a4594dc703843343a9ddd9bff1"},
] ]
djlint = [ djlint = [
{file = "djlint-0.7.3-py3-none-any.whl", hash = "sha256:714ed457e022047149c8bff57d5be00ce30f8846b60e866791c66d27e7d11e7f"}, {file = "djlint-1.3.0-py3-none-any.whl", hash = "sha256:0c986bf542cdac3025d431a5b15e6c3977f652f2e76e408dbb5e7aaab6b73d99"},
{file = "djlint-0.7.3.tar.gz", hash = "sha256:68aad9ddfef883cc9d9e0d177387b74840af5ca12dcce6e4629eb7075c97dc05"}, {file = "djlint-1.3.0.tar.gz", hash = "sha256:b2d8e6c0a14f88da165296f0da05795d15299b7ab0a9093d670ce9ffd867bc79"},
] ]
dkimpy = [ dkimpy = [
{file = "dkimpy-1.0.5.tar.gz", hash = "sha256:9a2420bf09af686736773153fca32a02ae11ecbe24b540c26104628959f91121"}, {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-py3-none-any.whl", hash = "sha256:929b7d63ec5b7d6b71b0fa5ac14e030b3f70b75747cef1b10da9b879fef15836"},
{file = "filelock-3.0.12.tar.gz", hash = "sha256:18d82244ee114f543149c66a6e0c14e9c4f8a1044b5cdaadd0f82159d6a6ff59"}, {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 = [ flanker = [
{file = "flanker-0.9.11.tar.gz", hash = "sha256:974418e5b498fd3bcb3859c22e22d26495257f9cf98b744c17f2335aca86e001"}, {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-py2.py3-none-any.whl", hash = "sha256:cd4a810dd51bf497552cf3f863b575dabd73d6ad6a91075b65936b151cbf4f9c"},
{file = "gunicorn-20.0.4.tar.gz", hash = "sha256:1904bb2b8a43658807108d59c3f3d56c2b6121a701161de0ddf9ad140073c626"}, {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 = [ httplib2 = [
{file = "httplib2-0.18.1-py3-none-any.whl", hash = "sha256:ca2914b015b6247791c4866782fa6042f495b94401a0f0bd3e1d6e0ba2236782"}, {file = "httplib2-0.18.1-py3-none-any.whl", hash = "sha256:ca2914b015b6247791c4866782fa6042f495b94401a0f0bd3e1d6e0ba2236782"},
{file = "httplib2-0.18.1.tar.gz", hash = "sha256:8af66c1c52c7ffe1aa5dc4bcd7c769885254b0756e6e69f953c7f0ab49a70ba3"}, {file = "httplib2-0.18.1.tar.gz", hash = "sha256:8af66c1c52c7ffe1aa5dc4bcd7c769885254b0756e6e69f953c7f0ab49a70ba3"},
@ -2491,8 +2553,8 @@ idna = [
{file = "idna-2.10.tar.gz", hash = "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6"}, {file = "idna-2.10.tar.gz", hash = "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6"},
] ]
importlib-metadata = [ importlib-metadata = [
{file = "importlib_metadata-1.7.0-py2.py3-none-any.whl", hash = "sha256:dc15b2969b4ce36305c51eebe62d418ac7791e9a157911d58bfb1f9ccd8e2070"}, {file = "importlib_metadata-4.12.0-py3-none-any.whl", hash = "sha256:7401a975809ea1fdc658c3aa4f78cc2195a0e019c5cbc4c06122884e9ae80c23"},
{file = "importlib_metadata-1.7.0.tar.gz", hash = "sha256:90bb658cdbbf6d1735b6341ce708fc7024a3e14e99ffdc5783edea9f9b077f83"}, {file = "importlib_metadata-4.12.0.tar.gz", hash = "sha256:637245b8bab2b6502fcbc752cc4b7a6f6243bb02b31c5c26156ad103d3d45670"},
] ]
iniconfig = [ iniconfig = [
{file = "iniconfig-1.0.1-py3-none-any.whl", hash = "sha256:80cf40c597eb564e86346103f609d74efce0f6b4d4f30ec8ce9e2c26411ba437"}, {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-py2.py3-none-any.whl", hash = "sha256:72dd37233799e619666c9f639a9da83c34013a73e8bbc79a7a6348d93c61fab8"},
{file = "ipython_genutils-0.2.0.tar.gz", hash = "sha256:eb2e116e75ecef9d4d228fdc66af54269afa26ab4463042e33785b887c628ba8"}, {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 = [ itsdangerous = [
{file = "itsdangerous-1.1.0-py2.py3-none-any.whl", hash = "sha256:b12271b2047cb23eeb98c8b5622e2e5c5e9abd9784a153e9d8ef9cb4dd09d749"}, {file = "itsdangerous-1.1.0-py2.py3-none-any.whl", hash = "sha256:b12271b2047cb23eeb98c8b5622e2e5c5e9abd9784a153e9d8ef9cb4dd09d749"},
{file = "itsdangerous-1.1.0.tar.gz", hash = "sha256:321b033d07f2a4136d3ec762eac9f16a10ccd60f53c0c91af90217ace7ba1f19"}, {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-py2.py3-none-any.whl", hash = "sha256:16e17faa4dce36551ade3a3ccb06236a61e5924ea1db163c9be9827acf935a82"},
{file = "jwcrypto-0.8.tar.gz", hash = "sha256:b7fee2635bbefdf145399392f5be26ad54161c8271c66b5fe107b4b452f06c24"}, {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 = [ limits = [
{file = "limits-1.5.1-py2-none-any.whl", hash = "sha256:0e5f8b10f18dd809eb2342f5046eb9aa5e4e69a0258567b5f4aa270647d438b3"}, {file = "limits-1.5.1-py2-none-any.whl", hash = "sha256:0e5f8b10f18dd809eb2342f5046eb9aa5e4e69a0258567b5f4aa270647d438b3"},
{file = "limits-1.5.1.tar.gz", hash = "sha256:f0c3319f032c4bfad68438ed1325c0fac86dac64582c7c25cddc87a0b658fa20"}, {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"}, {file = "matplotlib_inline-0.1.3-py3-none-any.whl", hash = "sha256:aed605ba3b72462d64d475a21a9296f400a19c4f74a31b59103d2a99ffd5aa5c"},
] ]
mccabe = [ mccabe = [
{file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"},
{file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"},
] ]
memory-profiler = [ memory-profiler = [
{file = "memory_profiler-0.57.0.tar.gz", hash = "sha256:23b196f91ea9ac9996e30bfab1e82fecc30a4a1d24870e81d1e81625f786a2c3"}, {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.6.egg", hash = "sha256:cbac4bc38d117f2a49aeedec4407d23e8866ea4ac27ff2cf7fb3e5b570df19e0"},
{file = "pyasn1_modules-0.2.8-py3.7.egg", hash = "sha256:c29a5e5cc7a3f05926aff34e097e84f8589cd790ce0ed41b67aed6857b26aafd"}, {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 = [ pycparser = [
{file = "pycparser-2.20-py2.py3-none-any.whl", hash = "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705"}, {file = "pycparser-2.20-py2.py3-none-any.whl", hash = "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705"},
{file = "pycparser-2.20.tar.gz", hash = "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0"}, {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-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:9f62d21bc693f3d7d444f17ed2ad7a913b4c37c15cd807895d013c39c0517dfd"},
{file = "pycryptodome-3.9.8.tar.gz", hash = "sha256:0e24171cf01021bc5dc17d6a9d4f33a048f09d62cc3f62541e95ef104588bda4"}, {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 = [ pygments = [
{file = "Pygments-2.7.4-py3-none-any.whl", hash = "sha256:bc9591213a8f0e0ca1a5e68a479b4887fdc3e75d0774e5c71c31920c427de435"}, {file = "Pygments-2.7.4-py3-none-any.whl", hash = "sha256:bc9591213a8f0e0ca1a5e68a479b4887fdc3e75d0774e5c71c31920c427de435"},
{file = "Pygments-2.7.4.tar.gz", hash = "sha256:df49d09b498e83c1a73128295860250b0b7edd4c723a32e9bc0d295c7c2ec337"}, {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-py3-none-any.whl", hash = "sha256:72d1d253f32dbd4f5c88eaf1fdc62f3a19f676ccbadb9dbc5d07e951b2b26daf"},
{file = "PyJWT-2.4.0.tar.gz", hash = "sha256:d42908208c699b3b973cbeb01a969ba6a96c821eefb1c5bfe4c390c01d67abba"}, {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 = [ pyopenssl = [
{file = "pyOpenSSL-19.1.0-py2.py3-none-any.whl", hash = "sha256:621880965a720b8ece2f1b2f54ea2071966ab00e2970ad2ce11d596102063504"}, {file = "pyOpenSSL-19.1.0-py2.py3-none-any.whl", hash = "sha256:621880965a720b8ece2f1b2f54ea2071966ab00e2970ad2ce11d596102063504"},
{file = "pyOpenSSL-19.1.0.tar.gz", hash = "sha256:9a24494b2602aaf402be5c9e30a0b82d4a5c67528fe8fb475e3f3bc00dd69507"}, {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"}, {file = "toml-0.10.1.tar.gz", hash = "sha256:926b612be1e5ce0634a2ca03470f95169cf16f939018233a670519cb4ac58b0f"},
] ]
tomli = [ tomli = [
{file = "tomli-2.0.0-py3-none-any.whl", hash = "sha256:b5bde28da1fed24b9bd1d4d2b8cba62300bfb4ec9a6187a957e8ddb9434c5224"}, {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
{file = "tomli-2.0.0.tar.gz", hash = "sha256:c292c34f58502a1eb2bbb9f5bbc9a5ebc37bee10ffb8c2d6bbdfa8eb13cc14e1"}, {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
] ]
tomlkit = [ tomlkit = [
{file = "tomlkit-0.8.0-py3-none-any.whl", hash = "sha256:b824e3466f1d475b2b5f1c392954c6cb7ea04d64354ff7300dc7c14257dc85db"}, {file = "tomlkit-0.11.0-py3-none-any.whl", hash = "sha256:0f4050db66fd445b885778900ce4dd9aea8c90c4721141fde0d6ade893820ef1"},
{file = "tomlkit-0.8.0.tar.gz", hash = "sha256:29e84a855712dfe0e88a48f6d05c21118dbafb283bb2eed614d46f80deb8e9a1"}, {file = "tomlkit-0.11.0.tar.gz", hash = "sha256:71ceb10c0eefd8b8f11fe34e8a51ad07812cb1dc3de23247425fbc9ddc47b9dd"},
] ]
tqdm = [ tqdm = [
{file = "tqdm-4.64.0-py2.py3-none-any.whl", hash = "sha256:74a2cdefe14d11442cedf3ba4e21a3b84ff9a2dbdc6cfae2c34addb2a14a5ea6"}, {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"}, {file = "urllib3-1.25.10.tar.gz", hash = "sha256:91056c15fa70756691db97756772bb1eb9678fa585d9184f24534b100dc60f4a"},
] ]
virtualenv = [ virtualenv = [
{file = "virtualenv-20.0.31-py2.py3-none-any.whl", hash = "sha256:e0305af10299a7fb0d69393d8f04cb2965dda9351140d11ac8db4e5e3970451b"}, {file = "virtualenv-20.8.1-py2.py3-none-any.whl", hash = "sha256:10062e34c204b5e4ec5f62e6ef2473f8ba76513a9a617e873f1f8fb4a519d300"},
{file = "virtualenv-20.0.31.tar.gz", hash = "sha256:43add625c53c596d38f971a465553f6318decc39d98512bc100fa1b1e839c8dc"}, {file = "virtualenv-20.8.1.tar.gz", hash = "sha256:bcc17f0b3a29670dd777d6f0755a4c04f28815395bca279cdcb213b97199a6b8"},
] ]
watchtower = [ watchtower = [
{file = "watchtower-0.8.0-py2.py3-none-any.whl", hash = "sha256:d6704b258494bddc3e9ddda286ef5067e4b09ab4287aea4289afdd035cc4742b"}, {file = "watchtower-0.8.0-py2.py3-none-any.whl", hash = "sha256:d6704b258494bddc3e9ddda286ef5067e4b09ab4287aea4289afdd035cc4742b"},

View File

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

View File

@ -1,7 +1,7 @@
#!/bin/sh #!/bin/sh
export DB_URI=postgresql://myuser:mypassword@localhost:15432/simplelogin 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 alembic upgrade head
poetry run flask dummy-data poetry run flask dummy-data

View File

@ -1,6 +1,6 @@
#!/bin/sh #!/bin/sh
export DB_URI=postgresql://myuser:mypassword@localhost:15432/test 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 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.`; let message = `Maybe you want to disable the alias instead? Please note once deleted, it <b>can't</b> be restored.`;
if (aliasDomainTrashUrl !== undefined) { 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>`; <a href="${aliasDomainTrashUrl}">trash</a>`;
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -26,441 +26,491 @@
display: none; display: none;
} }
</style> </style>
{% endblock %} {% endblock %}
{% block title %}Alias{% endblock %} {% block title %}Alias{% endblock %}
{% block default_content %} {% block default_content %}
<!-- Global Stats --> <!-- Global Stats -->
<div class="row"> <div class="row">
<div class="col-12 col-md-6 col-lg-3"> <div class="col-12 col-md-6 col-lg-3">
<div class="card"> <div class="card">
<div class="card-body"> <div class="card-body">
<div class="d-flex align-items-center"> <div class="d-flex align-items-center">
<div class="subheader">Aliases</div> <div class="subheader">Aliases</div>
<div class="text-muted" style="order: 2; margin-left: auto; font-size: .8rem">All time</div> <div class="text-muted"
</div> style="order: 2; margin-left: auto; font-size: .8rem">All time</div>
<div class="h1 m-0">{{ stats.nb_alias }}</div> </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>
</div> </div>
<div> </div>
<a data-alias="{{ alias.id }}" <div class="col-12 col-md-6 col-lg-3">
class="save-mailbox btn btn-sm btn-outline-info w-100"> <div class="card">
Update <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> </a>
</div> </div>
</div> {% if not current_user.expand_alias_info %}
{% 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" <div class="col text-right">
data-toogle="tooltip" <a class="btn btn-sm collapsed"
title="You can decide to turn off the PGP for an alias. This can be useful if the sender already encrypts the emails"> data-toggle="collapse"
PGP href="#alias-{{ alias.id }}"
<i class="fe fe-help-circle"></i> 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>
<div> <!-- END Send Email && More button -->
<label class="custom-switch cursor pl-0"> <!-- Collapse section -->
<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 "" }}> <div class="{% if not current_user.expand_alias_info %} collapse{% endif %} mt-2"
<span class="custom-switch-indicator"></span> id="alias-{{ alias.id }}">
</label> {% if alias_info.latest_email_log != None %}
</div>
{% endif %} <div style="font-size: 12px">
<div class="small-text mt-2" Alias created {{ alias.created_at | dt }}
data-toogle="tooltip" </div>
title="it's always pinned on top"> {% endif %}
Pin this alias <span class="alias-activity">{{ alias_info.nb_forward }}</span> forwards,
<i class="fe fe-help-circle"></i> <span class="alias-activity">{{ alias_info.nb_blocked }}</span> blocks,
</div> <span class="alias-activity">{{ alias_info.nb_reply }}</span> sents
<div> in the last 14 days
<label class="custom-switch cursor pl-0"> <a href="{{ url_for('dashboard.alias_log', alias_id=alias.id) }}"
<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"> class="btn btn-sm btn-link">
Transfer See All &nbsp;
<i class="ml-0 dropdown-icon fe fe-share-2 text-primary"></i>
</a> </a>
<form method="post"> {% if mailboxes|length > 1 %}
<input type="hidden" name="form-name" value="delete-alias">
<input type="hidden" name="alias-id" value="{{ alias.id }}"> <div class="small-text">
<input type="hidden" name="alias" class="alias" value="{{ alias.email }}"> Current mailbox
<span class="btn btn-link btn-sm float-right text-danger" </div>
onclick="confirmDeleteAlias.call(this)" <div class="d-flex">
{% if alias.custom_domain %} data-custom-domain-trash-url="{{ alias.custom_domain.get_trash_url() }}"{% endif %} <div class="flex-grow-1 mr-2">
data-alias="{{ alias.id }}" <select required
data-alias-email="{{ alias.email }}"> id="mailbox-{{ alias.id }}"
Delete&nbsp; &nbsp;<i class="dropdown-icon fe fe-trash-2 text-danger"></i> data-width="100%"
</span> class="mailbox-select"
</form> 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> </div>
<!-- END Collapse section -->
</div> </div>
</div> </div>
<!-- END Collapse section -->
</div>
</div>
{% endfor %} {% endfor %}
</div> </div>
<!-- END Alias list --> <!-- END Alias list -->

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,7 +1,7 @@
X-SimpleLogin-Client-IP: 40.92.66.13 X-SimpleLogin-Client-IP: 40.92.66.13
Received-SPF: Pass (mailfrom) identity=mailfrom; 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; 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 Received: from EUR01-VE1-obe.outbound.protection.outlook.com
(mail-oln040092066013.outbound.protection.outlook.com [40.92.66.13]) (mail-oln040092066013.outbound.protection.outlook.com [40.92.66.13])
(using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (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> From: <staff@hotmail.com>
Subject: complaint about message from 176.119.200.162 Subject: complaint about message from 176.119.200.162
To: {{ postmaster }} To: {{ postmaster }}
MIME-Version: 1.0 MIME-Version: 1.0
Content-Type: multipart/mixed; Content-Type: multipart/mixed;
boundary="31A9507D-D0B3-4DCD-AFBB-413468892CFE" boundary="31A9507D-D0B3-4DCD-AFBB-413468892CFE"
X-IncomingHeaderCount: 6 X-IncomingHeaderCount: 6
Message-ID: Message-ID:
<1d63d9ee-8f3e-4876-955c-1807db5ad138@AM6EUR05FT047.eop-eur05.prod.protection.outlook.com> <1d63d9ee-8f3e-4876-955c-1807db5ad138@AM6EUR05FT047.eop-eur05.prod.protection.outlook.com>
X-EOPAttributedMessage: 0 X-EOPAttributedMessage: 0
X-MS-PublicTrafficType: Email X-MS-PublicTrafficType: Email
X-MS-Office365-Filtering-Correlation-Id: 44e9ec0b-6c5d-4cea-6417-08da31bd7000 X-MS-Office365-Filtering-Correlation-Id: 44e9ec0b-6c5d-4cea-6417-08da31bd7000
X-MS-TrafficTypeDiagnostic: AM0PR02MB4563:EE_ X-MS-TrafficTypeDiagnostic: AM0PR02MB4563:EE_
X-Microsoft-Antispam: BCL:0; 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= lK5xD4UZS47NfR0tHc3wEp4HHOifZ4SDBb8aKx7H/vEW8Rg8rXXH12G4lWdpzr8qTsCmvzuhj5x6IAumOKQ8lWLj5Lp3jyml91wVnwCtUnk5cTXpQwDZd9QMgtEW07GoLdWjkbShAhLRDf+9Y4DxidHCacOAYxcNX42wo3vYZOEHDzVRUxSmY0c7Km60pDtiYzEk+P9AoE2YKYG2rDwDx0vgoLgqFspGqQ+2OeHD2ZAEyATHR/sQy6tf5S2d4wA3HcHrwrGMlz/4d9VbT5h9a5cqj9S59wpuc6g8nyYhmK3AHJkB5nXmpBZBihTw5X/Qh5PZqUYwPxkwpq3WlaEuXvzaKFiwJFvtuRGX+mEioClCxiwPROb7sI9ZHWPw48AHysF+whYGBfleRy4c2SuW6e1D5uewGry+lXVljxg7qKo=
X-OriginatorOrg: sct-15-20-4755-11-msonline-outlook-ab7de.templateTenant X-OriginatorOrg: sct-15-20-4755-11-msonline-outlook-ab7de.templateTenant
X-MS-Exchange-CrossTenant-OriginalArrivalTime: 09 May 2022 13:11:32.0875 X-MS-Exchange-CrossTenant-OriginalArrivalTime: 09 May 2022 13:11:32.0875
(UTC) (UTC)
X-MS-Exchange-CrossTenant-Network-Message-Id: X-MS-Exchange-CrossTenant-Network-Message-Id:
44e9ec0b-6c5d-4cea-6417-08da31bd7000 44e9ec0b-6c5d-4cea-6417-08da31bd7000
X-MS-Exchange-CrossTenant-Id: 84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa 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 AM6EUR05FT047.eop-eur05.prod.protection.outlook.com
X-MS-Exchange-CrossTenant-AuthAs: Anonymous X-MS-Exchange-CrossTenant-AuthAs: Anonymous
X-MS-Exchange-CrossTenant-FromEntityHeader: Internet X-MS-Exchange-CrossTenant-FromEntityHeader: Internet
X-MS-Exchange-CrossTenant-RMS-PersistedConsumerOrg: X-MS-Exchange-CrossTenant-RMS-PersistedConsumerOrg:
00000000-0000-0000-0000-000000000000 00000000-0000-0000-0000-000000000000
X-MS-Exchange-Transport-CrossTenantHeadersStamped: AM0PR02MB4563 X-MS-Exchange-Transport-CrossTenantHeadersStamped: AM0PR02MB4563
X-Spamd-Result: default: False [-1.75 / 13.00]; 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 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 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 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 OriginalChecksum:5EBD8C309CA888838EDC898C63E28E1EC00EF74772276A54C08DA83D658756F4;UpperCasedChecksum:E102374CD208D4ACB2034F1A17F76DA6345BD176395C6D4EADEC3B47BFF41ECC;SizeAsReceived:1262;Count:15
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=simplelogin.co; DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=simplelogin.co;
s=dkim; t=1652070640; h=From:To:Subject:Message-ID:Date; 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 5NybKJmB6B6KL5hl5LG3KzCdaWfe3dAAhD4e2gIU80dal596dlzluyvLR1k+6rdM4JvlGq
OVWLR42Oj4anrnOqLCUkL44ILIhLpAE= OVWLR42Oj4anrnOqLCUkL44ILIhLpAE=
Date: Mon, 9 May 2022 00:30:38 -0400 (EDT) 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> <10627474.1041327707.1652070638478.JavaMail.cloud@p2-mta-0301.p2.messagegears.net>
Subject: Original Subject Subject: Original Subject
Content-Type: multipart/mixed; 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-ExpirationStartTimeReason: OriginalSubmit
X-MS-Exchange-Organization-ExpirationInterval: 1:00:00:00.0000000 X-MS-Exchange-Organization-ExpirationInterval: 1:00:00:00.0000000
X-MS-Exchange-Organization-ExpirationIntervalReason: OriginalSubmit 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 ede92e41-5acb-4474-c5be-08da3174af2b
X-EOPAttributedMessage: 0 X-EOPAttributedMessage: 0
X-EOPTenantAttributedMessage: 84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa:0 X-EOPTenantAttributedMessage: 84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa:0
X-MS-Exchange-Organization-MessageDirectionality: Incoming X-MS-Exchange-Organization-MessageDirectionality: Incoming
X-MS-PublicTrafficType: Email X-MS-PublicTrafficType: Email
X-MS-Exchange-Organization-AuthSource: X-MS-Exchange-Organization-AuthSource:
BN8NAM11FT053.eop-nam11.prod.protection.outlook.com BN8NAM11FT053.eop-nam11.prod.protection.outlook.com
X-MS-Exchange-Organization-AuthAs: Anonymous X-MS-Exchange-Organization-AuthAs: Anonymous
X-MS-UserLastLogonTime: 5/9/2022 3:30:52 AM 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-Microsoft-Antispam: BCL:0;
X-MS-Exchange-CrossTenant-OriginalArrivalTime: 09 May 2022 04:30:44.9945 X-MS-Exchange-CrossTenant-OriginalArrivalTime: 09 May 2022 04:30:44.9945
(UTC) (UTC)
X-MS-Exchange-CrossTenant-Network-Message-Id: X-MS-Exchange-CrossTenant-Network-Message-Id:
ede92e41-5acb-4474-c5be-08da3174af2b ede92e41-5acb-4474-c5be-08da3174af2b
X-MS-Exchange-CrossTenant-Id: 84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa 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 BN8NAM11FT053.eop-nam11.prod.protection.outlook.com
X-MS-Exchange-CrossTenant-AuthAs: Anonymous X-MS-Exchange-CrossTenant-AuthAs: Anonymous
X-MS-Exchange-CrossTenant-FromEntityHeader: Internet X-MS-Exchange-CrossTenant-FromEntityHeader: Internet
X-MS-Exchange-CrossTenant-RMS-PersistedConsumerOrg: X-MS-Exchange-CrossTenant-RMS-PersistedConsumerOrg:
00000000-0000-0000-0000-000000000000 00000000-0000-0000-0000-000000000000
X-MS-Exchange-Transport-CrossTenantHeadersStamped: SJ0PR11MB4958 X-MS-Exchange-Transport-CrossTenantHeadersStamped: SJ0PR11MB4958
X-MS-Exchange-Transport-EndToEndLatency: 00:00:03.3271765 X-MS-Exchange-Transport-EndToEndLatency: 00:00:03.3271765
X-MS-Exchange-Processed-By-BccFoldering: 15.20.5227.023 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); 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== 5vMbyqxGkdcvoPRAk5ACFywqndfpuBMcVz6K/12RtMALmdfGi+GpgO+lXQe3PiGwHtV5wXFRStQwg29XySZZo6tOyvshTSJ1uafhX53S93r5MaqDxJrR0UNGr2VYdKiAm1jYIYQm84v/mEbSAGjjBwEgS1PHlzM72I96JadXzfV9Fmsd5pHlfoLxEqXe6hBJAAQS99CcpwPDnaVA9UZUHA==
X-Message-Delivery: Vj0xLjE7dXM9MDtsPTA7YT0wO0Q9MTtHRD0xO1NDTD0tMQ== X-Message-Delivery: Vj0xLjE7dXM9MDtsPTA7YT0wO0Q9MTtHRD0xO1NDTD0tMQ==
X-Microsoft-Antispam-Message-Info: X-Microsoft-Antispam-Message-Info:
=?utf-8?B?VjZIQkpKR05oRUo1Vzc0YTBDUW52S0lsYkJSMGRzY0hJMnRMOWdyRGowcGpk?= =?utf-8?B?VjZIQkpKR05oRUo1Vzc0YTBDUW52S0lsYkJSMGRzY0hJMnRMOWdyRGowcGpk?=
=?utf-8?B?SUJLSDRPaStzakpJUHlaWVFnNWpBSGRsZ1Z4aEFmaXJOR1ZMUWxTTnQ1SXg1?= =?utf-8?B?SUJLSDRPaStzakpJUHlaWVFnNWpBSGRsZ1Z4aEFmaXJOR1ZMUWxTTnQ1SXg1?=
=?utf-8?B?anhFNTJ5RGU2YjRiTWhWK3FvWXBJU29YSWdqM3VvUkZpY21aaW5lSkJ5WWph?= =?utf-8?B?anhFNTJ5RGU2YjRiTWhWK3FvWXBJU29YSWdqM3VvUkZpY21aaW5lSkJ5WWph?=

View File

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