package webfilesystem import ( "context" "errors" "path" "personalwebsite/apps/appCtx" "strings" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" "go.mongodb.org/mongo-driver/mongo" ) type WebFileSystem struct { webfsCollection *mongo.Collection webfsFilesTable *mongo.Collection webfsFilesData *mongo.Collection ctx context.Context } func NewWebFileSystem(mongoClient *mongo.Client, dBName string, fsCollectionName string) *WebFileSystem { return &WebFileSystem{ webfsCollection: mongoClient.Database(dBName).Collection(fsCollectionName), // TODO Check collection is exist webfsFilesTable: mongoClient.Database(dBName).Collection("webfs-table"), // TODO Check collection is exist, //FIXME webfsFilesData: mongoClient.Database(dBName).Collection("webfs-data"), // TODO Check collection is exist, //FIXME ctx: context.Background(), } } type FileHeader struct { MongoId primitive.ObjectID `bson:"_id" json:"-"` Name string `bson:"name" json:"name"` Type string `bson:"type" json:"type"` Icon string `bson:"icon" json:"icon"` Data primitive.ObjectID `bson:"data_id" json:"-"` //TODO rename to DataId } func (fh *FileHeader) GetType() string { //TODO return by extension and etc return fh.Type } func (fh *FileHeader) GetExtension() string { return strings.Split(fh.Name, ".")[len(strings.Split(fh.Name, "."))-1] } type BinaryFileData struct { MongoId primitive.ObjectID `bson:"_id" json:"-"` Bin []byte `bson:"bin" json:"-"` } type PlainTextFileData struct { MongoId primitive.ObjectID `bson:"_id" json:"-"` Data string `bson:"data" json:"-"` } type ObjectLinkFileData struct { MongoId primitive.ObjectID `bson:"_id" json:"-"` Link_id primitive.ObjectID `bson:"link_id" json:"-"` } type PathLinkFileData struct { MongoId primitive.ObjectID `bson:"_id" json:"-"` Path string `bson:"path" json:"path"` } type FrontEndFile struct { Name string `json:"name"` Type string `json:"filetype"` //TODO: Rename to Type ParentPath string `json:"parentPath"` } //TODO To private, get name from path and set it to file struct; force set newObjectID func (fs *WebFileSystem) Write(filePath string, file *FileHeader, data interface{}) (primitive.ObjectID, primitive.ObjectID, error) { if fs.CheckFileExist(filePath) { return primitive.NilObjectID, primitive.NilObjectID, errors.New("file exists") } headerId, dataId, err := fs.writeFileToMongo(file, data) if err != nil { return primitive.NilObjectID, primitive.NilObjectID, err } parentPath := fs.GetParentPath(filePath) parentDir, err := fs.FindFile(parentPath) if err != nil { return primitive.NilObjectID, primitive.NilObjectID, err } if parentDir.IsZero() { return primitive.NilObjectID, primitive.NilObjectID, errors.New("parent dir not found") } err = fs.AppendChildToDirectory(headerId, parentDir) if err != nil { return primitive.NilObjectID, primitive.NilObjectID, err } return headerId, dataId, nil } func (fs *WebFileSystem) UpdateFileData(file *FileHeader, data interface{}) error { update := bson.M{"$set": data} // println("updating data file: " + file.MongoId.String()) res, err := fs.webfsFilesData.UpdateByID(fs.ctx, file.Data, update) if err != nil { return err } if res.ModifiedCount < 1 { return errors.New("no data updated") } return err } func (fs *WebFileSystem) InitFS() error { //FIXME Can't set parent_id to itself rootData := DirectoryData{ MongoId: primitive.NewObjectID(), Parent: primitive.NewObjectID(), Children: []primitive.ObjectID{}, } rootHeader := FileHeader{ MongoId: primitive.NewObjectID(), Name: "/", Type: "directory", Icon: "", } _, _, err := fs.writeFileToMongo(&rootHeader, &rootData) if err != nil { return err } return nil } //TODO get on boot and safe to struct func (fs *WebFileSystem) GetRootDir() (*FileHeader, *DirectoryData, error) { filter := primitive.M{ "name": "/", } res := fs.webfsFilesTable.FindOne(fs.ctx, filter) if res == nil { return nil, nil, errors.New("TODO") //TODO } rootDir := FileHeader{} err := res.Decode(&rootDir) if res == nil { return nil, nil, err } filterData := primitive.M{ "_id": rootDir.Data, } resData := fs.webfsFilesData.FindOne(fs.ctx, filterData) if resData.Err() != nil { return nil, nil, err } rootDirData := DirectoryData{} err = resData.Decode(&rootDirData) if err != nil { return nil, nil, err } return &rootDir, &rootDirData, nil } func (fs *WebFileSystem) Move(sourcePath string, targetPath string) error { //TODO Check about move dir to itself //TODO if it a dir fix its parentId //TODO Rename file if in path set new name for it sourceFileHeader, err := fs.Read(sourcePath, nil) if err != nil { return err } sourceParentDirPath := fs.GetParentPath(sourcePath) sourceParentDirData := DirectoryData{} sourceParentDirHeader, err := fs.Read(sourceParentDirPath, &sourceParentDirData) if err != nil { return err } if sourceParentDirHeader.Type != "directory" { return errors.New("source parent object is not a directory") } targetParentDirPath := fs.GetParentPath(targetPath) targetParentDirData := DirectoryData{} targetParentDirHeader, err := fs.Read(targetParentDirPath, &targetParentDirData) if err != nil { return err } //TODO: use moveByID() if targetParentDirHeader.Type != "directory" { return errors.New("target parent object is not a directory") } err = fs.RemoveChildToDirectory(sourceFileHeader.MongoId, sourceParentDirHeader.MongoId) if err != nil { return err } err = fs.AppendChildToDirectory(sourceFileHeader.MongoId, targetParentDirHeader.MongoId) if err != nil { return err } return nil } func (fs *WebFileSystem) moveByID(fileID primitive.ObjectID, oldDirID primitive.ObjectID, newDirID primitive.ObjectID, isForced bool) error { //TODO check, if directory is child for parent-parent dir and other shit targetDirData := DirectoryData{} targetDirHeader, err := fs.readFSDocs(newDirID, &targetDirData) if err != nil { return err } if targetDirHeader.Type != "directory" { return errors.New("parent directory path is bad") } for _, childID := range targetDirData.Children { if childID == fileID { return errors.New("file already in this directory") } } err = fs.RemoveChildToDirectory(fileID, oldDirID) if err != nil && !isForced { return err } err = fs.AppendChildToDirectory(fileID, newDirID) if err != nil { return err } return nil } func (fs *WebFileSystem) Remove(filePath string) error { parentPath := fs.GetParentPath(filePath) parentDirId, err := fs.FindFile(parentPath) if err != nil { return err } //TODO: Check, if parent file is dir parentDirData := DirectoryData{} parentDir, err := fs.readFSDocs(parentDirId, &parentDirData) if err != nil { return err } fileId, err := fs.FindFile(filePath) if err != nil { return err } newChildren := []primitive.ObjectID{} for _, childId := range parentDirData.Children { if childId != fileId { newChildren = append(newChildren, childId) } } parentDirData.Children = newChildren err = fs.UpdateFileData(parentDir, parentDirData) if err != nil { return err } if parentPath == "/orfaned" { //TODO path to struct err := fs.removeFromMongo(fileId) if err != nil { return err } } return nil } func (fs *WebFileSystem) CreateObjectLink(sourcePath string, linkPath string) error { linkSplittedPath := fs.SplitPath(linkPath) linkFileName := linkSplittedPath[len(linkSplittedPath)-1] linkParentDirPath := fs.GetParentPath(linkPath) linkParentDirPathID, err := fs.FindFile(linkParentDirPath) if err != nil { return err } sourceFileID, err := fs.FindFile(sourcePath) if err != nil { return err } newLinkHeader := FileHeader{ MongoId: primitive.NewObjectID(), Name: linkFileName, Type: "objectlink", Icon: "", Data: primitive.NewObjectID(), } newLinkData := ObjectLinkFileData{ MongoId: primitive.NewObjectID(), Link_id: sourceFileID, } linkFileID, _, err := fs.Write(sourcePath, &newLinkHeader, newLinkData) if err != nil { return err } err = fs.AppendChildToDirectory(linkFileID, linkParentDirPathID) if err != nil { return err } return nil } func (fs *WebFileSystem) CreatePathLink(sourcePath string, linkPath string) (primitive.ObjectID, primitive.ObjectID, error) { sourceFileHeader, err := fs.Read(sourcePath, nil) if err != nil { return primitive.NilObjectID, primitive.NilObjectID, err } if sourceFileHeader.Type == "pathlink" { return primitive.NilObjectID, primitive.NilObjectID, errors.New("multiplies pathlinks not supported yet") //TODO } splittedPath := strings.Split(linkPath, "/") newLinkHeader := FileHeader{ MongoId: primitive.NewObjectID(), Name: splittedPath[len(splittedPath)-1], Type: "pathlink", Icon: "", Data: primitive.NewObjectID(), } newLinkData := PathLinkFileData{ MongoId: primitive.NewObjectID(), Path: sourcePath, } headerID, dataID, err := fs.Write(linkPath, &newLinkHeader, newLinkData) if err != nil { return primitive.NilObjectID, primitive.NilObjectID, err } return headerID, dataID, nil } func (fs *WebFileSystem) SplitPath(path string) []string { resPath := []string{} splittedPath := strings.Split(path, "/") splittedPath[0] = "/" for _, split := range splittedPath { if split != "" { resPath = append(resPath, split) } } return resPath } func (fs *WebFileSystem) GetExtension(filename string) string { splittedName := strings.Split(filename, ".") return splittedName[len(splittedName)-1] } func (fs *WebFileSystem) GetParentPath(path string) string { splittedPath := fs.SplitPath(path) if len(splittedPath) > 1 { return "/" + strings.Join(splittedPath[1:len(splittedPath)-1], "/") } return "/" } func (fs *WebFileSystem) CheckFileExist(filePath string) bool { fileHeader, err := fs.Read(filePath, nil) if err == nil && fileHeader != nil { return true } return false } func (fs *WebFileSystem) RelativeToAbsolute(appCtx appCtx.AppContext, filePath string) string { switch filePath[0] { case '.': return path.Join(appCtx.RunPath, filePath) case ':': filePath = strings.ReplaceAll(filePath, ":", "") return path.Join(appCtx.BundlePath, filePath) default: return filePath } }