mirror of
https://github.com/fromchat-messenger/web.git
synced 2026-09-22 19:15:08 +03:00
Fix production deployment issues
This commit is contained in:
@@ -92,13 +92,14 @@ from fastapi import UploadFile, File, HTTPException, Request, Depends
|
||||
from fastapi.responses import FileResponse
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
# File serving directories (matching main service structure)
|
||||
FILES_BASE_DIR = Path("data/uploads/files")
|
||||
FILES_NORMAL_DIR = FILES_BASE_DIR / "normal"
|
||||
FILES_ENCRYPTED_DIR = FILES_BASE_DIR / "encrypted"
|
||||
|
||||
# Base storage directories
|
||||
BASE_DIR = Path("files")
|
||||
# Legacy upload layout (was data/uploads/files on monolith main under /app/data).
|
||||
# Keep under BASE_DIR so Docker uses the file_storage volume (/app/files), not /app/data
|
||||
# (different uid / optional mount → PermissionError on prod).
|
||||
FILES_BASE_DIR = BASE_DIR / "data" / "uploads" / "files"
|
||||
FILES_NORMAL_DIR = FILES_BASE_DIR / "normal"
|
||||
FILES_ENCRYPTED_DIR = FILES_BASE_DIR / "encrypted"
|
||||
FILES_DIR = BASE_DIR / "files"
|
||||
THUMBS_DIR = BASE_DIR / "thumbs"
|
||||
TMP_DIR = BASE_DIR / "tmp"
|
||||
|
||||
@@ -319,15 +319,6 @@ async def health_check():
|
||||
return {"status": "healthy", "service": "main"}
|
||||
|
||||
|
||||
@app.get("/key/public")
|
||||
async def key_public_proxy():
|
||||
"""
|
||||
Proxy endpoint for messaging public key. In dev this calls the in-process function,
|
||||
in production it will proxy to the external messaging service via the keys helper.
|
||||
"""
|
||||
return await keys.get_public_key()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import uvicorn
|
||||
port = int(os.getenv("PORT", "8300"))
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from typing import Dict, Any
|
||||
from fastapi import APIRouter, HTTPException
|
||||
import os
|
||||
import logging
|
||||
import base64
|
||||
|
||||
router = APIRouter(prefix="/api")
|
||||
router = APIRouter()
|
||||
logger = logging.getLogger("uvicorn.error")
|
||||
|
||||
|
||||
@@ -58,37 +56,3 @@ async def get_public_key():
|
||||
logger.error(f"Failed to fetch messaging public key via HTTP: {e}")
|
||||
raise HTTPException(status_code=502, detail="Failed to contact messaging service")
|
||||
|
||||
|
||||
@router.post("/key/invalidate")
|
||||
async def invalidate_key():
|
||||
"""
|
||||
Request messaging service to invalidate its current ephemeral key (rotate).
|
||||
"""
|
||||
messaging_module = _get_messaging_module()
|
||||
if messaging_module:
|
||||
try:
|
||||
data = await messaging_module.invalidate_key() # type: ignore
|
||||
return data
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to invalidate key in in-process messaging module: {e}")
|
||||
raise HTTPException(status_code=500, detail="Failed to invalidate messaging key")
|
||||
|
||||
messaging_url = os.getenv("MESSAGING_SERVICE_URL", "http://messaging:8301")
|
||||
url = f"{messaging_url.rstrip('/')}/key/invalidate"
|
||||
try:
|
||||
try:
|
||||
import httpx
|
||||
resp = httpx.post(url, timeout=5.0)
|
||||
resp.raise_for_status()
|
||||
return resp.json()
|
||||
except Exception:
|
||||
from urllib import request, error
|
||||
import json
|
||||
req = request.Request(url, method="POST")
|
||||
with request.urlopen(req, timeout=5) as r:
|
||||
body = r.read()
|
||||
return json.loads(body)
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to call messaging invalidate endpoint via HTTP: {e}")
|
||||
raise HTTPException(status_code=502, detail="Failed to contact messaging service")
|
||||
|
||||
|
||||
@@ -137,7 +137,8 @@ def decrypt_transport_blob(
|
||||
ephemeral transport private key and the client's public key.
|
||||
|
||||
Args:
|
||||
client_public_key_b64: Sender public key in base64 (raw X25519).
|
||||
client_public_key_b64: Client ephemeral public key in base64 (raw X25519),
|
||||
the same key as used for the transport-encrypted message body.
|
||||
encrypted_blob: Raw bytes of `nonce || ciphertext`.
|
||||
ephemeral_private_key: Server ephemeral X25519 private key.
|
||||
nonce_size: Nonce size in bytes (24 for XSalsa20-Poly1305).
|
||||
|
||||
@@ -6,8 +6,7 @@ providing compliance access while ensuring zero-knowledge storage of plaintext c
|
||||
|
||||
API Endpoints:
|
||||
- GET /health: Health check
|
||||
- GET /key/public: Get current ephemeral transport public key
|
||||
- POST /key/invalidate: Rotate ephemeral keys
|
||||
- GET /key/transport/public: Get current ephemeral transport public key
|
||||
- POST /process: Process encrypted message through envelope encryption pipeline
|
||||
"""
|
||||
|
||||
@@ -203,7 +202,8 @@ class ProcessMessageRequest(BaseModel):
|
||||
|
||||
class ProcessMessageWithFilesFile(BaseModel):
|
||||
"""
|
||||
A single transport-encrypted file blob (base64 of nonce||ciphertext).
|
||||
A single transport-encrypted file blob (base64 of nonce||ciphertext),
|
||||
encrypted with the same ephemeral client key as the message body.
|
||||
"""
|
||||
encrypted_file_data_b64: str
|
||||
filename: str = "file"
|
||||
@@ -213,6 +213,8 @@ class ProcessMessageWithFilesRequest(ProcessMessageRequest):
|
||||
"""
|
||||
Process a transport-encrypted message and a list of transport-encrypted files
|
||||
using a single MEK for the whole envelope.
|
||||
|
||||
Each file blob uses the same client_public_key_b64 / X25519 ephemeral pair as the message.
|
||||
"""
|
||||
files: list[ProcessMessageWithFilesFile]
|
||||
|
||||
@@ -353,6 +355,9 @@ async def process_message_with_files(
|
||||
):
|
||||
"""
|
||||
In-process helper: process message + transport-encrypted files with one MEK.
|
||||
|
||||
File blobs must be encrypted with the same ephemeral client key as the message
|
||||
(same client_public_key_b64), not the sender's long-term identity key.
|
||||
transport_files: list of {"encrypted_file_data_b64": str, "filename": str}
|
||||
"""
|
||||
private_key = _get_ephemeral_private_key()
|
||||
@@ -371,7 +376,7 @@ async def process_message_with_files(
|
||||
transport_blob = base64.b64decode(enc_b64)
|
||||
plaintext_files.append(
|
||||
decrypt_transport_blob(
|
||||
client_public_key_b64=sender_public_key_b64,
|
||||
client_public_key_b64=client_public_key_b64,
|
||||
encrypted_blob=transport_blob,
|
||||
ephemeral_private_key=private_key,
|
||||
)
|
||||
@@ -393,8 +398,8 @@ async def process_message_with_files_http(request: ProcessMessageWithFilesReques
|
||||
"""
|
||||
Process an encrypted message and its files using a single MEK.
|
||||
|
||||
- Message transport layer is decrypted using the message client ephemeral key
|
||||
- File transport layer is decrypted using the sender long-term public key
|
||||
- Message and file transport layers use the same client ephemeral X25519 keypair
|
||||
(client_public_key_b64); files are NaCl box ciphertexts to the server transport key
|
||||
- One MEK is generated and used to encrypt message + all files
|
||||
- MEK is wrapped for compliance, sender, and recipient (stored on DM envelope)
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user