diff --git a/pyproject.toml b/pyproject.toml index fa23250..9749397 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,12 +5,13 @@ description = "Add your description here" readme = "README.md" requires-python = ">=3.13" dependencies = [ - "boto3==1.26.137", - "flask==2.2.2", - "flask-limiter==3.8.0", + "boto3==1.39.8", + "flask==3.1.1", + "flask-limiter==3.11.0", "flask-recaptcha==0.4.2", - "gunicorn==20.1.0", - "jinja2==3.0.3", - "python-dotenv==0.21.0", - "werkzeug==2.2.2", + "gunicorn==23.0.0", + "jinja2==3.1.6", + "python-dotenv==1.1.1", + "requests==2.32.4", + "werkzeug==3.1.3", ] diff --git a/server.py b/server.py index 3145739..cb0bc64 100644 --- a/server.py +++ b/server.py @@ -18,9 +18,9 @@ load_dotenv() class Config: - MAX_CONTENT_LENGTH = 15 * 1024 * 1024 # 15 MB + MAX_CONTENT_LENGTH = 40 * 1024 * 1024 # 40MB - this is the SES limit and is greated than the 20MB limit imposed in the dropzone/frontend to allow for PGP overhead EMAIL_DOMAIN = "@ethereum.org" - DEFAULT_RECIPIENT_EMAIL = "kyc@ethereum.org" + DEFAULT_RECIPIENT_EMAIL = os.getenv('DEFAULT_RECIPIENT_EMAIL', 'kyc@ethereum.org') NUMBER_OF_ATTACHMENTS = int(os.getenv('NUMBEROFATTACHMENTS', 10)) SECRET_KEY = os.getenv('SECRET_KEY', 'you-should-set-a-secret-key') @@ -134,39 +134,63 @@ def validate_turnstile(turnstile_response): def send_email(message): """ - Sends the email using AWS SES and logs detailed information for debugging. + Sends the email using AWS SES V2 and logs detailed information for debugging. """ try: - # Send the email - response = ses_client.send_raw_email( - Source=message['From'], - Destinations=[message['To']], - RawMessage={ - 'Data': message.as_string() + # Convert MIME message to bytes for SES V2 + raw_message_data = message.as_string().encode('utf-8') + + # Check message size before sending (AWS SES limit is 40MB) + message_size_mb = len(raw_message_data) / (1024 * 1024) + if message_size_mb > 40: + logging.error(f'Email message size ({message_size_mb:.2f} MB) exceeds AWS SES limit of 40MB') + raise ValueError(f'Error: Email message is too large ({message_size_mb:.2f} MB). AWS SES has a 40MB limit. Please reduce the size of attachments.') + + logging.info(f'Sending email with size: {message_size_mb:.2f} MB') + + # Send the email using SES V2 + response = ses_client.send_email( + FromEmailAddress=message['From'], + Destination={ + 'ToAddresses': [message['To']] + }, + Content={ + 'Raw': { + 'Data': raw_message_data + } } ) # Log the response message_id = response['MessageId'] - logging.info('AWS SES email sent successfully. MessageId: %s', message_id) + logging.info('AWS SES V2 email sent successfully. MessageId: %s', message_id) except ClientError as e: error_code = e.response['Error']['Code'] error_message = e.response['Error']['Message'] - logging.error('AWS SES error: Code=%s, Message=%s', error_code, error_message) + logging.error('AWS SES V2 error: Code=%s, Message=%s', error_code, error_message) # Provide user-friendly error messages - if error_code == 'MessageRejected': + if error_code == '413' or error_code == 'RequestEntityTooLarge': + # Log the message size for debugging + message_size_mb = len(raw_message_data) / (1024 * 1024) + logging.error(f'Email message size: {message_size_mb:.2f} MB') + raise ValueError('Error: Email message is too large. AWS SES has a 40MB limit for raw messages. Please reduce the size of attachments.') + elif error_code == 'MessageRejected': raise ValueError('Error: Email was rejected by AWS SES. Please check the email configuration.') elif error_code == 'MailFromDomainNotVerified': raise ValueError('Error: The sender email domain is not verified in AWS SES.') elif error_code == 'ConfigurationSetDoesNotExist': raise ValueError('Error: AWS SES configuration error.') + elif error_code == 'AccountSuspendedException': + raise ValueError('Error: AWS SES account is suspended.') + elif error_code == 'SendingPausedException': + raise ValueError('Error: AWS SES sending is paused for this account.') else: raise ValueError(f'Error: Failed to send email. {error_message}') except Exception as e: - logging.error('Error sending email via AWS SES: %s', str(e)) + logging.error('Error sending email via AWS SES V2: %s', str(e)) raise @@ -366,9 +390,9 @@ def send_identifier_to_kissflow(grant_id, legal_identifier): AWS_REGION = os.environ['AWS_REGION'] FROMEMAIL = os.environ['SES_FROM_EMAIL'] -# Initialize AWS SES client +# Initialize AWS SES V2 client ses_client = boto3.client( - 'ses', + 'sesv2', region_name=AWS_REGION, aws_access_key_id=AWS_ACCESS_KEY_ID, aws_secret_access_key=AWS_SECRET_ACCESS_KEY @@ -377,8 +401,6 @@ def send_identifier_to_kissflow(grant_id, legal_identifier): app = Flask(__name__) app.config.from_object(Config) - - # Initialize rate limiting limiter = Limiter(get_forwarded_address, app=app, default_limits=["200 per day", "50 per hour"]) @@ -388,6 +410,15 @@ def send_identifier_to_kissflow(grant_id, legal_identifier): logging.basicConfig(filename=log_file, level=logging.INFO) else: logging.basicConfig(level=logging.INFO) + +# DEBUG: Print Config values on startup +logging.info("=== DEBUG: Config Values on Startup ===") +logging.info(f"MAX_CONTENT_LENGTH: {Config.MAX_CONTENT_LENGTH}") +logging.info(f"EMAIL_DOMAIN: {Config.EMAIL_DOMAIN}") +logging.info(f"DEFAULT_RECIPIENT_EMAIL: {Config.DEFAULT_RECIPIENT_EMAIL}") +logging.info(f"NUMBER_OF_ATTACHMENTS: {Config.NUMBER_OF_ATTACHMENTS}") +logging.info(f"SECRET_KEY: {'[SET]' if Config.SECRET_KEY != 'you-should-set-a-secret-key' else '[USING DEFAULT - PLEASE SET!]'}") +logging.info("=====================================") @app.route('/', methods=['GET']) def index(): diff --git a/static/js/app.js b/static/js/app.js index 63a00af..4623413 100644 --- a/static/js/app.js +++ b/static/js/app.js @@ -23,7 +23,7 @@ function hideError() { } Dropzone.options.dropzoneArea = { - maxFilesize: 15, // Max file size per file in MB + maxFilesize: 20, // Max file size per file in MB maxFiles: 10, // Max number of files url: '/fake', paramName: 'attachment', @@ -31,7 +31,7 @@ Dropzone.options.dropzoneArea = { autoQueue: false, addRemoveLinks: true, uploadMultiple: true, - dictDefaultMessage: 'Drag & drop your files here - or click to browse. You can attach multiple files, up to a total of 15 MB.', + dictDefaultMessage: 'Drag & drop your files here - or click to browse. You can attach multiple files, up to a total of 20MB.', dictFileTooBig: 'File is too big ({{filesize}}MB). Max filesize: {{maxFilesize}}MB.', dictMaxFilesExceeded: 'You can only upload a maximum of {{maxFiles}} files.', init: function() { @@ -41,9 +41,9 @@ Dropzone.options.dropzoneArea = { hideError(); // Clear any existing errors // Check individual file size - if (file.size > 15 * 1024 * 1024) { + if (file.size > 20 * 1024 * 1024) { this.removeFile(file); - showError(`Error: File "${file.name}" is too large (${(file.size / 1024 / 1024).toFixed(2)}MB). Maximum file size is 15MB.`); + showError(`Error: File "${file.name}" is too large (${(file.size / 1024 / 1024).toFixed(2)}MB). Maximum file size is 20MB.`); return; } @@ -52,10 +52,10 @@ Dropzone.options.dropzoneArea = { return total + f.size; }, 0); - // If the total added file size is greater than 15 MB, remove the file - if (totalSize > 15 * 1024 * 1024) { + // If the total added file size is greater than 20 MB, remove the file + if (totalSize > 20 * 1024 * 1024) { this.removeFile(file); - showError(`Error: Total file size would exceed the 15MB limit. Current total: ${(totalSize / 1024 / 1024).toFixed(2)}MB`); + showError(`Error: Total file size would exceed the 20MB limit. Current total: ${(totalSize / 1024 / 1024).toFixed(2)}MB`); } }); @@ -151,16 +151,6 @@ document.addEventListener('DOMContentLoaded', function() { // Trigger change event on page load to set initial state recipient.dispatchEvent(new Event('change')); - // Redirect clicks on a greed button - const addFileButton = document.getElementById('add-file-button'); - addFileButton.addEventListener('click', (event) => { - // Get a reference to the file input element used by Dropzone.js - var fileInput = document.querySelector(".dz-hidden-input"); - - // Simulate a click event on the file input element - fileInput.click(); - }); - // Multi file upload meets encryption document.forms[0].addEventListener("submit", function(evt) { evt.preventDefault(); @@ -189,15 +179,15 @@ document.addEventListener('DOMContentLoaded', function() { return total + file.size; }, 0); - if (totalSize > 15 * 1024 * 1024) { - showError(`Error: Total file size exceeds the 15MB limit. Current total: ${(totalSize / 1024 / 1024).toFixed(2)}MB`); + if (totalSize > 20 * 1024 * 1024) { + showError(`Error: Total file size exceeds the 20MB limit. Current total: ${(totalSize / 1024 / 1024).toFixed(2)}MB`); return false; } // Check individual file sizes for (let i = 0; i < selectedFiles.length; i++) { - if (selectedFiles[i].size > 15 * 1024 * 1024) { - showError(`Error: File "${selectedFiles[i].name}" is too large (${(selectedFiles[i].size / 1024 / 1024).toFixed(2)}MB). Maximum file size is 15MB.`); + if (selectedFiles[i].size > 20 * 1024 * 1024) { + showError(`Error: File "${selectedFiles[i].name}" is too large (${(selectedFiles[i].size / 1024 / 1024).toFixed(2)}MB). Maximum file size is 20MB.`); return false; } } diff --git a/templates/413.html b/templates/413.html index a862104..cbe4b4d 100644 --- a/templates/413.html +++ b/templates/413.html @@ -1,4 +1,4 @@ {% extends "layout.html" %} {% block body %} - Error: File size is too big to process. File size must be below 15Mb. + Error: File size is too big to process. File size must be below 20Mb. {% endblock %} \ No newline at end of file diff --git a/templates/index.html b/templates/index.html index 6b9cdd6..f7509db 100644 --- a/templates/index.html +++ b/templates/index.html @@ -10,7 +10,7 @@ {% endblock %} {% block body %} {% if notice %}

{{ notice }}

{% endif %} -
+
Secure Submission Form diff --git a/uv.lock b/uv.lock index 91269fe..e3674e3 100644 --- a/uv.lock +++ b/uv.lock @@ -1,33 +1,42 @@ version = 1 -revision = 2 +revision = 3 requires-python = ">=3.13" +[[package]] +name = "blinker" +version = "1.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/21/28/9b3f50ce0e048515135495f198351908d99540d69bfdc8c1d15b73dc55ce/blinker-1.9.0.tar.gz", hash = "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf", size = 22460, upload-time = "2024-11-08T17:25:47.436Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/10/cb/f2ad4230dc2eb1a74edf38f1a38b9b52277f75bef262d8908e60d957e13c/blinker-1.9.0-py3-none-any.whl", hash = "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc", size = 8458, upload-time = "2024-11-08T17:25:46.184Z" }, +] + [[package]] name = "boto3" -version = "1.26.137" +version = "1.39.8" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "botocore" }, { name = "jmespath" }, { name = "s3transfer" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/57/e9/b2e32fbf241c9846d16f2d72b6071586d81ad718397b20b03a617438989c/boto3-1.26.137.tar.gz", hash = "sha256:cac699fc46b43c10ca12aa6ea087c0b979613c5e3570aea11d86891652cb581e", size = 103258, upload-time = "2023-05-19T19:28:28.186Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d1/ef/f8dbe6482bdf9eb0230f2639483cdd40ef5aaa89c2fb651f2edeee9c248a/boto3-1.39.8.tar.gz", hash = "sha256:456ea6baef037eb6205d64e012259d14f0c9300c9b30603890746c1a0882fa01", size = 111829, upload-time = "2025-07-17T19:19:14.828Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/20/8d/de46b65fccc863223d62ded9bd4b1e582036a2cb5658273644b9e7e4f582/boto3-1.26.137-py3-none-any.whl", hash = "sha256:b6d729beec0462ac1ae4a83a0fb04a62061a9f1f406d3151b45345daa9d1a5bc", size = 135594, upload-time = "2023-05-19T19:28:25.091Z" }, + { url = "https://files.pythonhosted.org/packages/3c/f0/f3701472b2e6192e62d80e703186ae9c789b3d607ba22943702c500897d2/boto3-1.39.8-py3-none-any.whl", hash = "sha256:dcea5270ccced0b4b962eb5874cb71b6232ccfc6203e05bf834a314442e4a79c", size = 139886, upload-time = "2025-07-17T19:19:12.634Z" }, ] [[package]] name = "botocore" -version = "1.29.165" +version = "1.39.16" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jmespath" }, { name = "python-dateutil" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/3d/f6/d35a27c73dc1053abdfe8524d1e488073fccb51e43c88da61b8fe29522e3/botocore-1.29.165.tar.gz", hash = "sha256:988b948be685006b43c4bbd8f5c0cb93e77c66deb70561994e0c5b31b5a67210", size = 11180165, upload-time = "2023-06-30T19:38:04.5Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ac/b8/f5bde4a029e05683300a57c0c13d4534a0410e1543601b0def6e1f6b205a/botocore-1.39.16.tar.gz", hash = "sha256:b5a1416849637aa8e72292ee3e7b11cd0c22f9b96f6043d2ac6ba0092a193188", size = 14241828, upload-time = "2025-07-29T19:21:20.849Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/46/20/e7a9a8e6746872afcc4e3ad5ab503702c38813b3a532df27cce95c98b8cb/botocore-1.29.165-py3-none-any.whl", hash = "sha256:6f35d59e230095aed7cd747604fe248fa384bebb7d09549077892f936a8ca3df", size = 10975299, upload-time = "2023-06-30T19:37:58.476Z" }, + { url = "https://files.pythonhosted.org/packages/d4/69/273f907a4296e74740e12d1e4e777b4977df8d7722d14a94be2b7c95575d/botocore-1.39.16-py3-none-any.whl", hash = "sha256:1f1c3b614ac88fd68f824c481cfd7686460c38fe13c01e2963556e7186be3248", size = 13901879, upload-time = "2025-07-29T19:21:14.381Z" }, ] [[package]] @@ -96,22 +105,24 @@ wheels = [ [[package]] name = "flask" -version = "2.2.2" +version = "3.1.1" source = { registry = "https://pypi.org/simple" } dependencies = [ + { name = "blinker" }, { name = "click" }, { name = "itsdangerous" }, { name = "jinja2" }, + { name = "markupsafe" }, { name = "werkzeug" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/69/b6/53cfa30eed5aa7343daff36622843688ba8c6fe9829bb2b92e193ab1163f/Flask-2.2.2.tar.gz", hash = "sha256:642c450d19c4ad482f96729bd2a8f6d32554aa1e231f4f6b4e7e5264b16cca2b", size = 677389, upload-time = "2022-08-08T23:26:33.199Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c0/de/e47735752347f4128bcf354e0da07ef311a78244eba9e3dc1d4a5ab21a98/flask-3.1.1.tar.gz", hash = "sha256:284c7b8f2f58cb737f0cf1c30fd7eaf0ccfcde196099d24ecede3fc2005aa59e", size = 753440, upload-time = "2025-05-13T15:01:17.447Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0f/43/15f4f9ab225b0b25352412e8daa3d0e3d135fcf5e127070c74c3632c8b4c/Flask-2.2.2-py3-none-any.whl", hash = "sha256:b9c46cc36662a7949f34b52d8ec7bb59c0d74ba08ba6cb9ce9adc1d8676d9526", size = 101477, upload-time = "2022-08-08T23:26:30.228Z" }, + { url = "https://files.pythonhosted.org/packages/3d/68/9d4508e893976286d2ead7f8f571314af6c2037af34853a30fd769c02e9d/flask-3.1.1-py3-none-any.whl", hash = "sha256:07aae2bb5eaf77993ef57e357491839f5fd9f4dc281593a81a9e4d79a24f295c", size = 103305, upload-time = "2025-05-13T15:01:15.591Z" }, ] [[package]] name = "flask-limiter" -version = "3.8.0" +version = "3.11.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "flask" }, @@ -120,9 +131,9 @@ dependencies = [ { name = "rich" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d7/e8/12ea2ddf6a91f6422da4fb75f28f397e90287523db1806dee85962951ba3/flask_limiter-3.8.0.tar.gz", hash = "sha256:686f8b4a75404e47b91565a795c70d29f69c145f6907f1f32522e962b134dada", size = 301890, upload-time = "2024-07-21T01:54:23.484Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9c/a4/02f67783825a4207d4ae7de4c8be45596c3fba5b65ace77fd6dc3878020d/flask_limiter-3.11.0.tar.gz", hash = "sha256:57b037fb8be423ef7ebac4fbb279fbfdc42d9aa5378467ab6798d6ce3d912117", size = 303361, upload-time = "2025-03-11T20:37:59.839Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b4/c5/d5a91940f87a645997b1acf0e7ddd97738d01bd819dcb3cbda2552160195/Flask_Limiter-3.8.0-py3-none-any.whl", hash = "sha256:0ab44f586d8cc349412791711b6cbafe8f86e7b60ad9e8f24f2686009f00900e", size = 28635, upload-time = "2024-07-21T01:54:21.39Z" }, + { url = "https://files.pythonhosted.org/packages/0d/6b/54101d1e595686da1fb90e0fb23497de959a39c7f398d4dda7fcdcc5cd76/flask_limiter-3.11.0-py3-none-any.whl", hash = "sha256:ae7ef0b3742228df91073d72eab0ce114fe6b00e6201ad9e12aefd53fe597352", size = 28684, upload-time = "2025-03-11T20:37:58.227Z" }, ] [[package]] @@ -137,14 +148,14 @@ sdist = { url = "https://files.pythonhosted.org/packages/1d/77/980dd481ef66770a3 [[package]] name = "gunicorn" -version = "20.1.0" +version = "23.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "setuptools" }, + { name = "packaging" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/28/5b/0d1f0296485a6af03366604142ea8f19f0833894db3512a40ed07b2a56dd/gunicorn-20.1.0.tar.gz", hash = "sha256:e0a968b5ba15f8a328fdfd7ab1fcb5af4470c28aaf7e55df02a99bc13138e6e8", size = 370601, upload-time = "2021-03-27T01:54:37.202Z" } +sdist = { url = "https://files.pythonhosted.org/packages/34/72/9614c465dc206155d93eff0ca20d42e1e35afc533971379482de953521a4/gunicorn-23.0.0.tar.gz", hash = "sha256:f014447a0101dc57e294f6c18ca6b40227a4c90e9bdb586042628030cba004ec", size = 375031, upload-time = "2024-08-10T20:25:27.378Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e4/dd/5b190393e6066286773a67dfcc2f9492058e9b57c4867a95f1ba5caf0a83/gunicorn-20.1.0-py3-none-any.whl", hash = "sha256:9dcc4547dbb1cb284accfb15ab5667a0e5d1881cc443e0677b4882a4067a807e", size = 79531, upload-time = "2021-04-27T12:16:23.375Z" }, + { url = "https://files.pythonhosted.org/packages/cb/7d/6dac2a6e1eba33ee43f318edbed4ff29151a49b5d37f080aad1e6469bca4/gunicorn-23.0.0-py3-none-any.whl", hash = "sha256:ec400d38950de4dfd418cff8328b2c8faed0edb0d517d3394e457c317908ca4d", size = 85029, upload-time = "2024-08-10T20:25:24.996Z" }, ] [[package]] @@ -167,14 +178,14 @@ wheels = [ [[package]] name = "jinja2" -version = "3.0.3" +version = "3.1.6" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markupsafe" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/91/a5/429efc6246119e1e3fbf562c00187d04e83e54619249eb732bb423efa6c6/Jinja2-3.0.3.tar.gz", hash = "sha256:611bb273cd68f3b993fabdc4064fc858c5b47a973cb5aa7999ec1ba405c87cd7", size = 269196, upload-time = "2021-11-09T20:27:29.541Z" } +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/20/9a/e5d9ec41927401e41aea8af6d16e78b5e612bca4699d417f646a9610a076/Jinja2-3.0.3-py3-none-any.whl", hash = "sha256:077ce6014f7b40d03b47d1f1ca4b0fc8328a692bd284016f806ed0eaca390ad8", size = 133630, upload-time = "2021-11-09T20:27:27.116Z" }, + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, ] [[package]] @@ -290,11 +301,11 @@ wheels = [ [[package]] name = "python-dotenv" -version = "0.21.0" +version = "1.1.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/87/8d/ab7352188f605e3f663f34692b2ed7457da5985857e9e4c2335cd12fb3c9/python-dotenv-0.21.0.tar.gz", hash = "sha256:b77d08274639e3d34145dfa6c7008e66df0f04b7be7a75fd0d5292c191d79045", size = 34984, upload-time = "2022-09-03T15:16:14.39Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f6/b0/4bc07ccd3572a2f9df7e6782f52b0c6c90dcbb803ac4a167702d7d0dfe1e/python_dotenv-1.1.1.tar.gz", hash = "sha256:a8a6399716257f45be6a007360200409fce5cda2661e3dec71d23dc15f6189ab", size = 41978, upload-time = "2025-06-24T04:21:07.341Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2d/10/ff4f2f5b2a420fd09e1331d63cc87cf4367c5745c0a4ce99cea92b1cbacb/python_dotenv-0.21.0-py3-none-any.whl", hash = "sha256:1684eb44636dd462b66c3ee016599815514527ad99965de77f43e0944634a7e5", size = 18818, upload-time = "2022-09-03T15:16:12.291Z" }, + { url = "https://files.pythonhosted.org/packages/5f/ed/539768cf28c661b5b068d66d96a2f155c4971a5d55684a514c1a0e0dec2f/python_dotenv-1.1.1-py3-none-any.whl", hash = "sha256:31f23644fe2602f88ff55e1f5c79ba497e01224ee7737937930c448e4d0e24dc", size = 20556, upload-time = "2025-06-24T04:21:06.073Z" }, ] [[package]] @@ -327,14 +338,14 @@ wheels = [ [[package]] name = "s3transfer" -version = "0.6.2" +version = "0.13.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "botocore" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5a/47/d676353674e651910085e3537866f093d2b9e9699e95e89d960e78df9ecf/s3transfer-0.6.2.tar.gz", hash = "sha256:cab66d3380cca3e70939ef2255d01cd8aece6a4907a9528740f668c4b0611861", size = 132821, upload-time = "2023-08-15T22:06:41.78Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6d/05/d52bf1e65044b4e5e27d4e63e8d1579dbdec54fce685908ae09bc3720030/s3transfer-0.13.1.tar.gz", hash = "sha256:c3fdba22ba1bd367922f27ec8032d6a1cf5f10c934fb5d68cf60fd5a23d936cf", size = 150589, upload-time = "2025-07-18T19:22:42.31Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d9/17/a3b666f5ef9543cfd3c661d39d1e193abb9649d0cfbbfee3cf3b51d5af02/s3transfer-0.6.2-py3-none-any.whl", hash = "sha256:b014be3a8a2aab98cfe1abc7229cc5a9a0cf05eb9c1f2b86b230fd8df3f78084", size = 79765, upload-time = "2023-08-15T22:06:39.88Z" }, + { url = "https://files.pythonhosted.org/packages/6d/4f/d073e09df851cfa251ef7840007d04db3293a0482ce607d2b993926089be/s3transfer-0.13.1-py3-none-any.whl", hash = "sha256:a981aa7429be23fe6dfc13e80e4020057cbab622b08c0315288758d67cabc724", size = 85308, upload-time = "2025-07-18T19:22:40.947Z" }, ] [[package]] @@ -349,28 +360,21 @@ dependencies = [ { name = "gunicorn" }, { name = "jinja2" }, { name = "python-dotenv" }, + { name = "requests" }, { name = "werkzeug" }, ] [package.metadata] requires-dist = [ - { name = "boto3", specifier = "==1.26.137" }, - { name = "flask", specifier = "==2.2.2" }, - { name = "flask-limiter", specifier = "==3.8.0" }, + { name = "boto3", specifier = "==1.39.8" }, + { name = "flask", specifier = "==3.1.1" }, + { name = "flask-limiter", specifier = "==3.11.0" }, { name = "flask-recaptcha", specifier = "==0.4.2" }, - { name = "gunicorn", specifier = "==20.1.0" }, - { name = "jinja2", specifier = "==3.0.3" }, - { name = "python-dotenv", specifier = "==0.21.0" }, - { name = "werkzeug", specifier = "==2.2.2" }, -] - -[[package]] -name = "setuptools" -version = "80.9.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/18/5d/3bf57dcd21979b887f014ea83c24ae194cfcd12b9e0fda66b957c69d1fca/setuptools-80.9.0.tar.gz", hash = "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c", size = 1319958, upload-time = "2025-05-27T00:56:51.443Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a3/dc/17031897dae0efacfea57dfd3a82fdd2a2aeb58e0ff71b77b87e44edc772/setuptools-80.9.0-py3-none-any.whl", hash = "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922", size = 1201486, upload-time = "2025-05-27T00:56:49.664Z" }, + { name = "gunicorn", specifier = "==23.0.0" }, + { name = "jinja2", specifier = "==3.1.6" }, + { name = "python-dotenv", specifier = "==1.1.1" }, + { name = "requests", specifier = "==2.32.4" }, + { name = "werkzeug", specifier = "==3.1.3" }, ] [[package]] @@ -402,14 +406,14 @@ wheels = [ [[package]] name = "werkzeug" -version = "2.2.2" +version = "3.1.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markupsafe" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f8/c1/1c8e539f040acd80f844c69a5ef8e2fccdf8b442dabb969e497b55d544e1/Werkzeug-2.2.2.tar.gz", hash = "sha256:7ea2d48322cc7c0f8b3a215ed73eabd7b5d75d0b50e31ab006286ccff9e00b8f", size = 844378, upload-time = "2022-08-08T21:44:15.376Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9f/69/83029f1f6300c5fb2471d621ab06f6ec6b3324685a2ce0f9777fd4a8b71e/werkzeug-3.1.3.tar.gz", hash = "sha256:60723ce945c19328679790e3282cc758aa4a6040e4bb330f53d30fa546d44746", size = 806925, upload-time = "2024-11-08T15:52:18.093Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c8/27/be6ddbcf60115305205de79c29004a0c6bc53cec814f733467b1bb89386d/Werkzeug-2.2.2-py3-none-any.whl", hash = "sha256:f979ab81f58d7318e064e99c4506445d60135ac5cd2e177a2de0089bfd4c9bd5", size = 232700, upload-time = "2022-08-08T21:44:13.251Z" }, + { url = "https://files.pythonhosted.org/packages/52/24/ab44c871b0f07f491e5d2ad12c9bd7358e527510618cb1b803a88e986db1/werkzeug-3.1.3-py3-none-any.whl", hash = "sha256:54b78bf3716d19a65be4fceccc0d1d7b89e608834989dfae50ea87564639213e", size = 224498, upload-time = "2024-11-08T15:52:16.132Z" }, ] [[package]]