Files
mostlymatter/store/sql_file_info_store.go
George Goldberg 50fc6e1e9e PLT-???? Prepare file upload infrastructure for Data Retention. (#7266)
* Prepare file upload infrastructure for Data Retention.

This commit prepares the file upload infrastructure for the data
retention feature that is under construction. Changes are:

* Move file management code to utils to allow access to it from jobs.

* From now on, store all file uploads in a top level folder which is the
  date of the day on which they were uploaded.

This commit is based on Harrison Healey's branch, but updated to work
with the latest master.

* Use NewAppError
2017-08-25 10:38:13 -04:00

282 строки
6.8 KiB
Go

// See License.txt for license information.
package store
import (
"database/sql"
"net/http"
"github.com/mattermost/platform/einterfaces"
"github.com/mattermost/platform/model"
"github.com/mattermost/platform/utils"
)
type SqlFileInfoStore struct {
SqlStore
}
const (
FILE_INFO_CACHE_SIZE = 25000
FILE_INFO_CACHE_SEC = 1800 // 30 minutes
)
var fileInfoCache *utils.Cache = utils.NewLru(FILE_INFO_CACHE_SIZE)
func ClearFileCaches() {
fileInfoCache.Purge()
}
func NewSqlFileInfoStore(sqlStore SqlStore) FileInfoStore {
s := &SqlFileInfoStore{sqlStore}
for _, db := range sqlStore.GetAllConns() {
table := db.AddTableWithName(model.FileInfo{}, "FileInfo").SetKeys(false, "Id")
table.ColMap("Id").SetMaxSize(26)
table.ColMap("CreatorId").SetMaxSize(26)
table.ColMap("PostId").SetMaxSize(26)
table.ColMap("Path").SetMaxSize(512)
table.ColMap("ThumbnailPath").SetMaxSize(512)
table.ColMap("PreviewPath").SetMaxSize(512)
table.ColMap("Name").SetMaxSize(256)
table.ColMap("Extension").SetMaxSize(64)
table.ColMap("MimeType").SetMaxSize(256)
}
return s
}
func (fs SqlFileInfoStore) CreateIndexesIfNotExists() {
fs.CreateIndexIfNotExists("idx_fileinfo_update_at", "FileInfo", "UpdateAt")
fs.CreateIndexIfNotExists("idx_fileinfo_create_at", "FileInfo", "CreateAt")
fs.CreateIndexIfNotExists("idx_fileinfo_delete_at", "FileInfo", "DeleteAt")
fs.CreateIndexIfNotExists("idx_fileinfo_postid_at", "FileInfo", "PostId")
}
func (fs SqlFileInfoStore) Save(info *model.FileInfo) StoreChannel {
storeChannel := make(StoreChannel, 1)
go func() {
result := StoreResult{}
info.PreSave()
if result.Err = info.IsValid(); result.Err != nil {
storeChannel <- result
close(storeChannel)
return
}
if err := fs.GetMaster().Insert(info); err != nil {
result.Err = model.NewLocAppError("SqlFileInfoStore.Save", "store.sql_file_info.save.app_error", nil, err.Error())
} else {
result.Data = info
}
storeChannel <- result
close(storeChannel)
}()
return storeChannel
}
func (fs SqlFileInfoStore) Get(id string) StoreChannel {
storeChannel := make(StoreChannel, 1)
go func() {
result := StoreResult{}
info := &model.FileInfo{}
if err := fs.GetReplica().SelectOne(info,
`SELECT
*
FROM
FileInfo
WHERE
Id = :Id
AND DeleteAt = 0`, map[string]interface{}{"Id": id}); err != nil {
if err == sql.ErrNoRows {
result.Err = model.NewAppError("SqlFileInfoStore.Get", "store.sql_file_info.get.app_error", nil, "id="+id+", "+err.Error(), http.StatusNotFound)
} else {
result.Err = model.NewAppError("SqlFileInfoStore.Get", "store.sql_file_info.get.app_error", nil, "id="+id+", "+err.Error(), http.StatusInternalServerError)
}
} else {
result.Data = info
}
storeChannel <- result
close(storeChannel)
}()
return storeChannel
}
func (fs SqlFileInfoStore) GetByPath(path string) StoreChannel {
storeChannel := make(StoreChannel, 1)
go func() {
result := StoreResult{}
info := &model.FileInfo{}
if err := fs.GetReplica().SelectOne(info,
`SELECT
*
FROM
FileInfo
WHERE
Path = :Path
AND DeleteAt = 0
LIMIT 1`, map[string]interface{}{"Path": path}); err != nil {
result.Err = model.NewAppError("SqlFileInfoStore.GetByPath", "store.sql_file_info.get_by_path.app_error", nil, "path="+path+", "+err.Error(), http.StatusInternalServerError)
} else {
result.Data = info
}
storeChannel <- result
close(storeChannel)
}()
return storeChannel
}
func (fs SqlFileInfoStore) InvalidateFileInfosForPostCache(postId string) {
fileInfoCache.Remove(postId)
}
func (fs SqlFileInfoStore) GetForPost(postId string, readFromMaster bool, allowFromCache bool) StoreChannel {
storeChannel := make(StoreChannel, 1)
go func() {
result := StoreResult{}
metrics := einterfaces.GetMetricsInterface()
if allowFromCache {
if cacheItem, ok := fileInfoCache.Get(postId); ok {
if metrics != nil {
metrics.IncrementMemCacheHitCounter("File Info Cache")
}
result.Data = cacheItem.([]*model.FileInfo)
storeChannel <- result
close(storeChannel)
return
} else {
if metrics != nil {
metrics.IncrementMemCacheMissCounter("File Info Cache")
}
}
} else {
if metrics != nil {
metrics.IncrementMemCacheMissCounter("File Info Cache")
}
}
var infos []*model.FileInfo
dbmap := fs.GetReplica()
if readFromMaster {
dbmap = fs.GetMaster()
}
if _, err := dbmap.Select(&infos,
`SELECT
*
FROM
FileInfo
WHERE
PostId = :PostId
AND DeleteAt = 0
ORDER BY
CreateAt`, map[string]interface{}{"PostId": postId}); err != nil {
result.Err = model.NewAppError("SqlFileInfoStore.GetForPost",
"store.sql_file_info.get_for_post.app_error", nil, "post_id="+postId+", "+err.Error(), http.StatusInternalServerError)
} else {
if len(infos) > 0 {
fileInfoCache.AddWithExpiresInSecs(postId, infos, FILE_INFO_CACHE_SEC)
}
result.Data = infos
}
storeChannel <- result
close(storeChannel)
}()
return storeChannel
}
func (fs SqlFileInfoStore) AttachToPost(fileId, postId string) StoreChannel {
storeChannel := make(StoreChannel, 1)
go func() {
result := StoreResult{}
if _, err := fs.GetMaster().Exec(
`UPDATE
FileInfo
SET
PostId = :PostId
WHERE
Id = :Id
AND PostId = ''`, map[string]interface{}{"PostId": postId, "Id": fileId}); err != nil {
result.Err = model.NewAppError("SqlFileInfoStore.AttachToPost",
"store.sql_file_info.attach_to_post.app_error", nil, "post_id="+postId+", file_id="+fileId+", err="+err.Error(), http.StatusInternalServerError)
}
storeChannel <- result
close(storeChannel)
}()
return storeChannel
}
func (fs SqlFileInfoStore) DeleteForPost(postId string) StoreChannel {
storeChannel := make(StoreChannel, 1)
go func() {
result := StoreResult{}
if _, err := fs.GetMaster().Exec(
`UPDATE
FileInfo
SET
DeleteAt = :DeleteAt
WHERE
PostId = :PostId`, map[string]interface{}{"DeleteAt": model.GetMillis(), "PostId": postId}); err != nil {
result.Err = model.NewAppError("SqlFileInfoStore.DeleteForPost",
"store.sql_file_info.delete_for_post.app_error", nil, "post_id="+postId+", err="+err.Error(), http.StatusInternalServerError)
} else {
result.Data = postId
}
storeChannel <- result
close(storeChannel)
}()
return storeChannel
}
func (fs SqlFileInfoStore) PermanentDelete(fileId string) StoreChannel {
storeChannel := make(StoreChannel, 1)
go func() {
result := StoreResult{}
if _, err := fs.GetMaster().Exec(
`DELETE FROM
FileInfo
WHERE
Id = :FileId`, map[string]interface{}{"FileId": fileId}); err != nil {
result.Err = model.NewAppError("SqlFileInfoStore.PermanentDelete",
"store.sql_file_info.permanent_delete.app_error", nil, "file_id="+fileId+", err="+err.Error(), http.StatusInternalServerError)
}
storeChannel <- result
close(storeChannel)
}()
return storeChannel
}