KUMOS/Server/Includes/FileSystem.pbi
2026-05-02 15:49:06 +02:00

299 lines
9.2 KiB
Plaintext

Module FileSystem
EnableExplicit
; Private constants
#BlobsDir = "blobs/"
;- Helpers
Procedure CanAccess(UserID, NodeID)
Protected OwnerID = Database::FSGetOwner(NodeID)
ProcedureReturn Bool(OwnerID = UserID Or OwnerID = 0)
EndProcedure
Procedure ResolveFromQuery(*Request, UserID)
Protected IDStr.s = General::GetQueryField(*Request, "id")
If IDStr <> "" : ProcedureReturn Val(IDStr) : EndIf
ProcedureReturn Database::FSResolve(UserID, General::GetQueryField(*Request, "path"))
EndProcedure
Procedure ResolveFromPost(*Request, UserID)
Protected IDStr.s = General::GetPostField(*Request, "id")
If IDStr <> "" : ProcedureReturn Val(IDStr) : EndIf
ProcedureReturn Database::FSResolve(UserID, General::GetPostField(*Request, "path"))
EndProcedure
Procedure ResolveParent(UserID, Path.s)
While Right(Path, 1) = "/" : Path = Left(Path, Len(Path) - 1) : Wend
ProcedureReturn Database::FSResolve(UserID, Path)
EndProcedure
;- Public procedures
; GET /api/fs/list?path=... or ?id=...
Procedure HandleList(*Request)
Protected UserID, NodeID
Protected Username.s
Username = Auth::GetSessionUser(*Request)
If Username = ""
General::RespondJSON(*Request, ~"{\"error\":\"Unauthorized\"}", "401 Unauthorized")
ProcedureReturn
EndIf
UserID = Database::FindUser(Username)
NodeID = ResolveFromQuery(*Request, UserID)
If NodeID = 0
General::RespondJSON(*Request, ~"{\"error\":\"Not found\"}", "404 Not Found")
ProcedureReturn
EndIf
If Not CanAccess(UserID, NodeID)
General::RespondJSON(*Request, ~"{\"error\":\"Forbidden\"}", "403 Forbidden")
ProcedureReturn
EndIf
General::RespondJSON(*Request, Database::FSList(NodeID))
EndProcedure
; GET /api/fs/stat?path=... or ?id=...
Procedure HandleStat(*Request)
Protected UserID, NodeID
Protected Username.s, Stat.s
Username = Auth::GetSessionUser(*Request)
If Username = ""
General::RespondJSON(*Request, ~"{\"error\":\"Unauthorized\"}", "401 Unauthorized")
ProcedureReturn
EndIf
UserID = Database::FindUser(Username)
NodeID = ResolveFromQuery(*Request, UserID)
If NodeID = 0
General::RespondJSON(*Request, ~"{\"error\":\"Not found\"}", "404 Not Found")
ProcedureReturn
EndIf
If Not CanAccess(UserID, NodeID)
General::RespondJSON(*Request, ~"{\"error\":\"Forbidden\"}", "403 Forbidden")
ProcedureReturn
EndIf
Stat = Database::FSStat(NodeID)
If Stat = ""
General::RespondJSON(*Request, ~"{\"error\":\"Not found\"}", "404 Not Found")
Else
General::RespondJSON(*Request, Stat)
EndIf
EndProcedure
; GET /api/fs/read?path=... or ?id=...
Procedure HandleRead(*Request)
Protected UserID, NodeID
Protected IsDir.Integer
Protected Username.s
Username = Auth::GetSessionUser(*Request)
If Username = ""
General::RespondJSON(*Request, ~"{\"error\":\"Unauthorized\"}", "401 Unauthorized")
ProcedureReturn
EndIf
UserID = Database::FindUser(Username)
NodeID = ResolveFromQuery(*Request, UserID)
If NodeID = 0
General::RespondJSON(*Request, ~"{\"error\":\"Not found\"}", "404 Not Found")
ProcedureReturn
EndIf
If Not CanAccess(UserID, NodeID)
General::RespondJSON(*Request, ~"{\"error\":\"Forbidden\"}", "403 Forbidden")
ProcedureReturn
EndIf
Database::FSGetOwner(NodeID, @IsDir)
If IsDir\i
General::RespondJSON(*Request, ~"{\"error\":\"Is a directory\"}", "400 Bad Request")
ProcedureReturn
EndIf
If Not FastCGI::RespondFile(*Request, #BlobsDir + NodeID)
General::RespondJSON(*Request, ~"{\"error\":\"Blob missing\"}", "404 Not Found")
EndIf
EndProcedure
; POST /api/fs/write body: path=...&content=... [&mime_type=...]
Procedure HandleWrite(*Request)
Protected UserID, ParentID, NodeID, FileID, BufLen, Size, *Buf
Protected Username.s, Path.s, Content.s, MimeType.s
Username = Auth::GetSessionUser(*Request)
If Username = ""
General::RespondJSON(*Request, ~"{\"error\":\"Unauthorized\"}", "401 Unauthorized")
ProcedureReturn
EndIf
UserID = Database::FindUser(Username)
Path = General::GetPostField(*Request, "path")
Content = General::GetPostField(*Request, "content")
MimeType = General::GetPostField(*Request, "mime_type")
If Path = ""
General::RespondJSON(*Request, ~"{\"error\":\"Missing path\"}")
ProcedureReturn
EndIf
If MimeType = "" : MimeType = "text/plain" : EndIf
ParentID = ResolveParent(UserID, GetPathPart(Path))
If ParentID = 0
General::RespondJSON(*Request, ~"{\"error\":\"Parent directory not found\"}", "404 Not Found")
ProcedureReturn
EndIf
If Not CanAccess(UserID, ParentID)
General::RespondJSON(*Request, ~"{\"error\":\"Forbidden\"}", "403 Forbidden")
ProcedureReturn
EndIf
NodeID = Database::FSResolve(UserID, Path)
If NodeID = 0
NodeID = Database::FSCreateFile(UserID, ParentID, GetFilePart(Path), MimeType)
EndIf
If NodeID = 0
General::RespondJSON(*Request, ~"{\"error\":\"Could not create node\"}", "500 Internal Server Error")
ProcedureReturn
EndIf
FileID = CreateFile(#PB_Any, #BlobsDir + NodeID)
If FileID
BufLen = StringByteLength(Content, #PB_UTF8)
If BufLen > 0
*Buf = AllocateMemory(BufLen)
If *Buf
PokeS(*Buf, Content, -1, #PB_UTF8 | #PB_String_NoZero)
WriteData(FileID, *Buf, BufLen)
FreeMemory(*Buf)
EndIf
EndIf
Size = Lof(FileID)
CloseFile(FileID)
Database::FSUpdateFile(NodeID, Size)
General::RespondJSON(*Request, ~"{\"success\":true,\"id\":" + NodeID + "}")
Else
General::RespondJSON(*Request, ~"{\"error\":\"Could not write blob\"}", "500 Internal Server Error")
EndIf
EndProcedure
; POST /api/fs/mkdir body: path=...
Procedure HandleMkdir(*Request)
Protected UserID, ParentID, DirID
Protected Username.s, Path.s
Username = Auth::GetSessionUser(*Request)
If Username = ""
General::RespondJSON(*Request, ~"{\"error\":\"Unauthorized\"}", "401 Unauthorized")
ProcedureReturn
EndIf
UserID = Database::FindUser(Username)
Path = General::GetPostField(*Request, "path")
If Path = ""
General::RespondJSON(*Request, ~"{\"error\":\"Missing path\"}")
ProcedureReturn
EndIf
ParentID = ResolveParent(UserID, GetPathPart(Path))
If ParentID = 0
General::RespondJSON(*Request, ~"{\"error\":\"Parent not found\"}", "404 Not Found")
ProcedureReturn
EndIf
If Not CanAccess(UserID, ParentID)
General::RespondJSON(*Request, ~"{\"error\":\"Forbidden\"}", "403 Forbidden")
ProcedureReturn
EndIf
DirID = Database::FSMkdir(UserID, ParentID, GetFilePart(Path))
If DirID = 0
General::RespondJSON(*Request, ~"{\"error\":\"Already exists or could not create\"}", "409 Conflict")
Else
General::RespondJSON(*Request, ~"{\"success\":true,\"id\":" + DirID + "}")
EndIf
EndProcedure
; POST /api/fs/delete body: path=... or id=...
Procedure HandleDelete(*Request)
Protected UserID, NodeID, OwnerID
Protected IsDir.Integer
Protected Username.s
Username = Auth::GetSessionUser(*Request)
If Username = ""
General::RespondJSON(*Request, ~"{\"error\":\"Unauthorized\"}", "401 Unauthorized")
ProcedureReturn
EndIf
UserID = Database::FindUser(Username)
NodeID = ResolveFromPost(*Request, UserID)
If NodeID = 0
General::RespondJSON(*Request, ~"{\"error\":\"Not found\"}", "404 Not Found")
ProcedureReturn
EndIf
OwnerID = Database::FSGetOwner(NodeID, @IsDir)
If OwnerID <> UserID
General::RespondJSON(*Request, ~"{\"error\":\"Forbidden\"}", "403 Forbidden")
ProcedureReturn
EndIf
If Not IsDir\i : DeleteFile(#BlobsDir + NodeID) : EndIf
Database::FSDelete(NodeID)
General::RespondJSON(*Request, ~"{\"success\":true}")
EndProcedure
; POST /api/fs/move body: path=...&to=... or id=...&to_parent_id=...&name=...
Procedure HandleMove(*Request)
Protected UserID, NodeID, NewParentID, OwnerID
Protected IsDir.Integer
Protected Username.s, IDStr.s, NewName.s, FromPath.s, ToPath.s
Username = Auth::GetSessionUser(*Request)
If Username = ""
General::RespondJSON(*Request, ~"{\"error\":\"Unauthorized\"}", "401 Unauthorized")
ProcedureReturn
EndIf
UserID = Database::FindUser(Username)
IDStr = General::GetPostField(*Request, "id")
If IDStr <> ""
NodeID = Val(IDStr)
NewParentID = Val(General::GetPostField(*Request, "to_parent_id"))
NewName = General::GetPostField(*Request, "name")
Else
FromPath = General::GetPostField(*Request, "path")
ToPath = General::GetPostField(*Request, "to")
NodeID = Database::FSResolve(UserID, FromPath)
NewParentID = ResolveParent(UserID, GetPathPart(ToPath))
NewName = GetFilePart(ToPath)
EndIf
If NodeID = 0 Or NewParentID = 0 Or NewName = ""
General::RespondJSON(*Request, ~"{\"error\":\"Not found\"}", "404 Not Found")
ProcedureReturn
EndIf
OwnerID = Database::FSGetOwner(NodeID, @IsDir)
If OwnerID <> UserID
General::RespondJSON(*Request, ~"{\"error\":\"Forbidden\"}", "403 Forbidden")
ProcedureReturn
EndIf
If Not CanAccess(UserID, NewParentID)
General::RespondJSON(*Request, ~"{\"error\":\"Forbidden\"}", "403 Forbidden")
ProcedureReturn
EndIf
Database::FSMove(NodeID, NewParentID, NewName)
General::RespondJSON(*Request, ~"{\"success\":true}")
EndProcedure
EndModule
; IDE Options = PureBasic 6.30 (Windows - x64)
; CursorPosition = 2
; Folding = BA-
; EnableXP
; DPIAware