This commit is contained in:
Untone 2024-05-06 11:33:53 +03:00
commit 3242758817
6 changed files with 70 additions and 54 deletions

View File

@ -1,8 +1,5 @@
name: 'deploy' name: 'Deploy on Push'
on: on: [push]
push:
branches:
- main
jobs: jobs:
deploy: deploy:
@ -21,10 +18,19 @@ jobs:
id: branch_name id: branch_name
run: echo "::set-output name=branch::$(echo ${GITHUB_REF##*/})" 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 uses: dokku/github-action@master
with: with:
branch: 'main' 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 }} 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 }}

1
.gitignore vendored
View File

@ -8,3 +8,4 @@ __pycache__
.pytest_cache .pytest_cache
.venv .venv
*.gz.tar *.gz.tar
DELETEME

View File

@ -25,3 +25,18 @@ poetry env use .venv/bin/python3.12
poetry update poetry update
poetry run python main.py 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

34
auth.py
View File

@ -1,22 +1,18 @@
from functools import wraps from functools import wraps
from starlette.responses import JSONResponse
import aiohttp import aiohttp
from aiohttp import web
AUTH_URL = 'https://auth.discours.io' AUTH_URL = 'https://auth.discours.io'
async def check_auth(req): async def check_auth(req):
token = req.headers.get("Authorization") 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}") print(f"[services.auth] checking auth token: {token}")
query_name = "session"
query_type = "query"
operation = "GetUserId"
gql = { gql = {
"query": query_type + " " + operation + " { " + query_name + " { user { id } } }", "query": "query GetUserId { session { user { id } } }",
"operationName": operation, "operationName": "GetUserId",
"variables": None, "variables": None,
} }
@ -27,25 +23,19 @@ async def check_auth(req):
return False, None return False, None
r = await response.json() r = await response.json()
if r: 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 is_authenticated = user_id is not None
return is_authenticated, user_id return is_authenticated, user_id
return False, None return False, None
def login_required(f): def login_required(f):
@wraps(f) @wraps(f)
async def decorated_function(*args, **kwargs): async def decorated_function(request, *args, **kwargs):
info = args[1] is_authenticated, user_id = await check_auth(request)
context = info.context
req = context.get("request")
is_authenticated, user_id = await check_auth(req)
if not is_authenticated: if not is_authenticated:
raise web.HTTPUnauthorized(text="You are not logged in") # Return HTTP 401 Unauthorized return JSONResponse({'error': 'Unauthorized'}, status_code=401)
else:
context["user_id"] = user_id
# If the user is authenticated, execute the resolver
return await f(*args, **kwargs)
# 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 return decorated_function

46
main.py
View File

@ -1,14 +1,16 @@
import logging
import os import os
import tempfile import tempfile
import uuid import uuid
import boto3 import aioboto3
from botocore.exceptions import BotoCoreError, ClientError
from starlette.applications import Starlette from starlette.applications import Starlette
from starlette.responses import JSONResponse from starlette.responses import JSONResponse
from starlette.routing import Route from starlette.routing import Route
from starlette.requests import Request from starlette.requests import Request
from auth import check_auth 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_ACCESS_KEY = os.environ.get('STORJ_ACCESS_KEY')
STORJ_SECRET_KEY = os.environ.get('STORJ_SECRET_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') STORJ_BUCKET_NAME = os.environ.get('STORJ_BUCKET_NAME')
CDN_DOMAIN = os.environ.get('CDN_DOMAIN') CDN_DOMAIN = os.environ.get('CDN_DOMAIN')
@check_auth @check_auth
async def upload_handler(request: Request): async def upload_handler(request: Request):
logging.debug("Received upload request")
form = await request.form() form = await request.form()
file = form.get('file') file = form.get('file')
if file is None: if file is None:
logging.error("No file uploaded")
return JSONResponse({'error': 'No file uploaded'}, status_code=400) return JSONResponse({'error': 'No file uploaded'}, status_code=400)
file_name, file_extension = os.path.splitext(file.filename) file_name, file_extension = os.path.splitext(file.filename)
key = str(uuid.uuid4()) + file_extension key = str(uuid.uuid4()) + file_extension
logging.debug(f"Generated file key: {key}")
s3 = boto3.client('s3', async with aioboto3.client('s3',
aws_access_key_id=STORJ_ACCESS_KEY, aws_access_key_id=STORJ_ACCESS_KEY,
aws_secret_access_key=STORJ_SECRET_KEY, aws_secret_access_key=STORJ_SECRET_KEY,
endpoint_url=STORJ_END_POINT) endpoint_url=STORJ_END_POINT) as s3:
try:
with tempfile.NamedTemporaryFile() as tmp_file: with tempfile.NamedTemporaryFile() as tmp_file:
while True: while True:
chunk = await file.read(8192) # 8192 bytes by default. chunk = await file.read(8192)
if not chunk: if not chunk:
break break
tmp_file.write(chunk) 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, Filename=tmp_file.name,
Bucket=STORJ_BUCKET_NAME, Bucket=STORJ_BUCKET_NAME,
Key=key, Key=key,
ExtraArgs={ ExtraArgs={"ContentType": file.content_type}
"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}) return JSONResponse({'url': url, 'originalFilename': file.filename})
except (BotoCoreError, ClientError) as e: async def home(request: Request):
print(e) logging.debug("Home route called")
return JSONResponse({'error': 'Failed to upload file'}, status_code=500) return JSONResponse({'message': 'Hello World!'})
routes = [ routes = [
Route('/upload', upload_handler, methods=['POST']), Route('/test', home, methods=['GET']),
Route('/', upload_handler, methods=['POST'])
] ]
app = Starlette(debug=True, routes=routes) app = Starlette(debug=True, routes=routes)
if __name__ == "__main__": if __name__ == "__main__":
import uvicorn import uvicorn
uvicorn.run(app, host='0.0.0.0', port=80) uvicorn.run(app, host='0.0.0.0', port=8080)

View File

@ -11,8 +11,8 @@ python = "^3.12"
aiohttp = "^3.9.1" aiohttp = "^3.9.1"
uvicorn = "^0.24.0.post1" uvicorn = "^0.24.0.post1"
starlette = "^0.33.0" starlette = "^0.33.0"
boto3 = "^1.33.6" aioboto3 = "^9.0.0"
botocore = "^1.33.6" python-multipart = "^0.0.5"
[tool.poetry.dev-dependencies] [tool.poetry.dev-dependencies]
black = "^23.10.1" black = "^23.10.1"