Skip to content

Commit

Permalink
merge settings
Browse files Browse the repository at this point in the history
  • Loading branch information
eelcovdw committed Oct 9, 2024
2 parents 1756718 + 8363788 commit aa8c8c0
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 84 deletions.
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ build-backend = "setuptools.build_meta"
# this will be completely ignored in the built wheel
dev-dependencies = [
"bump2version>=1.0.1",
"pre-commit>=4.0.1",
"pytest-cov>=5.0.0",
"pytest-xdist[psutil]>=3.6.1",
"pytest>=8.3.3",
Expand Down
90 changes: 46 additions & 44 deletions syftbox/server/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from typing import Optional

import uvicorn
from fastapi import FastAPI, Request
from fastapi import Depends, FastAPI, Request
from fastapi.responses import (
FileResponse,
HTMLResponse,
Expand All @@ -32,20 +32,13 @@
hash_dir,
strtobin,
)
from syftbox.server.settings import ServerSettings, get_server_settings

from .users.router import user_router
from .users.user import UserManager

current_dir = Path(__file__).parent


DATA_FOLDER = "data"
SNAPSHOT_FOLDER = f"{DATA_FOLDER}/snapshot"
USER_FILE_PATH = f"{DATA_FOLDER}/users.json"

FOLDERS = [DATA_FOLDER, SNAPSHOT_FOLDER]


def load_dict(cls, filepath: str) -> list[Any]:
try:
with open(filepath) as f:
Expand Down Expand Up @@ -76,20 +69,21 @@ class User(Jsonable):


class Users:
def __init__(self) -> None:
def __init__(self, path: Path) -> None:
self.path = path
self.users = {}
self.load()

def load(self):
if os.path.exists(USER_FILE_PATH):
users = load_dict(User, USER_FILE_PATH)
if os.path.exists(str(self.path)):
users = load_dict(User, str(self.path))
else:
users = None
if users:
self.users = users

def save(self):
save_dict(self.users, USER_FILE_PATH)
save_dict(self.users, str(self.path))

def get_user(self, email: str) -> Optional[User]:
if email not in self.users:
Expand All @@ -113,14 +107,9 @@ def __repr__(self) -> str:
string += f"{email}: {user}"
return string

# def key_for_email(self, email: str) -> int | None:
# user = self.get_user(email)
# if user:
# return user.public_key
# return None


USERS = Users()
def get_users(request: Request) -> Users:
return request.state.users


def create_folders(folders: list[str]) -> None:
Expand All @@ -133,17 +122,22 @@ def create_folders(folders: list[str]) -> None:
async def lifespan(app: FastAPI):
# Startup
print("> Starting Server")
settings = ServerSettings()
print(settings)

print("> Creating Folders")
create_folders(FOLDERS)

create_folders(settings.folders)

print("> Loading Users")
print(USERS)
users = Users(path=settings.user_file_path)
print(users)

state = {
"user_manager": UserManager(),
yield {
"server_settings": settings,
"users": users,
}

yield state

print("> Shutting down server")


Expand Down Expand Up @@ -193,7 +187,10 @@ async def get_wheel(request: Request, path: str):
return filename


def get_file_list(directory="."):
def get_file_list(directory: str | Path = ".") -> list[dict[str, Any]]:
# TODO rewrite with pathlib
directory = str(directory)

file_list = []
for item in os.listdir(directory):
item_path = os.path.join(directory, item)
Expand All @@ -207,9 +204,8 @@ def get_file_list(directory="."):


@app.get("/datasites", response_class=HTMLResponse)
async def list_datasites(request: Request):
datasite_path = os.path.join(SNAPSHOT_FOLDER)
files = get_file_list(datasite_path)
async def list_datasites(request: Request, server_settings: ServerSettings = Depends(get_server_settings)):
files = get_file_list(server_settings.snapshot_folder)
template_path = current_dir / "templates" / "datasites.html"
html = ""
with open(template_path) as f:
Expand All @@ -227,17 +223,22 @@ async def list_datasites(request: Request):


@app.get("/datasites/{path:path}", response_class=HTMLResponse)
async def browse_datasite(request: Request, path: str):
async def browse_datasite(
request: Request,
path: str,
server_settings: ServerSettings = Depends(get_server_settings),
):
if path == "": # Check if path is empty (meaning "/datasites/")
return RedirectResponse(url="/datasites")

snapshot_folder = str(server_settings.snapshot_folder)
datasite_part = path.split("/")[0]
datasites = get_datasites(SNAPSHOT_FOLDER)
datasites = get_datasites(snapshot_folder)
if datasite_part in datasites:
slug = path[len(datasite_part) :]
if slug == "":
slug = "/"
datasite_path = os.path.join(SNAPSHOT_FOLDER, datasite_part)
datasite_path = os.path.join(snapshot_folder, datasite_part)
datasite_public = datasite_path + "/public"
if not os.path.exists(datasite_public):
return "No public datasite"
Expand Down Expand Up @@ -286,24 +287,24 @@ async def browse_datasite(request: Request, path: str):


@app.post("/register")
async def register(request: Request):
async def register(request: Request, users: Users = Depends(get_users)):
data = await request.json()
email = data["email"]
token = USERS.create_user(email)
token = users.create_user(email)
print(f"> {email} registering: {token}")
return JSONResponse({"status": "success", "token": token}, status_code=200)


@app.post("/write")
async def write(request: Request):
async def write(request: Request, server_settings: ServerSettings = Depends(get_server_settings)):
try:
data = await request.json()
email = data["email"]
change_dict = data["change"]
change_dict["kind"] = FileChangeKind(change_dict["kind"])
change = FileChange(**change_dict)

change.sync_folder = os.path.abspath(SNAPSHOT_FOLDER)
change.sync_folder = os.path.abspath(str(server_settings.snapshot_folder))
result = True
accepted = True
if change.newer():
Expand Down Expand Up @@ -351,13 +352,13 @@ async def write(request: Request):


@app.post("/read")
async def read(request: Request):
async def read(request: Request, server_settings: ServerSettings = Depends(get_server_settings)):
data = await request.json()
email = data["email"]
change_dict = data["change"]
change_dict["kind"] = FileChangeKind(change_dict["kind"])
change = FileChange(**change_dict)
change.sync_folder = os.path.abspath(SNAPSHOT_FOLDER)
change.sync_folder = os.path.abspath(str(server_settings.snapshot_folder))

json_dict = {"change": change.to_dict()}

Expand All @@ -380,13 +381,14 @@ async def read(request: Request):


@app.post("/dir_state")
async def dir_state(request: Request):
async def dir_state(request: Request, server_settings: ServerSettings = Depends(get_server_settings)):
try:
data = await request.json()
email = data["email"]
sub_path = data["sub_path"]
full_path = os.path.join(SNAPSHOT_FOLDER, sub_path)
remote_dir_state = hash_dir(SNAPSHOT_FOLDER, sub_path)
snapshot_folder = str(server_settings.snapshot_folder)
full_path = os.path.join(snapshot_folder, sub_path)
remote_dir_state = hash_dir(snapshot_folder, sub_path)

# get the top level perm file
perm_tree = PermissionTree.from_path(full_path)
Expand All @@ -404,8 +406,8 @@ async def dir_state(request: Request):


@app.get("/list_datasites")
async def datasites(request: Request):
datasites = get_datasites(SNAPSHOT_FOLDER)
async def datasites(request: Request, server_settings: ServerSettings = Depends(get_server_settings)):
datasites = get_datasites(server_settings.snapshot_folder)
response_json = {"datasites": datasites}
if datasites:
return JSONResponse({"status": "success"} | response_json, status_code=200)
Expand Down
23 changes: 15 additions & 8 deletions syftbox/server/settings.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,32 @@
from pathlib import Path

from fastapi import Request
from pydantic_settings import BaseSettings, SettingsConfigDict


class SMTPSettings(BaseSettings):
class SMTSettings(BaseSettings):
model_config = SettingsConfigDict(env_prefix="SMTP_")
tls: bool = True

use_tls: bool = False
port: int = 587
host: str
username: str
password: str
sender: str
host: str = "localhost"
username: str = "syftbox"
password: str = "syftbox"
email_sender: str = "noreply@openmined.org"


class ServerSettings(BaseSettings):
model_config = SettingsConfigDict(env_prefix="SYFTBOX_")

data_folder: Path = Path("data")
snapshot_folder: Path = Path("data/snapshot")
user_file_path: Path = Path("data/users.json")
smtp: "SMTPSettings" = SMTPSettings()
smtp: SMTSettings = SMTSettings()

@property
def folders(self):
def folders(self) -> list[Path]:
return [self.data_folder, self.snapshot_folder]


def get_server_settings(request: Request) -> ServerSettings:
return request.state.server_settings
16 changes: 16 additions & 0 deletions tests/server/settings_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import os
from pathlib import Path

from syftbox.server.settings import ServerSettings


def test_server_settings_from_env():
os.environ["SYFTBOX_DATA_FOLDER"] = "data_folder"
os.environ["SYFTBOX_SNAPSHOT_FOLDER"] = "data_folder/snapshot_folder"
os.environ["SYFTBOX_USER_FILE_PATH"] = "data_folder/user_file_path.json"

settings = ServerSettings()
print(settings)
assert settings.data_folder == Path("data_folder")
assert settings.snapshot_folder == Path("data_folder/snapshot_folder")
assert settings.user_file_path == Path("data_folder/user_file_path.json")
33 changes: 1 addition & 32 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit aa8c8c0

Please sign in to comment.