more updates on the deployment stuff
This commit is contained in:
parent
a5a7344d3b
commit
06fcb36121
|
@ -1,16 +1,38 @@
|
|||
# environment directory
|
||||
.venv/
|
||||
.pydevproject
|
||||
# Python
|
||||
*.pyc
|
||||
__pycache__/
|
||||
*.pyo
|
||||
*.pyd
|
||||
.Python
|
||||
*.egg-info/
|
||||
dist/
|
||||
build/
|
||||
env/
|
||||
/.project
|
||||
/logs
|
||||
/media
|
||||
/static
|
||||
__pycache__
|
||||
/*/migrations
|
||||
.venv/
|
||||
*.egg
|
||||
|
||||
# Django
|
||||
*.log
|
||||
*.pot
|
||||
*.sqlite3
|
||||
media/
|
||||
static/
|
||||
migrations/
|
||||
*.code-workspace
|
||||
.env/
|
||||
|
||||
# IDE/Editor
|
||||
.vscode/
|
||||
memory-bank/
|
||||
.cursor/rules/
|
||||
run/celery/
|
||||
.pydevproject
|
||||
.project
|
||||
.idea/
|
||||
*.sublime-project
|
||||
*.sublime-workspace
|
||||
|
||||
# OS/Environment
|
||||
.DS_Store
|
||||
.env
|
||||
*.bak
|
||||
*~
|
||||
|
||||
# Package-specific
|
||||
aws_ses.log
|
37
LICENSE
37
LICENSE
|
@ -1,22 +1,21 @@
|
|||
Copyright (c) 2011 Harry Marr
|
||||
MIT License
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
Copyright (c) 2025 ZeeksGeeks
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 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'
|
||||
|
||||
# When changing this, remember to change it in setup.py
|
||||
VERSION = (0, 0, 1)
|
||||
__version__ = '.'.join([str(x) for x in VERSION])
|
||||
VERSION = (0, 1, 0)
|
||||
__version__ = '.'.join(str(x) for x in VERSION)
|
||||
__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
|
||||
|
||||
|
||||
def read(*path):
|
||||
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',
|
||||
]
|
||||
with open("README.md", "r", encoding="utf-8") as f:
|
||||
long_description = f.read()
|
||||
|
||||
setup(
|
||||
name='django_aws_ses',
|
||||
version=version,
|
||||
packages=find_packages(exclude=['example', '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"],
|
||||
name="django_aws_ses",
|
||||
version="0.1.0",
|
||||
packages=find_packages(exclude=["tests"]),
|
||||
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={
|
||||
'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