2020-02-07 17:27:45 +01:00
|
|
|
// Vikunja is a to-do list application to facilitate your life.
|
2020-01-09 18:33:22 +01:00
|
|
|
// Copyright 2018-2020 Vikunja and contributors. All rights reserved.
|
2019-10-16 22:52:29 +02:00
|
|
|
//
|
2019-12-04 20:39:56 +01:00
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
|
|
// it under the terms of the GNU General Public License as published by
|
|
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
|
|
// (at your option) any later version.
|
2019-10-16 22:52:29 +02:00
|
|
|
//
|
2019-12-04 20:39:56 +01:00
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
// GNU General Public License for more details.
|
2019-10-16 22:52:29 +02:00
|
|
|
//
|
2019-12-04 20:39:56 +01:00
|
|
|
// You should have received a copy of the GNU General Public License
|
|
|
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
2019-10-16 22:52:29 +02:00
|
|
|
|
|
|
|
package files
|
|
|
|
|
|
|
|
import (
|
|
|
|
"code.vikunja.io/api/pkg/config"
|
2020-02-08 13:48:49 +01:00
|
|
|
"code.vikunja.io/api/pkg/timeutil"
|
2019-10-16 22:52:29 +02:00
|
|
|
"code.vikunja.io/web"
|
2019-10-18 17:30:25 +02:00
|
|
|
"github.com/c2h5oh/datasize"
|
2019-10-16 22:52:29 +02:00
|
|
|
"github.com/spf13/afero"
|
|
|
|
"io"
|
|
|
|
"strconv"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
// File holds all information about a file
|
|
|
|
type File struct {
|
|
|
|
ID int64 `xorm:"int(11) autoincr not null unique pk" json:"id"`
|
|
|
|
Name string `xorm:"text not null" json:"name"`
|
|
|
|
Mime string `xorm:"text null" json:"mime"`
|
2019-10-18 17:30:25 +02:00
|
|
|
Size uint64 `xorm:"int(11) not null" json:"size"`
|
2019-10-16 22:52:29 +02:00
|
|
|
|
|
|
|
Created time.Time `xorm:"-" json:"created"`
|
|
|
|
|
2020-02-08 13:48:49 +01:00
|
|
|
CreatedUnix timeutil.TimeStamp `xorm:"created" json:"-"`
|
|
|
|
CreatedByID int64 `xorm:"int(11) not null" json:"-"`
|
2019-10-16 22:52:29 +02:00
|
|
|
|
|
|
|
File afero.File `xorm:"-" json:"-"`
|
2020-01-19 17:52:16 +01:00
|
|
|
// This ReadCloser is only used for migration purposes. Use with care!
|
|
|
|
// There is currentlc no better way of doing this.
|
|
|
|
FileContent []byte `xorm:"-" json:"-"`
|
2019-10-16 22:52:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// TableName is the table name for the files table
|
|
|
|
func (File) TableName() string {
|
|
|
|
return "files"
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *File) getFileName() string {
|
|
|
|
return config.FilesBasePath.GetString() + "/" + strconv.FormatInt(f.ID, 10)
|
|
|
|
}
|
|
|
|
|
|
|
|
// LoadFileByID returns a file by its ID
|
|
|
|
func (f *File) LoadFileByID() (err error) {
|
|
|
|
f.File, err = afs.Open(f.getFileName())
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// LoadFileMetaByID loads everything about a file without loading the actual file
|
|
|
|
func (f *File) LoadFileMetaByID() (err error) {
|
|
|
|
exists, err := x.Where("id = ?", f.ID).Get(f)
|
|
|
|
if !exists {
|
|
|
|
return ErrFileDoesNotExist{FileID: f.ID}
|
|
|
|
}
|
2020-02-08 13:48:49 +01:00
|
|
|
f.Created = f.CreatedUnix.ToTime()
|
2019-10-16 22:52:29 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create creates a new file from an FileHeader
|
2019-10-18 17:30:25 +02:00
|
|
|
func Create(f io.ReadCloser, realname string, realsize uint64, a web.Auth) (file *File, err error) {
|
2019-10-16 22:52:29 +02:00
|
|
|
|
2019-10-18 17:30:25 +02:00
|
|
|
// Get and parse the configured file size
|
|
|
|
var maxSize datasize.ByteSize
|
|
|
|
err = maxSize.UnmarshalText([]byte(config.FilesMaxSize.GetString()))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if realsize > maxSize.Bytes() {
|
2019-10-16 22:52:29 +02:00
|
|
|
return nil, ErrFileIsTooLarge{Size: realsize}
|
|
|
|
}
|
|
|
|
|
|
|
|
// We first insert the file into the db to get it's ID
|
|
|
|
file = &File{
|
|
|
|
Name: realname,
|
|
|
|
Size: realsize,
|
|
|
|
CreatedByID: a.GetID(),
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err = x.Insert(file)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Save the file to storage with its new ID as path
|
|
|
|
err = afs.WriteReader(file.getFileName(), f)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Delete removes a file from the DB and the file system
|
|
|
|
func (f *File) Delete() (err error) {
|
|
|
|
deleted, err := x.Where("id = ?", f.ID).Delete(f)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if deleted == 0 {
|
|
|
|
return ErrFileDoesNotExist{FileID: f.ID}
|
|
|
|
}
|
|
|
|
|
|
|
|
err = afs.Remove(f.getFileName())
|
|
|
|
return
|
|
|
|
}
|