Skip to content

Commit 36e756e

Browse files
authored
Merge pull request #109 from nebulabroadcast/ts-migration
Switch to typescript
2 parents 3ba90aa + 92201a4 commit 36e756e

File tree

169 files changed

+12515
-4452
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

169 files changed

+12515
-4452
lines changed

Dockerfile

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,27 @@
11
FROM node:latest AS build
22

3-
RUN mkdir /frontend
3+
WORKDIR /frontend
44

5-
COPY ./frontend/index.html /frontend/index.html
6-
COPY ./frontend/package.json /frontend/package.json
7-
COPY ./frontend/vite.config.js /frontend/vite.config.js
8-
COPY ./frontend/src /frontend/src
5+
COPY ./frontend/index.html .
6+
COPY ./frontend/package.json .
7+
COPY ./frontend/vite.config.ts .
8+
COPY ./frontend/tsconfig.json .
9+
COPY ./frontend/tsconfig.node.json .
910
COPY ./frontend/public /frontend/public
1011

11-
WORKDIR /frontend
12-
RUN yarn install && yarn build
12+
RUN yarn install
13+
COPY ./frontend/src /frontend/src
14+
RUN yarn build
1315

1416
FROM python:3.12-slim
1517
ENV PYTHONBUFFERED=1
1618

1719
RUN \
1820
apt-get update \
1921
&& apt-get -yqq install \
22+
curl \
2023
cifs-utils \
24+
procps \
2125
&& apt-get clean \
2226
&& rm -rf /var/lib/apt/lists/*
2327

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ check:
1111
uv run ruff check --fix . && \
1212
uv run mypy .
1313

14-
build: check
14+
build:
1515
docker build -t $(IMAGE_NAME) .
1616

1717
dist: build

backend/api/init/init_request.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from nebula.settings.common import LanguageCode
1010
from server.context import ScopedEndpoint, server_context
1111
from server.dependencies import CurrentUserOptional
12-
from server.models import ResponseModel
12+
from server.models import ResponseModel, UserModel
1313
from server.request import APIRequest
1414

1515
from .client_settings import ClientSettingsModel, get_client_settings
@@ -33,9 +33,9 @@ class InitResponseModel(ResponseModel):
3333
] = None
3434

3535
user: Annotated[
36-
dict[str, Any] | None,
36+
UserModel | None,
3737
Field(
38-
title="User data",
38+
title="Current user",
3939
description="User data if user is logged in",
4040
),
4141
] = None
@@ -85,7 +85,7 @@ class InitRequest(APIRequest):
8585
"""
8686

8787
name = "init"
88-
title = "Login"
88+
title = "Init"
8989
response_model = InitResponseModel
9090

9191
async def handle(
@@ -127,10 +127,12 @@ async def handle(
127127
client_settings.server_url = f"{request.url.scheme}://{request.url.netloc}"
128128
plugins = get_frontend_plugins()
129129

130+
# Return response
131+
130132
return InitResponseModel(
131133
installed=True,
132134
motd=motd,
133-
user=user.meta,
135+
user=UserModel.from_meta(user.meta),
134136
settings=client_settings,
135137
frontend_plugins=plugins,
136138
scoped_endpoints=server_context.scoped_endpoints,

backend/api/proxy.py

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ def get_file_size(file_name: str) -> int:
2121
return os.stat(file_name).st_size
2222

2323

24-
async def get_bytes_range(file_name: str, start: int, end: int) -> bytes:
24+
async def get_bytes_range2(file_name: str, start: int, end: int) -> bytes:
2525
"""Get a range of bytes from a file"""
2626
async with aiofiles.open(file_name, mode="rb") as f:
2727
await f.seek(start)
@@ -30,6 +30,33 @@ async def get_bytes_range(file_name: str, start: int, end: int) -> bytes:
3030
return await f.read(read_size)
3131

3232

33+
async def get_bytes_range(
34+
file_name: str, start: int, end: int, request: Request
35+
) -> bytes:
36+
"""Get a range of bytes from a file"""
37+
async with aiofiles.open(file_name, mode="rb") as f:
38+
await f.seek(start)
39+
pos = start
40+
read_size = end - pos + 1
41+
chunk_size = 1024 * 64 # Read in chunks of 64KB
42+
data = bytearray()
43+
44+
while read_size > 0:
45+
if await request.is_disconnected():
46+
raise HTTPException(
47+
status_code=status.HTTP_499_CLIENT_CLOSED_REQUEST,
48+
detail="Client disconnected",
49+
)
50+
51+
chunk = await f.read(min(chunk_size, read_size))
52+
if not chunk:
53+
break
54+
data.extend(chunk)
55+
read_size -= len(chunk)
56+
57+
return bytes(data)
58+
59+
3360
def _get_range_header(range_header: str, file_size: int) -> tuple[int, int]:
3461
"""
3562
Parse the Range header to determine the start and end byte positions.
@@ -114,7 +141,7 @@ async def range_requests_response(
114141
else:
115142
status_code = status.HTTP_206_PARTIAL_CONTENT
116143

117-
payload = await get_bytes_range(file_path, start, end)
144+
payload = await get_bytes_range(file_path, start, end, request)
118145

119146
if status_code == status.HTTP_200_OK:
120147
headers["cache-control"] = "private, max-age=600"

backend/api/sessions/invalidate_session_request.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ class InvalidateSessionRequest(APIRequest):
1919
used to log out other users.
2020
"""
2121

22-
name = "invalidate_session"
22+
name = "invalidate-session"
2323
title = "Invalidate session"
2424
responses = [204]
2525

backend/api/sessions/list_sessions_request.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ class ListSessionsRequestModel(RequestModel):
1414
class ListSessionsRequest(APIRequest):
1515
"""List user sessions."""
1616

17-
name = "sessions"
17+
name = "list-sessions"
1818
title = "List sessions"
1919

2020
async def handle(

backend/api/users/list_users_request.py

Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,9 @@
22

33
import nebula
44
from server.dependencies import CurrentUser
5-
from server.models import ResponseModel
5+
from server.models import ResponseModel, UserModel
66
from server.request import APIRequest
77

8-
from .user_model import UserModel
9-
108

119
class ListUsersResponseModel(ResponseModel):
1210
"""Response model for listing users"""
@@ -17,7 +15,7 @@ class ListUsersResponseModel(ResponseModel):
1715
class ListUsersRequest(APIRequest):
1816
"""Get a list of users"""
1917

20-
name = "user_list"
18+
name = "list-users"
2119
title = "Get user list"
2220
response_model = ListUsersResponseModel
2321

@@ -26,22 +24,8 @@ async def handle(self, user: CurrentUser) -> ListUsersResponseModel:
2624
raise nebula.ForbiddenException("You are not allowed to list users")
2725

2826
query = "SELECT meta FROM users ORDER BY login ASC"
29-
3027
users = []
3128
async for row in nebula.db.iterate(query):
32-
meta = {}
33-
for key, value in row["meta"].items():
34-
if key == "api_key_preview":
35-
continue
36-
if key == "api_key":
37-
meta[key] = row["meta"].get("api_key_preview", "*****")
38-
elif key == "password":
39-
continue
40-
elif key.startswith("can/"):
41-
meta[key.replace("can/", "can_")] = value
42-
else:
43-
meta[key] = value
44-
45-
users.append(UserModel(**meta))
29+
users.append(UserModel.from_meta(row["meta"]))
4630

4731
return ListUsersResponseModel(users=users)

backend/api/users/save_user_request.py

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,16 @@
22

33
import nebula
44
from server.dependencies import CurrentUser
5+
from server.models import UserModel
56
from server.request import APIRequest
67
from server.session import Session
78

8-
from .user_model import UserModel
9-
109

1110
class SaveUserRequest(APIRequest):
1211
"""Save user data"""
1312

14-
name = "save_user"
15-
title = "Save user data"
13+
name = "save-user"
14+
title = "Save user"
1615
responses = [204, 201]
1716

1817
async def handle(self, current_user: CurrentUser, payload: UserModel) -> Response:
@@ -21,16 +20,15 @@ async def handle(self, current_user: CurrentUser, payload: UserModel) -> Respons
2120
if not current_user.is_admin:
2221
raise nebula.ForbiddenException("You are not allowed to edit users")
2322

24-
meta = payload.dict()
23+
meta = payload.model_dump()
2524
meta.pop("id", None)
2625

2726
password = meta.pop("password", None)
2827
api_key = meta.pop("api_key", None)
28+
permissions = meta.pop("permissions", {})
2929

30-
for key, value in list(meta.items()):
31-
if key.startswith("can_"):
32-
meta[key.replace("can_", "can/")] = value
33-
del meta[key]
30+
for key, value in permissions.items():
31+
meta[f"can/{key}"] = value
3432

3533
if new_user:
3634
user = nebula.User.from_meta(meta)

backend/api/users/user_model.py

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

backend/server/models.py

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

0 commit comments

Comments
 (0)