Skip to content

Commit aa7b507

Browse files
committed
refactor: add new coldwire & strandlock protocol support
1 parent 8b7ff11 commit aa7b507

File tree

13 files changed

+98
-181
lines changed

13 files changed

+98
-181
lines changed

app/core/constants.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22
APP_NAME = "Coldwire Python Server"
33
APP_VERSION = "0.1"
44

5+
# Coldwire protocol misc (bytes)
6+
COLDWIRE_DATA_SEP = b"\0"
7+
COLDWIRE_LEN_OFFSET = 3
8+
59
# network defaults (seconds)
610
LONGPOLL_MIN = 5
711
LONGPOLL_MAX = 30

app/db/redis.py

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,3 @@ def get_redis():
1313
return redis_client
1414

1515

16-
# Returns a json list, from a key's value list
17-
def get_redis_list(client, key: str) -> list:
18-
messages = []
19-
20-
raw_messages = client.lrange(key, 0, -1)
21-
for raw in raw_messages:
22-
messages.append(json.loads(raw))
23-
24-
return messages
25-

app/logic/authentication.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from app.db.sqlite import get_db
2-
from app.db.redis import get_redis, get_redis_list
2+
from app.db.redis import get_redis
33
from app.utils.helper_utils import generate_user_id
44
from app.utils.jwt import create_jwt_token
55
from base64 import b64encode

app/logic/data.py

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,48 @@
1-
from app.db.sqlite import get_db
2-
from app.db.redis import get_redis, get_redis_list
3-
import json
4-
import logging
1+
from app.db.sqlite import get_db, check_user_exists
2+
from app.db.redis import get_redis
3+
from app.core.constants import (
4+
COLDWIRE_LEN_OFFSET,
5+
COLDWIRE_DATA_SEP
6+
)
57

68

79
redis_client = get_redis()
810

9-
def check_new_data(user_id: str) -> list:
10-
messages = get_redis_list(redis_client, user_id)
11-
redis_client.delete(user_id)
12-
return messages
1311

12+
def get_redis_list(client, key: str) -> list:
13+
data = b""
14+
while True:
15+
raw = client.lpop(key)
16+
if raw is None:
17+
break
18+
19+
data += raw
20+
21+
return data
22+
23+
def check_new_data(user_id: str) -> bytes:
24+
return get_redis_list(redis_client, user_id)
25+
26+
def data_processor(user_id: str, recipient_id: str, blob: bytes) -> None:
27+
if not check_user_exists(recipient_id):
28+
raise ValueError("Recipient_id does not exist")
29+
30+
user_id = user_id.encode("utf-8")
31+
32+
# \0 is seperator. User_ids cannot have a seperator.
33+
34+
if COLDWIRE_DATA_SEP in user_id:
35+
raise ValueError("User ID cannot have null byte!")
36+
37+
payload = user_id + COLDWIRE_DATA_SEP + blob
38+
length_prefix = len(payload).to_bytes(COLDWIRE_LEN_OFFSET, "big")
39+
40+
payload = length_prefix + payload
41+
42+
redis_client.rpush(recipient_id, payload)
43+
44+
45+
"""
1446
def delete_old_data(target_id: str, data_type: str, sender_id: str) -> None:
1547
all_msgs = redis_client.lrange(target_id, 0, -1)
1648
keep = []
@@ -26,4 +58,4 @@ def delete_old_data(target_id: str, data_type: str, sender_id: str) -> None:
2658
pipe.rpush(target_id, *keep)
2759
pipe.execute()
2860
29-
61+
"""

app/logic/message.py

Lines changed: 0 additions & 20 deletions
This file was deleted.

app/logic/pfs.py

Lines changed: 0 additions & 26 deletions
This file was deleted.

app/main.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
from fastapi import FastAPI
22
from fastapi.staticfiles import StaticFiles
3-
from app.routes import authentication_router, get_user_router, smp_router, pfs_router, message_router, data_router
3+
from app.routes import (
4+
authentication_router,
5+
get_user_router,
6+
data_router
7+
)
48
from app.db.sqlite import init_db
59
from app.utils.jwt import check_jwt_exists
610
import logging
@@ -12,14 +16,10 @@
1216
)
1317
logger = logging.getLogger("coldwire")
1418

15-
app = FastAPI()
16-
1719
check_jwt_exists()
1820
init_db()
1921

22+
app = FastAPI()
2023
app.include_router(authentication_router)
2124
app.include_router(get_user_router)
22-
app.include_router(smp_router)
23-
app.include_router(pfs_router)
24-
app.include_router(message_router)
2525
app.include_router(data_router)

app/routes/__init__.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
from .authentication import router as authentication_router
22
from .get_user import router as get_user_router
3-
from .smp import router as smp_router
4-
from .pfs import router as pfs_router
5-
from .message import router as message_router
63
from .data import router as data_router
74

85

9-
__all__ = ["authentication_router", "get_user_router", "smp_router", "pfs_router", "message_router", "data_router"]
6+
__all__ = ["authentication_router", "get_user_router", "data_router"]

app/routes/data.py

Lines changed: 42 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
from fastapi import APIRouter, Request, Response, Depends
2-
from app.logic.data import check_new_data
1+
from fastapi import APIRouter, Request, Response, Depends, Form, UploadFile, File
2+
from app.logic.data import check_new_data, data_processor
33
from app.utils.jwt import verify_jwt_token
44
import asyncio
5+
import json
56

67
router = APIRouter()
78

@@ -12,13 +13,46 @@ async def get_data_longpoll(request: Request, response: Response, user=Depends(v
1213
if await request.is_disconnected():
1314
# Don't attempt to check for new data if client disconnects before 30 seconds
1415
# This is crucial to perserve data as they get deleted after being read
15-
return
16+
return Response(content=b'', media_type="application/octet-stream")
17+
1618

17-
messages = await asyncio.to_thread(check_new_data, user["id"])
18-
19-
if messages:
20-
return {"messages": messages}
19+
data = await asyncio.to_thread(check_new_data, user["id"])
2120

21+
if data:
22+
return Response(content = data, media_type="application/octet-stream")
2223
await asyncio.sleep(1)
2324

24-
return {"messages": []}
25+
return Response(content=b'', media_type="application/octet-stream")
26+
27+
28+
@router.post("/data/send")
29+
async def send(metadata: str = Form(...), blob: UploadFile = File(...), user=Depends(verify_jwt_token)):
30+
user_id = user["id"]
31+
32+
metadata = json.loads(metadata)
33+
34+
if "metadata" not in metadata:
35+
raise HTTPException(status_code=400, detail="Missing metadata")
36+
37+
if "recipient" not in metadata["metadata"]:
38+
raise HTTPException(status_code=400, detail="Missing recipient")
39+
40+
recipient = metadata["metadata"]["recipient"]
41+
42+
if (not recipient.isdigit()) or len(recipient) != 16:
43+
raise HTTPException(status_code=400, detail="Invalid recipient")
44+
45+
46+
blob_data = await blob.read()
47+
if not blob_data:
48+
raise HTTPException(status_code=400, detail="Empty blob is not allowed")
49+
50+
try:
51+
await asyncio.to_thread(data_processor, user_id, recipient, blob_data)
52+
except ValueError as e:
53+
raise HTTPException(status_code=400, detail=e)
54+
55+
56+
57+
return {"status": "success"}
58+

app/routes/get_user.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,17 @@
11
from fastapi import APIRouter, Request, HTTPException, Response, Depends
22
from pydantic import BaseModel, validator
3-
from base64 import b64encode, b64decode
4-
from app.core.crypto import verify_signature
5-
from app.utils.helper_utils import valid_b64
63
from app.logic.get_user import check_user
4+
from app.utils.jwt import verify_jwt_token
75
import asyncio
86

97
router = APIRouter()
108

119

12-
# The reason we dont specify the id as int is because it can trailing digits
1310
class GetUserParams(BaseModel):
1411
user_id: str
1512

1613
@router.get("/get_user")
17-
async def get_user(response: Response, params: GetUserParams = Depends()):
14+
async def get_user(response: Response, params: GetUserParams = Depends(), user=Depends(verify_jwt_token)):
1815
user_id = params.user_id
1916

2017
if (not user_id.isdigit()) or len(user_id) != 16:

0 commit comments

Comments
 (0)