Files
kubernetes/dev/zos-web/backend/main.py
T
2026-05-31 16:07:30 +02:00

126 lines
3.3 KiB
Python

from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import StreamingResponse
from pydantic import BaseModel
from contextlib import asynccontextmanager
from tn3270_service import TN3270Session
import asyncio
import logging
import json
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
session = None
@asynccontextmanager
async def lifespan(app):
global session
session = TN3270Session()
try:
session.connect()
logger.info("TN3270 session ready")
except Exception as e:
logger.error(f"Failed to connect: {e}")
yield
if session:
session.disconnect()
app = FastAPI(title="z/OS Web API", version="1.0.0", lifespan=lifespan)
app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"])
class CommandRequest(BaseModel):
text: str
def check():
if not session or not session.connected:
raise HTTPException(503, "Not connected to z/OS")
@app.get("/health")
def health():
return {"status": "ok", "connected": session.connected if session else False}
@app.get("/screen")
def get_screen():
check()
return {"screen": session.get_screen()}
@app.post("/command")
def send_command(req: CommandRequest):
check()
return {"screen": session.send_command(req.text)}
@app.post("/enter")
def send_enter():
check()
return {"screen": session.send_enter()}
@app.post("/pf/{key}")
def send_pf(key: int):
check()
if not 1 <= key <= 24:
raise HTTPException(400, "PF key must be 1-24")
return {"screen": session.send_pf(key)}
@app.post("/tab")
def send_tab():
check()
return {"screen": session.send_tab()}
class KeyRequest(BaseModel):
key: str
class CharRequest(BaseModel):
char: str
@app.post("/key")
def send_key(req: KeyRequest):
check()
# Map key names to s3270 commands
key_map = {
"Tab": "Tab()",
"BackTab": "BackTab()",
"Backspace": "BackSpace()",
"Delete": "Delete()",
"Home": "Home()",
"End": "FieldEnd()",
"Insert": "Insert()",
"Clear": "Clear()",
"Attn": "Attn()",
"PA1": "PA(1)",
"PA2": "PA(2)",
}
s3270_key = key_map.get(req.key, req.key)
return {"screen": session.send_key(s3270_key)}
@app.post("/char")
def send_char(req: CharRequest):
check()
return {"screen": session.send_char(req.char)}
@app.get("/stream/screen")
async def stream_screen():
async def event_generator():
last_screen = ""
while True:
try:
if session and session.connected:
if not session.lock.locked():
screen = session.get_screen()
if screen != last_screen:
data = json.dumps({"screen": screen})
yield f"data: {data}\n\n"
last_screen = screen
await asyncio.sleep(0.5)
except Exception as e:
logger.error(f"Stream error: {e}")
break
return StreamingResponse(
event_generator(),
media_type="text/event-stream",
headers={
"Cache-Control": "no-cache",
"X-Accel-Buffering": "no",
}
)