mirror of
https://github.com/HyPnoTiiK/stream-fusion.git
synced 2026-08-03 17:56:57 +00:00
Introduces a secure peer-to-peer cache sharing system using HMAC-SHA256 request signatures and Fernet (AES-128-CBC) response encryption. - New peer_keys table for per-peer credential management (key_id, secret) - New /api/peer/check endpoint returns encrypted debrid availability data - New /api/peer/items endpoint returns encrypted torrent metadata - Rate limiting per peer key with configurable limits and windows - Admin UI for creating, viewing, revoking, and deleting peer keys - Peer cache enrichment integrated into BaseDebrid L2.5 lookup pipeline - StremThruDebrid override with cross-service PG L2 check + peer cache - Removed old /api/share/cache endpoint (replaced by /api/peer/*) - Added cryptography package dependency BREAKING CHANGE: The /api/share/cache endpoint has been removed and replaced with /api/peer/check. Clients must update to use HMAC authentication with X-Peer-Key-Id, X-Peer-Timestamp, and X-Peer-Signature headers.
37 lines
1.7 KiB
Python
37 lines
1.7 KiB
Python
import time
|
|
from typing import Optional
|
|
|
|
from sqlalchemy import BigInteger, Boolean, Integer, String, UniqueConstraint
|
|
from sqlalchemy.orm import Mapped, mapped_column
|
|
|
|
from stream_fusion.services.postgresql.base import Base
|
|
|
|
|
|
class PeerKeyModel(Base):
|
|
"""Application key for authenticated peer-to-peer cache sharing.
|
|
|
|
Each peer instance is granted a unique (key_id, secret) pair.
|
|
The secret is used for both HMAC request authentication and Fernet
|
|
response encryption — only the holder of the secret can authenticate
|
|
requests and decrypt responses.
|
|
"""
|
|
|
|
__tablename__ = "peer_keys"
|
|
|
|
id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True)
|
|
key_id: Mapped[str] = mapped_column(String(36), nullable=False) # UUID4 — sent in header
|
|
secret: Mapped[str] = mapped_column(String(128), nullable=False) # 64-char hex (256 bits)
|
|
name: Mapped[str] = mapped_column(String(100), nullable=False) # human label
|
|
is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
|
|
rate_limit: Mapped[int] = mapped_column(Integer, default=60, nullable=False) # max req per window
|
|
rate_window: Mapped[int] = mapped_column(Integer, default=60, nullable=False) # window in seconds
|
|
expires_at: Mapped[Optional[int]] = mapped_column(BigInteger, nullable=True) # None = never expires
|
|
last_used_at: Mapped[Optional[int]] = mapped_column(BigInteger, nullable=True)
|
|
total_queries: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
|
|
created_at: Mapped[int] = mapped_column(
|
|
BigInteger, nullable=False, default=lambda: int(time.time())
|
|
)
|
|
|
|
__table_args__ = (
|
|
UniqueConstraint("key_id", name="uq_peer_keys_key_id"),
|
|
)
|