Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
653 changes: 592 additions & 61 deletions pdm.lock

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -102,17 +102,17 @@ dependencies = [
"grpcio-reflection==1.75.1",
"pydantic==2.11.9",
"pydantic-settings==2.11.0",
"psycopg2-binary==2.9.10",
"watchfiles==1.1.0",
"botocore>=1.34.0",
"aioboto3>=13.0.0",
"python-dotenv>=1.0.0",
"asyncpg>=0.30.0",
]
requires-python = ">=3.13"
readme = "README.md"
license = {text = "MIT"}

[project.scripts]
run-server = "modservice.server:serve"
run-server = "modservice.server:main"

[dependency-groups]
dev = [
Expand Down
4 changes: 2 additions & 2 deletions src/modservice/handler/create_mod.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@
from modservice.service.service import ModService


def CreateMod(
async def CreateMod(
service: ModService,
request: mod_pb2.CreateModRequest,
context: grpc.ServicerContext, # noqa: ARG001
) -> mod_pb2.CreateModResponse:
mod_id, s3_key, upload_url = service.create_mod(
mod_id, s3_key, upload_url = await service.create_mod(
request.title,
request.author_id,
request.description,
Expand Down
4 changes: 2 additions & 2 deletions src/modservice/handler/get_mod_download_link.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@
from modservice.service.service import ModService


def GetDownloadLink(
async def GetDownloadLink(
service: ModService,
request: mod_pb2.GetModDownloadLinkRequest,
context: grpc.ServicerContext, # noqa: ARG001
) -> mod_pb2.GetModDownloadLinkResponse:
link_url = service.get_mod_download_link(request.mod_id)
link_url = await service.get_mod_download_link(request.mod_id)

return mod_pb2.GetModDownloadLinkResponse(link_url=link_url)
4 changes: 2 additions & 2 deletions src/modservice/handler/get_mods.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@
}


def GetMods(
async def GetMods(
service: ModService,
request: mod_pb2.GetModsRequest, # noqa: ARG001
context: grpc.ServicerContext, # noqa: ARG001
) -> mod_pb2.GetModsResponse:
mods_data = service.get_mods()
mods_data = await service.get_mods()

mods = []
for mod_data in mods_data:
Expand Down
16 changes: 8 additions & 8 deletions src/modservice/handler/handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,30 +14,30 @@ class ModHandler(mod_pb2_grpc.ModServiceServicer):
def __init__(self, service: ModService):
self._service = service

def CreateMod(
async def CreateMod(
self,
request: mod_pb2.CreateModRequest,
context: grpc.ServicerContext,
) -> mod_pb2.CreateModResponse:
return _create_mod(self._service, request, context)
return await _create_mod(self._service, request, context)

def GetModDownloadLink(
async def GetModDownloadLink(
self,
request: mod_pb2.GetModDownloadLinkRequest,
context: grpc.ServicerContext,
) -> mod_pb2.GetModDownloadLinkResponse:
return _get_mod_download_link(self._service, request, context)
return await _get_mod_download_link(self._service, request, context)

def SetStatus(
async def SetStatus(
self,
request: mod_pb2.SetStatusRequest,
context: grpc.ServicerContext,
) -> mod_pb2.SetStatusResponse:
return _set_status(self._service, request, context)
return await _set_status(self._service, request, context)

def GetMods(
async def GetMods(
self,
request: mod_pb2.GetModsRequest,
context: grpc.ServicerContext,
) -> mod_pb2.GetModsResponse:
return _get_mods(self._service, request, context)
return await _get_mods(self._service, request, context)
4 changes: 2 additions & 2 deletions src/modservice/handler/set_status.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@ def _convert_enum_to_status(status_value: int) -> str:
return _ENUM_TO_DB_STATUS_BY_VALUE[status_value]


def SetStatus(
async def SetStatus(
service: ModService,
request: mod_pb2.SetStatusRequest,
context: grpc.ServicerContext, # noqa: ARG001
) -> mod_pb2.SetStatusResponse:
try:
status_str = _convert_enum_to_status(request.status)
success = service.set_status(request.mod_id, status_str)
success = await service.set_status(request.mod_id, status_str)
return mod_pb2.SetStatusResponse(success=success)
except ValueError as e:
context.set_code(grpc.StatusCode.INVALID_ARGUMENT)
Expand Down
40 changes: 16 additions & 24 deletions src/modservice/repository/create_mod.py
Original file line number Diff line number Diff line change
@@ -1,30 +1,22 @@
from psycopg2.pool import ThreadedConnectionPool
from asyncpg import Pool # type: ignore[import-untyped]


def create_mod(
db_pool: ThreadedConnectionPool,
async def create_mod(
db_pool: Pool,
title: str, # noqa: ARG001
author_id: int, # noqa: ARG001
description: str, # noqa: ARG001
) -> int:
conn = db_pool.getconn()
try:
with conn.cursor() as cursor:
cursor.execute(
"""
INSERT INTO mods (author_id, title, description, version, status, created_at)
VALUES (%s, %s, %s, %s, 'UPLOADING', NOW())
RETURNING id
""",
(
author_id,
title,
description,
1,
),
)
result = cursor.fetchone()
conn.commit()
return result[0] if result else 0
finally:
db_pool.putconn(conn)
async with db_pool.acquire() as conn:
mod_id: int = await conn.fetchval(
"""
INSERT INTO mods (author_id, title, description, version, status, created_at)
VALUES ($1, $2, $3, $4, 'UPLOADING', NOW())
RETURNING id
""",
author_id,
title,
description,
1,
)
return mod_id
31 changes: 13 additions & 18 deletions src/modservice/repository/get_mod_s3_key.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,15 @@
from psycopg2.pool import ThreadedConnectionPool
from asyncpg import Pool # type: ignore[import-untyped]


def get_mod_s3_key(db_pool: ThreadedConnectionPool, id: int) -> int:
conn = db_pool.getconn()
try:
with conn.cursor() as cursor:
cursor.execute(
"""
SELECT s3_key
FROM mods
WHERE id = %s
AND status = 'UPLOADED';
""",
[id],
)
result = cursor.fetchone()
return result[0] if result else 0
finally:
db_pool.putconn(conn)
async def get_mod_s3_key(db_pool: Pool, id: int) -> int:
async with db_pool.acquire() as conn:
s3_key = await conn.fetchval(
"""
SELECT s3_key
FROM mods
WHERE id = $1
AND status = 'UPLOADED';
""",
id,
)
return s3_key if s3_key else 0
82 changes: 38 additions & 44 deletions src/modservice/repository/get_mods.py
Original file line number Diff line number Diff line change
@@ -1,51 +1,45 @@
from typing import Any

from psycopg2.pool import ThreadedConnectionPool
from asyncpg import Pool # type: ignore[import-untyped]


def get_mods(
db_pool: ThreadedConnectionPool,
async def get_mods(
db_pool: Pool,
) -> list[dict[str, Any]]:
conn = db_pool.getconn()
try:
with conn.cursor() as cursor:
# NOTE: Поля для будущего добавления в БД:
# - avatar_url
# - download_count
# - tags
# - updated_at
cursor.execute(
"""
SELECT
id,
author_id,
title,
description,
version,
s3_key,
status,
created_at
FROM mods
ORDER BY created_at DESC
"""
)
async with db_pool.acquire() as conn:
# NOTE: Поля для будущего добавления в БД:
# - avatar_url
# - download_count
# - tags
# - updated_at
results = await conn.fetch(
"""
SELECT
id,
author_id,
title,
description,
version,
s3_key,
status,
created_at
FROM mods
ORDER BY created_at DESC
"""
)

results = cursor.fetchall()
mods = []
for row in results:
mod = {
"id": row["id"],
"author_id": row["author_id"],
"title": row["title"],
"description": row["description"],
"version": row["version"],
"s3_key": row["s3_key"],
"status": row["status"],
"created_at": row["created_at"],
}
mods.append(mod)

mods = []
for row in results:
mod = {
"id": row[0],
"author_id": row[1],
"title": row[2],
"description": row[3],
"version": row[4],
"s3_key": row[5],
"status": row[6],
"created_at": row[7],
}
mods.append(mod)

return mods
finally:
db_pool.putconn(conn)
return mods
39 changes: 14 additions & 25 deletions src/modservice/repository/insert_s3_key.py
Original file line number Diff line number Diff line change
@@ -1,33 +1,22 @@
from psycopg2.pool import ThreadedConnectionPool
from asyncpg import Pool # type: ignore[import-untyped]


def generate_s3_key(author_id: int, mod_id: int) -> str:
return f"{author_id}/{mod_id}"


def insert_s3_key(
db_pool: ThreadedConnectionPool,
mod_id: int,
author_id: int,
) -> str:
conn = db_pool.getconn()
try:
with conn.cursor() as cursor:
s3_key = generate_s3_key(author_id, mod_id)
async def insert_s3_key(db_pool: Pool, mod_id: int, author_id: int) -> str:
async with db_pool.acquire() as conn:
s3_key = generate_s3_key(author_id, mod_id)

cursor.execute(
"""
UPDATE mods
SET s3_key = %s
WHERE id = %s
""",
(
s3_key,
mod_id,
),
)
await conn.execute(
"""
UPDATE mods
SET s3_key = $1
WHERE id = $2
""",
s3_key,
mod_id,
)

conn.commit()
return s3_key
finally:
db_pool.putconn(conn)
return s3_key
24 changes: 12 additions & 12 deletions src/modservice/repository/repository.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from typing import Any

from psycopg2.pool import ThreadedConnectionPool
from asyncpg import Pool # type: ignore[import-untyped]

from modservice.repository.create_mod import create_mod as _create_mod
from modservice.repository.get_mod_s3_key import (
Expand All @@ -12,31 +12,31 @@


class ModRepository:
def __init__(self, db_pool: ThreadedConnectionPool):
def __init__(self, db_pool: Pool):
self._db_pool = db_pool

def create_mod(
async def create_mod(
self,
title: str,
author_id: int,
description: str,
) -> int:
return _create_mod(self._db_pool, title, author_id, description)
return await _create_mod(self._db_pool, title, author_id, description)

def insert_s3_key(
async def insert_s3_key(
self,
mod_id: int,
author_id: int,
) -> str:
return _insert_s3_key(self._db_pool, mod_id, author_id)
return await _insert_s3_key(self._db_pool, mod_id, author_id)

def get_mod_s3_key(self, mod_id: int) -> str:
return str(_get_mod_s3_key(self._db_pool, mod_id))
async def get_mod_s3_key(self, mod_id: int) -> str:
return str(await _get_mod_s3_key(self._db_pool, mod_id))

def set_status(self, mod_id: int, status: str) -> bool:
return _set_status(self._db_pool, mod_id, status)
async def set_status(self, mod_id: int, status: str) -> bool:
return await _set_status(self._db_pool, mod_id, status)

def get_mods(
async def get_mods(
self,
) -> list[dict[str, Any]]:
return _get_mods(self._db_pool)
return await _get_mods(self._db_pool)
Loading
Loading