Initial commit — StreamStack v1

Five-service streaming platform: auth, catalogue, streaming, ingest, thumbnailer.
Includes React frontend served by nginx, NATS JetStream event bus, aiobotocore
async S3, PyAV video metadata + thumbnail extraction, service-to-service JWT auth,
and a full unit + e2e test suite.
This commit is contained in:
2026-05-04 22:16:39 +10:00
commit 2309e9f43a
80 changed files with 6339 additions and 0 deletions
View File
+60
View File
@@ -0,0 +1,60 @@
import subprocess
from unittest.mock import patch
import pytest
from streamstack.core.auth import create_access_token, create_refresh_token, decode_token
@pytest.fixture(scope="module")
def rsa_key_pair(tmp_path_factory):
tmp = tmp_path_factory.mktemp("keys")
priv = tmp / "private.pem"
pub = tmp / "public.pem"
subprocess.run(
["openssl", "genrsa", "-out", str(priv), "2048"],
check=True,
capture_output=True,
)
subprocess.run(
["openssl", "rsa", "-in", str(priv), "-pubout", "-out", str(pub)],
check=True,
capture_output=True,
)
return str(priv), str(pub)
def test_access_token_roundtrip(rsa_key_pair):
priv, pub = rsa_key_pair
with patch("streamstack.core.auth.settings") as mock_settings:
mock_settings.jwt_private_key_path = priv
mock_settings.jwt_public_key_path = pub
mock_settings.jwt_algorithm = "RS256"
mock_settings.jwt_expire_minutes = 30
token = create_access_token("user-123", "test@example.com", ["viewer"])
with patch("streamstack.core.auth.settings") as mock_settings:
mock_settings.jwt_public_key_path = pub
mock_settings.jwt_algorithm = "RS256"
payload = decode_token(token)
assert payload["sub"] == "user-123"
assert payload["email"] == "test@example.com"
assert payload["roles"] == ["viewer"]
assert "jti" in payload
assert "exp" in payload
def test_refresh_token_has_type(rsa_key_pair):
priv, pub = rsa_key_pair
with patch("streamstack.core.auth.settings") as mock_settings:
mock_settings.jwt_private_key_path = priv
mock_settings.jwt_public_key_path = pub
mock_settings.jwt_algorithm = "RS256"
mock_settings.jwt_expire_minutes = 30
mock_settings.jwt_refresh_expire_days = 7
token = create_refresh_token("user-123")
with patch("streamstack.core.auth.settings") as mock_settings:
mock_settings.jwt_public_key_path = pub
mock_settings.jwt_algorithm = "RS256"
payload = decode_token(token)
assert payload["type"] == "refresh"
assert payload["sub"] == "user-123"
+105
View File
@@ -0,0 +1,105 @@
import io
import boto3
import pytest
from moto import mock_aws
from streamstack.core.s3 import S3SeekableFile, open_s3_buffered
BUCKET = "test-bucket"
KEY = "test/video.mp4"
CONTENT = b"Hello World! This is seekable S3 content for testing purposes."
@pytest.fixture
def s3_object():
with mock_aws():
s3 = boto3.client("s3", region_name="us-east-1")
s3.create_bucket(Bucket=BUCKET)
s3.put_object(Bucket=BUCKET, Key=KEY, Body=CONTENT)
yield s3
def test_readable_and_seekable(s3_object):
f = S3SeekableFile(BUCKET, KEY, s3_client=s3_object)
assert f.readable() is True
assert f.seekable() is True
def test_size(s3_object):
f = S3SeekableFile(BUCKET, KEY, s3_client=s3_object)
assert f.size == len(CONTENT)
def test_initial_position(s3_object):
f = S3SeekableFile(BUCKET, KEY, s3_client=s3_object)
assert f.tell() == 0
def test_read_from_start(s3_object):
f = S3SeekableFile(BUCKET, KEY, s3_client=s3_object)
data = f.read(5)
assert data == CONTENT[:5]
assert f.tell() == 5
def test_seek_set(s3_object):
f = S3SeekableFile(BUCKET, KEY, s3_client=s3_object)
pos = f.seek(7)
assert pos == 7
assert f.tell() == 7
data = f.read(5)
assert data == CONTENT[7:12]
def test_seek_cur(s3_object):
f = S3SeekableFile(BUCKET, KEY, s3_client=s3_object)
f.read(5)
f.seek(2, io.SEEK_CUR)
assert f.tell() == 7
def test_seek_end(s3_object):
f = S3SeekableFile(BUCKET, KEY, s3_client=s3_object)
pos = f.seek(-5, io.SEEK_END)
assert pos == len(CONTENT) - 5
data = f.read()
assert data == CONTENT[-5:]
def test_seek_beyond_end_clamps(s3_object):
f = S3SeekableFile(BUCKET, KEY, s3_client=s3_object)
pos = f.seek(len(CONTENT) + 100)
assert pos == len(CONTENT)
def test_read_at_eof_returns_empty(s3_object):
f = S3SeekableFile(BUCKET, KEY, s3_client=s3_object)
f.seek(0, io.SEEK_END)
buf = bytearray(10)
n = f.readinto(buf)
assert n == 0
def test_full_read(s3_object):
f = S3SeekableFile(BUCKET, KEY, s3_client=s3_object)
data = f.read()
assert data == CONTENT
def test_open_s3_buffered(s3_object):
with mock_aws():
s3 = boto3.client("s3", region_name="us-east-1")
s3.create_bucket(Bucket=BUCKET)
s3.put_object(Bucket=BUCKET, Key=KEY, Body=CONTENT)
with patch_s3_client(s3):
buf = open_s3_buffered(BUCKET, KEY)
assert isinstance(buf, io.BufferedReader)
data = buf.read(5)
assert data == CONTENT[:5]
def patch_s3_client(client):
from unittest.mock import patch
return patch("streamstack.core.s3.get_s3_client", return_value=client)