Files
open-save-cloud-server/upload/upload.go
2022-06-04 13:04:15 +02:00

161 lines
3.3 KiB
Go

package upload
import (
"errors"
"fmt"
"github.com/google/uuid"
"io"
"mime/multipart"
"opensavecloudserver/config"
"opensavecloudserver/database"
"os"
"path"
"strconv"
"sync"
"time"
)
var (
locks map[int]GameUploadToken
mu sync.Mutex
)
type GameUploadToken struct {
GameId int `json:"-"`
UploadToken string `json:"upload_token"`
Expire time.Time `json:"expire"`
}
func init() {
locks = make(map[int]GameUploadToken)
go func() {
for {
time.Sleep(time.Minute)
clearLocks()
}
}()
}
func MoveFile(sourcePath, destPath string) error {
inputFile, err := os.Open(sourcePath)
if err != nil {
return fmt.Errorf("couldn't open source file: %s", err)
}
outputFile, err := os.Create(destPath)
if err != nil {
inputFile.Close()
return fmt.Errorf("couldn't open dest file: %s", err)
}
defer outputFile.Close()
_, err = io.Copy(outputFile, inputFile)
if err != nil {
inputFile.Close()
return fmt.Errorf("writing to output file failed: %s", err)
}
inputFile.Close()
// The copy was successful, so now delete the original file
err = os.Remove(sourcePath)
if err != nil {
return fmt.Errorf("failed removing original file: %s", err)
}
return nil
}
// AskForUpload Create a lock for upload a new revision of a game
func AskForUpload(userId, gameId int) (*GameUploadToken, error) {
mu.Lock()
defer mu.Unlock()
_, err := database.GameInfoById(userId, gameId)
if err != nil {
return nil, err
}
if _, ok := locks[gameId]; !ok {
token := uuid.New()
lock := GameUploadToken{
GameId: gameId,
UploadToken: token.String(),
}
locks[gameId] = lock
return &lock, nil
}
return nil, errors.New("game already locked")
}
func CheckUploadToken(uploadToken string) (int, bool) {
mu.Lock()
defer mu.Unlock()
for _, lock := range locks {
if lock.UploadToken == uploadToken {
return lock.GameId, true
}
}
return -1, false
}
func UploadSave(file multipart.File, game *database.Game) error {
filePath := path.Join(config.Path().Cache, strconv.Itoa(game.UserId))
if _, err := os.Stat(filePath); err != nil {
err = os.Mkdir(filePath, 0766)
if err != nil {
return err
}
}
filePath = path.Join(filePath, game.PathStorage)
f, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
return err
}
defer f.Close()
_, err = io.Copy(f, file)
if err != nil {
return err
}
err = moveToStorage(filePath, game)
if err != nil {
return err
}
return nil
}
func moveToStorage(cachePath string, game *database.Game) error {
filePath := path.Join(config.Path().Storage, strconv.Itoa(game.UserId))
if _, err := os.Stat(filePath); err != nil {
err = os.Mkdir(filePath, 0766)
if err != nil {
return err
}
}
filePath = path.Join(filePath, game.PathStorage)
if _, err := os.Stat(filePath); err == nil {
if err = os.Remove(filePath); err != nil {
return err
}
}
if err := MoveFile(cachePath, filePath); err != nil {
return err
}
return nil
}
func UnlockGame(gameId int) {
mu.Lock()
defer mu.Unlock()
delete(locks, gameId)
}
// clearLocks clear lock of zombi upload
func clearLocks() {
mu.Lock()
defer mu.Unlock()
now := time.Now()
toUnlock := make([]int, 0)
for gameId, lock := range locks {
if lock.Expire.After(now) {
toUnlock = append(toUnlock, gameId)
}
}
for _, gameId := range toUnlock {
delete(locks, gameId)
}
}