flask-Humanify 0.2.1__tar.gz → 0.2.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. {flask_humanify-0.2.1/flask_Humanify.egg-info → flask_humanify-0.2.2}/PKG-INFO +78 -1
  2. {flask_humanify-0.2.1 → flask_humanify-0.2.2}/README.md +77 -0
  3. {flask_humanify-0.2.1 → flask_humanify-0.2.2/flask_Humanify.egg-info}/PKG-INFO +78 -1
  4. {flask_humanify-0.2.1 → flask_humanify-0.2.2}/flask_Humanify.egg-info/SOURCES.txt +2 -0
  5. {flask_humanify-0.2.1 → flask_humanify-0.2.2}/flask_humanify/__init__.py +3 -2
  6. flask_humanify-0.2.2/flask_humanify/features/error_handler.py +177 -0
  7. {flask_humanify-0.2.1 → flask_humanify-0.2.2}/flask_humanify/templates/audio_challenge.html +3 -1
  8. flask_humanify-0.2.2/flask_humanify/templates/exception.html +66 -0
  9. {flask_humanify-0.2.1 → flask_humanify-0.2.2}/pyproject.toml +1 -1
  10. {flask_humanify-0.2.1 → flask_humanify-0.2.2}/LICENSE +0 -0
  11. {flask_humanify-0.2.1 → flask_humanify-0.2.2}/MANIFEST.in +0 -0
  12. {flask_humanify-0.2.1 → flask_humanify-0.2.2}/flask_Humanify.egg-info/dependency_links.txt +0 -0
  13. {flask_humanify-0.2.1 → flask_humanify-0.2.2}/flask_Humanify.egg-info/requires.txt +0 -0
  14. {flask_humanify-0.2.1 → flask_humanify-0.2.2}/flask_Humanify.egg-info/top_level.txt +0 -0
  15. {flask_humanify-0.2.1 → flask_humanify-0.2.2}/flask_humanify/datasets/ai_dogs.pkl +0 -0
  16. {flask_humanify-0.2.1 → flask_humanify-0.2.2}/flask_humanify/datasets/animals.pkl +0 -0
  17. {flask_humanify-0.2.1 → flask_humanify-0.2.2}/flask_humanify/datasets/characters.pkl +0 -0
  18. {flask_humanify-0.2.1 → flask_humanify-0.2.2}/flask_humanify/datasets/ipset.json +0 -0
  19. {flask_humanify-0.2.1 → flask_humanify-0.2.2}/flask_humanify/datasets/keys.pkl +0 -0
  20. {flask_humanify-0.2.1 → flask_humanify-0.2.2}/flask_humanify/features/rate_limiter.py +0 -0
  21. {flask_humanify-0.2.1 → flask_humanify-0.2.2}/flask_humanify/humanify.py +0 -0
  22. {flask_humanify-0.2.1 → flask_humanify-0.2.2}/flask_humanify/memory_server.py +0 -0
  23. {flask_humanify-0.2.1 → flask_humanify-0.2.2}/flask_humanify/templates/access_denied.html +0 -0
  24. {flask_humanify-0.2.1 → flask_humanify-0.2.2}/flask_humanify/templates/grid_challenge.html +0 -0
  25. {flask_humanify-0.2.1 → flask_humanify-0.2.2}/flask_humanify/templates/one_click_challenge.html +0 -0
  26. {flask_humanify-0.2.1 → flask_humanify-0.2.2}/flask_humanify/templates/rate_limited.html +0 -0
  27. {flask_humanify-0.2.1 → flask_humanify-0.2.2}/flask_humanify/utils.py +0 -0
  28. {flask_humanify-0.2.1 → flask_humanify-0.2.2}/setup.cfg +0 -0
  29. {flask_humanify-0.2.1 → flask_humanify-0.2.2}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: flask-Humanify
3
- Version: 0.2.1
3
+ Version: 0.2.2
4
4
  Summary: Protect against bots and DDoS attacks
5
5
  Author-email: TN3W <tn3w@protonmail.com>
6
6
  License-Expression: Apache-2.0
@@ -129,3 +129,80 @@ Add the extension to your Flask app:
129
129
  app = Flask(__name__)
130
130
  humanify = Humanify(app)
131
131
  ```
132
+
133
+ ## Additional Features
134
+
135
+ ### Rate Limiting
136
+
137
+ Flask-Humanify includes a rate limiting feature to protect your application from excessive requests:
138
+
139
+ ```python
140
+ from flask import Flask
141
+ from flask_humanify import Humanify, RateLimiter
142
+
143
+ app = Flask(__name__)
144
+ humanify = Humanify(app)
145
+ # Default: 10 requests per 10 seconds
146
+ rate_limiter = RateLimiter(app)
147
+
148
+ # Or customize rate limits
149
+ rate_limiter = RateLimiter(app, max_requests=20, time_window=30)
150
+ ```
151
+
152
+ The rate limiter will automatically:
153
+
154
+ - Track requests by IP address
155
+ - Hash IPs for privacy
156
+ - Redirect to a rate-limited page when limits are exceeded
157
+ - Ignore rate limits for special pages like the rate-limited and access-denied pages
158
+
159
+ ### Error Handling
160
+
161
+ Flask-Humanify provides a clean error handling system:
162
+
163
+ ```python
164
+ from flask import Flask
165
+ from flask_humanify import Humanify, ErrorHandler
166
+
167
+ app = Flask(__name__)
168
+ humanify = Humanify(app)
169
+ # Handle all standard HTTP errors
170
+ error_handler = ErrorHandler(app)
171
+
172
+ # Or handle only specific error codes
173
+ error_handler = ErrorHandler(app, errors=[404, 429, 500])
174
+ ```
175
+
176
+ The error handler:
177
+
178
+ - Renders user-friendly error pages
179
+ - Uses the custom exception.html template
180
+ - Provides appropriate error messages and descriptions
181
+ - Includes HTTP status codes and titles
182
+
183
+ ### Complete Example
184
+
185
+ Here's a complete example combining all features:
186
+
187
+ ```python
188
+ from flask import Flask
189
+ from flask_humanify import Humanify, RateLimiter, ErrorHandler
190
+
191
+ app = Flask(__name__)
192
+ # Setup core protection
193
+ humanify = Humanify(app, challenge_type="one_click", image_dataset="animals")
194
+ humanify.register_middleware(action="challenge")
195
+
196
+ # Add rate limiting
197
+ rate_limiter = RateLimiter(app, max_requests=15, time_window=60)
198
+
199
+ # Add error handling
200
+ error_handler = ErrorHandler(app)
201
+
202
+ @app.route("/")
203
+ def index():
204
+ return "Hello, Human!"
205
+
206
+ if __name__ == "__main__":
207
+ app.run(debug=True)
208
+ ```
@@ -93,3 +93,80 @@ Add the extension to your Flask app:
93
93
  app = Flask(__name__)
94
94
  humanify = Humanify(app)
95
95
  ```
96
+
97
+ ## Additional Features
98
+
99
+ ### Rate Limiting
100
+
101
+ Flask-Humanify includes a rate limiting feature to protect your application from excessive requests:
102
+
103
+ ```python
104
+ from flask import Flask
105
+ from flask_humanify import Humanify, RateLimiter
106
+
107
+ app = Flask(__name__)
108
+ humanify = Humanify(app)
109
+ # Default: 10 requests per 10 seconds
110
+ rate_limiter = RateLimiter(app)
111
+
112
+ # Or customize rate limits
113
+ rate_limiter = RateLimiter(app, max_requests=20, time_window=30)
114
+ ```
115
+
116
+ The rate limiter will automatically:
117
+
118
+ - Track requests by IP address
119
+ - Hash IPs for privacy
120
+ - Redirect to a rate-limited page when limits are exceeded
121
+ - Ignore rate limits for special pages like the rate-limited and access-denied pages
122
+
123
+ ### Error Handling
124
+
125
+ Flask-Humanify provides a clean error handling system:
126
+
127
+ ```python
128
+ from flask import Flask
129
+ from flask_humanify import Humanify, ErrorHandler
130
+
131
+ app = Flask(__name__)
132
+ humanify = Humanify(app)
133
+ # Handle all standard HTTP errors
134
+ error_handler = ErrorHandler(app)
135
+
136
+ # Or handle only specific error codes
137
+ error_handler = ErrorHandler(app, errors=[404, 429, 500])
138
+ ```
139
+
140
+ The error handler:
141
+
142
+ - Renders user-friendly error pages
143
+ - Uses the custom exception.html template
144
+ - Provides appropriate error messages and descriptions
145
+ - Includes HTTP status codes and titles
146
+
147
+ ### Complete Example
148
+
149
+ Here's a complete example combining all features:
150
+
151
+ ```python
152
+ from flask import Flask
153
+ from flask_humanify import Humanify, RateLimiter, ErrorHandler
154
+
155
+ app = Flask(__name__)
156
+ # Setup core protection
157
+ humanify = Humanify(app, challenge_type="one_click", image_dataset="animals")
158
+ humanify.register_middleware(action="challenge")
159
+
160
+ # Add rate limiting
161
+ rate_limiter = RateLimiter(app, max_requests=15, time_window=60)
162
+
163
+ # Add error handling
164
+ error_handler = ErrorHandler(app)
165
+
166
+ @app.route("/")
167
+ def index():
168
+ return "Hello, Human!"
169
+
170
+ if __name__ == "__main__":
171
+ app.run(debug=True)
172
+ ```
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: flask-Humanify
3
- Version: 0.2.1
3
+ Version: 0.2.2
4
4
  Summary: Protect against bots and DDoS attacks
5
5
  Author-email: TN3W <tn3w@protonmail.com>
6
6
  License-Expression: Apache-2.0
@@ -129,3 +129,80 @@ Add the extension to your Flask app:
129
129
  app = Flask(__name__)
130
130
  humanify = Humanify(app)
131
131
  ```
132
+
133
+ ## Additional Features
134
+
135
+ ### Rate Limiting
136
+
137
+ Flask-Humanify includes a rate limiting feature to protect your application from excessive requests:
138
+
139
+ ```python
140
+ from flask import Flask
141
+ from flask_humanify import Humanify, RateLimiter
142
+
143
+ app = Flask(__name__)
144
+ humanify = Humanify(app)
145
+ # Default: 10 requests per 10 seconds
146
+ rate_limiter = RateLimiter(app)
147
+
148
+ # Or customize rate limits
149
+ rate_limiter = RateLimiter(app, max_requests=20, time_window=30)
150
+ ```
151
+
152
+ The rate limiter will automatically:
153
+
154
+ - Track requests by IP address
155
+ - Hash IPs for privacy
156
+ - Redirect to a rate-limited page when limits are exceeded
157
+ - Ignore rate limits for special pages like the rate-limited and access-denied pages
158
+
159
+ ### Error Handling
160
+
161
+ Flask-Humanify provides a clean error handling system:
162
+
163
+ ```python
164
+ from flask import Flask
165
+ from flask_humanify import Humanify, ErrorHandler
166
+
167
+ app = Flask(__name__)
168
+ humanify = Humanify(app)
169
+ # Handle all standard HTTP errors
170
+ error_handler = ErrorHandler(app)
171
+
172
+ # Or handle only specific error codes
173
+ error_handler = ErrorHandler(app, errors=[404, 429, 500])
174
+ ```
175
+
176
+ The error handler:
177
+
178
+ - Renders user-friendly error pages
179
+ - Uses the custom exception.html template
180
+ - Provides appropriate error messages and descriptions
181
+ - Includes HTTP status codes and titles
182
+
183
+ ### Complete Example
184
+
185
+ Here's a complete example combining all features:
186
+
187
+ ```python
188
+ from flask import Flask
189
+ from flask_humanify import Humanify, RateLimiter, ErrorHandler
190
+
191
+ app = Flask(__name__)
192
+ # Setup core protection
193
+ humanify = Humanify(app, challenge_type="one_click", image_dataset="animals")
194
+ humanify.register_middleware(action="challenge")
195
+
196
+ # Add rate limiting
197
+ rate_limiter = RateLimiter(app, max_requests=15, time_window=60)
198
+
199
+ # Add error handling
200
+ error_handler = ErrorHandler(app)
201
+
202
+ @app.route("/")
203
+ def index():
204
+ return "Hello, Human!"
205
+
206
+ if __name__ == "__main__":
207
+ app.run(debug=True)
208
+ ```
@@ -17,9 +17,11 @@ flask_humanify/datasets/animals.pkl
17
17
  flask_humanify/datasets/characters.pkl
18
18
  flask_humanify/datasets/ipset.json
19
19
  flask_humanify/datasets/keys.pkl
20
+ flask_humanify/features/error_handler.py
20
21
  flask_humanify/features/rate_limiter.py
21
22
  flask_humanify/templates/access_denied.html
22
23
  flask_humanify/templates/audio_challenge.html
24
+ flask_humanify/templates/exception.html
23
25
  flask_humanify/templates/grid_challenge.html
24
26
  flask_humanify/templates/one_click_challenge.html
25
27
  flask_humanify/templates/rate_limited.html
@@ -4,11 +4,12 @@ Flask-Humanify
4
4
  A Flask extension that protects against bots and DDoS attacks.
5
5
  """
6
6
 
7
- __version__ = "0.2.1"
7
+ __version__ = "0.2.2"
8
8
 
9
9
  from . import utils
10
10
  from .humanify import Humanify
11
11
  from .features.rate_limiter import RateLimiter
12
+ from .features.error_handler import ErrorHandler
12
13
 
13
14
 
14
- __all__ = ["Humanify", "RateLimiter", "utils"]
15
+ __all__ = ["Humanify", "RateLimiter", "ErrorHandler", "utils"]
@@ -0,0 +1,177 @@
1
+ from typing import Final, Optional
2
+ from flask import Flask, render_template
3
+
4
+
5
+ ERROR_CODES: Final[dict] = {
6
+ 400: {
7
+ "title": "Bad Request",
8
+ "description": "The server could not understand your request due to invalid syntax.",
9
+ },
10
+ 401: {
11
+ "title": "Unauthorized",
12
+ "description": "You must authenticate yourself to get the requested response.",
13
+ },
14
+ 403: {
15
+ "title": "Forbidden",
16
+ "description": "You do not have access rights to the content.",
17
+ },
18
+ 404: {
19
+ "title": "Not Found",
20
+ "description": "The server cannot find the requested resource.",
21
+ },
22
+ 405: {
23
+ "title": "Method Not Allowed",
24
+ "description": "The request method is known by the server but is not supported by the target resource.",
25
+ },
26
+ 406: {
27
+ "title": "Not Acceptable",
28
+ "description": (
29
+ "The server cannot produce a response matching the list of acceptable values "
30
+ "defined in your request's proactive content negotiation headers."
31
+ ),
32
+ },
33
+ 408: {
34
+ "title": "Request Timeout",
35
+ "description": (
36
+ "The server did not receive a complete request message from you within the time that "
37
+ "it was prepared to wait."
38
+ ),
39
+ },
40
+ 409: {
41
+ "title": "Conflict",
42
+ "description": (
43
+ "The request could not be completed due to a conflict with the current state of "
44
+ "the target resource."
45
+ ),
46
+ },
47
+ 410: {
48
+ "title": "Gone",
49
+ "description": "The requested resource is no longer available and will not be available again.",
50
+ },
51
+ 411: {
52
+ "title": "Length Required",
53
+ "description": "The server refuses to accept the request without a defined Content-Length header.",
54
+ },
55
+ 412: {
56
+ "title": "Precondition Failed",
57
+ "description": (
58
+ "The server does not meet one of the preconditions that you put on "
59
+ "the request header fields."
60
+ ),
61
+ },
62
+ 413: {
63
+ "title": "Payload Too Large",
64
+ "description": "The request entity is larger than limits defined by the server.",
65
+ },
66
+ 414: {
67
+ "title": "URI Too Long",
68
+ "description": "The URI requested by you is longer than the server is willing to interpret.",
69
+ },
70
+ 415: {
71
+ "title": "Unsupported Media Type",
72
+ "description": "The media format of the requested data is not supported by the server.",
73
+ },
74
+ 416: {
75
+ "title": "Range Not Satisfiable",
76
+ "description": "The range specified by the Range header field in your request can't be fulfilled.",
77
+ },
78
+ 417: {
79
+ "title": "Expectation Failed",
80
+ "description": (
81
+ "The expectation given in your request's Expect header field could not be met by at "
82
+ "least one of the inbound servers."
83
+ ),
84
+ },
85
+ 418: {
86
+ "title": "I'm a teapot",
87
+ "description": "The web server rejects the attempt to make coffee with a teapot.",
88
+ },
89
+ 422: {
90
+ "title": "Unprocessable Entity",
91
+ "description": "The request was well-formed but was unable to be followed due to semantic errors.",
92
+ },
93
+ 423: {
94
+ "title": "Locked",
95
+ "description": "The resource that is being accessed is locked.",
96
+ },
97
+ 424: {
98
+ "title": "Failed Dependency",
99
+ "description": "The request failed due to failure of a previous request.",
100
+ },
101
+ 428: {
102
+ "title": "Precondition Required",
103
+ "description": "The origin server requires your request to be conditional.",
104
+ },
105
+ 429: {
106
+ "title": "Too Many Requests",
107
+ "description": "You have sent too many requests in a given amount of time.",
108
+ },
109
+ 431: {
110
+ "title": "Request Header Fields Too Large",
111
+ "description": (
112
+ "The server is unwilling to process your request because its header "
113
+ "fields are too large."
114
+ ),
115
+ },
116
+ 451: {
117
+ "title": "Unavailable For Legal Reasons",
118
+ "description": "The server is denying access to the resource as a consequence of a legal demand.",
119
+ },
120
+ 500: {
121
+ "title": "Internal Server Error",
122
+ "description": "The server has encountered a situation it doesn't know how to handle.",
123
+ },
124
+ 501: {
125
+ "title": "Not Implemented",
126
+ "description": "The request method is not supported by the server and cannot be handled.",
127
+ },
128
+ 502: {
129
+ "title": "Bad Gateway",
130
+ "description": (
131
+ "The server, while acting as a gateway or proxy, received an invalid response from "
132
+ "the upstream server."
133
+ ),
134
+ },
135
+ 503: {
136
+ "title": "Service Unavailable",
137
+ "description": "The server is not ready to handle the request.",
138
+ },
139
+ 504: {
140
+ "title": "Gateway Timeout",
141
+ "description": (
142
+ "The server is acting as a gateway or proxy and did not receive a timely response "
143
+ "from the upstream server."
144
+ ),
145
+ },
146
+ 505: {
147
+ "title": "HTTP Version Not Supported",
148
+ "description": "The HTTP version used in your request is not supported by the server.",
149
+ },
150
+ }
151
+
152
+
153
+ class ErrorHandler:
154
+ def __init__(self, app: Flask, errors: Optional[list[int]] = None):
155
+ self.app = app
156
+
157
+ for error_code in errors or ERROR_CODES:
158
+ self.app.register_error_handler(error_code, self.handle_error)
159
+
160
+ def handle_error(self, error: Exception) -> tuple:
161
+ """Render exception page with appropriate error information."""
162
+ code = getattr(error, "code", type(error).__name__)
163
+ info = ERROR_CODES.get(code, {})
164
+ title = f"{code} | {info.get('title', 'Error')}"
165
+ message = (
166
+ info.get("description")
167
+ or str(error).split(" ", 1)[-1].strip()
168
+ or "An error occurred"
169
+ )
170
+
171
+ return render_template("exception.html").replace(
172
+ "EXCEPTION_TITLE", title
173
+ ).replace("EXCEPTION_CODE", str(code)).replace(
174
+ "EXCEPTION_MESSAGE", message
175
+ ), getattr(
176
+ error, "code", 500
177
+ )
@@ -198,7 +198,9 @@
198
198
  href="{{ url_for('humanify.challenge', return_url=return_url) }}"
199
199
  >
200
200
  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
201
- <path d="M96 416q-14 0-23-9t-9-23V128q0-14 9-23t23-9h320q14 0 23 9t9 23v256q0 14-9 23t-23 9zm88-176q20 0 34-14t14-34-14-34-34-14-34 14-14 34 14 34 34 14m216 128v-64l-64-64-96 96-56-57-88 89z"/>
201
+ <path
202
+ d="M96 416q-14 0-23-9t-9-23V128q0-14 9-23t23-9h320q14 0 23 9t9 23v256q0 14-9 23t-23 9zm88-176q20 0 34-14t14-34-14-34-34-14-34 14-14 34 14 34 34 14m216 128v-64l-64-64-96 96-56-57-88 89z"
203
+ />
202
204
  </svg>
203
205
  Image challenge
204
206
  </a>
@@ -0,0 +1,66 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>EXCEPTION_TITLE</title>
7
+ <style>
8
+ body {
9
+ font-family: system-ui, sans-serif;
10
+ background: #f2f2f2;
11
+ color: #181818;
12
+ margin: 0;
13
+ line-height: 1.5;
14
+ text-align: center;
15
+ display: grid;
16
+ place-items: center;
17
+ height: 100vh;
18
+ padding: 0 20px;
19
+ }
20
+
21
+ @media (prefers-color-scheme: dark) {
22
+ body {
23
+ background: #121212;
24
+ color: #f2f2f2;
25
+ }
26
+
27
+ .btn {
28
+ background: #f2f2f2;
29
+ color: #121212;
30
+ }
31
+ }
32
+
33
+ .content {
34
+ max-width: 600px;
35
+ }
36
+
37
+ h1 {
38
+ font-size: 64px;
39
+ margin: 15px 0;
40
+ }
41
+
42
+ p {
43
+ font-size: 20px;
44
+ margin: 15px 0;
45
+ opacity: 0.8;
46
+ }
47
+
48
+ .btn {
49
+ display: inline-block;
50
+ padding: 12px 24px;
51
+ background: #181818;
52
+ color: #f2f2f2;
53
+ border-radius: 6px;
54
+ text-decoration: none;
55
+ margin-top: 20px;
56
+ }
57
+ </style>
58
+ </head>
59
+ <body>
60
+ <div class="content">
61
+ <h1>EXCEPTION_CODE</h1>
62
+ <p>EXCEPTION_MESSAGE</p>
63
+ <a href="/" class="btn">Back to home</a>
64
+ </div>
65
+ </body>
66
+ </html>
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "flask-Humanify"
3
- version = "0.2.1"
3
+ version = "0.2.2"
4
4
  description = "Protect against bots and DDoS attacks"
5
5
  readme = "README.md"
6
6
  authors = [
File without changes
File without changes
File without changes