package module // import ( // "context" // "fmt" // "io" // "mime/multipart" // "os" // "path/filepath" // "time" // "forge.cadoles.com/arcad/edge/pkg/app" // "forge.cadoles.com/arcad/edge/pkg/bus" // "github.com/dop251/goja" // "github.com/genjidb/genji" // "github.com/genjidb/genji/database" // "github.com/genjidb/genji/document" // "github.com/google/uuid" // "github.com/pkg/errors" // "github.com/spf13/afero" // "gitlab.com/wpetit/goweb/logger" // ) // const ( // fileKeyID = "id" // collectionFiles = "files" // ) // type FileEntry struct { // ID string // Filename string // Size int64 // ContentType string // CreatedAt string // Metadata map[string]interface{} // } // type FileModule struct { // appID app.ID // backend *app.Backend // bus bus.Bus // db *AppDatabaseMixin // dataDir string // fs afero.Fs // } // func (m *FileModule) Name() string { // return "file" // } // func (m *FileModule) Export(export *goja.Object) { // // if err := export.Set("ls", m.ls); err != nil { // // panic(errors.Wrap(err, "could not set 'save' function")) // // } // } // func (m *FileModule) logAndPanic(msg string, err error) { // err = errors.Wrap(err, msg) // logger.Error(context.Background(), msg, logger.E(err)) // panic(errors.WithStack(err)) // } // func (m *FileModule) handleMessages() { // ctx := context.Background() // ns := createAppMessageNamespace(m.appID) // go func() { // err := m.bus.Reply(ctx, ns, MessageTypeUploadRequest, func(msg bus.Message) (bus.Message, error) { // uploadRequest, ok := msg.(*MessageUploadRequest) // if !ok { // return nil, errors.Wrapf(bus.ErrUnexpectedMessage, "expected message upload request, got '%s'", msg.MessageType()) // } // res, err := m.handleUploadRequest(uploadRequest) // if err != nil { // logger.Error(ctx, "could not handle upload request", logger.E(errors.WithStack(err))) // return nil, errors.WithStack(err) // } // logger.Debug(ctx, "upload request response", logger.F("response", res)) // return res, nil // }) // if err != nil { // panic(errors.WithStack(err)) // } // }() // err := m.bus.Reply(ctx, ns, MessageTypeDownloadRequest, func(msg bus.Message) (bus.Message, error) { // downloadRequest, ok := msg.(*MessageDownloadRequest) // if !ok { // return nil, errors.Wrapf(bus.ErrUnexpectedMessage, "expected message download request, got '%s'", msg.MessageType()) // } // res, err := m.handleDownloadRequest(downloadRequest) // if err != nil { // logger.Error(ctx, "could not handle download request", logger.E(errors.WithStack(err))) // return nil, errors.WithStack(err) // } // return res, nil // }) // if err != nil { // panic(errors.WithStack(err)) // } // } // func (m *FileModule) handleUploadRequest(req *MessageUploadRequest) (*MessageUploadResponse, error) { // fileInfo := map[string]interface{}{ // "filename": req.Header.Filename, // "contentType": req.Header.Header.Get("Content-Type"), // "size": req.Header.Size, // "metadata": req.Metadata, // } // res := NewMessageUploadResponse(req.AppID, req.UserID, req.RequestID) // fileID := uuid.New().String() // result, err := m.backend.ExecFuncByName("onFileUpload", req.UserID, fileID, fileInfo) // if err != nil { // if errors.Is(err, app.ErrFuncDoesNotExist) { // res.Allow = false // return res, nil // } // return nil, errors.WithStack(err) // } // res.Allow = result.ToBoolean() // if res.Allow { // if err := m.saveFile(fileID, req.UserID, req.Header, req.File, req.Metadata); err != nil { // return nil, errors.WithStack(err) // } // res.FileID = fileID // } // return res, nil // } // func (m *FileModule) saveFile(fileID string, header *multipart.FileHeader, file multipart.File, metadata map[string]interface{}) error { // err := m.db.WithCollectionTx(collectionFiles, func(tx *genji.Tx) error { // entry := &FileEntry{ // ID: fileID, // Filename: header.Filename, // Size: header.Size, // ContentType: header.Header.Get("Content-Type"), // CreatedAt: time.Now().UTC().String(), // Metadata: metadata, // } // insertQuery := fmt.Sprintf("INSERT INTO `%s` VALUES ?", collectionFiles) // if err := tx.Exec(insertQuery, &entry); err != nil { // return errors.WithStack(err) // } // fileDir := m.getFileDir(fileID) // if err := m.fs.MkdirAll(fileDir, 0o755); err != nil { // return errors.WithStack(err) // } // filePath := filepath.Join(fileDir, fileID) // newFile, err := m.fs.Create(filePath) // if err != nil { // return errors.WithStack(err) // } // defer newFile.Close() // if _, err := io.Copy(newFile, file); err != nil { // return errors.WithStack(err) // } // return nil // }) // if err != nil { // return errors.WithStack(err) // } // return nil // } // func (m *FileModule) handleDownloadRequest(req *MessageDownloadRequest) (*MessageDownloadResponse, error) { // res := NewMessageDownloadResponse(req.AppID, req.UserID, req.RequestID) // result, err := m.backend.ExecFuncByName("onFileDownload", req.UserID, req.FileID) // if err != nil { // if errors.Is(err, app.ErrFuncDoesNotExist) { // res.Allow = false // return res, nil // } // return nil, errors.WithStack(err) // } // res.Allow = result.ToBoolean() // file, fileEntry, err := m.openFile(req.FileID) // if err != nil && !os.IsNotExist(errors.Cause(err)) { // return nil, errors.WithStack(err) // } // if file != nil { // res.File = file // } // if fileEntry != nil { // res.Filename = fileEntry.Filename // res.ContentType = fileEntry.ContentType // res.Size = fileEntry.Size // } // return res, nil // } // func (m *FileModule) openFile(fileID string) (afero.File, *FileEntry, error) { // var ( // fileEntry *FileEntry // file afero.File // ) // err := m.db.WithCollectionTx(collectionFiles, func(tx *genji.Tx) error { // selectQuery := fmt.Sprintf("SELECT * FROM `%s` WHERE id = ?", collectionFiles) // doc, err := tx.QueryDocument(selectQuery, fileID) // if err != nil { // if errors.Is(err, database.ErrDocumentNotFound) { // return nil // } // return errors.WithStack(err) // } // fileEntry = &FileEntry{} // if err := document.StructScan(doc, fileEntry); err != nil { // return errors.WithStack(err) // } // fileDir := m.getFileDir(fileID) // filePath := filepath.Join(fileDir, fileID) // file, err = m.fs.Open(filePath) // if err != nil { // file = nil // return errors.WithStack(err) // } // return nil // }) // if err != nil { // return nil, nil, errors.WithStack(err) // } // return file, fileEntry, nil // } // func (m *FileModule) getFileDir(fileID string) string { // return filepath.Join(m.dataDir, string(m.appID), "files", fileID[0:2], fileID[2:4], fileID[4:6]) // } // func FileModuleFactory(dataDir string, bus bus.Bus) app.BackendModuleFactory { // return func(appID app.ID, backend *app.Backend) app.BackendModule { // var fs afero.Fs // if dataDir == inMemory { // fs = afero.NewMemMapFs() // } else { // fs = afero.NewOsFs() // } // mod := &FileModule{ // dataDir: dataDir, // appID: appID, // bus: bus, // backend: backend, // db: NewAppDatabaseMixin(dataDir, "file.db", appID), // fs: fs, // } // go mod.handleMessages() // return mod // } // }