nice version

This commit is contained in:
root 2025-09-20 20:21:49 +00:00
parent 360f8110a4
commit 38e54f0ab2
5 changed files with 38 additions and 14 deletions

View File

@ -28,7 +28,7 @@ async def s_api_v1_content_list(request):
select(StoredContent)
.where(
StoredContent.type.like(store + '%'),
StoredContent.disabled == False
StoredContent.disabled.is_(None)
)
.order_by(StoredContent.created.desc())
.offset(offset)

View File

@ -27,14 +27,29 @@ async def s_api_v1_upload_tus_hook(request):
tusd HTTP hook endpoint. We mainly handle post-finish to: encrypt -> IPFS add+pin -> record DB.
"""
try:
payload: Dict[str, Any] = request.json or {}
payload: Dict[str, Any] = request.json
except Exception:
payload = None
if payload is None:
raw_body = request.body or b''
try:
payload = json.loads(raw_body) if raw_body else {}
except Exception:
payload = {}
event = payload.get("Type") or payload.get("type") or payload.get("Event") or payload.get("event")
event = (payload.get("Type") or payload.get("type") or
payload.get("Event") or payload.get("event") or
payload.get("Hook") or payload.get("hook") or
payload.get("HookName") or payload.get("hook_name") or
request.headers.get("Hook-Name") or request.headers.get("hook-name"))
upload = payload.get("Upload") or payload.get("upload") or {}
if not event:
return response.json({"ok": False, "error": "NO_EVENT"}, status=400)
hook_name = (payload.get("HookName") or payload.get("hook") or
payload.get("hook_name") or request.headers.get("Hook-Name"))
raw = request.body or b''
preview = raw[:512]
make_log("tus-hook", f"Missing event type in hook payload; ignoring (hook={hook_name}, keys={list(payload.keys())}, raw={preview!r})", level="warning")
return response.json({"ok": True, "skipped": True})
if event not in ("post-finish", "postfinish"):
# accept but ignore other events

View File

@ -3,7 +3,7 @@ import os
import json
import shutil
from datetime import datetime
from typing import List, Tuple
from typing import List, Tuple, Optional
from sqlalchemy import select
@ -57,7 +57,7 @@ async def _save_derivative(file_path: str, filename: str) -> Tuple[str, int]:
return file_hash, size
async def _run_media_converter(input_host_path: str, input_ext: str, quality: str, trim_value: str | None, is_audio: bool) -> Tuple[str, dict]:
async def _run_media_converter(input_host_path: str, input_ext: str, quality: str, trim_value: Optional[str], is_audio: bool) -> Tuple[str, dict]:
rid = __import__('uuid').uuid4().hex[:8]
output_dir_container = f"/tmp/conv_{rid}"
output_dir_host = f"/tmp/conv_{rid}"
@ -215,7 +215,7 @@ async def _pick_pending(limit: int) -> List[Tuple[EncryptedContent, str]]:
if required.issubset(kinds_ready):
continue
# Always decrypt from IPFS using local or remote key
storage_path: str | None = None
storage_path: Optional[str] = None
ck = (await session.execute(select(ContentKey).where(ContentKey.content_id == ec.id))).scalars().first()
if ck:
storage_path = await stage_plain_from_ipfs(ec, ck.key_ciphertext_b64)
@ -285,7 +285,7 @@ async def main_fn(memory):
await worker_loop()
async def stage_plain_from_ipfs(ec: EncryptedContent, dek_wrapped: str) -> str | None:
async def stage_plain_from_ipfs(ec: EncryptedContent, dek_wrapped: str) -> Optional[str]:
"""Download encrypted ENCF stream from IPFS and decrypt on the fly into a temp file."""
import tempfile
try:

View File

@ -4,7 +4,6 @@ import os
import struct
from typing import BinaryIO, Iterator, AsyncIterator
from Crypto.Cipher import SIV
from Crypto.Cipher import AES
@ -62,7 +61,7 @@ def encrypt_file_to_encf(src: BinaryIO, key: bytes, chunk_bytes: int, salt: byte
block = src.read(chunk_bytes)
if not block:
break
siv = SIV.new(key=key, ciphermod=AES) # new object per message
siv = AES.new(key, AES.MODE_SIV) # new object per message
siv.update(_ad(salt, idx))
ciph, tag = siv.encrypt_and_digest(block)
yield struct.pack(">I", len(block))
@ -76,7 +75,6 @@ async def decrypt_encf_to_file(byte_iter: AsyncIterator[bytes], key: bytes, out_
Parse ENCF v1 stream from async byte iterator and write plaintext to out_path.
"""
import aiofiles
from Crypto.Cipher import SIV as _SIV
from Crypto.Cipher import AES as _AES
buf = bytearray()
@ -129,7 +127,7 @@ async def decrypt_encf_to_file(byte_iter: AsyncIterator[bytes], key: bytes, out_
c = bytes(buf[:p_len])
t = bytes(buf[p_len:p_len+TAG_LEN])
del buf[:p_len+TAG_LEN]
siv = _SIV.new(key=key, ciphermod=_AES)
siv = _AES.new(key, _AES.MODE_SIV)
siv.update(_ad(salt, idx))
p = siv.decrypt_and_verify(c, t)
await out.write(p)

View File

@ -26,8 +26,19 @@ async def add_streamed_file(stream_iter: Iterable[bytes], filename: str = "file.
}
q = {**default_params, **params}
class _StreamAdapter:
def __init__(self, iterable):
self._iter = iter(iterable)
def read(self, size=-1):
try:
return next(self._iter)
except StopIteration:
return b''
stream = _StreamAdapter(stream_iter)
async with httpx.AsyncClient(timeout=None) as client:
files = {"file": (filename, stream_iter, "application/octet-stream")}
files = {"file": (filename, stream, "application/octet-stream")}
r = await client.post(f"{IPFS_API_URL}/api/v0/add", params=q, files=files)
r.raise_for_status()
# /add may emit NDJSON lines; most often single JSON