Skip to content

Commit

Permalink
Merge pull request #822 from rommapp/master
Browse files Browse the repository at this point in the history
v3.1.0
  • Loading branch information
zurdi15 committed Apr 20, 2024
2 parents 5482d98 + fdac52a commit 6147059
Show file tree
Hide file tree
Showing 91 changed files with 2,525 additions and 1,082 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ RomM (ROM Manager) allows you to scan, enrich, and browse your game collection w

## Features

- Scans your existing games library and enchances it with metadata from [IGDB][igdb] and [MobyGames][mobygames]
- Scans your existing games library and enchances it with metadata from [IGDB][igdb-api] and [MobyGames][mobygames-api]
- Supports a large number of **[platforms][platform-support]**
- Play games directly from the browser using [EmulatorJS][wiki-emulatorjs-url]
- Share your library with friend while [limiting access and permissions][authentication]
Expand Down
39 changes: 39 additions & 0 deletions backend/alembic/versions/0016_user_last_login_active.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
"""empty message
Revision ID: 0016_user_last_login_active
Revises: 0015_mobygames_data
Create Date: 2024-04-06 15:16:50.539968
"""

from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision = "0016_user_last_login_active"
down_revision = "0015_mobygames_data"
branch_labels = None
depends_on = None


def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table("users", schema=None) as batch_op:
batch_op.add_column(
sa.Column("last_login", sa.DateTime(timezone=True), nullable=True)
)
batch_op.add_column(
sa.Column("last_active", sa.DateTime(timezone=True), nullable=True)
)

# ### end Alembic commands ###


def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table("users", schema=None) as batch_op:
batch_op.drop_column("last_active")
batch_op.drop_column("last_login")

# ### end Alembic commands ###
5 changes: 5 additions & 0 deletions backend/config/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
REDIS_HOST: Final = os.environ.get("REDIS_HOST", "127.0.0.1")
REDIS_PORT: Final = os.environ.get("REDIS_PORT", 6379)
REDIS_PASSWORD: Final = os.environ.get("REDIS_PASSWORD")
REDIS_USERNAME: Final = os.environ.get("REDIS_USERNAME", "")
REDIS_DB: Final = int(os.environ.get("REDIS_DB", 0))

# IGDB
IGDB_CLIENT_ID: Final = os.environ.get(
Expand All @@ -54,6 +56,9 @@
"ROMM_AUTH_SECRET_KEY", secrets.token_hex(32)
)
DISABLE_CSRF_PROTECTION = os.environ.get("DISABLE_CSRF_PROTECTION", "false") == "true"
DISABLE_DOWNLOAD_ENDPOINT_AUTH = (
os.environ.get("DISABLE_DOWNLOAD_ENDPOINT_AUTH", "false") == "true"
)

# SCANS
SCAN_TIMEOUT: Final = int(os.environ.get("SCAN_TIMEOUT", 60 * 60 * 4)) # 4 hours
Expand Down
9 changes: 7 additions & 2 deletions backend/endpoints/auth.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from datetime import timedelta
from datetime import timedelta, datetime
from typing import Annotated, Final

from endpoints.forms.identity import OAuth2RequestForm
Expand All @@ -7,7 +7,7 @@
from exceptions.auth_exceptions import AuthCredentialsException, DisabledException
from fastapi import APIRouter, Depends, HTTPException, Request, status
from fastapi.security.http import HTTPBasic
from handler import auth_handler, oauth_handler
from handler import auth_handler, oauth_handler, db_user_handler

ACCESS_TOKEN_EXPIRE_MINUTES: Final = 30
REFRESH_TOKEN_EXPIRE_DAYS: Final = 7
Expand Down Expand Up @@ -156,6 +156,11 @@ def login(request: Request, credentials=Depends(HTTPBasic())) -> MessageResponse

request.session.update({"iss": "romm:auth", "sub": user.username})

# Update last login and active times
db_user_handler.update_user(
user.id, {"last_login": datetime.now(), "last_active": datetime.now()}
)

return {"msg": "Successfully logged in"}


Expand Down
55 changes: 48 additions & 7 deletions backend/endpoints/webrcade.py → backend/endpoints/feeds.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
from config import ROMM_HOST
from config import ROMM_HOST, DISABLE_DOWNLOAD_ENDPOINT_AUTH
from decorators.auth import protected_route
from endpoints.responses.webrcade import (
from models.rom import Rom
from fastapi import APIRouter, Request
from handler import db_platform_handler, db_rom_handler
from .responses.feeds import (
WEBRCADE_SLUG_TO_TYPE_MAP,
WEBRCADE_SUPPORTED_PLATFORM_SLUGS,
WebrcadeFeedSchema,
TinfoilFeedSchema,
)
from fastapi import APIRouter, Request
from handler import db_platform_handler, db_rom_handler

router = APIRouter()


@protected_route(router.get, "/webrcade/feed", ["roms.read"])
@protected_route(
router.get,
"/webrcade/feed",
[] if DISABLE_DOWNLOAD_ENDPOINT_AUTH else ["roms.read"],
)
def platforms_webrcade_feed(request: Request) -> WebrcadeFeedSchema:
"""Get webrcade feed endpoint
Expand Down Expand Up @@ -45,12 +51,47 @@ def platforms_webrcade_feed(request: Request) -> WebrcadeFeedSchema:
"type": WEBRCADE_SLUG_TO_TYPE_MAP.get(p.slug, p.slug),
"thumbnail": f"{ROMM_HOST}/assets/romm/resources/{rom.path_cover_s}",
"background": f"{ROMM_HOST}/assets/romm/resources/{rom.path_cover_l}",
"props": {"rom": f"{ROMM_HOST}/api/roms/{rom.id}/content/{rom.file_name}"},
"props": {
"rom": f"{ROMM_HOST}/api/roms/{rom.id}/content/{rom.file_name}"
},
}
for rom in session.scalars(db_rom_handler.get_roms(platform_id=p.id)).all()
for rom in session.scalars(
db_rom_handler.get_roms(platform_id=p.id)
).all()
],
}
for p in platforms
if p.slug in WEBRCADE_SUPPORTED_PLATFORM_SLUGS
],
}


@protected_route(router.get, "/tinfoil/feed", ["roms.read"])
def tinfoil_index_feed(request: Request, slug: str = "switch") -> TinfoilFeedSchema:
"""Get tinfoil custom index feed endpoint
https://blawar.github.io/tinfoil/custom_index/
Args:
request (Request): Fastapi Request object
slug (str, optional): Platform slug. Defaults to "switch".
Returns:
TinfoilFeedSchema: Tinfoil feed object schema
"""
switch = db_platform_handler.get_platform_by_fs_slug(slug)
with db_rom_handler.session.begin() as session:
files: list[Rom] = session.scalars(
db_rom_handler.get_roms(platform_id=switch.id)
).all()

return {
"files": [
{
"url": f"{ROMM_HOST}/api/roms/{file.id}/content/{file.file_name}",
"size": file.file_size_bytes,
}
for file in files
],
"directories": [],
"success": "RomM Switch Library",
}
2 changes: 1 addition & 1 deletion backend/endpoints/heartbeat.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def heartbeat() -> HeartbeatResponse:
"ENABLED": ENABLE_SCHEDULED_UPDATE_SWITCH_TITLEDB, # noqa
"CRON": SCHEDULED_UPDATE_SWITCH_TITLEDB_CRON,
"TITLE": "Scheduled Switch TitleDB update",
"MESSAGE": "Updates the Nintedo Switch TitleDB file",
"MESSAGE": "Updates the Nintendo Switch TitleDB file",
},
}
}
46 changes: 42 additions & 4 deletions backend/endpoints/platform.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,36 @@
from decorators.auth import protected_route
from endpoints.responses import MessageResponse
from endpoints.responses.platform import PlatformSchema
from exceptions.fs_exceptions import PlatformAlreadyExistsException
from fastapi import APIRouter, HTTPException, Request, status
from handler import db_platform_handler
from handler import db_platform_handler, fs_platform_handler
from handler.metadata_handler.igdb_handler import IGDB_PLATFORM_LIST
from handler.scan_handler import scan_platform
from logger.logger import log

router = APIRouter()


@protected_route(router.post, "/platforms", ["platforms.write"])
def add_platforms(request: Request) -> MessageResponse:
async def add_platforms(request: Request) -> PlatformSchema:
"""Create platform endpoint
Args:
request (Request): Fastapi Request object
Returns:
MessageResponse: Standard message response
PlatformSchema: Just created platform
"""

return {"msg": "Enpoint not available yet"}
data = await request.json()
fs_slug = data["fs_slug"]
try:
fs_platform_handler.add_platforms(fs_slug=fs_slug)
except PlatformAlreadyExistsException:
log.info(f"Detected platform: {fs_slug}")
scanned_platform = scan_platform(fs_slug, [fs_slug])
platform = db_platform_handler.add_platform(scanned_platform)
return platform


@protected_route(router.get, "/platforms", ["platforms.read"])
Expand All @@ -37,6 +48,33 @@ def get_platforms(request: Request) -> list[PlatformSchema]:
return db_platform_handler.get_platforms()


@protected_route(router.get, "/platforms/supported", ["platforms.read"])
def get_supported_platforms(request: Request) -> list[PlatformSchema]:
"""Get list of supported platforms endpoint
Args:
request (Request): Fastapi Request object
Returns:
list[PlatformSchema]: List of supported platforms
"""

supported_platforms = []
db_platforms: list = db_platform_handler.get_platforms()
# This double loop probably can be done better
for platform in IGDB_PLATFORM_LIST:
platform["id"] = -1
for p in db_platforms:
if p.name == platform["name"]:
platform["id"] = p.id
platform["fs_slug"] = platform["slug"]
platform["logo_path"] = ""
platform["roms"] = []
platform["rom_count"] = 0
supported_platforms.append(PlatformSchema.model_validate(platform).model_dump())
return supported_platforms


@protected_route(router.get, "/platforms/{id}", ["platforms.read"])
def get_platform(request: Request, id: int) -> PlatformSchema:
"""Get platforms endpoint
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,9 @@ class WebrcadeFeedSchema(TypedDict):
thumbnail: str
background: str
categories: list[dict]


class TinfoilFeedSchema(TypedDict):
files: list[dict]
directories: list[str]
success: str
3 changes: 3 additions & 0 deletions backend/endpoints/responses/identity.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from datetime import datetime
from models.user import Role
from pydantic import BaseModel

Expand All @@ -9,6 +10,8 @@ class UserSchema(BaseModel):
role: Role
oauth_scopes: list[str]
avatar_path: str
last_login: datetime | None
last_active: datetime | None

class Config:
from_attributes = True
4 changes: 2 additions & 2 deletions backend/endpoints/responses/platform.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ class PlatformSchema(BaseModel):
igdb_id: Optional[int] = None
sgdb_id: Optional[int] = None
moby_id: Optional[int] = None
name: Optional[str]
logo_path: str
name: str
logo_path: Optional[str] = ""
rom_count: int

class Config:
Expand Down
3 changes: 2 additions & 1 deletion backend/endpoints/responses/search.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ class SearchRomSchema(BaseModel):
slug: str
name: str
summary: str
url_cover: str
igdb_url_cover: str = ""
moby_url_cover: str = ""
url_screenshots: list[str]
Loading

0 comments on commit 6147059

Please sign in to comment.