diff --git a/.gitea/workflows/main.yml b/.gitea/workflows/main.yml index 587c919..114f497 100644 --- a/.gitea/workflows/main.yml +++ b/.gitea/workflows/main.yml @@ -1,8 +1,5 @@ -name: 'deploy' -on: - push: - branches: - - main +name: 'Deploy on Push' +on: [push] jobs: deploy: @@ -21,10 +18,19 @@ jobs: id: branch_name run: echo "::set-output name=branch::$(echo ${GITHUB_REF##*/})" - - name: Push to dokku + - name: Push to dokku for main branch + if: github.ref == 'refs/heads/main' uses: dokku/github-action@master with: branch: 'main' - git_remote_url: 'ssh://dokku@staging.discours.io:22/${{ steps.repo_name.outputs.repo }}' + git_remote_url: 'ssh://dokku@v2.discours.io:22/uploader' ssh_private_key: ${{ secrets.SSH_PRIVATE_KEY }} - git_push_flags: '--force' + + - name: Push to dokku for staging branch + if: github.ref == 'refs/heads/dev' + uses: dokku/github-action@master + with: + branch: 'main' + git_remote_url: 'ssh://dokku@staging.discours.io:22/uploader' + ssh_private_key: ${{ secrets.SSH_PRIVATE_KEY }} + diff --git a/.gitignore b/.gitignore index 871cfd6..f2f4f01 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,5 @@ __pycache__ .ruff_cache .pytest_cache .venv -*.gz.tar \ No newline at end of file +*.gz.tar +DELETEME diff --git a/README.md b/README.md index fdf659d..a0ae99d 100644 --- a/README.md +++ b/README.md @@ -25,3 +25,18 @@ poetry env use .venv/bin/python3.12 poetry update poetry run python main.py ``` +### Интеграция в Core + +Добавить в nginx.conf.sigil ссылку на /upload +``` +# Custom location block for /upload +location /connect/ { + http://upload-8080/; + {{ $proxy_settings }} + {{ $gzip_settings }} + {{ $cors_headers_options }} + {{ $cors_headers_post }} + {{ $cors_headers_get }} +} +``` +при такой структуре ссылка будет core.domain.com/upload \ No newline at end of file diff --git a/auth.py b/auth.py index 30e3d09..cae595a 100644 --- a/auth.py +++ b/auth.py @@ -1,22 +1,18 @@ from functools import wraps +from starlette.responses import JSONResponse import aiohttp -from aiohttp import web AUTH_URL = 'https://auth.discours.io' - async def check_auth(req): token = req.headers.get("Authorization") - headers = {"Authorization": token, "Content-Type": "application/json"} # "Bearer " + removed + headers = {"Authorization": token, "Content-Type": "application/json"} + print(f"[services.auth] checking auth token: {token}") - query_name = "session" - query_type = "query" - operation = "GetUserId" - gql = { - "query": query_type + " " + operation + " { " + query_name + " { user { id } } }", - "operationName": operation, + "query": "query GetUserId { session { user { id } } }", + "operationName": "GetUserId", "variables": None, } @@ -27,25 +23,19 @@ async def check_auth(req): return False, None r = await response.json() if r: - user_id = r.get("data", {}).get(query_name, {}).get("user", {}).get("id", None) + user_id = r.get("data", {}).get("session", {}).get("user", {}).get("id", None) is_authenticated = user_id is not None return is_authenticated, user_id return False, None - def login_required(f): @wraps(f) - async def decorated_function(*args, **kwargs): - info = args[1] - context = info.context - req = context.get("request") - is_authenticated, user_id = await check_auth(req) + async def decorated_function(request, *args, **kwargs): + is_authenticated, user_id = await check_auth(request) if not is_authenticated: - raise web.HTTPUnauthorized(text="You are not logged in") # Return HTTP 401 Unauthorized - else: - context["user_id"] = user_id - - # If the user is authenticated, execute the resolver - return await f(*args, **kwargs) + return JSONResponse({'error': 'Unauthorized'}, status_code=401) + # Make user_id available to the route handler, if needed + request.state.user_id = user_id + return await f(request, *args, **kwargs) return decorated_function diff --git a/main.py b/main.py index c802d5c..9841731 100644 --- a/main.py +++ b/main.py @@ -1,14 +1,16 @@ +import logging import os import tempfile import uuid -import boto3 -from botocore.exceptions import BotoCoreError, ClientError +import aioboto3 from starlette.applications import Starlette from starlette.responses import JSONResponse from starlette.routing import Route from starlette.requests import Request from auth import check_auth +# Logging configuration +logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s') STORJ_ACCESS_KEY = os.environ.get('STORJ_ACCESS_KEY') STORJ_SECRET_KEY = os.environ.get('STORJ_SECRET_KEY') @@ -16,54 +18,56 @@ STORJ_END_POINT = os.environ.get('STORJ_END_POINT') STORJ_BUCKET_NAME = os.environ.get('STORJ_BUCKET_NAME') CDN_DOMAIN = os.environ.get('CDN_DOMAIN') - @check_auth async def upload_handler(request: Request): + logging.debug("Received upload request") form = await request.form() file = form.get('file') if file is None: + logging.error("No file uploaded") return JSONResponse({'error': 'No file uploaded'}, status_code=400) file_name, file_extension = os.path.splitext(file.filename) key = str(uuid.uuid4()) + file_extension + logging.debug(f"Generated file key: {key}") - s3 = boto3.client('s3', - aws_access_key_id=STORJ_ACCESS_KEY, - aws_secret_access_key=STORJ_SECRET_KEY, - endpoint_url=STORJ_END_POINT) - - try: + async with aioboto3.client('s3', + aws_access_key_id=STORJ_ACCESS_KEY, + aws_secret_access_key=STORJ_SECRET_KEY, + endpoint_url=STORJ_END_POINT) as s3: with tempfile.NamedTemporaryFile() as tmp_file: while True: - chunk = await file.read(8192) # 8192 bytes by default. + chunk = await file.read(8192) if not chunk: break tmp_file.write(chunk) + tmp_file.flush() - s3.upload_file( + logging.debug("Starting file upload to S3") + await s3.upload_file( Filename=tmp_file.name, Bucket=STORJ_BUCKET_NAME, Key=key, - ExtraArgs={ - "ContentType": file.content_type - } + ExtraArgs={"ContentType": file.content_type} ) + logging.debug("File upload completed") - url = 'http://' + CDN_DOMAIN + '/' + key - + url = f'http://{CDN_DOMAIN}/{key}' + logging.info(f"File uploaded successfully: {url}") return JSONResponse({'url': url, 'originalFilename': file.filename}) - except (BotoCoreError, ClientError) as e: - print(e) - return JSONResponse({'error': 'Failed to upload file'}, status_code=500) +async def home(request: Request): + logging.debug("Home route called") + return JSONResponse({'message': 'Hello World!'}) routes = [ - Route('/upload', upload_handler, methods=['POST']), + Route('/test', home, methods=['GET']), + Route('/', upload_handler, methods=['POST']) ] app = Starlette(debug=True, routes=routes) if __name__ == "__main__": import uvicorn - uvicorn.run(app, host='0.0.0.0', port=80) \ No newline at end of file + uvicorn.run(app, host='0.0.0.0', port=8080) diff --git a/pyproject.toml b/pyproject.toml index ed67076..8d5ea3a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,8 +11,8 @@ python = "^3.12" aiohttp = "^3.9.1" uvicorn = "^0.24.0.post1" starlette = "^0.33.0" -boto3 = "^1.33.6" -botocore = "^1.33.6" +aioboto3 = "^9.0.0" +python-multipart = "^0.0.5" [tool.poetry.dev-dependencies] black = "^23.10.1"