diff --git a/Dockerfile b/Dockerfile index 0dcc7c0..db5f54b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,4 +7,4 @@ RUN pip install --no-cache-dir -r requirements.txt COPY . . -CMD [ "python", "-m", "hypercorn", "shorter:app", "--bind", "0.0.0.0:8000", "--access-logfile", "-" ] +CMD [ "python", "-m", "hypercorn", "shorter:app", "--bind", "0.0.0.0:8000", "--access-logfile", "-" ] \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 2070dd0..cd21e47 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,9 +1,10 @@ -aioredis==1.3.1 +redis>=4.2.0rc1 black==20.8b1 Cerberus==1.3.2 -Hypercorn==0.11.1 +Hypercorn==0.17.3 python-dotenv==0.15.0 Quart==0.13.1 Quart-CORS==0.3.0 Quart-Rate-Limiter==0.4.0 Jinja2<=3.0.3 +werkzeug==2.1.2 \ No newline at end of file diff --git a/shorter/.DS_Store b/shorter/.DS_Store new file mode 100644 index 0000000..8a337ed Binary files /dev/null and b/shorter/.DS_Store differ diff --git a/shorter/__init__.py b/shorter/__init__.py index 8802335..540002e 100644 --- a/shorter/__init__.py +++ b/shorter/__init__.py @@ -1,34 +1,30 @@ import asyncio import os -import aioredis +from redis import asyncio as aioredis from quart import Quart, exceptions from quart_cors import cors import shorter.bp.shortener from shorter import config from shorter.errors import ApiError -from shorter.ratelimit import rate_limiter - app = Quart(__name__) app = cors(app, expose_headers=["Location"]) -rate_limiter.init_app(app) - app.register_blueprint(shorter.bp.shortener.bp) @app.before_serving async def before_serving(): - loop = asyncio.get_event_loop() - app.redis = await aioredis.create_redis_pool( + cpool = aioredis.ConnectionPool.from_url( config.redis_address, - loop=loop, encoding="utf-8", ) + app.redis = aioredis.Redis.from_pool(cpool) + @app.errorhandler(ApiError) def handle_api_error(error: ApiError): diff --git a/shorter/bp/shortener.py b/shorter/bp/shortener.py index 653d581..b53321f 100644 --- a/shorter/bp/shortener.py +++ b/shorter/bp/shortener.py @@ -28,7 +28,7 @@ async def create(): shortname = await generate_shortname(8) key = f"{current_app.import_name}-shorten-{shortname}" await current_app.redis.set(key, url) - await current_app.redis.expire(key, ttl) + await current_app.redis.expire(key, int(ttl)) url = urlunparse( ( diff --git a/shorter/ratelimit.py b/shorter/ratelimit.py deleted file mode 100644 index 285ae58..0000000 --- a/shorter/ratelimit.py +++ /dev/null @@ -1,43 +0,0 @@ -import ipaddress -from datetime import datetime -from typing import Any, Optional - -from aioredis import Redis, create_redis -from quart import request -from quart_rate_limiter import RateLimiter -from quart_rate_limiter.store import RateLimiterStoreABC - -from shorter import config - - -class RedisStore(RateLimiterStoreABC): - async def get(self, key: str, default: datetime): - result = await self.redis.get(key) - if result is None: - return default - else: - return datetime.fromtimestamp(float(result)) - - async def set(self, key: str, tat: datetime): - await self.redis.set(key, tat.timestamp()) - await self.redis.expire(key, (tat - datetime.utcnow()).total_seconds() + 5.0) - - async def before_serving(self): - self.redis = await create_redis(config.redis_address, encoding="utf-8") - - async def after_serving(self): - self.redis.close() - await self.redis.wait_closed() - self.redis = None - - -async def get_rate_limit_key(): - ip = request.headers.get("X-Real-IP", request.remote_addr) - addr = ipaddress.ip_address(ip) - if isinstance(addr, ipaddress.IPv6Address): - addr = ipaddress.IPv6Network(f"{addr}/64", strict=False).network_address - - return str(addr) - - -rate_limiter = RateLimiter(store=RedisStore(), key_function=get_rate_limit_key)