Skip to content

Commit

Permalink
Merge pull request #645 from zurdi15/filter-assets-per-user
Browse files Browse the repository at this point in the history
General fixes for filtering assets per user
  • Loading branch information
zurdi15 authored Feb 8, 2024
2 parents 0d3593d + 01c3054 commit 88111a8
Show file tree
Hide file tree
Showing 31 changed files with 199 additions and 182 deletions.
6 changes: 3 additions & 3 deletions backend/endpoints/responses/assets.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

class BaseAsset(BaseModel):
id: int
rom_id: int
user_id: int
file_name: str
file_name_no_tags: str
file_name_no_ext: str
Expand All @@ -23,7 +25,7 @@ class Config:


class ScreenshotSchema(BaseAsset):
rom_id: int
pass


class UploadedScreenshotsResponse(TypedDict):
Expand All @@ -34,7 +36,6 @@ class UploadedScreenshotsResponse(TypedDict):


class SaveSchema(BaseAsset):
rom_id: int
emulator: Optional[str]
screenshot: Optional[ScreenshotSchema]

Expand All @@ -45,7 +46,6 @@ class UploadedSavesResponse(TypedDict):


class StateSchema(BaseAsset):
rom_id: int
emulator: Optional[str]
screenshot: Optional[ScreenshotSchema]

Expand Down
56 changes: 46 additions & 10 deletions backend/endpoints/responses/rom.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
import re
from typing import Optional

from endpoints.responses.assets import SaveSchema, ScreenshotSchema, StateSchema
from fastapi import Request
from fastapi.responses import StreamingResponse
from handler import socket_handler
from handler.igdb_handler import IGDBRelatedGame
from pydantic import BaseModel
from pydantic import BaseModel, computed_field, Field
from models.rom import Rom
from typing_extensions import TypedDict, NotRequired


SORT_COMPARE_REGEX = r"^([Tt]he|[Aa]|[Aa]nd)\s"


class RomMetadata(TypedDict):
expansions: NotRequired[list[IGDBRelatedGame]]
dlcs: NotRequired[list[IGDBRelatedGame]]
Expand Down Expand Up @@ -50,9 +56,6 @@ class RomSchema(BaseModel):
game_modes: list[str]
igdb_metadata: Optional[RomMetadata]

# Used for sorting on the frontend
sort_comparator: str

path_cover_s: Optional[str]
path_cover_l: Optional[str]
has_cover: bool
Expand All @@ -65,20 +68,53 @@ class RomSchema(BaseModel):

multi: bool
files: list[str]
saves: list[SaveSchema]
states: list[StateSchema]
screenshots: list[ScreenshotSchema]
url_screenshots: list[str]
merged_screenshots: list[str]
full_path: str
download_path: str

sibling_roms: list["RomSchema"] = Field(default_factory=list)
user_saves: list[SaveSchema] = Field(default_factory=list)
user_states: list[StateSchema] = Field(default_factory=list)
user_screenshots: list[ScreenshotSchema] = Field(default_factory=list)

class Config:
from_attributes = True


class EnhancedRomSchema(RomSchema):
sibling_roms: list["RomSchema"]
@computed_field
@property
def sort_comparator(self) -> str:
return (
re.sub(
SORT_COMPARE_REGEX,
"",
self.name or self.file_name_no_tags,
)
.strip()
.lower()
)

@classmethod
def from_orm_with_request(cls, db_rom: Rom, request: Request) -> "RomSchema":
rom = cls.model_validate(db_rom)
user_id = request.user.id

rom.sibling_roms = [
RomSchema.model_validate(r) for r in db_rom.get_sibling_roms()
]
rom.user_saves = [
SaveSchema.model_validate(s) for s in db_rom.saves if s.user_id == user_id
]
rom.user_states = [
StateSchema.model_validate(s) for s in db_rom.states if s.user_id == user_id
]
rom.user_screenshots = [
ScreenshotSchema.model_validate(s)
for s in db_rom.screenshots
if s.user_id == user_id
]

return rom


class AddRomsResponse(TypedDict):
Expand Down
12 changes: 5 additions & 7 deletions backend/endpoints/rom.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
from endpoints.responses.rom import (
AddRomsResponse,
CustomStreamingResponse,
EnhancedRomSchema,
RomSchema,
)
from exceptions.fs_exceptions import RomAlreadyExistsException
Expand Down Expand Up @@ -102,7 +101,7 @@ def get_roms(
id (int, optional): Rom internal id
Returns:
EnhancedRomSchema: Rom stored in RomM's database
RomSchema: Rom stored in the database
"""

with db_rom_handler.session.begin() as session:
Expand All @@ -117,18 +116,17 @@ def get_roms(


@protected_route(router.get, "/roms/{id}", ["roms.read"])
def get_rom(request: Request, id: int) -> EnhancedRomSchema:
def get_rom(request: Request, id: int) -> RomSchema:
"""Get rom endpoint
Args:
request (Request): Fastapi Request object
id (int): Rom internal id
Returns:
EnhancedRomSchema: Rom stored in RomM's database
RomSchema: Rom stored in the database
"""

return db_rom_handler.get_roms(id)
return RomSchema.from_orm_with_request(db_rom_handler.get_roms(id), request)


@protected_route(router.get, "/roms/{id}/content", ["roms.read"])
Expand Down Expand Up @@ -200,7 +198,7 @@ async def update_rom(
HTTPException: If a rom already have that name when enabling the rename_as_igdb flag
Returns:
RomSchema: Rom stored in RomM's database
RomSchema: Rom stored in the database
"""

data = await request.form()
Expand Down
25 changes: 21 additions & 4 deletions backend/endpoints/saves.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ def add_saves(
emulator: str = None,
) -> UploadedSavesResponse:
rom = db_rom_handler.get_roms(rom_id)
current_user = request.user
log.info(f"Uploading saves to {rom.name}")

if saves is None:
log.error("No saves were uploaded")
raise HTTPException(
Expand All @@ -44,20 +46,25 @@ def add_saves(
platform_fs_slug=rom.platform.fs_slug,
emulator=emulator,
)
db_save = db_save_handler.get_save_by_filename(rom.id, save.filename)
db_save = db_save_handler.get_save_by_filename(
rom_id=rom.id, user_id=current_user.id, file_name=save.filename
)
if db_save:
db_save_handler.update_save(
db_save.id, {"file_size_bytes": scanned_save.file_size_bytes}
)
continue

scanned_save.rom_id = rom.id
scanned_save.user_id = request.user.id
scanned_save.user_id = current_user.id
scanned_save.emulator = emulator
db_save_handler.add_save(scanned_save)

rom = db_rom_handler.get_roms(rom_id)
return {"uploaded": len(saves), "saves": rom.saves}
return {
"uploaded": len(saves),
"saves": [s for s in rom.saves if s.user_id == current_user.id],
}


# @protected_route(router.get, "/saves", ["assets.read"])
Expand All @@ -80,6 +87,11 @@ async def update_save(request: Request, id: int) -> SaveSchema:
log.error(error)
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=error)

if db_save.user_id != request.user.id:
error = "You are not authorized to update this save"
log.error(error)
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail=error)

if "file" in data:
file: UploadFile = data["file"]
fs_asset_handler.write_file(file=file, path=db_save.file_path)
Expand Down Expand Up @@ -107,6 +119,11 @@ async def delete_saves(request: Request) -> MessageResponse:
log.error(error)
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=error)

if save.user_id != request.user.id:
error = "You are not authorized to delete this save"
log.error(error)
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail=error)

db_save_handler.delete_save(save_id)

if delete_from_fs:
Expand Down Expand Up @@ -134,4 +151,4 @@ async def delete_saves(request: Request) -> MessageResponse:
error = f"Screenshot file {save.screenshot.file_name} not found for save {save.file_name}"
log.error(error)

return {"msg": f"Successfully deleted {len(save_ids)} saves."}
return {"msg": f"Successfully deleted {len(save_ids)} saves"}
8 changes: 5 additions & 3 deletions backend/endpoints/screenshots.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ def add_screenshots(
request: Request, rom_id: int, screenshots: list[UploadFile] = File(...)
) -> UploadedScreenshotsResponse:
rom = db_rom_handler.get_roms(rom_id)
current_user = request.user
log.info(f"Uploading screenshots to {rom.name}")

if screenshots is None:
log.error("No screenshots were uploaded")
raise HTTPException(
Expand All @@ -35,7 +37,7 @@ def add_screenshots(
platform_fs_slug=rom.platform_slug,
)
db_screenshot = db_screenshot_handler.get_screenshot_by_filename(
file_name=screenshot.filename, rom_id=rom.id
rom_id=rom.id, user_id=current_user.id, file_name=screenshot.filename
)
if db_screenshot:
db_screenshot_handler.update_screenshot(
Expand All @@ -45,13 +47,13 @@ def add_screenshots(
continue

scanned_screenshot.rom_id = rom.id
scanned_screenshot.user_id = request.user.id
scanned_screenshot.user_id = current_user.id
db_screenshot_handler.add_screenshot(scanned_screenshot)

rom = db_rom_handler.get_roms(rom_id)
return {
"uploaded": len(screenshots),
"screenshots": rom.screenshots,
"screenshots": [s for s in rom.screenshots if s.user_id == current_user.id],
"url_screenshots": rom.url_screenshots,
"merged_screenshots": rom.merged_screenshots,
}
25 changes: 21 additions & 4 deletions backend/endpoints/states.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ def add_states(
emulator: str = None,
) -> UploadedStatesResponse:
rom = db_rom_handler.get_roms(rom_id)
current_user = request.user
log.info(f"Uploading states to {rom.name}")

if states is None:
log.error("No states were uploaded")
raise HTTPException(
Expand All @@ -44,20 +46,25 @@ def add_states(
platform_fs_slug=rom.platform.fs_slug,
emulator=emulator,
)
db_state = db_state_handler.get_state_by_filename(rom.id, state.filename)
db_state = db_state_handler.get_state_by_filename(
rom_id=rom.id, user_id=current_user.id, file_name=state.filename
)
if db_state:
db_state_handler.update_state(
db_state.id, {"file_size_bytes": scanned_state.file_size_bytes}
)
continue

scanned_state.rom_id = rom.id
scanned_state.user_id = request.user.id
scanned_state.user_id = current_user.id
scanned_state.emulator = emulator
db_state_handler.add_state(scanned_state)

rom = db_rom_handler.get_roms(rom_id)
return {"uploaded": len(states), "states": rom.states}
return {
"uploaded": len(states),
"states": [s for s in rom.states if s.user_id == current_user.id],
}


# @protected_route(router.get, "/states", ["assets.read"])
Expand All @@ -80,6 +87,11 @@ async def update_state(request: Request, id: int) -> StateSchema:
log.error(error)
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=error)

if db_state.user_id != request.user.id:
error = "You are not authorized to update this save state"
log.error(error)
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail=error)

if "file" in data:
file: UploadFile = data["file"]
fs_asset_handler.write_file(file=file, path=db_state.file_path)
Expand Down Expand Up @@ -107,6 +119,11 @@ async def delete_states(request: Request) -> MessageResponse:
log.error(error)
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=error)

if state.user_id != request.user.id:
error = "You are not authorized to delete this save state"
log.error(error)
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail=error)

db_state_handler.delete_state(state_id)

if delete_from_fs:
Expand All @@ -133,4 +150,4 @@ async def delete_states(request: Request) -> MessageResponse:
error = f"Screenshot file {state.screenshot.file_name} not found for state {state.file_name}"
log.error(error)

return {"msg": f"Successfully deleted {len(state_ids)} states."}
return {"msg": f"Successfully deleted {len(state_ids)} states"}
4 changes: 2 additions & 2 deletions backend/endpoints/tests/test_assets.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def test_delete_saves(access_token, save):
assert response.status_code == 200

body = response.json()
assert body['msg'] == "Successfully deleted 1 saves."
assert body['msg'] == "Successfully deleted 1 saves"


def test_delete_states(access_token, state):
Expand All @@ -26,4 +26,4 @@ def test_delete_states(access_token, state):
assert response.status_code == 200

body = response.json()
assert body['msg'] == "Successfully deleted 1 states."
assert body['msg'] == "Successfully deleted 1 states"
2 changes: 1 addition & 1 deletion backend/handler/auth_handler/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,12 @@
"roms.read": "View ROMs",
"platforms.read": "View platforms",
"assets.read": "View assets",
"assets.write": "Modify assets",
}

WRITE_SCOPES_MAP: Final = {
"roms.write": "Modify ROMs",
"platforms.write": "Modify platforms",
"assets.write": "Modify assets",
}

FULL_SCOPES_MAP: Final = {
Expand Down
Loading

0 comments on commit 88111a8

Please sign in to comment.