Add requesting and downloading a file
This commit is contained in:
parent
2c00b4976e
commit
279722d943
@ -21,6 +21,7 @@ import (
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
"xorm.io/xorm"
|
||||
|
||||
"code.vikunja.io/api/pkg/config"
|
||||
"code.vikunja.io/api/pkg/db"
|
||||
@ -77,7 +78,18 @@ func Create(f io.Reader, realname string, realsize uint64, a web.Auth) (file *Fi
|
||||
|
||||
// CreateWithMime creates a new file from an FileHeader and sets its mime type
|
||||
func CreateWithMime(f io.Reader, realname string, realsize uint64, a web.Auth, mime string) (file *File, err error) {
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
file, err = CreateWithMimeAndSession(s, f, realname, realsize, a, mime)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func CreateWithMimeAndSession(s *xorm.Session, f io.Reader, realname string, realsize uint64, a web.Auth, mime string) (file *File, err error) {
|
||||
// Get and parse the configured file size
|
||||
var maxSize datasize.ByteSize
|
||||
err = maxSize.UnmarshalText([]byte(config.FilesMaxSize.GetString()))
|
||||
@ -96,21 +108,13 @@ func CreateWithMime(f io.Reader, realname string, realsize uint64, a web.Auth, m
|
||||
Mime: mime,
|
||||
}
|
||||
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
_, err = s.Insert(file)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return
|
||||
}
|
||||
|
||||
// Save the file to storage with its new ID as path
|
||||
err = file.Save(f)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -42,8 +42,10 @@ func ExportUserData(s *xorm.Session, u *user.User) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
tmpFilename := exportDir + strconv.FormatInt(u.ID, 10) + "_" + time.Now().Format("2006-01-02_15-03-05") + ".zip"
|
||||
|
||||
// Open zip
|
||||
dumpFile, err := os.Create(exportDir + strconv.FormatInt(u.ID, 64) + "_" + time.Now().Format("2006-01-02_15-03-05") + ".zip")
|
||||
dumpFile, err := os.Create(tmpFilename)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error opening dump file: %s", err)
|
||||
}
|
||||
@ -78,12 +80,22 @@ func ExportUserData(s *xorm.Session, u *user.User) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
stat, err := dumpFile.Stat()
|
||||
// If we reuse the same file again, saving it as a file in Vikunja will save it as a file with 0 bytes in size.
|
||||
// Closing and reopening does work.
|
||||
dumpWriter.Close()
|
||||
dumpFile.Close()
|
||||
|
||||
exported, err := os.Open(tmpFilename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
exportFile, err := files.Create(dumpFile, dumpFile.Name(), uint64(stat.Size()), u)
|
||||
stat, err := exported.Stat()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
exportFile, err := files.CreateWithMimeAndSession(s, exported, tmpFilename, uint64(stat.Size()), u, "application/zip")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -96,7 +108,7 @@ func ExportUserData(s *xorm.Session, u *user.User) (err error) {
|
||||
}
|
||||
|
||||
// Remove the old file
|
||||
err = os.Remove(dumpFile.Name())
|
||||
err = os.Remove(exported.Name())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -598,5 +598,6 @@ func (s *HandleUserDataExport) Handle(msg *message.Message) (err error) {
|
||||
|
||||
log.Debugf("Done exporting user data for user %d...", event.User.ID)
|
||||
|
||||
return sess.Commit()
|
||||
err = sess.Commit()
|
||||
return err
|
||||
}
|
||||
|
@ -19,59 +19,47 @@ package v1
|
||||
import (
|
||||
"code.vikunja.io/api/pkg/db"
|
||||
"code.vikunja.io/api/pkg/events"
|
||||
"code.vikunja.io/api/pkg/files"
|
||||
"code.vikunja.io/api/pkg/models"
|
||||
"code.vikunja.io/api/pkg/user"
|
||||
"code.vikunja.io/web/handler"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
|
||||
"net/http"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func exportHandler(c echo.Context, closure func(*user.User) error) error {
|
||||
func checkExportRequest(c echo.Context) (s *xorm.Session, u *user.User, err error) {
|
||||
var pass UserPasswordConfirmation
|
||||
if err := c.Bind(&pass); err != nil {
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "No password provided.")
|
||||
return nil, nil, echo.NewHTTPError(http.StatusBadRequest, "No password provided.")
|
||||
}
|
||||
|
||||
err := c.Validate(pass)
|
||||
err = c.Validate(pass)
|
||||
if err != nil {
|
||||
return echo.NewHTTPError(http.StatusBadRequest, err)
|
||||
return nil, nil, echo.NewHTTPError(http.StatusBadRequest, err)
|
||||
}
|
||||
|
||||
s := db.NewSession()
|
||||
s = db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
err = s.Begin()
|
||||
if err != nil {
|
||||
return handler.HandleHTTPError(err, c)
|
||||
return nil, nil, handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
u, err := user.GetCurrentUserFromDB(s, c)
|
||||
u, err = user.GetCurrentUserFromDB(s, c)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
return nil, nil, handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
err = user.CheckUserPassword(u, pass.Password)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
return nil, nil, handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
err = closure(u)
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
err = s.Commit()
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
return c.JSON(http.StatusOK, models.Message{Message: "Successfully requested data export. We will send you an email when it's ready."})
|
||||
return
|
||||
}
|
||||
|
||||
// RequestUserDataExport is the handler to request a user data export
|
||||
@ -86,15 +74,25 @@ func exportHandler(c echo.Context, closure func(*user.User) error) error {
|
||||
// @Failure 500 {object} models.Message "Internal server error."
|
||||
// @Router /user/export/request [post]
|
||||
func RequestUserDataExport(c echo.Context) error {
|
||||
err := exportHandler(c, func(u *user.User) error {
|
||||
return events.Dispatch(&models.UserDataExportRequestedEvent{
|
||||
User: u,
|
||||
})
|
||||
})
|
||||
s, u, err := checkExportRequest(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = events.Dispatch(&models.UserDataExportRequestedEvent{
|
||||
User: u,
|
||||
})
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
err = s.Commit()
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
return c.JSON(http.StatusOK, models.Message{Message: "Successfully requested data export. We will send you an email when it's ready."})
|
||||
}
|
||||
|
||||
@ -110,13 +108,30 @@ func RequestUserDataExport(c echo.Context) error {
|
||||
// @Failure 500 {object} models.Message "Internal server error."
|
||||
// @Router /user/export/download [post]
|
||||
func DownloadUserDataExport(c echo.Context) error {
|
||||
err := exportHandler(c, func(u *user.User) error {
|
||||
})
|
||||
s, u, err := checkExportRequest(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO
|
||||
err = s.Commit()
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
return c.JSON(http.StatusOK, models.Message{Message: "Successfully requested data export. We will send you an email when it's ready."})
|
||||
// Download
|
||||
exportFile := &files.File{ID: u.ExportFileID}
|
||||
err = exportFile.LoadFileByID()
|
||||
if err != nil {
|
||||
_ = s.Rollback()
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
var buf []byte
|
||||
_, err = exportFile.File.Read(buf)
|
||||
if err != nil {
|
||||
return handler.HandleHTTPError(err, c)
|
||||
}
|
||||
|
||||
return c.Blob(http.StatusOK, exportFile.Mime, buf)
|
||||
}
|
||||
|
@ -304,7 +304,7 @@ func registerAPIRoutes(a *echo.Group) {
|
||||
u.PUT("/settings/avatar/upload", apiv1.UploadAvatar)
|
||||
u.POST("/settings/general", apiv1.UpdateGeneralUserSettings)
|
||||
u.POST("/export/request", apiv1.RequestUserDataExport)
|
||||
u.POST("/export/dowload", apiv1.DownloadUserDataExport)
|
||||
u.POST("/export/download", apiv1.DownloadUserDataExport)
|
||||
|
||||
if config.ServiceEnableTotp.GetBool() {
|
||||
u.GET("/settings/totp", apiv1.UserTOTP)
|
||||
|
Loading…
x
Reference in New Issue
Block a user