Working on socket communications
This commit is contained in:
@@ -1,12 +1,13 @@
|
||||
#!/usr/bin/env python
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Optional, Annotated
|
||||
from typing import Optional, Annotated, Dict
|
||||
from datetime import timedelta, datetime, timezone
|
||||
import re
|
||||
import asyncio
|
||||
import logging
|
||||
import subprocess
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
from fastapi import FastAPI, Depends, HTTPException, status, Request, Form, Query, BackgroundTasks, WebSocket
|
||||
@@ -23,6 +24,7 @@ from sqlalchemy.exc import OperationalError
|
||||
from alembic.config import Config as AlembicConfig
|
||||
from alembic import command
|
||||
from websockets.asyncio.client import connect
|
||||
from pydantic import BaseModel
|
||||
|
||||
try:
|
||||
# Try relative imports first (when run as module)
|
||||
@@ -52,6 +54,10 @@ engine = create_engine(
|
||||
)
|
||||
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
||||
|
||||
class GameData(BaseModel):
|
||||
filename: str
|
||||
url: str
|
||||
|
||||
# Enable WAL mode for better concurrency during startup
|
||||
try:
|
||||
with engine.connect() as conn:
|
||||
@@ -627,31 +633,20 @@ async def toggle_favorite(
|
||||
db.commit()
|
||||
return {"message": f"Game {action} from favorites"}
|
||||
|
||||
@app.post("/run/{game_id}")
|
||||
async def run_game(request: Request, game_id: int, db: Session = Depends(get_db), current_user: User_table = Depends(require_auth)):
|
||||
logging.info(f"Run request for game ID {game_id} from user {current_user.username}")
|
||||
|
||||
@app.post("/run")
|
||||
async def run_game(
|
||||
request: Request,
|
||||
game_data: GameData,
|
||||
current_user: User_table = Depends(require_auth)
|
||||
):
|
||||
breakpoint()
|
||||
logging.info(f"Run request for game ID {game_data.filename} from user {current_user.username}")
|
||||
game_json = json.dumps(game_data.dict())
|
||||
if current_user.role == UserRole.DEMO.value:
|
||||
raise HTTPException(status_code=403, detail="Demo users cannot download games")
|
||||
|
||||
game = db.get(Game_table, game_id)
|
||||
if not game:
|
||||
raise HTTPException(status_code=404, detail="Game not found")
|
||||
|
||||
if not game.path.exists():
|
||||
raise HTTPException(status_code=404, detail="Game file not found")
|
||||
forwarded_for = request.headers.get("x-forwarded-for")
|
||||
if forwarded_for:
|
||||
ip_address = forwarded_for.split(",")[0].strip()
|
||||
elif real_ip := request.headers.get("x-real-ip"):
|
||||
ip_address = real_ip.strip()
|
||||
elif request.client:
|
||||
ip_address = request.client.host
|
||||
else:
|
||||
ip_address = "unknown"
|
||||
logging.warning("Could not determine client IP address")
|
||||
return HTTPException(status_code=400, detail="Could not determine client IP address")
|
||||
web_socket_port: int = config.websocket_port
|
||||
breakpoint()
|
||||
try:
|
||||
ws = connect(f"ws://{ip_address}:{web_socket_port}")
|
||||
except Exception as e:
|
||||
@@ -659,17 +654,17 @@ async def run_game(request: Request, game_id: int, db: Session = Depends(get_db)
|
||||
raise HTTPException(status_code=400, detail="Could not connect to client WebSocket")
|
||||
try:
|
||||
async with ws as websocket:
|
||||
await websocket.send(f"RUN::{game.path}")
|
||||
await websocket.send(f"RUN::{game_json}")
|
||||
response = await websocket.recv()
|
||||
if response != "OK":
|
||||
logging.error(f"Client returned error: {response}")
|
||||
raise HTTPException(status_code=400, detail=f"Client error: {response}")
|
||||
else:
|
||||
logging.info(f"Sent RUN command for game {game.title} to {ip_address}")
|
||||
logging.info(f"Sent RUN command for game ")
|
||||
except Exception as e:
|
||||
logging.error(f"WebSocket communication error: {e}")
|
||||
raise HTTPException(status_code=400, detail="Error communicating with client WebSocket")
|
||||
return {"web_socket_port": web_socket_port, "game_path": str(game.path)}
|
||||
return {"web_socket_port": web_socket_port, "game_path": str(game_data.filename)}
|
||||
|
||||
|
||||
@app.get("/download/{game_id}")
|
||||
|
||||
@@ -664,7 +664,7 @@
|
||||
alert('Download failed. Please try again.');
|
||||
}
|
||||
}
|
||||
async function runGame(gameId) {
|
||||
async function downloadGameStorage(gameId) {
|
||||
const token = localStorage.getItem('authToken');
|
||||
if (!token) {
|
||||
showLogin();
|
||||
@@ -672,13 +672,62 @@
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`/run/${gameId}`, {
|
||||
method: 'POST',
|
||||
const response = await fetch(`/download/${gameId}`, {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`
|
||||
}
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const blob = await response.blob();
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.style.display = 'none';
|
||||
a.href = url;
|
||||
// Get filename from Content-Disposition header, removing quotes and underscores
|
||||
let filename = response.headers.get('Content-Disposition')?.split('filename=')[1] || 'game.zip';
|
||||
filename = filename.replace(/^["\_]+|["\_]+$/g, ''); // Remove quotes and underscores from start and end
|
||||
a.download = filename;
|
||||
return { url: url, filename: filename };
|
||||
} else if (response.status === 401) {
|
||||
localStorage.removeItem('authToken');
|
||||
showLogin();
|
||||
} else {
|
||||
alert('Download failed. Please try again.');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Download error:', error);
|
||||
alert('Download failed. Please try again.');
|
||||
}
|
||||
}
|
||||
async function runGame(gameId) {
|
||||
const token = localStorage.getItem('authToken');
|
||||
if (!token) {
|
||||
showLogin();
|
||||
return;
|
||||
}
|
||||
const file = await downloadGameStorage(gameId);
|
||||
const response = await fetch(file.url)
|
||||
const arrayBuffer = await response.arrayBuffer();
|
||||
const blob = new Blob([arrayBuffer],
|
||||
{ type: response.headers.get(
|
||||
'content-type') || 'application/octet-stream'
|
||||
}
|
||||
);
|
||||
const _data = new FormData();
|
||||
debugger;
|
||||
_data.append('file', blob, ".zip");
|
||||
try {
|
||||
const response = await fetch(`/run`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
//body: JSON.stringify(file)
|
||||
body: _data
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
alert('Game is starting in your DOSBox environment.');
|
||||
} else if (response.status === 401) {
|
||||
|
||||
Reference in New Issue
Block a user