mirror of
https://github.com/fromchat-messenger/web.git
synced 2026-09-22 19:15:08 +03:00
Implement centralized Signal Protocol init
This commit is contained in:
@@ -106,6 +106,20 @@ class SignalSession(Base):
|
||||
__table_args__ = (UniqueConstraint('user_id', 'recipient_id', 'device_id', name='_user_recipient_device_uc'),)
|
||||
|
||||
|
||||
class SentMessagePlaintext(Base):
|
||||
"""Stores encrypted plaintexts of sent messages for history display"""
|
||||
__tablename__ = "sent_message_plaintext"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
user_id = Column(Integer, ForeignKey("user.id"), nullable=False, index=True)
|
||||
message_id = Column(Integer, nullable=False, index=True) # DM envelope ID
|
||||
recipient_id = Column(Integer, nullable=False, index=True) # The recipient of the message
|
||||
encrypted_data = Column(Text, nullable=False) # Encrypted plaintext (JSON with salt, iv, ciphertext)
|
||||
created_at = Column(DateTime, default=datetime.now, index=True)
|
||||
|
||||
__table_args__ = (UniqueConstraint('user_id', 'message_id', name='_user_message_uc'),)
|
||||
|
||||
|
||||
class DMEnvelope(Base):
|
||||
__tablename__ = "dm_envelope"
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
|
||||
|
||||
from constants import OWNER_USERNAME
|
||||
from dependencies import get_current_user, get_db
|
||||
from models import LoginRequest, RegisterRequest, ChangePasswordRequest, User, CryptoPublicKey, CryptoBackup, DeviceSession, SignalSession
|
||||
from models import LoginRequest, RegisterRequest, ChangePasswordRequest, User, CryptoPublicKey, CryptoBackup, DeviceSession, SignalSession, SentMessagePlaintext
|
||||
from utils import create_token, get_password_hash, verify_password, get_client_ip
|
||||
from validation import is_valid_password, is_valid_username, is_valid_display_name
|
||||
import os
|
||||
@@ -790,6 +790,93 @@ def get_signal_sessions(
|
||||
}
|
||||
|
||||
|
||||
@router.post("/crypto/signal/message-plaintexts")
|
||||
@rate_limit_per_ip("100/minute")
|
||||
def upload_message_plaintexts(
|
||||
request: Request,
|
||||
payload: dict,
|
||||
current_user: User = Depends(get_current_user),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""Upload encrypted plaintexts of sent messages"""
|
||||
import json
|
||||
from datetime import datetime
|
||||
|
||||
messages = payload.get("messages")
|
||||
if not isinstance(messages, list):
|
||||
raise HTTPException(status_code=400, detail="messages must be a list")
|
||||
|
||||
uploaded_count = 0
|
||||
for msg_data in messages:
|
||||
if not isinstance(msg_data, dict):
|
||||
continue
|
||||
|
||||
message_id = msg_data.get("messageId")
|
||||
recipient_id = msg_data.get("recipientId")
|
||||
encrypted_data = msg_data.get("encryptedData")
|
||||
|
||||
if not message_id or not recipient_id or not encrypted_data:
|
||||
continue
|
||||
|
||||
try:
|
||||
# Validate encrypted_data is valid JSON
|
||||
json.loads(encrypted_data)
|
||||
except (json.JSONDecodeError, TypeError):
|
||||
continue
|
||||
|
||||
# Store or update plaintext
|
||||
existing = db.query(SentMessagePlaintext).filter(
|
||||
SentMessagePlaintext.user_id == current_user.id,
|
||||
SentMessagePlaintext.message_id == message_id
|
||||
).first()
|
||||
|
||||
if existing:
|
||||
existing.encrypted_data = encrypted_data
|
||||
else:
|
||||
new_plaintext = SentMessagePlaintext(
|
||||
user_id=current_user.id,
|
||||
message_id=message_id,
|
||||
recipient_id=recipient_id,
|
||||
encrypted_data=encrypted_data
|
||||
)
|
||||
db.add(new_plaintext)
|
||||
uploaded_count += 1
|
||||
|
||||
db.commit()
|
||||
return {"status": "ok", "uploaded_count": uploaded_count}
|
||||
|
||||
|
||||
@router.get("/crypto/signal/message-plaintexts")
|
||||
@rate_limit_per_ip("60/minute")
|
||||
def get_message_plaintexts(
|
||||
request: Request,
|
||||
recipient_id: int | None = None, # Optional filter by recipient
|
||||
current_user: User = Depends(get_current_user),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""Get encrypted plaintexts of sent messages for the current user"""
|
||||
query = db.query(SentMessagePlaintext).filter(
|
||||
SentMessagePlaintext.user_id == current_user.id
|
||||
)
|
||||
|
||||
if recipient_id is not None:
|
||||
query = query.filter(SentMessagePlaintext.recipient_id == recipient_id)
|
||||
|
||||
plaintexts = query.all()
|
||||
|
||||
return {
|
||||
"messages": [
|
||||
{
|
||||
"messageId": p.message_id,
|
||||
"recipientId": p.recipient_id,
|
||||
"encryptedData": p.encrypted_data,
|
||||
"createdAt": p.created_at.isoformat()
|
||||
}
|
||||
for p in plaintexts
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@router.get("/users/search")
|
||||
@rate_limit_per_ip("60/minute") # Per-IP limit to prevent abuse
|
||||
def search_users(request: Request, q: str, current_user: User = Depends(get_current_user), db: Session = Depends(get_db)):
|
||||
|
||||
Reference in New Issue
Block a user