mirror of
https://github.com/rommapp/romm.git
synced 2026-06-28 14:56:01 +00:00
start pre-4.8 cleanup
This commit is contained in:
@@ -3,9 +3,9 @@ from typing import NotRequired, TypedDict
|
||||
|
||||
class TokenResponse(TypedDict):
|
||||
access_token: str
|
||||
refresh_token: NotRequired[str]
|
||||
token_type: str
|
||||
expires: int
|
||||
refresh_token: NotRequired[str]
|
||||
refresh_expires: NotRequired[int]
|
||||
|
||||
|
||||
|
||||
@@ -315,60 +315,6 @@ async def get_task_by_id(request: Request, task_id: str) -> TaskStatusResponse:
|
||||
return _build_task_status_response(job)
|
||||
|
||||
|
||||
@protected_route(router.post, "/run", [Scope.TASKS_RUN])
|
||||
async def run_all_tasks(request: Request) -> list[TaskExecutionResponse]:
|
||||
"""Run all runnable tasks endpoint
|
||||
|
||||
Args:
|
||||
request (Request): FastAPI Request object
|
||||
Returns:
|
||||
TaskExecutionResponse: Task execution response with details
|
||||
"""
|
||||
# Filter only runnable tasks
|
||||
runnable_tasks = {
|
||||
task["name"]: task["task"]
|
||||
for task in manual_tasks + scheduled_tasks
|
||||
if task["task"].enabled and task["task"].manual_run
|
||||
}
|
||||
|
||||
if not runnable_tasks:
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail="No runnable tasks available to run",
|
||||
)
|
||||
|
||||
jobs = [
|
||||
(
|
||||
task_name,
|
||||
low_prio_queue.enqueue(
|
||||
task_instance.run,
|
||||
job_timeout=TASK_TIMEOUT,
|
||||
result_ttl=TASK_RESULT_TTL,
|
||||
meta={
|
||||
"task_name": task_instance.title,
|
||||
"task_type": task_instance.task_type.value,
|
||||
},
|
||||
),
|
||||
)
|
||||
for task_name, task_instance in runnable_tasks.items()
|
||||
]
|
||||
|
||||
return [
|
||||
TaskExecutionResponse(
|
||||
task_name=task_name,
|
||||
task_id=job.get_id(),
|
||||
status=job.get_status() or JobStatus.QUEUED,
|
||||
created_at=(
|
||||
job.created_at.isoformat()
|
||||
if job.created_at
|
||||
else datetime.now(timezone.utc).isoformat()
|
||||
),
|
||||
enqueued_at=job.enqueued_at.isoformat() if job.enqueued_at else None,
|
||||
)
|
||||
for (task_name, job) in jobs
|
||||
]
|
||||
|
||||
|
||||
TASK_KWARGS = Body(default=None)
|
||||
|
||||
|
||||
|
||||
@@ -141,7 +141,7 @@ def create_invite_link(
|
||||
)
|
||||
|
||||
if expiration is not None and expiration <= 0:
|
||||
msg = "expiration must be a positive integer"
|
||||
msg = "Invite link expiration must be a positive integer"
|
||||
log.error(msg)
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
|
||||
@@ -333,6 +333,7 @@ class OAuthHandler:
|
||||
|
||||
if not user.enabled:
|
||||
raise UserDisabledException
|
||||
|
||||
return user, payload.claims
|
||||
|
||||
async def get_current_active_user_from_bearer_token(self, token: str):
|
||||
@@ -415,9 +416,8 @@ class OpenIDHandler:
|
||||
)
|
||||
|
||||
role = Role.VIEWER
|
||||
claims_provided = False
|
||||
if OIDC_CLAIM_ROLES and OIDC_CLAIM_ROLES in userinfo:
|
||||
claims_provided = True
|
||||
claims_provided = OIDC_CLAIM_ROLES and OIDC_CLAIM_ROLES in userinfo
|
||||
if claims_provided:
|
||||
roles = userinfo[OIDC_CLAIM_ROLES] or []
|
||||
if OIDC_ROLE_ADMIN and OIDC_ROLE_ADMIN in roles:
|
||||
role = Role.ADMIN
|
||||
|
||||
@@ -69,6 +69,7 @@ class DBClientTokensHandler(DBBaseHandler):
|
||||
stmt = delete(ClientToken).where(ClientToken.id == token_id)
|
||||
if user_id is not None:
|
||||
stmt = stmt.where(ClientToken.user_id == user_id)
|
||||
|
||||
result = session.execute(stmt.execution_options(synchronize_session="evaluate"))
|
||||
return result.rowcount
|
||||
|
||||
@@ -82,11 +83,13 @@ class DBClientTokensHandler(DBBaseHandler):
|
||||
token = session.get(ClientToken, token_id)
|
||||
if token is None:
|
||||
return
|
||||
|
||||
if (
|
||||
token.last_used_at
|
||||
and (now - to_utc(token.last_used_at)) < LAST_USED_DEBOUNCE
|
||||
):
|
||||
return
|
||||
|
||||
session.execute(
|
||||
update(ClientToken)
|
||||
.where(ClientToken.id == token_id)
|
||||
@@ -108,11 +111,14 @@ class DBClientTokensHandler(DBBaseHandler):
|
||||
.values(hashed_token=new_hash, last_used_at=None)
|
||||
.execution_options(synchronize_session="evaluate")
|
||||
)
|
||||
|
||||
if user_id is not None:
|
||||
stmt = stmt.where(ClientToken.user_id == user_id)
|
||||
|
||||
result = session.execute(stmt)
|
||||
if result.rowcount == 0:
|
||||
return None
|
||||
|
||||
return session.get(ClientToken, token_id)
|
||||
|
||||
@begin_session
|
||||
@@ -140,4 +146,5 @@ class DBClientTokensHandler(DBBaseHandler):
|
||||
stmt = select(ClientToken).where(ClientToken.id == token_id)
|
||||
if user_id is not None:
|
||||
stmt = stmt.where(ClientToken.user_id == user_id)
|
||||
|
||||
return session.scalar(stmt)
|
||||
|
||||
@@ -97,7 +97,7 @@ EJS_SUPPORTED_PLATFORMS = [
|
||||
UPS.WONDERSWAN_COLOR,
|
||||
]
|
||||
|
||||
OTHER_SUPPORTED_PLATFORMS = [
|
||||
RUFFLE_SUPPORTED_PLATFORMS = [
|
||||
UPS.BROWSER,
|
||||
]
|
||||
|
||||
@@ -309,7 +309,7 @@ class DBRomsHandler(DBBaseHandler):
|
||||
"""Filter based on whether the rom is playable on supported platforms."""
|
||||
predicate = or_(
|
||||
Platform.slug.in_(EJS_SUPPORTED_PLATFORMS),
|
||||
Platform.slug.in_(OTHER_SUPPORTED_PLATFORMS),
|
||||
Platform.slug.in_(RUFFLE_SUPPORTED_PLATFORMS),
|
||||
)
|
||||
if not value:
|
||||
predicate = not_(predicate)
|
||||
|
||||
@@ -5,6 +5,7 @@ from sqlalchemy.orm import Session
|
||||
from sqlalchemy.orm.attributes import InstrumentedAttribute
|
||||
|
||||
from decorators.database import begin_session
|
||||
from endpoints.responses.stats import MetadataCoverageItem, RegionBreakdownItem
|
||||
from models.assets import Save, Screenshot, State
|
||||
from models.rom import Rom, RomFile
|
||||
|
||||
@@ -101,7 +102,7 @@ class DBStatsHandler(DBBaseHandler):
|
||||
def get_metadata_coverage_by_platform(
|
||||
self,
|
||||
session: Session = None, # type: ignore
|
||||
) -> dict[int, list[dict]]:
|
||||
) -> dict[int, list[MetadataCoverageItem]]:
|
||||
"""Get the count of ROMs matched per metadata source, grouped by platform."""
|
||||
rows = session.execute(
|
||||
select(
|
||||
@@ -115,20 +116,21 @@ class DBStatsHandler(DBBaseHandler):
|
||||
.group_by(Rom.platform_id)
|
||||
).all()
|
||||
|
||||
result: dict[int, list[dict]] = {}
|
||||
result: dict[int, list[MetadataCoverageItem]] = {}
|
||||
for row in rows:
|
||||
result[row.platform_id] = [
|
||||
{"source": key, "matched": getattr(row, key)}
|
||||
MetadataCoverageItem(source=key, matched=getattr(row, key))
|
||||
for key in _METADATA_SOURCE_COLUMNS
|
||||
if getattr(row, key) > 0
|
||||
]
|
||||
|
||||
return result
|
||||
|
||||
@begin_session
|
||||
def get_region_breakdown_by_platform(
|
||||
self,
|
||||
session: Session = None, # type: ignore
|
||||
) -> dict[int, list[dict]]:
|
||||
) -> dict[int, list[RegionBreakdownItem]]:
|
||||
"""Get the count of ROMs per region, grouped by platform."""
|
||||
rows = session.execute(
|
||||
select(Rom.platform_id, Rom.regions).where(Rom.regions.is_not(None))
|
||||
|
||||
@@ -95,7 +95,7 @@ class FSResourcesHandler(FSHandler):
|
||||
else:
|
||||
# Handle HTTP URLs
|
||||
# Validate URL to prevent SSRF attacks
|
||||
validate_url_for_http_request(url_cover, "Cover URL")
|
||||
validate_url_for_http_request(url_cover, "url_cover")
|
||||
|
||||
httpx_client = ctx_httpx_client.get()
|
||||
try:
|
||||
@@ -264,7 +264,7 @@ class FSResourcesHandler(FSHandler):
|
||||
else:
|
||||
# Handle HTTP URLs
|
||||
# Validate URL to prevent SSRF attacks
|
||||
validate_url_for_http_request(url_screenhot, "Screenshot URL")
|
||||
validate_url_for_http_request(url_screenhot, "url_screenshot")
|
||||
|
||||
httpx_client = ctx_httpx_client.get()
|
||||
try:
|
||||
@@ -380,7 +380,7 @@ class FSResourcesHandler(FSHandler):
|
||||
else:
|
||||
# Handle HTTP URL
|
||||
# Validate URL to prevent SSRF attacks
|
||||
validate_url_for_http_request(url_manual, "Manual URL")
|
||||
validate_url_for_http_request(url_manual, "url_manual")
|
||||
|
||||
httpx_client = ctx_httpx_client.get()
|
||||
try:
|
||||
@@ -441,7 +441,7 @@ class FSResourcesHandler(FSHandler):
|
||||
# Retroachievements
|
||||
async def store_ra_badge(self, url: str, path: str) -> None:
|
||||
# Validate URL to prevent SSRF attacks
|
||||
validate_url_for_http_request(url, "Badge URL")
|
||||
validate_url_for_http_request(url, "url_badge")
|
||||
|
||||
httpx_client = ctx_httpx_client.get()
|
||||
directory, filename = os.path.split(path)
|
||||
@@ -484,7 +484,7 @@ class FSResourcesHandler(FSHandler):
|
||||
) -> str:
|
||||
return os.path.join("roms", str(platform_id), str(rom_id), media_type.value)
|
||||
|
||||
async def store_media_file(self, url: str, dest_path: str) -> None:
|
||||
async def store_media_file(self, url_media: str, dest_path: str) -> None:
|
||||
httpx_client = ctx_httpx_client.get()
|
||||
directory, filename = os.path.split(dest_path)
|
||||
|
||||
@@ -496,22 +496,24 @@ class FSResourcesHandler(FSHandler):
|
||||
await self.make_directory(directory)
|
||||
|
||||
# Handle file:// URLs for gamelist.xml
|
||||
if url.startswith("file://"):
|
||||
if url_media.startswith("file://"):
|
||||
try:
|
||||
file_path = AnyioPath(url[7:]) # Remove "file://" prefix
|
||||
file_path = AnyioPath(url_media[7:]) # Remove "file://" prefix
|
||||
if await file_path.exists():
|
||||
await self.copy_file(Path(str(file_path)), dest_path)
|
||||
except Exception as exc:
|
||||
log.error(f"Unable to copy media file {url}: {str(exc)}")
|
||||
log.error(f"Unable to copy media file {url_media}: {str(exc)}")
|
||||
return None
|
||||
else:
|
||||
# Handle HTTP URLs
|
||||
# Validate URL to prevent SSRF attacks
|
||||
validate_url_for_http_request(url, "Media URL")
|
||||
validate_url_for_http_request(url_media, "url_media")
|
||||
|
||||
httpx_client = ctx_httpx_client.get()
|
||||
try:
|
||||
async with httpx_client.stream("GET", url, timeout=120) as response:
|
||||
async with httpx_client.stream(
|
||||
"GET", url_media, timeout=120
|
||||
) as response:
|
||||
if response.status_code == status.HTTP_200_OK:
|
||||
async with await self.write_file_streamed(
|
||||
path=directory, filename=filename
|
||||
@@ -519,7 +521,7 @@ class FSResourcesHandler(FSHandler):
|
||||
async for chunk in response.aiter_raw():
|
||||
await f.write(chunk)
|
||||
except httpx.TransportError as exc:
|
||||
log.error(f"Unable to fetch media file at {url}: {str(exc)}")
|
||||
log.error(f"Unable to fetch media file at {url_media}: {str(exc)}")
|
||||
return None
|
||||
|
||||
async def remove_media_resources_path(
|
||||
|
||||
7
frontend/src/__generated__/index.ts
generated
7
frontend/src/__generated__/index.ts
generated
@@ -34,6 +34,12 @@ export type { BulkOperationResponse } from './models/BulkOperationResponse';
|
||||
export type { CleanupStats } from './models/CleanupStats';
|
||||
export type { CleanupTaskMeta } from './models/CleanupTaskMeta';
|
||||
export type { CleanupTaskStatusResponse } from './models/CleanupTaskStatusResponse';
|
||||
export type { ClientTokenAdminSchema } from './models/ClientTokenAdminSchema';
|
||||
export type { ClientTokenCreatePayload } from './models/ClientTokenCreatePayload';
|
||||
export type { ClientTokenCreateSchema } from './models/ClientTokenCreateSchema';
|
||||
export type { ClientTokenExchangePayload } from './models/ClientTokenExchangePayload';
|
||||
export type { ClientTokenPairSchema } from './models/ClientTokenPairSchema';
|
||||
export type { ClientTokenSchema } from './models/ClientTokenSchema';
|
||||
export type { CollectionSchema } from './models/CollectionSchema';
|
||||
export type { ConfigResponse } from './models/ConfigResponse';
|
||||
export type { ConversionStats } from './models/ConversionStats';
|
||||
@@ -92,6 +98,7 @@ export type { RomMetadataSchema } from './models/RomMetadataSchema';
|
||||
export type { RomMobyMetadata } from './models/RomMobyMetadata';
|
||||
export type { RomRAMetadata } from './models/RomRAMetadata';
|
||||
export type { RomSSMetadata } from './models/RomSSMetadata';
|
||||
export type { RomUserData } from './models/RomUserData';
|
||||
export type { RomUserSchema } from './models/RomUserSchema';
|
||||
export type { RomUserStatus } from './models/RomUserStatus';
|
||||
export type { RomUserUpdatePayload } from './models/RomUserUpdatePayload';
|
||||
|
||||
15
frontend/src/__generated__/models/ClientTokenAdminSchema.ts
generated
Normal file
15
frontend/src/__generated__/models/ClientTokenAdminSchema.ts
generated
Normal file
@@ -0,0 +1,15 @@
|
||||
/* generated using openapi-typescript-codegen -- do not edit */
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export type ClientTokenAdminSchema = {
|
||||
id: number;
|
||||
name: string;
|
||||
scopes: Array<string>;
|
||||
expires_at: (string | null);
|
||||
last_used_at: (string | null);
|
||||
created_at: string;
|
||||
user_id: number;
|
||||
username: string;
|
||||
};
|
||||
|
||||
10
frontend/src/__generated__/models/ClientTokenCreatePayload.ts
generated
Normal file
10
frontend/src/__generated__/models/ClientTokenCreatePayload.ts
generated
Normal file
@@ -0,0 +1,10 @@
|
||||
/* generated using openapi-typescript-codegen -- do not edit */
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export type ClientTokenCreatePayload = {
|
||||
name: string;
|
||||
scopes: Array<string>;
|
||||
expires_in?: (string | null);
|
||||
};
|
||||
|
||||
15
frontend/src/__generated__/models/ClientTokenCreateSchema.ts
generated
Normal file
15
frontend/src/__generated__/models/ClientTokenCreateSchema.ts
generated
Normal file
@@ -0,0 +1,15 @@
|
||||
/* generated using openapi-typescript-codegen -- do not edit */
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export type ClientTokenCreateSchema = {
|
||||
id: number;
|
||||
name: string;
|
||||
scopes: Array<string>;
|
||||
expires_at: (string | null);
|
||||
last_used_at: (string | null);
|
||||
created_at: string;
|
||||
user_id: number;
|
||||
raw_token: string;
|
||||
};
|
||||
|
||||
8
frontend/src/__generated__/models/ClientTokenExchangePayload.ts
generated
Normal file
8
frontend/src/__generated__/models/ClientTokenExchangePayload.ts
generated
Normal file
@@ -0,0 +1,8 @@
|
||||
/* generated using openapi-typescript-codegen -- do not edit */
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export type ClientTokenExchangePayload = {
|
||||
code: string;
|
||||
};
|
||||
|
||||
9
frontend/src/__generated__/models/ClientTokenPairSchema.ts
generated
Normal file
9
frontend/src/__generated__/models/ClientTokenPairSchema.ts
generated
Normal file
@@ -0,0 +1,9 @@
|
||||
/* generated using openapi-typescript-codegen -- do not edit */
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export type ClientTokenPairSchema = {
|
||||
code: string;
|
||||
expires_in: number;
|
||||
};
|
||||
|
||||
14
frontend/src/__generated__/models/ClientTokenSchema.ts
generated
Normal file
14
frontend/src/__generated__/models/ClientTokenSchema.ts
generated
Normal file
@@ -0,0 +1,14 @@
|
||||
/* generated using openapi-typescript-codegen -- do not edit */
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export type ClientTokenSchema = {
|
||||
id: number;
|
||||
name: string;
|
||||
scopes: Array<string>;
|
||||
expires_at: (string | null);
|
||||
last_used_at: (string | null);
|
||||
created_at: string;
|
||||
user_id: number;
|
||||
};
|
||||
|
||||
@@ -9,6 +9,7 @@ export type RAUserGameProgression = {
|
||||
num_awarded: (number | null);
|
||||
num_awarded_hardcore: (number | null);
|
||||
most_recent_awarded_date?: (string | null);
|
||||
highest_award_kind?: (string | null);
|
||||
earned_achievements: Array<EarnedAchievement>;
|
||||
};
|
||||
|
||||
|
||||
40
frontend/src/__generated__/models/RomUserData.ts
generated
Normal file
40
frontend/src/__generated__/models/RomUserData.ts
generated
Normal file
@@ -0,0 +1,40 @@
|
||||
/* generated using openapi-typescript-codegen -- do not edit */
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
import type { RomUserStatus } from './RomUserStatus';
|
||||
export type RomUserData = {
|
||||
/**
|
||||
* Whether this rom is the main sibling.
|
||||
*/
|
||||
is_main_sibling?: (boolean | null);
|
||||
/**
|
||||
* Whether this rom is in the backlog.
|
||||
*/
|
||||
backlogged?: (boolean | null);
|
||||
/**
|
||||
* Whether this rom is currently being played.
|
||||
*/
|
||||
now_playing?: (boolean | null);
|
||||
/**
|
||||
* Whether this rom is hidden.
|
||||
*/
|
||||
hidden?: (boolean | null);
|
||||
/**
|
||||
* User rating for this rom (0-10).
|
||||
*/
|
||||
rating?: (number | null);
|
||||
/**
|
||||
* User difficulty rating for this rom (0-10).
|
||||
*/
|
||||
difficulty?: (number | null);
|
||||
/**
|
||||
* User completion percentage for this rom (0-100).
|
||||
*/
|
||||
completion?: (number | null);
|
||||
/**
|
||||
* User play status for this rom.
|
||||
*/
|
||||
status?: (RomUserStatus | null);
|
||||
};
|
||||
|
||||
@@ -2,9 +2,19 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
import type { RomUserData } from './RomUserData';
|
||||
export type RomUserUpdatePayload = {
|
||||
data?: Record<string, any>;
|
||||
/**
|
||||
* Partial rom user data to update. Only provided fields will be updated.
|
||||
*/
|
||||
data?: RomUserData;
|
||||
/**
|
||||
* Set last played timestamp to now.
|
||||
*/
|
||||
update_last_played?: boolean;
|
||||
/**
|
||||
* Clear the last played timestamp.
|
||||
*/
|
||||
remove_last_played?: boolean;
|
||||
};
|
||||
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
/* eslint-disable */
|
||||
export type TokenResponse = {
|
||||
access_token: string;
|
||||
refresh_token?: string;
|
||||
token_type: string;
|
||||
expires: number;
|
||||
refresh_token?: string;
|
||||
refresh_expires?: number;
|
||||
};
|
||||
|
||||
|
||||
@@ -10,10 +10,6 @@ async function getTaskById(taskId: string) {
|
||||
return api.get<TaskStatusResponse>(`/tasks/${taskId}`);
|
||||
}
|
||||
|
||||
async function runAllTasks() {
|
||||
return api.post<TaskExecutionResponse[]>("/tasks/run");
|
||||
}
|
||||
|
||||
async function runTask(taskName: string, body?: Record<string, unknown>) {
|
||||
return api.post<TaskExecutionResponse>(`/tasks/run/${taskName}`, body);
|
||||
}
|
||||
@@ -25,7 +21,6 @@ async function getTaskStatus() {
|
||||
export default {
|
||||
getTasks,
|
||||
getTaskById,
|
||||
runAllTasks,
|
||||
runTask,
|
||||
getTaskStatus,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user