diff --git a/backend/alembic/versions/0020_created_and_updated.py b/backend/alembic/versions/0020_created_and_updated.py new file mode 100644 index 000000000..e43074190 --- /dev/null +++ b/backend/alembic/versions/0020_created_and_updated.py @@ -0,0 +1,145 @@ +"""empty message + +Revision ID: 0020_created_and_updated +Revises: 0019_resources_refactor +Create Date: 2024-06-27 12:08:13.886766 + +""" + +import sqlalchemy as sa +from alembic import op +from sqlalchemy.dialects import mysql + +# revision identifiers, used by Alembic. +revision = "0020_created_and_updated" +down_revision = "0019_resources_refactor" +branch_labels = None +depends_on = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table("firmware", schema=None) as batch_op: + batch_op.add_column( + sa.Column( + "created_at", + sa.DateTime(timezone=True), + server_default=sa.text("now()"), + nullable=False, + ) + ) + batch_op.add_column( + sa.Column( + "updated_at", + sa.DateTime(timezone=True), + server_default=sa.text("now()"), + nullable=False, + ) + ) + + with op.batch_alter_table("platforms", schema=None) as batch_op: + batch_op.add_column( + sa.Column( + "created_at", + sa.DateTime(timezone=True), + server_default=sa.text("now()"), + nullable=False, + ) + ) + batch_op.add_column( + sa.Column( + "updated_at", + sa.DateTime(timezone=True), + server_default=sa.text("now()"), + nullable=False, + ) + ) + + with op.batch_alter_table("rom_notes", schema=None) as batch_op: + batch_op.add_column( + sa.Column( + "created_at", + sa.DateTime(timezone=True), + server_default=sa.text("now()"), + nullable=False, + ) + ) + batch_op.add_column( + sa.Column( + "updated_at", + sa.DateTime(timezone=True), + server_default=sa.text("now()"), + nullable=False, + ) + ) + batch_op.drop_column("last_edited_at") + + with op.batch_alter_table("roms", schema=None) as batch_op: + batch_op.add_column( + sa.Column( + "created_at", + sa.DateTime(timezone=True), + server_default=sa.text("now()"), + nullable=False, + ) + ) + batch_op.add_column( + sa.Column( + "updated_at", + sa.DateTime(timezone=True), + server_default=sa.text("now()"), + nullable=False, + ) + ) + + with op.batch_alter_table("users", schema=None) as batch_op: + batch_op.add_column( + sa.Column( + "created_at", + sa.DateTime(timezone=True), + server_default=sa.text("now()"), + nullable=False, + ) + ) + batch_op.add_column( + sa.Column( + "updated_at", + sa.DateTime(timezone=True), + server_default=sa.text("now()"), + nullable=False, + ) + ) + + # ### 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("updated_at") + batch_op.drop_column("created_at") + + with op.batch_alter_table("roms", schema=None) as batch_op: + batch_op.drop_column("updated_at") + batch_op.drop_column("created_at") + + with op.batch_alter_table("rom_notes", schema=None) as batch_op: + batch_op.add_column( + sa.Column( + "last_edited_at", + mysql.DATETIME(), + server_default=sa.text("current_timestamp()"), + nullable=False, + ) + ) + batch_op.drop_column("updated_at") + + with op.batch_alter_table("platforms", schema=None) as batch_op: + batch_op.drop_column("updated_at") + batch_op.drop_column("created_at") + + with op.batch_alter_table("firmware", schema=None) as batch_op: + batch_op.drop_column("updated_at") + batch_op.drop_column("created_at") + + # ### end Alembic commands ### diff --git a/backend/endpoints/heartbeat.py b/backend/endpoints/heartbeat.py index 3f90f822e..10e06a11b 100644 --- a/backend/endpoints/heartbeat.py +++ b/backend/endpoints/heartbeat.py @@ -11,6 +11,7 @@ from handler.filesystem import fs_platform_handler from handler.metadata.igdb_handler import IGDB_API_ENABLED from handler.metadata.moby_handler import MOBY_API_ENABLED +from handler.metadata.sgdb_handler import STEAMGRIDDB_API_ENABLED from utils import get_version router = APIRouter() @@ -27,6 +28,7 @@ def heartbeat() -> HeartbeatResponse: return { "VERSION": get_version(), "ANY_SOURCE_ENABLED": IGDB_API_ENABLED or MOBY_API_ENABLED, + "STEAMGRIDDB_ENABLED": STEAMGRIDDB_API_ENABLED, "METADATA_SOURCES": { "IGDB_API_ENABLED": IGDB_API_ENABLED, "MOBY_API_ENABLED": MOBY_API_ENABLED, diff --git a/backend/endpoints/responses/firmware.py b/backend/endpoints/responses/firmware.py index bf40fb759..8faa3a306 100644 --- a/backend/endpoints/responses/firmware.py +++ b/backend/endpoints/responses/firmware.py @@ -1,3 +1,5 @@ +from datetime import datetime + from pydantic import BaseModel from typing_extensions import TypedDict @@ -18,6 +20,9 @@ class FirmwareSchema(BaseModel): md5_hash: str sha1_hash: str + created_at: datetime + updated_at: datetime + class Config: from_attributes = True diff --git a/backend/endpoints/responses/heartbeat.py b/backend/endpoints/responses/heartbeat.py index cacdf55a2..208f15367 100644 --- a/backend/endpoints/responses/heartbeat.py +++ b/backend/endpoints/responses/heartbeat.py @@ -27,4 +27,5 @@ class HeartbeatResponse(TypedDict): SCHEDULER: SchedulerDict ANY_SOURCE_ENABLED: bool METADATA_SOURCES: MetadataSourcesDict + STEAMGRIDDB_ENABLED: bool FS_PLATFORMS: list diff --git a/backend/endpoints/responses/identity.py b/backend/endpoints/responses/identity.py index eba4589f5..07005495d 100644 --- a/backend/endpoints/responses/identity.py +++ b/backend/endpoints/responses/identity.py @@ -14,5 +14,8 @@ class UserSchema(BaseModel): last_login: datetime | None last_active: datetime | None + created_at: datetime + updated_at: datetime + class Config: from_attributes = True diff --git a/backend/endpoints/responses/platform.py b/backend/endpoints/responses/platform.py index f4177ff01..5d7126794 100644 --- a/backend/endpoints/responses/platform.py +++ b/backend/endpoints/responses/platform.py @@ -1,3 +1,5 @@ +from datetime import datetime + from pydantic import BaseModel, Field from .firmware import FirmwareSchema @@ -15,5 +17,8 @@ class PlatformSchema(BaseModel): logo_path: str | None = "" firmware: list[FirmwareSchema] = Field(default_factory=list) + created_at: datetime + updated_at: datetime + class Config: from_attributes = True diff --git a/backend/endpoints/responses/rom.py b/backend/endpoints/responses/rom.py index 063012b6f..4e5280db8 100644 --- a/backend/endpoints/responses/rom.py +++ b/backend/endpoints/responses/rom.py @@ -30,7 +30,7 @@ class RomNoteSchema(BaseModel): id: int user_id: int rom_id: int - last_edited_at: datetime + updated_at: datetime raw_markdown: str is_public: bool user__username: str @@ -94,6 +94,9 @@ class RomSchema(BaseModel): files: list[str] full_path: str + created_at: datetime + updated_at: datetime + class Config: from_attributes = True diff --git a/backend/endpoints/responses/search.py b/backend/endpoints/responses/search.py index 4219410da..0e3011ad9 100644 --- a/backend/endpoints/responses/search.py +++ b/backend/endpoints/responses/search.py @@ -9,3 +9,8 @@ class SearchRomSchema(BaseModel): summary: str igdb_url_cover: str = "" moby_url_cover: str = "" + + +class SearchCoverSchema(BaseModel): + name: str + resources: list diff --git a/backend/endpoints/search.py b/backend/endpoints/search.py index 26fa5ec07..63653cc0e 100644 --- a/backend/endpoints/search.py +++ b/backend/endpoints/search.py @@ -1,9 +1,9 @@ import emoji from decorators.auth import protected_route -from endpoints.responses.search import SearchRomSchema +from endpoints.responses.search import SearchCoverSchema, SearchRomSchema from fastapi import APIRouter, HTTPException, Request, status from handler.database import db_rom_handler -from handler.metadata import meta_igdb_handler, meta_moby_handler +from handler.metadata import meta_igdb_handler, meta_moby_handler, meta_sgdb_handler from handler.metadata.igdb_handler import IGDB_API_ENABLED from handler.metadata.moby_handler import MOBY_API_ENABLED from handler.scan_handler import _get_main_platform_igdb_id @@ -107,3 +107,12 @@ async def search_rom( log.info(f"\t - {m_rom['name']}") return matched_roms + + +@protected_route(router.get, "/search/cover", ["roms.read"]) +async def search_cover( + request: Request, + search_term: str | None = None, +) -> list[SearchCoverSchema]: + + return meta_sgdb_handler.get_details(search_term) diff --git a/backend/handler/filesystem/resources_handler.py b/backend/handler/filesystem/resources_handler.py index 5fcbcba9c..4c13d0c54 100644 --- a/backend/handler/filesystem/resources_handler.py +++ b/backend/handler/filesystem/resources_handler.py @@ -38,8 +38,12 @@ def _cover_exists(rom: Rom, size: CoverSize): def resize_cover_to_small(cover_path: str): """Path of the cover image to resize""" cover = Image.open(cover_path) - small_width = int(cover.width * 0.1) - small_height = int(cover.height * 0.1) + if cover.height >= 1000: + ratio = 0.2 + else: + ratio = 0.4 + small_width = int(cover.width * ratio) + small_height = int(cover.height * ratio) small_size = (small_width, small_height) small_img = cover.resize(small_size) small_img.save(cover_path) diff --git a/backend/handler/metadata/igdb_handler.py b/backend/handler/metadata/igdb_handler.py index 98488b978..59b36c35d 100644 --- a/backend/handler/metadata/igdb_handler.py +++ b/backend/handler/metadata/igdb_handler.py @@ -179,13 +179,14 @@ def extract_metadata_from_igdb_rom(rom: dict) -> IGDBMetadata: class IGDBBaseHandler(MetadataHandler): def __init__(self) -> None: - self.platform_endpoint = "https://api.igdb.com/v4/platforms" - self.platform_version_endpoint = "https://api.igdb.com/v4/platform_versions" - self.platforms_fields = ["id", "name"] - self.games_endpoint = "https://api.igdb.com/v4/games" + self.BASE_URL = "https://api.igdb.com/v4" + self.platform_endpoint = f"{self.BASE_URL}/platforms" + self.platform_version_endpoint = f"{self.BASE_URL}/platform_versions" + self.platforms_fields = PLATFORMS_FIELDS + self.games_endpoint = f"{self.BASE_URL}/games" self.games_fields = GAMES_FIELDS - self.search_endpoint = "https://api.igdb.com/v4/search" - self.search_fields = ["game.id", "name"] + self.search_endpoint = f"{self.BASE_URL}/search" + self.search_fields = SEARCH_FIELDS self.pagination_limit = 200 self.twitch_auth = TwitchAuth() self.headers = { @@ -617,6 +618,8 @@ def get_oauth_token(self) -> str: return token +PLATFORMS_FIELDS = ["id", "name"] + GAMES_FIELDS = [ "id", "name", @@ -667,6 +670,8 @@ def get_oauth_token(self) -> str: "similar_games.cover.url", ] +SEARCH_FIELDS = ["game.id", "name"] + # Generated from the following code on https://www.igdb.com/platforms/: # Array.from(document.querySelectorAll(".media-body a")).map(a => ({ # slug: a.href.split("/")[4], diff --git a/backend/handler/metadata/sgdb_handler.py b/backend/handler/metadata/sgdb_handler.py index c75ff29fb..f690bfc5c 100644 --- a/backend/handler/metadata/sgdb_handler.py +++ b/backend/handler/metadata/sgdb_handler.py @@ -1,42 +1,99 @@ +from typing import Final + import requests from config import STEAMGRIDDB_API_KEY from logger.logger import log +# Used to display the Mobygames API status in the frontend +STEAMGRIDDB_API_ENABLED: Final = bool(STEAMGRIDDB_API_KEY) + +# SteamGridDB dimensions +STEAMVERTICAL: Final = "600x900" +GALAXY342: Final = "342x482" +GALAXY660: Final = "660x930" +SQUARE512: Final = "512x512" +SQUARE1024: Final = "1024x1024" + +# SteamGridDB types +STATIC: Final = "static" +ANIMATED: Final = "animated" + +SGDB_API_COVER_LIMIT: Final = 50 + class SGDBBaseHandler: def __init__(self) -> None: + self.BASE_URL = "https://www.steamgriddb.com/api/v2" + self.search_endpoint = f"{self.BASE_URL}/search/autocomplete" + self.grid_endpoint = f"{self.BASE_URL}/grids/game" self.headers = { "Authorization": f"Bearer {STEAMGRIDDB_API_KEY}", "Accept": "*/*", } - self.BASE_URL = "https://www.steamgriddb.com/api/v2" - self.DEFAULT_IMAGE_URL = "https://www.steamgriddb.com/static/img/logo-512.png" - def get_details(self, term): + def get_details(self, search_term): search_response = requests.get( - f"{self.BASE_URL}/search/autocomplete/{term}", + f"{self.search_endpoint}/{search_term}", headers=self.headers, timeout=120, ).json() if len(search_response["data"]) == 0: - log.info(f"Could not find {term} on SteamGridDB") - return ("", "", self.DEFAULT_IMAGE_URL) + log.warning(f"Could not find '{search_term}' on SteamGridDB") + return [] - game_id = search_response["data"][0]["id"] - game_name = search_response["data"][0]["name"] - - game_response = requests.get( - f"{self.BASE_URL}/grid/game/{game_id}", headers=self.headers, timeout=120 - ).json() + games = [] + for game in search_response["data"]: + page = 0 + covers_response = requests.get( + f"{self.grid_endpoint}/{game['id']}", + headers=self.headers, + timeout=120, + params={ + "dimensions": f"{STEAMVERTICAL},{GALAXY342},{GALAXY660},{SQUARE512},{SQUARE1024}", + "types": f"{STATIC},{ANIMATED}", + "page": page, + }, + ).json() - if len(game_response["data"]) == 0: - log.info(f"Could not find {game_name} image on SteamGridDB") - return (game_id, game_name, self.DEFAULT_IMAGE_URL) + while ( + len(covers_response["data"]) < covers_response["total"] + and covers_response["total"] > SGDB_API_COVER_LIMIT + ): + page += 1 + covers_response["data"].extend( + requests.get( + f"{self.grid_endpoint}/{game['id']}", + headers=self.headers, + timeout=120, + params={ + "dimensions": f"{STEAMVERTICAL},{GALAXY342},{GALAXY660},{SQUARE512},{SQUARE1024}", + "types": f"{STATIC},{ANIMATED}", + "page": page, + }, + ).json()["data"] + ) - game_image_url = game_response["data"][0]["url"] + if len(covers_response["data"]) > 0: + games.append( + { + "name": game["name"], + "resources": [ + { + "thumb": cover["thumb"], + "url": cover["url"], + "type": ( + "animated" + if cover["thumb"].split(".")[-1] == "webm" + else "static" + ), + } + for cover in covers_response["data"] + ], + } + ) - return (game_id, game_name, game_image_url) + return games sgdb_handler = SGDBBaseHandler() diff --git a/backend/models/assets.py b/backend/models/assets.py index 0403fa2b9..292c0d127 100644 --- a/backend/models/assets.py +++ b/backend/models/assets.py @@ -1,9 +1,8 @@ -from datetime import datetime from functools import cached_property from typing import TYPE_CHECKING, Optional from models.base import BaseModel -from sqlalchemy import BigInteger, DateTime, ForeignKey, String, func +from sqlalchemy import BigInteger, ForeignKey, String from sqlalchemy.orm import Mapped, mapped_column, relationship if TYPE_CHECKING: @@ -15,13 +14,6 @@ class BaseAsset(BaseModel): __abstract__ = True id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True) - created_at: Mapped[datetime] = mapped_column( - DateTime(timezone=True), server_default=func.now() - ) - updated_at: Mapped[datetime] = mapped_column( - DateTime(timezone=True), server_default=func.now(), onupdate=func.now() - ) - file_name: Mapped[str] = mapped_column(String(length=450)) file_name_no_tags: Mapped[str] = mapped_column(String(length=450)) file_name_no_ext: Mapped[str] = mapped_column(String(length=450)) diff --git a/backend/models/base.py b/backend/models/base.py index f56251667..286af85cf 100644 --- a/backend/models/base.py +++ b/backend/models/base.py @@ -1,4 +1,13 @@ -from sqlalchemy.orm import DeclarativeBase +from datetime import datetime +from sqlalchemy import DateTime, func +from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column -class BaseModel(DeclarativeBase): ... + +class BaseModel(DeclarativeBase): + created_at: Mapped[datetime] = mapped_column( + DateTime(timezone=True), server_default=func.now() + ) + updated_at: Mapped[datetime] = mapped_column( + DateTime(timezone=True), server_default=func.now(), onupdate=func.now() + ) diff --git a/backend/models/rom.py b/backend/models/rom.py index 27218a9fe..745d16161 100644 --- a/backend/models/rom.py +++ b/backend/models/rom.py @@ -186,7 +186,7 @@ class RomNote(BaseModel): ) id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True) - last_edited_at: Mapped[datetime] = mapped_column( + updated_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), server_default=func.now() ) raw_markdown: Mapped[str] = mapped_column(Text, default="") diff --git a/frontend/assets/scrappers/sgdb.svg b/frontend/assets/scrappers/sgdb.svg new file mode 100644 index 000000000..b1084e1a4 --- /dev/null +++ b/frontend/assets/scrappers/sgdb.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/__generated__/index.ts b/frontend/src/__generated__/index.ts index 68dd6dcc6..b4c5aafa9 100644 --- a/frontend/src/__generated__/index.ts +++ b/frontend/src/__generated__/index.ts @@ -32,6 +32,7 @@ export type { RomSchema } from "./models/RomSchema"; export type { SaveSchema } from "./models/SaveSchema"; export type { SchedulerDict } from "./models/SchedulerDict"; export type { ScreenshotSchema } from "./models/ScreenshotSchema"; +export type { SearchCoverSchema } from "./models/SearchCoverSchema"; export type { SearchRomSchema } from "./models/SearchRomSchema"; export type { StateSchema } from "./models/StateSchema"; export type { StatsReturn } from "./models/StatsReturn"; diff --git a/frontend/src/__generated__/models/DetailedRomSchema.ts b/frontend/src/__generated__/models/DetailedRomSchema.ts index 206c4fa1f..10ad1628c 100644 --- a/frontend/src/__generated__/models/DetailedRomSchema.ts +++ b/frontend/src/__generated__/models/DetailedRomSchema.ts @@ -48,6 +48,8 @@ export type DetailedRomSchema = { multi: boolean; files: Array; full_path: string; + created_at: string; + updated_at: string; merged_screenshots: Array; sibling_roms?: Array; user_saves?: Array; diff --git a/frontend/src/__generated__/models/FirmwareSchema.ts b/frontend/src/__generated__/models/FirmwareSchema.ts index 7d58b577d..6f3b91ea5 100644 --- a/frontend/src/__generated__/models/FirmwareSchema.ts +++ b/frontend/src/__generated__/models/FirmwareSchema.ts @@ -16,4 +16,6 @@ export type FirmwareSchema = { crc_hash: string; md5_hash: string; sha1_hash: string; + created_at: string; + updated_at: string; }; diff --git a/frontend/src/__generated__/models/HeartbeatResponse.ts b/frontend/src/__generated__/models/HeartbeatResponse.ts index 01a3e2c49..15f70be8b 100644 --- a/frontend/src/__generated__/models/HeartbeatResponse.ts +++ b/frontend/src/__generated__/models/HeartbeatResponse.ts @@ -13,5 +13,6 @@ export type HeartbeatResponse = { SCHEDULER: SchedulerDict; ANY_SOURCE_ENABLED: boolean; METADATA_SOURCES: MetadataSourcesDict; + STEAMGRIDDB_ENABLED: boolean; FS_PLATFORMS: Array; }; diff --git a/frontend/src/__generated__/models/PlatformSchema.ts b/frontend/src/__generated__/models/PlatformSchema.ts index 3a0358eb1..9edc5c46d 100644 --- a/frontend/src/__generated__/models/PlatformSchema.ts +++ b/frontend/src/__generated__/models/PlatformSchema.ts @@ -16,4 +16,6 @@ export type PlatformSchema = { moby_id?: number | null; logo_path?: string | null; firmware?: Array; + created_at: string; + updated_at: string; }; diff --git a/frontend/src/__generated__/models/RomNoteSchema.ts b/frontend/src/__generated__/models/RomNoteSchema.ts index 3dfb826a9..2c5b4d12e 100644 --- a/frontend/src/__generated__/models/RomNoteSchema.ts +++ b/frontend/src/__generated__/models/RomNoteSchema.ts @@ -7,7 +7,7 @@ export type RomNoteSchema = { id: number; user_id: number; rom_id: number; - last_edited_at: string; + updated_at: string; raw_markdown: string; is_public: boolean; user__username: string; diff --git a/frontend/src/__generated__/models/RomSchema.ts b/frontend/src/__generated__/models/RomSchema.ts index cf5dea9af..80f8b6d3a 100644 --- a/frontend/src/__generated__/models/RomSchema.ts +++ b/frontend/src/__generated__/models/RomSchema.ts @@ -43,5 +43,7 @@ export type RomSchema = { multi: boolean; files: Array; full_path: string; + created_at: string; + updated_at: string; readonly sort_comparator: string; }; diff --git a/frontend/src/__generated__/models/SearchCoverSchema.ts b/frontend/src/__generated__/models/SearchCoverSchema.ts new file mode 100644 index 000000000..7915dcb20 --- /dev/null +++ b/frontend/src/__generated__/models/SearchCoverSchema.ts @@ -0,0 +1,9 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ + +export type SearchCoverSchema = { + name: string; + resources: Array; +}; diff --git a/frontend/src/__generated__/models/UserSchema.ts b/frontend/src/__generated__/models/UserSchema.ts index 2a3e550c3..4117c59fa 100644 --- a/frontend/src/__generated__/models/UserSchema.ts +++ b/frontend/src/__generated__/models/UserSchema.ts @@ -14,4 +14,6 @@ export type UserSchema = { avatar_path: string; last_login: string | null; last_active: string | null; + created_at: string; + updated_at: string; }; diff --git a/frontend/src/components/Administration/Users/Dialog/DeleteUser.vue b/frontend/src/components/Administration/Users/Dialog/DeleteUser.vue index d05a634b9..e49fc418d 100644 --- a/frontend/src/components/Administration/Users/Dialog/DeleteUser.vue +++ b/frontend/src/components/Administration/Users/Dialog/DeleteUser.vue @@ -66,7 +66,7 @@ function closeDialog() { diff --git a/frontend/src/components/Administration/Users/Dialog/EditUser.vue b/frontend/src/components/Administration/Users/Dialog/EditUser.vue index b8f149581..f9868a59f 100644 --- a/frontend/src/components/Administration/Users/Dialog/EditUser.vue +++ b/frontend/src/components/Administration/Users/Dialog/EditUser.vue @@ -133,7 +133,7 @@ function closeDialog() { imagePreviewUrl ? imagePreviewUrl : user.avatar_path - ? `/assets/romm/assets/${user.avatar_path}` + ? `/assets/romm/assets/${user.avatar_path}?ts=${user.updated_at}` : defaultAvatarPath " > diff --git a/frontend/src/components/Administration/Users/Table.vue b/frontend/src/components/Administration/Users/Table.vue index 922caa732..9b9ea1bfd 100644 --- a/frontend/src/components/Administration/Users/Table.vue +++ b/frontend/src/components/Administration/Users/Table.vue @@ -127,7 +127,7 @@ onMounted(() => { diff --git a/frontend/src/components/Details/BackgroundHeader.vue b/frontend/src/components/Details/BackgroundHeader.vue index 8bc798e0c..53a289c9c 100644 --- a/frontend/src/components/Details/BackgroundHeader.vue +++ b/frontend/src/components/Details/BackgroundHeader.vue @@ -11,16 +11,16 @@ const theme = useTheme(); diff --git a/frontend/src/components/common/Game/AdminMenu.vue b/frontend/src/components/common/Game/AdminMenu.vue index c5b4c7701..2d4646beb 100644 --- a/frontend/src/components/common/Game/AdminMenu.vue +++ b/frontend/src/components/common/Game/AdminMenu.vue @@ -11,38 +11,46 @@ const heartbeat = storeHeartbeat();