From ee7739b875035d880203a92267149eb78bb363d8 Mon Sep 17 00:00:00 2001 From: artem Date: Fri, 16 Jan 2026 18:09:54 +0300 Subject: [PATCH] attachment --- server/admin/__main__.py | 17 +++++---- server/admin/config.py | 2 +- server/admin/views/attachments.py | 2 +- server/infra/db/pg.py | 11 +++--- server/infra/web/__init__.py | 3 +- server/infra/web/attachments.py | 35 +++++++++++++++++++ server/main.py | 3 +- .../attachments/services/attachment.py | 2 +- 8 files changed, 54 insertions(+), 21 deletions(-) create mode 100644 server/infra/web/attachments.py diff --git a/server/admin/__main__.py b/server/admin/__main__.py index bcd6874..0332c54 100644 --- a/server/admin/__main__.py +++ b/server/admin/__main__.py @@ -1,4 +1,3 @@ - import flask_login from flask import Response, redirect, render_template, request, url_for from flask_admin import Admin, AdminIndexView, expose @@ -40,14 +39,14 @@ async def get_file(raw_path: str): cache_ctrl = "public, max-age=864000" # 10 дней last_mod = attach[0].created_at.strftime("%a, %d %b %Y %H:%M:%S GMT") - return Response( - body, - mimetype=attach[0].content_type, - headers={ - "Cache-Control": cache_ctrl, - "Last-Modified": last_mod, - }, - ) + return Response( + body, + mimetype=attach[0].content_type, + headers={ + "Cache-Control": cache_ctrl, + "Last-Modified": last_mod, + }, + ) @login_manager.user_loader diff --git a/server/admin/config.py b/server/admin/config.py index 000e357..a90eaaf 100644 --- a/server/admin/config.py +++ b/server/admin/config.py @@ -12,7 +12,7 @@ app.config["SQLALCHEMY_DATABASE_URI"] = ( app.config["SQLALCHEMY_ENGINE_OPTIONS"] = { "pool_recycle": 600, "pool_pre_ping": True, - "pool_size": 5, + "pool_size": 30, } app.config["SECRET_KEY"] = cnf.admin_secret_key app.config["SQLALCHEMY_ECHO"] = True diff --git a/server/admin/views/attachments.py b/server/admin/views/attachments.py index a6a1471..5222e2e 100644 --- a/server/admin/views/attachments.py +++ b/server/admin/views/attachments.py @@ -23,7 +23,7 @@ class AttachmentView(ModelView): @staticmethod def _list_thumbnail(view, _context, model, _name): data = "" - data += f'' + data += f'' return Markup(data) diff --git a/server/infra/db/pg.py b/server/infra/db/pg.py index 24c93cc..2167e40 100644 --- a/server/infra/db/pg.py +++ b/server/infra/db/pg.py @@ -24,7 +24,7 @@ class AsyncDB(AbstractDB): # self.engine.execution_options(stream_results=True) if self.engine is None: raise ConnectError - session = asyncio.async_sessionmaker(self.engine, expire_on_commit=False) + session = asyncio.async_sessionmaker(self.engine, expire_on_commit=True) if session is None: raise ConnectError self.async_session = session @@ -35,13 +35,10 @@ class AsyncDB(AbstractDB): @property def session(self): - return asyncio.async_sessionmaker(self.engine, expire_on_commit=False) - - def new_session(self): - return asyncio.async_sessionmaker(self.engine, expire_on_commit=False)() + return asyncio.async_sessionmaker(self.engine, expire_on_commit=True) def session_master(self): - return self.new_session() + return self.async_session() def session_slave(self): - return self.new_session() + return self.async_session() diff --git a/server/infra/web/__init__.py b/server/infra/web/__init__.py index 469f7ab..2d646b5 100644 --- a/server/infra/web/__init__.py +++ b/server/infra/web/__init__.py @@ -1,6 +1,7 @@ +from server.infra.web.attachments import AtachmentController from server.infra.web.description import DescriptionController from server.infra.web.recognizer import BreedsController from server.infra.web.seo import SeoController from server.infra.web.vote import VoteController -__all__ = ("DescriptionController", "SeoController", "BreedsController", "VoteController") +__all__ = ("DescriptionController", "SeoController", "BreedsController", "VoteController", "AtachmentController") diff --git a/server/infra/web/attachments.py b/server/infra/web/attachments.py new file mode 100644 index 0000000..da0f05a --- /dev/null +++ b/server/infra/web/attachments.py @@ -0,0 +1,35 @@ +import inject +from litestar import Controller, get +from litestar.exceptions import HTTPException +from litestar.response import Response + +from server.modules.attachments import AtachmentService + + +class AtachmentController(Controller): + + @get("/attachments/{raw_path:path}", media_type="image/jpeg") + async def get_file(self, raw_path: str) -> Response: + attach_service: AtachmentService = inject.instance(AtachmentService) + + attach_path = attach_service.path_from_url(raw_path) + # Query within session scope + attach = await attach_service.get_info_bypath(session=None, path=[attach_path]) + if not attach: + raise HTTPException(status_code=404, detail="Attachment not found") + + # Get file data (assuming async) + body = await attach_service.get_data(attach_path) + + # Extract metadata within session scope + content_type = attach[0].content_type + last_mod = attach[0].created_at.strftime("%a, %d %b %Y %H:%M:%S GMT") + + return Response( + content=body, + media_type=content_type, + headers={ + "Cache-Control": "public, max-age=864000", + "Last-Modified": last_mod, + }, + ) diff --git a/server/main.py b/server/main.py index 6d9cc46..0744f07 100644 --- a/server/main.py +++ b/server/main.py @@ -12,7 +12,7 @@ from litestar.template.config import TemplateConfig from server.config import get_app_config from server.infra.db import AsyncDB -from server.infra.web import BreedsController, DescriptionController, SeoController, VoteController +from server.infra.web import AtachmentController, BreedsController, DescriptionController, SeoController, VoteController from server.modules.attachments import AtachmentService, DBAttachmentRepository, S3StorageDriver from server.modules.descriptions import CharactersService, PGCharactersRepository from server.modules.rate import PGVoteRepository, VotesService @@ -45,6 +45,7 @@ app = Litestar( DescriptionController, SeoController, VoteController, + AtachmentController, create_static_files_router(path="/static", directories=["server/static"]), ], template_config=TemplateConfig( diff --git a/server/modules/attachments/services/attachment.py b/server/modules/attachments/services/attachment.py index 1e53142..0ae7b0a 100644 --- a/server/modules/attachments/services/attachment.py +++ b/server/modules/attachments/services/attachment.py @@ -237,7 +237,7 @@ class AtachmentService: if ".original" not in url: raise ValueError(f"wrong url: {url}") parts = url.split(".original")[0] - return f"/{parts}" + return parts def url(self, attachment_id: str, content_type: str | None = None) -> str: return f"{self._cnf.app_public_url}/api/v0/attachment/{attachment_id}.original.{self.extension(content_type)}"