Initial commit
This commit is contained in:
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,142 @@
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from pydantic import BaseModel
|
||||
from pathlib import Path
|
||||
import os
|
||||
from typing import Optional
|
||||
from modules.logger_tool import initialise_logger
|
||||
|
||||
# Initialize logger
|
||||
logger = initialise_logger(__name__, os.getenv("LOG_LEVEL", "info"), os.getenv("LOG_PATH", "/logs"))
|
||||
|
||||
# Create router
|
||||
router = APIRouter()
|
||||
|
||||
# === CONFIG ===
|
||||
SOLID_BASE_URL = os.getenv("SOLID_BASE_URL", "http://solid.classroomcopilot.test")
|
||||
SOLID_STORAGE_PATH = os.getenv("SOLID_STORAGE_PATH", "/data/users") # Path relative to Solid server data directory
|
||||
KEYCLOAK_ISSUER = os.getenv("KEYCLOAK_ISSUER", "http://keycloak.classroomcopilot.test/realms/ClassroomCopilot")
|
||||
|
||||
# === DATA MODEL ===
|
||||
class UserCreateRequest(BaseModel):
|
||||
username: str
|
||||
full_name: str
|
||||
email: Optional[str] = None
|
||||
|
||||
# === UTILITIES ===
|
||||
def create_profile_card(username: str, full_name: str, email: Optional[str] = None) -> str:
|
||||
webid = f"{SOLID_BASE_URL}/users/{username}/profile/card#me"
|
||||
profile_content = f"""@prefix foaf: <http://xmlns.com/foaf/0.1/> .
|
||||
@prefix solid: <http://www.w3.org/ns/solid/terms#> .
|
||||
@prefix vcard: <http://www.w3.org/2006/vcard/ns#> .
|
||||
|
||||
<#me> a foaf:Person ;
|
||||
foaf:name "{full_name}" ;
|
||||
solid:oidcIssuer <{KEYCLOAK_ISSUER}> ."""
|
||||
|
||||
if email:
|
||||
profile_content += f"""
|
||||
vcard:hasEmail <mailto:{email}> ."""
|
||||
|
||||
return profile_content
|
||||
|
||||
def create_acl_file(username: str) -> str:
|
||||
return f"""@prefix acl: <http://www.w3.org/ns/acl#> .
|
||||
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
|
||||
|
||||
<#authorization>
|
||||
a acl:Authorization ;
|
||||
acl:agent <{SOLID_BASE_URL}/users/{username}/profile/card#me> ;
|
||||
acl:accessTo <./> ;
|
||||
acl:default <./> ;
|
||||
acl:mode acl:Read, acl:Write, acl:Control ;
|
||||
acl:agentClass foaf:Agent ."""
|
||||
|
||||
def create_public_acl() -> str:
|
||||
return """@prefix acl: <http://www.w3.org/ns/acl#> .
|
||||
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
|
||||
|
||||
<#authorization>
|
||||
a acl:Authorization ;
|
||||
acl:agentClass foaf:Agent ;
|
||||
acl:accessTo <./> ;
|
||||
acl:default <./> ;
|
||||
acl:mode acl:Read ."""
|
||||
|
||||
# === ENDPOINTS ===
|
||||
@router.post("/provision")
|
||||
async def provision_user(data: UserCreateRequest):
|
||||
"""
|
||||
Create a new Solid pod for a user with their profile card and ACL files.
|
||||
"""
|
||||
try:
|
||||
# Create user directory structure
|
||||
user_base = Path(SOLID_STORAGE_PATH) / data.username
|
||||
profile_dir = user_base / "profile"
|
||||
public_dir = user_base / "public"
|
||||
private_dir = user_base / "private"
|
||||
|
||||
# Create directories
|
||||
for dir_path in [profile_dir, public_dir, private_dir]:
|
||||
dir_path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Create profile card
|
||||
profile_path = profile_dir / "card.ttl"
|
||||
profile_content = create_profile_card(data.username, data.full_name, data.email)
|
||||
profile_path.write_text(profile_content)
|
||||
|
||||
# Create ACL files
|
||||
profile_acl_path = profile_dir / ".acl"
|
||||
profile_acl_content = create_acl_file(data.username)
|
||||
profile_acl_path.write_text(profile_acl_content)
|
||||
|
||||
public_acl_path = public_dir / ".acl"
|
||||
public_acl_content = create_public_acl()
|
||||
public_acl_path.write_text(public_acl_content)
|
||||
|
||||
private_acl_path = private_dir / ".acl"
|
||||
private_acl_content = create_acl_file(data.username)
|
||||
private_acl_path.write_text(private_acl_content)
|
||||
|
||||
webid = f"{SOLID_BASE_URL}/users/{data.username}/profile/card#me"
|
||||
|
||||
logger.info(f"Successfully provisioned Solid pod for user {data.username}")
|
||||
|
||||
return {
|
||||
"message": "User pod created successfully",
|
||||
"webid": webid,
|
||||
"profile_path": str(profile_path),
|
||||
"pod_structure": {
|
||||
"profile": str(profile_dir),
|
||||
"public": str(public_dir),
|
||||
"private": str(private_dir)
|
||||
}
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error provisioning Solid pod for user {data.username}: {str(e)}")
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
@router.get("/status/{username}")
|
||||
async def check_pod_status(username: str):
|
||||
"""
|
||||
Check if a user's Solid pod exists and return its status.
|
||||
"""
|
||||
try:
|
||||
user_base = Path(SOLID_STORAGE_PATH) / username
|
||||
profile_path = user_base / "profile" / "card.ttl"
|
||||
|
||||
if not profile_path.exists():
|
||||
return {
|
||||
"exists": False,
|
||||
"message": "Pod not found"
|
||||
}
|
||||
|
||||
return {
|
||||
"exists": True,
|
||||
"webid": f"{SOLID_BASE_URL}/users/{username}/profile/card#me",
|
||||
"profile_path": str(profile_path)
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error checking pod status for user {username}: {str(e)}")
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
Reference in New Issue
Block a user