mirror of
https://github.com/rommapp/romm.git
synced 2026-10-12 02:21:58 +00:00
* feat(permissions): gate emulator streaming behind a group permission Add a streaming permission entity. Read shows streaming and allows joining a session; write allows starting one. Users without read get a disabled streaming config, so every streaming surface hides. Migration 0152 grants read and write to every existing group, so nobody loses access on upgrade. Groups created afterwards start without it. Owner-scoped session controls (save, exit, release) stay ungated so a revoked user can still leave a running game without losing saves. * fix(streaming): pr-ready code-review fixes for the streaming grant Own-only read no longer joins other users' sessions, the config refetches on permissions:changed, the store skips the joinable request when streaming is off, and tests clean up the groups they create. * refactor(streaming): pr-ready simplify pass on the streaming grant Collapse the streaming permission helpers onto assert_can/can_access, return an empty joinable list instead of 403, skip the permission resolve when streaming is off, and add a stream.start action key so the UI hides Stream (but keeps Join) for users without the write grant. * docs(streaming): pr-ready review-polish pass on the streaming grant Trim the 0152 migration docstring to a single summary line, matching 0150 and 0151. * docs(streaming): pr-ready humanizer pass on the streaming grant Plainer wording for the stream.start action key comment and the Permission Groups paragraph, which now says an own-only read shows streaming without the join. * feat(permissions): gate in-browser emulation behind a group permission Adds an emulation entity next to streaming. Read plays in the browser (EmulatorJS, Ruffle, js-dos, PICO-8, EasyRPG) and joins netplay rooms, write hosts them. Migration 0152 now backfills both entities onto every existing group. * fix(streaming): address Greptile review on PR 5185 - Pin the claimed container in the stream player, so a permission refresh that turns streaming off mid-game keeps save, load and disc swap until the session ends. - Empty the Join list when a refreshed config turns streaming off, and discard any joinable request still in flight. - Update the installPermissionsHydration doc to name every layout that calls it. * fix(migrations): renumber play permissions migration to 0153 after master's 0152 Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]> * chore(permissions): drop redundant PermEntity comments Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]> * refactor(streaming): drop the live config refresh on permission changes Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]> --------- Co-authored-by: Georges-Antoine Assi <[email protected]> Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>
79 lines
2.1 KiB
Python
79 lines
2.1 KiB
Python
from typing import Dict, TypedDict
|
|
|
|
from fastapi import Request
|
|
|
|
from decorators.auth import protected_route
|
|
from exceptions.endpoint_exceptions import RomNotFoundInDatabaseException
|
|
from handler.auth.constants import Scope
|
|
from handler.auth.dependencies import assert_rom_visible, can_access, get_permissions
|
|
from handler.database import db_rom_handler
|
|
from handler.netplay_handler import NetplayRoom, netplay_handler
|
|
from models.permission import PermAction, PermEntity
|
|
from utils.router import APIRouter
|
|
|
|
router = APIRouter(
|
|
prefix="/netplay",
|
|
tags=["netplay"],
|
|
)
|
|
|
|
|
|
DEFAULT_MAX_PLAYERS = 4
|
|
|
|
|
|
def _get_owner_player_name(room: NetplayRoom) -> str:
|
|
owner_sid = room.get("owner")
|
|
if not owner_sid:
|
|
return "Unknown"
|
|
|
|
return next(
|
|
(
|
|
p["player_name"]
|
|
for p in room["players"].values()
|
|
if p["socketId"] == owner_sid
|
|
),
|
|
"Unknown",
|
|
)
|
|
|
|
|
|
def _is_room_open(room: NetplayRoom, game_id: int) -> bool:
|
|
if len(room["players"]) >= room["max_players"]:
|
|
return False
|
|
return str(room["game_id"]) == str(game_id)
|
|
|
|
|
|
class RoomsResponse(TypedDict):
|
|
room_name: str
|
|
current: int
|
|
max: int
|
|
player_name: str
|
|
hasPassword: bool
|
|
|
|
|
|
@protected_route(router.get, "/list", [Scope.ROMS_READ])
|
|
async def get_rooms(request: Request, game_id: int) -> Dict[str, RoomsResponse]:
|
|
rom = db_rom_handler.get_rom_visibility(game_id)
|
|
if not rom:
|
|
raise RomNotFoundInDatabaseException(game_id)
|
|
|
|
assert_rom_visible(request, rom)
|
|
|
|
# Empty rather than 403, like the streaming joinable list.
|
|
if not can_access(get_permissions(request), PermEntity.EMULATION, PermAction.READ):
|
|
return {}
|
|
|
|
netplay_rooms = await netplay_handler.get_all()
|
|
|
|
open_rooms: Dict[str, RoomsResponse] = {
|
|
session_id: RoomsResponse(
|
|
room_name=room["room_name"],
|
|
current=len(room["players"]),
|
|
max=room["max_players"],
|
|
player_name=_get_owner_player_name(room),
|
|
hasPassword=bool(room["password"]),
|
|
)
|
|
for session_id, room in netplay_rooms.items()
|
|
if _is_room_open(room, game_id)
|
|
}
|
|
|
|
return open_rooms
|