Просмотр исходного кода

Add calc-only API entrypoint and Docker service

calc_app.py is a thin FastAPI app exposing only /calc/* and /services
(config-only, no Store/CalDAV/service-API deps), serving calc.html at /.
entrypoint.sh gains a calc service branch; compose.yaml adds a calc
service on port 8002.
ZiB 4 месяцев назад
Родитель
Сommit
36c9e42388
4 измененных файлов с 77 добавлено и 9 удалено
  1. 18 0
      docker/README.md
  2. 13 0
      docker/compose.yaml
  3. 21 9
      docker/entrypoint.sh
  4. 25 0
      room_cal/api/calc_app.py

+ 18 - 0
docker/README.md

@@ -7,6 +7,12 @@
 podman-compose -f room_cal/docker/compose.yaml up -d
 ```
 
+### Calc only
+
+```bash
+podman-compose -f room_cal/docker/compose.yaml up -d calc
+```
+
 ### Build
 
 ```bash
@@ -36,6 +42,14 @@ podman run --rm -it -p 8000:8000 -v ./config:/config \
   $IMG_NAME
 ```
 
+### Calc (standalone fee/tax calculator)
+
+```bash
+podman run --rm -it -p 8000:8000 -v ./config:/config $IMG_NAME /entrypoint.sh calc
+```
+
+Serves only the `/calc/*` endpoints and the `calc.html` UI at `/`. No Store, CalDAV, or service-API credentials needed — only the `services` and `global.taxes` sections of `config.json` are read.
+
 ### MCP
 
 ```bash
@@ -71,6 +85,10 @@ Default entrypoint. Variable defaults are baked in; override with environment va
 | `SESSION_SECRET` | random per restart | Set this to a static value to preserve sessions between runs |
 | `DISABLE_PERIODIC` | *(unset)* | Set to `1` to skip credential probes and all periodic syncs (reservations, prices) in the health loop |
 
+### Calc service
+
+Pass `calc` as the first argument to `entrypoint.sh`. Uses the same `API_HOST`, `API_PORT`, and `LOG_LEVEL` variables as the API service. Only `services` and `global.taxes` are read from `config.json` — no store, auth, or service-API config required.
+
 ### MCP service
 
 Pass `mcp` as the first argument to `entrypoint.sh`. Variable defaults are baked in; override with environment variables if needed

+ 13 - 0
docker/compose.yaml

@@ -17,6 +17,19 @@ services:
       retries: 3
       start_period: 60s
 
+  calc:
+    build:
+      context: ..
+      dockerfile: docker/Dockerfile
+    command: ["/entrypoint.sh", "calc"]
+    ports:
+      - "${CALC_PORT:-8002}:${CALC_PORT:-8002}"
+    volumes:
+      - ./config:/config
+    environment:
+      - TZ=${TZ:-}
+      - API_PORT=${CALC_PORT:-8002}
+
   mcp:
     build:
       context: ..

+ 21 - 9
docker/entrypoint.sh

@@ -1,16 +1,17 @@
 #!/bin/sh
 set -e
 
+# Include app binaries in our path
 export PATH="/app/.venv/bin:$PATH"
+
+# Shared config
 : "${ROOM_CAL_DATA_HOME:=/config}"
 export ROOM_CAL_DATA_HOME
-
 if [ -z "$TZ" ] && [ -f "$ROOM_CAL_DATA_HOME/config.json" ]; then
   TZ=$(python3 -c "import json,sys; print(json.load(open('$ROOM_CAL_DATA_HOME/config.json')).get('timezone',''))" 2>/dev/null || true)
 fi
 : "${TZ:=UTC}"
 export TZ
-
 if [ -z "$ROOM_CAL_API_URL" ] && [ -f "$ROOM_CAL_DATA_HOME/config.json" ]; then
   ROOM_CAL_API_URL=$(python3 -c "import json,sys; print(json.load(open('$ROOM_CAL_DATA_HOME/config.json')).get('api_url',''))" 2>/dev/null || true)
 fi
@@ -18,26 +19,37 @@ export ROOM_CAL_API_URL
 
 SERVICE="${1:-api}"
 
+# MCP
 if [ "$SERVICE" = "mcp" ]; then
   : "${FASTMCP_HOST:=0.0.0.0}"
   : "${FASTMCP_PORT:=8001}"
   : "${FASTMCP_LOG_LEVEL:=INFO}"
   : "${FASTMCP_ENABLE_RICH_LOGGING:=false}"
   : "${FASTMCP_ENABLE_RICH_TRACEBACKS:=false}"
-
   export FASTMCP_TRANSPORT=http
   export FASTMCP_HOST FASTMCP_PORT FASTMCP_LOG_LEVEL
   export FASTMCP_ENABLE_RICH_LOGGING FASTMCP_ENABLE_RICH_TRACEBACKS
 
   exec room-mcp
-else
-  : "${API_HOST:=0.0.0.0}"
-  : "${API_PORT:=8000}"
-  : "${LOG_LEVEL:=info}"
+fi
 
-  LOG_CONFIG=/etc/uvicorn_log.json
-  [ -f /config/uvicorn_log.json ] && LOG_CONFIG=/config/uvicorn_log.json
+# Shared API config
+: "${API_HOST:=0.0.0.0}"
+: "${API_PORT:=8000}"
+: "${LOG_LEVEL:=info}"
+LOG_CONFIG=/etc/uvicorn_log.json
+[ -f "${ROOM_CAL_DATA_HOME}/uvicorn_log.json" ] && LOG_CONFIG="${ROOM_CAL_DATA_HOME}/uvicorn_log.json"
 
+# Calc-only API
+if [ "$SERVICE" = "calc" ]; then
+  exec uvicorn "room_cal.api.calc_app:app" \
+    --host "$API_HOST" \
+    --port "$API_PORT" \
+    --log-config "$LOG_CONFIG" \
+    --log-level "$LOG_LEVEL"
+
+# Standard API
+else
   exec uvicorn "room_cal.api:create_app" --factory \
     --host "$API_HOST" \
     --port "$API_PORT" \

+ 25 - 0
room_cal/api/calc_app.py

@@ -0,0 +1,25 @@
+import os
+from pathlib import Path
+from fastapi import FastAPI
+from fastapi.middleware.cors import CORSMiddleware
+from fastapi.responses import HTMLResponse
+from room_cal._utils import services as _services
+from room_cal.api._routes.calc import router as _calc_router
+
+_CALC_HTML = Path(__file__).parents[1] / 'vue' / 'calc.html'
+_API_BASE = os.getenv('APP_BASE_URL', '').rstrip('/')
+
+app = FastAPI(title='room_cal calc')
+app.add_middleware(CORSMiddleware, allow_origins=['*'], allow_methods=['*'], allow_headers=['*'])
+app.include_router(_calc_router)
+
+
+@app.get('/services')
+async def get_services():
+  return [{'key': s.key, 'name': s.name} for s in _services()]
+
+
+@app.get('/', response_class=HTMLResponse, include_in_schema=False)
+async def calc_ui():
+  html = _CALC_HTML.read_text()
+  return html.replace("const API_BASE = '';", f"const API_BASE = '{_API_BASE}';", 1)