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