more updates on the deployment stuff
This commit is contained in:
parent
a5a7344d3b
commit
06fcb36121
|
@ -1,16 +1,38 @@
|
||||||
# environment directory
|
# Python
|
||||||
.venv/
|
*.pyc
|
||||||
.pydevproject
|
__pycache__/
|
||||||
|
*.pyo
|
||||||
|
*.pyd
|
||||||
|
.Python
|
||||||
|
*.egg-info/
|
||||||
|
dist/
|
||||||
|
build/
|
||||||
env/
|
env/
|
||||||
/.project
|
.venv/
|
||||||
/logs
|
*.egg
|
||||||
/media
|
|
||||||
/static
|
# Django
|
||||||
__pycache__
|
*.log
|
||||||
/*/migrations
|
*.pot
|
||||||
|
*.sqlite3
|
||||||
|
media/
|
||||||
|
static/
|
||||||
|
migrations/
|
||||||
*.code-workspace
|
*.code-workspace
|
||||||
.env/
|
|
||||||
|
# IDE/Editor
|
||||||
.vscode/
|
.vscode/
|
||||||
memory-bank/
|
.pydevproject
|
||||||
.cursor/rules/
|
.project
|
||||||
run/celery/
|
.idea/
|
||||||
|
*.sublime-project
|
||||||
|
*.sublime-workspace
|
||||||
|
|
||||||
|
# OS/Environment
|
||||||
|
.DS_Store
|
||||||
|
.env
|
||||||
|
*.bak
|
||||||
|
*~
|
||||||
|
|
||||||
|
# Package-specific
|
||||||
|
aws_ses.log
|
43
LICENSE
43
LICENSE
|
@ -1,22 +1,21 @@
|
||||||
Copyright (c) 2011 Harry Marr
|
MIT License
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person
|
Copyright (c) 2025 ZeeksGeeks
|
||||||
obtaining a copy of this software and associated documentation
|
|
||||||
files (the "Software"), to deal in the Software without
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
restriction, including without limitation the rights to use,
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
in the Software without restriction, including without limitation the rights
|
||||||
copies of the Software, and to permit persons to whom the
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
Software is furnished to do so, subject to the following
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
conditions:
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be
|
The above copyright notice and this permission notice shall be included in all
|
||||||
included in all copies or substantial portions of the Software.
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
SOFTWARE.
|
||||||
OTHER DEALINGS IN THE SOFTWARE.
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
include LICENSE
|
||||||
|
include README.md
|
||||||
|
recursive-include django_aws_ses/templates *.html
|
||||||
|
recursive-include django_aws_ses/static *.css *.js
|
71
README.md
71
README.md
|
@ -0,0 +1,71 @@
|
||||||
|
# Django AWS SES
|
||||||
|
|
||||||
|
A Django email backend for sending emails via Amazon Simple Email Service (SES).
|
||||||
|
|
||||||
|
## Features
|
||||||
|
- Send emails using AWS SES with DKIM signing support.
|
||||||
|
- Handle bounce, complaint, and delivery notifications via SNS webhooks.
|
||||||
|
- Filter recipients based on bounce/complaint history and domain validation.
|
||||||
|
- Admin dashboard for SES statistics and verified emails.
|
||||||
|
- Unsubscribe functionality with secure URL generation.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
```bash
|
||||||
|
pip install django_aws_ses
|
||||||
|
```
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
- Python 3.8+
|
||||||
|
- Django 3.2+
|
||||||
|
- AWS SES account with verified domains/emails
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
1. Add to `INSTALLED_APPS`:
|
||||||
|
```python
|
||||||
|
INSTALLED_APPS = [
|
||||||
|
...
|
||||||
|
'django_aws_ses',
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Configure settings in `settings.py`:
|
||||||
|
```python
|
||||||
|
AWS_SES_ACCESS_KEY_ID = 'your-access-key'
|
||||||
|
AWS_SES_SECRET_ACCESS_KEY = 'your-secret-key'
|
||||||
|
AWS_SES_REGION_NAME = 'us-east-1'
|
||||||
|
AWS_SES_REGION_ENDPOINT = 'email.us-east-1.amazonaws.com'
|
||||||
|
EMAIL_BACKEND = 'django_aws_ses.backends.SESBackend'
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Apply migrations:
|
||||||
|
```bash
|
||||||
|
python manage.py migrate
|
||||||
|
```
|
||||||
|
|
||||||
|
4. (Optional) Enable DKIM signing:
|
||||||
|
```python
|
||||||
|
DKIM_DOMAIN = 'example.com'
|
||||||
|
DKIM_PRIVATE_KEY = 'your-private-key'
|
||||||
|
DKIM_SELECTOR = 'ses'
|
||||||
|
```
|
||||||
|
|
||||||
|
5. Set up SNS webhook for bounce/complaint handling:
|
||||||
|
- Add the URL `your-domain.com/aws_ses/bounce/` to your SNS subscription.
|
||||||
|
- Ensure the view is accessible (e.g., CSRF-exempt).
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
- Send emails using Django’s `send_mail` or `EmailMessage`.
|
||||||
|
- View SES statistics at `/aws_ses/status/` (superuser only).
|
||||||
|
- Unsubscribe users via `/aws_ses/unsubscribe/<uuid>/<hash>/`.
|
||||||
|
|
||||||
|
## Development
|
||||||
|
To contribute:
|
||||||
|
1. Clone the repo: `git clone https://github.com/zeeksgeeks/django_aws_ses`
|
||||||
|
2. Install dependencies: `pip install -r requirements.txt`
|
||||||
|
3. Run tests: `python manage.py test`
|
||||||
|
|
||||||
|
## License
|
||||||
|
MIT License. See [LICENSE](LICENSE) for details.
|
||||||
|
|
||||||
|
## Credits
|
||||||
|
Developed by Ray Jessop. Inspired by [django-ses](https://github.com/django-ses/django-ses).
|
|
@ -1,8 +1,8 @@
|
||||||
|
"""Django AWS SES: A Django email backend for Amazon SES."""
|
||||||
|
|
||||||
default_app_config = 'django_aws_ses.apps.DjangoAwsSesBackendConfig'
|
default_app_config = 'django_aws_ses.apps.DjangoAwsSesBackendConfig'
|
||||||
|
|
||||||
# When changing this, remember to change it in setup.py
|
VERSION = (0, 1, 0)
|
||||||
VERSION = (0, 0, 1)
|
__version__ = '.'.join(str(x) for x in VERSION)
|
||||||
__version__ = '.'.join([str(x) for x in VERSION])
|
|
||||||
__author__ = 'Ray Jessop'
|
__author__ = 'Ray Jessop'
|
||||||
__all__ = ('Django AWS SES Backend',)
|
__all__ = ('SESBackend', 'DjangoAwsSesBackendConfig')
|
176
setup.py
176
setup.py
|
@ -1,147 +1,45 @@
|
||||||
import ast
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
import sys
|
|
||||||
|
|
||||||
from fnmatch import fnmatchcase
|
|
||||||
|
|
||||||
from distutils.util import convert_path
|
|
||||||
from setuptools import setup, find_packages
|
from setuptools import setup, find_packages
|
||||||
|
|
||||||
|
with open("README.md", "r", encoding="utf-8") as f:
|
||||||
def read(*path):
|
long_description = f.read()
|
||||||
return open(os.path.join(os.path.abspath(os.path.dirname(__file__)),
|
|
||||||
*path)).read()
|
|
||||||
|
|
||||||
# Provided as an attribute, so you can append to these instead
|
|
||||||
# of replicating them:
|
|
||||||
standard_exclude = ["*.py", "*.pyc", "*~", ".*", "*.bak"]
|
|
||||||
standard_exclude_directories = [
|
|
||||||
".*", "CVS", "_darcs", "./build",
|
|
||||||
"./dist", "EGG-INFO", "*.egg-info"
|
|
||||||
]
|
|
||||||
|
|
||||||
# Copied from paste/util/finddata.py
|
|
||||||
def find_package_data(where=".", package="", exclude=standard_exclude,
|
|
||||||
exclude_directories=standard_exclude_directories,
|
|
||||||
only_in_packages=True, show_ignored=False):
|
|
||||||
"""
|
|
||||||
Return a dictionary suitable for use in ``package_data``
|
|
||||||
in a distutils ``setup.py`` file.
|
|
||||||
The dictionary looks like::
|
|
||||||
{"package": [files]}
|
|
||||||
Where ``files`` is a list of all the files in that package that
|
|
||||||
don't match anything in ``exclude``.
|
|
||||||
If ``only_in_packages`` is true, then top-level directories that
|
|
||||||
are not packages won't be included (but directories under packages
|
|
||||||
will).
|
|
||||||
Directories matching any pattern in ``exclude_directories`` will
|
|
||||||
be ignored; by default directories with leading ``.``, ``CVS``,
|
|
||||||
and ``_darcs`` will be ignored.
|
|
||||||
If ``show_ignored`` is true, then all the files that aren't
|
|
||||||
included in package data are shown on stderr (for debugging
|
|
||||||
purposes).
|
|
||||||
Note patterns use wildcards, or can be exact paths (including
|
|
||||||
leading ``./``), and all searching is case-insensitive.
|
|
||||||
"""
|
|
||||||
|
|
||||||
out = {}
|
|
||||||
stack = [(convert_path(where), "", package, only_in_packages)]
|
|
||||||
while stack:
|
|
||||||
where, prefix, package, only_in_packages = stack.pop(0)
|
|
||||||
for name in os.listdir(where):
|
|
||||||
fn = os.path.join(where, name)
|
|
||||||
if os.path.isdir(fn):
|
|
||||||
bad_name = False
|
|
||||||
for pattern in exclude_directories:
|
|
||||||
if (fnmatchcase(name, pattern) or
|
|
||||||
fn.lower() == pattern.lower()):
|
|
||||||
bad_name = True
|
|
||||||
if show_ignored:
|
|
||||||
sys.stderr.write("Directory %s ignored by pattern %s" % (fn, pattern))
|
|
||||||
break
|
|
||||||
if bad_name:
|
|
||||||
continue
|
|
||||||
if os.path.isfile(os.path.join(fn, "__init__.py")) \
|
|
||||||
and not prefix:
|
|
||||||
if not package:
|
|
||||||
new_package = name
|
|
||||||
else:
|
|
||||||
new_package = package + "." + name
|
|
||||||
stack.append((fn, "", new_package, False))
|
|
||||||
else:
|
|
||||||
stack.append((fn, prefix + name + "/", package,
|
|
||||||
only_in_packages))
|
|
||||||
elif package or not only_in_packages:
|
|
||||||
# is a file
|
|
||||||
bad_name = False
|
|
||||||
for pattern in exclude:
|
|
||||||
if (fnmatchcase(name, pattern) or
|
|
||||||
fn.lower() == pattern.lower()):
|
|
||||||
bad_name = True
|
|
||||||
if show_ignored:
|
|
||||||
sys.stderr.write("File %s ignored by pattern %s" % (fn, pattern))
|
|
||||||
break
|
|
||||||
if bad_name:
|
|
||||||
continue
|
|
||||||
out.setdefault(package, []).append(prefix + name)
|
|
||||||
return out
|
|
||||||
|
|
||||||
excluded_directories = standard_exclude_directories + ["example", "tests"]
|
|
||||||
package_data = find_package_data(exclude_directories=excluded_directories)
|
|
||||||
|
|
||||||
DESCRIPTION = "A Django email backend for Amazon's Simple Email Service"
|
|
||||||
|
|
||||||
LONG_DESCRIPTION = None
|
|
||||||
try:
|
|
||||||
LONG_DESCRIPTION = open('README.rst').read()
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
# Parse version
|
|
||||||
_version_re = re.compile(r"VERSION\s+=\s+(.*)")
|
|
||||||
with open("django_aws_ses/__init__.py", "rb") as f:
|
|
||||||
version = ".".join(
|
|
||||||
map(str, ast.literal_eval(_version_re.search(f.read().decode("utf-8")).group(1)))
|
|
||||||
)
|
|
||||||
|
|
||||||
CLASSIFIERS = [
|
|
||||||
'Development Status :: 4 - Beta',
|
|
||||||
'Intended Audience :: Developers',
|
|
||||||
'License :: OSI Approved :: MIT License',
|
|
||||||
'Operating System :: OS Independent',
|
|
||||||
'Programming Language :: Python',
|
|
||||||
'Topic :: Software Development :: Libraries :: Python Modules',
|
|
||||||
'Framework :: Django',
|
|
||||||
'Framework :: Django :: 1.11',
|
|
||||||
'Framework :: Django :: 2.0',
|
|
||||||
'Framework :: Django :: 2.1',
|
|
||||||
'Framework :: Django :: 2.2',
|
|
||||||
'Programming Language :: Python :: 2',
|
|
||||||
'Programming Language :: Python :: 2.7',
|
|
||||||
'Programming Language :: Python :: 3',
|
|
||||||
'Programming Language :: Python :: 3.4',
|
|
||||||
'Programming Language :: Python :: 3.5',
|
|
||||||
'Programming Language :: Python :: 3.6',
|
|
||||||
]
|
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
name='django_aws_ses',
|
name="django_aws_ses",
|
||||||
version=version,
|
version="0.1.0",
|
||||||
packages=find_packages(exclude=['example', 'tests']),
|
packages=find_packages(exclude=["tests"]),
|
||||||
package_data=package_data,
|
|
||||||
python_requires='>=2.7',
|
|
||||||
author='ZeeksGeeks',
|
|
||||||
author_email='development@zeeksgeeks.com',
|
|
||||||
url='https://github.com/django-ses/django-ses',
|
|
||||||
license='MIT',
|
|
||||||
description=DESCRIPTION,
|
|
||||||
long_description=LONG_DESCRIPTION,
|
|
||||||
platforms=['any'],
|
|
||||||
classifiers=CLASSIFIERS,
|
|
||||||
install_requires=["boto3>=1.0.0", "pytz>=2016.10", "future>=0.16.0", "django>1.10"],
|
|
||||||
include_package_data=True,
|
include_package_data=True,
|
||||||
|
python_requires=">=3.8",
|
||||||
|
install_requires=[
|
||||||
|
"django>=3.2",
|
||||||
|
"boto3>=1.18",
|
||||||
|
"requests>=2.25",
|
||||||
|
"cryptography>=3.4",
|
||||||
|
"dnspython>=2.1",
|
||||||
|
"pytz>=2021.1",
|
||||||
|
],
|
||||||
extras_require={
|
extras_require={
|
||||||
'bounce': ['requests<3', 'M2Crypto'],
|
"dkim": ["dkimpy>=1.0"],
|
||||||
},
|
},
|
||||||
|
author="Ray Jessop",
|
||||||
|
author_email="development@zeeksgeeks.com",
|
||||||
|
description="A Django email backend for Amazon SES",
|
||||||
|
long_description=long_description,
|
||||||
|
long_description_content_type="text/markdown",
|
||||||
|
url="https://github.com/zeeksgeeks/django_aws_ses",
|
||||||
|
license="MIT",
|
||||||
|
classifiers=[
|
||||||
|
"Development Status :: 4 - Beta",
|
||||||
|
"Framework :: Django",
|
||||||
|
"Framework :: Django :: 3.2",
|
||||||
|
"Framework :: Django :: 4.0",
|
||||||
|
"Framework :: Django :: 4.2",
|
||||||
|
"Intended Audience :: Developers",
|
||||||
|
"License :: OSI Approved :: MIT License",
|
||||||
|
"Programming Language :: Python :: 3",
|
||||||
|
"Programming Language :: Python :: 3.8",
|
||||||
|
"Programming Language :: Python :: 3.9",
|
||||||
|
"Programming Language :: Python :: 3.10",
|
||||||
|
"Programming Language :: Python :: 3.11",
|
||||||
|
"Topic :: Communications :: Email",
|
||||||
|
],
|
||||||
)
|
)
|
Loading…
Reference in New Issue