Comment, list all saves, set port, set hash

This commit is contained in:
Aurélie Delhaie
2022-05-24 23:49:47 +02:00
parent e3c2068ae9
commit 87957cb2d5
6 changed files with 100 additions and 74 deletions

View File

@@ -1,4 +1,6 @@
--- ---
server:
port: 8080
database: database:
host: localhost host: localhost
password: root password: root

View File

@@ -8,6 +8,7 @@ import (
) )
type Configuration struct { type Configuration struct {
Server ServerConfiguration `yaml:"server"`
Database DatabaseConfiguration `yaml:"database"` Database DatabaseConfiguration `yaml:"database"`
Features FeaturesConfiguration `yaml:"features"` Features FeaturesConfiguration `yaml:"features"`
Path PathConfiguration `yaml:"path"` Path PathConfiguration `yaml:"path"`
@@ -18,6 +19,10 @@ type PathConfiguration struct {
Storage string `yaml:"storage"` Storage string `yaml:"storage"`
} }
type ServerConfiguration struct {
Port int `yaml:"port"`
}
type DatabaseConfiguration struct { type DatabaseConfiguration struct {
Host string `yaml:"host"` Host string `yaml:"host"`
Port int `yaml:"port"` Port int `yaml:"port"`
@@ -55,3 +60,7 @@ func Features() *FeaturesConfiguration {
func Path() *PathConfiguration { func Path() *PathConfiguration {
return &currentConfig.Path return &currentConfig.Path
} }
func Server() *ServerConfiguration {
return &currentConfig.Server
}

View File

@@ -1,8 +1,6 @@
package database package database
import ( import (
"crypto/md5"
"encoding/hex"
"errors" "errors"
"fmt" "fmt"
"github.com/google/uuid" "github.com/google/uuid"
@@ -175,31 +173,18 @@ func UploadSave(file multipart.File, game *Game) error {
return nil return nil
} }
func UpdateGameRevision(game *Game) error { func UpdateGameRevision(game *Game, hash string) error {
filePath := path.Join(config.Path().Storage, game.PathStorage)
file, err := os.Open(filePath)
if err != nil {
return err
}
defer file.Close()
hash := md5.New()
_, err = io.Copy(hash, file)
if err != nil {
return err
}
sum := hash.Sum(nil)
game.Revision += 1 game.Revision += 1
if game.Hash == nil { if game.Hash == nil {
game.Hash = new(string) game.Hash = new(string)
} }
*game.Hash = hex.EncodeToString(sum) *game.Hash = hash
game.Available = true game.Available = true
if game.LastUpdate == nil { if game.LastUpdate == nil {
game.LastUpdate = new(time.Time) game.LastUpdate = new(time.Time)
} }
*game.LastUpdate = time.Now() *game.LastUpdate = time.Now()
err = db.Save(game).Error err := db.Save(game).Error
if err != nil { if err != nil {
return err return err
} }

View File

@@ -1,54 +1,54 @@
-- -------------------------------------------------------- -- --------------------------------------------------------
-- Host: 127.0.0.1 -- Host: 127.0.0.1
-- Server version: 8.0.27 - MySQL Community Server - GPL -- Server version: 8.0.27 - MySQL Community Server - GPL
-- Server OS: Win64 -- Server OS: Win64
-- HeidiSQL Version: 12.0.0.6468 -- HeidiSQL Version: 12.0.0.6468
-- -------------------------------------------------------- -- --------------------------------------------------------
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET NAMES utf8 */; /*!40101 SET NAMES utf8 */;
/*!50503 SET NAMES utf8mb4 */; /*!50503 SET NAMES utf8mb4 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; /*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */; /*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
-- Dumping database structure for osc -- Dumping database structure for osc
DROP DATABASE IF EXISTS `osc`; DROP DATABASE IF EXISTS `osc`;
CREATE DATABASE IF NOT EXISTS `osc` /*!40100 DEFAULT CHARACTER SET utf8 */ /*!80016 DEFAULT ENCRYPTION='N' */; CREATE DATABASE IF NOT EXISTS `osc` /*!40100 DEFAULT CHARACTER SET utf8 */ /*!80016 DEFAULT ENCRYPTION='N' */;
USE `osc`; USE `osc`;
-- Dumping structure for table osc.games -- Dumping structure for table osc.games
DROP TABLE IF EXISTS `games`; DROP TABLE IF EXISTS `games`;
CREATE TABLE IF NOT EXISTS `games` ( CREATE TABLE IF NOT EXISTS `games` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT, `id` bigint unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL DEFAULT '0', `name` varchar(255) NOT NULL DEFAULT '0',
`revision` bigint unsigned NOT NULL DEFAULT '0', `revision` bigint unsigned NOT NULL DEFAULT '0',
`path_storage` text NOT NULL, `path_storage` text NOT NULL,
`hash` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, `hash` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
`last_update` datetime DEFAULT NULL, `last_update` datetime DEFAULT NULL,
`user_id` bigint unsigned NOT NULL DEFAULT '0', `user_id` bigint unsigned NOT NULL DEFAULT '0',
`available` tinyint unsigned NOT NULL DEFAULT '0', `available` tinyint unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`id`) PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb3; ) ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb3;
-- Data exporting was unselected. -- Data exporting was unselected.
-- Dumping structure for table osc.users -- Dumping structure for table osc.users
DROP TABLE IF EXISTS `users`; DROP TABLE IF EXISTS `users`;
CREATE TABLE IF NOT EXISTS `users` ( CREATE TABLE IF NOT EXISTS `users` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT, `id` bigint unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL, `username` varchar(50) NOT NULL,
`password` binary(60) NOT NULL, `password` binary(60) NOT NULL,
PRIMARY KEY (`id`) PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb3; ) ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb3;
-- Data exporting was unselected. -- Data exporting was unselected.
/*!40103 SET TIME_ZONE=IFNULL(@OLD_TIME_ZONE, 'system') */; /*!40103 SET TIME_ZONE=IFNULL(@OLD_TIME_ZONE, 'system') */;
/*!40101 SET SQL_MODE=IFNULL(@OLD_SQL_MODE, '') */; /*!40101 SET SQL_MODE=IFNULL(@OLD_SQL_MODE, '') */;
/*!40014 SET FOREIGN_KEY_CHECKS=IFNULL(@OLD_FOREIGN_KEY_CHECKS, 1) */; /*!40014 SET FOREIGN_KEY_CHECKS=IFNULL(@OLD_FOREIGN_KEY_CHECKS, 1) */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40111 SET SQL_NOTES=IFNULL(@OLD_SQL_NOTES, 1) */; /*!40111 SET SQL_NOTES=IFNULL(@OLD_SQL_NOTES, 1) */;

View File

@@ -12,6 +12,7 @@ import (
"path/filepath" "path/filepath"
"strconv" "strconv"
"time" "time"
"unicode/utf8"
) )
type NewGameInfo struct { type NewGameInfo struct {
@@ -26,6 +27,7 @@ type LockError struct {
Message string `json:"message"` Message string `json:"message"`
} }
// CreateGame create a game entry to the database
func CreateGame(w http.ResponseWriter, r *http.Request) { func CreateGame(w http.ResponseWriter, r *http.Request) {
userId, err := userIdFromContext(r.Context()) userId, err := userIdFromContext(r.Context())
if err != nil { if err != nil {
@@ -55,6 +57,7 @@ func CreateGame(w http.ResponseWriter, r *http.Request) {
ok(game, w, r) ok(game, w, r)
} }
// GameInfoByID get the game save information from the database
func GameInfoByID(w http.ResponseWriter, r *http.Request) { func GameInfoByID(w http.ResponseWriter, r *http.Request) {
userId, err := userIdFromContext(r.Context()) userId, err := userIdFromContext(r.Context())
if err != nil { if err != nil {
@@ -78,6 +81,24 @@ func GameInfoByID(w http.ResponseWriter, r *http.Request) {
ok(game, w, r) ok(game, w, r)
} }
// AllGamesInformation all game saves information for a user
func AllGamesInformation(w http.ResponseWriter, r *http.Request) {
userId, err := userIdFromContext(r.Context())
if err != nil {
internalServerError(w, r)
log.Println(err)
return
}
games, err := database.GameInfosByUserId(userId)
if err != nil {
internalServerError(w, r)
log.Println(err)
return
}
ok(games, w, r)
}
// AskForUpload check if the game save is not lock, then lock it and generate a token
func AskForUpload(w http.ResponseWriter, r *http.Request) { func AskForUpload(w http.ResponseWriter, r *http.Request) {
userId, err := userIdFromContext(r.Context()) userId, err := userIdFromContext(r.Context())
if err != nil { if err != nil {
@@ -106,6 +127,7 @@ func AskForUpload(w http.ResponseWriter, r *http.Request) {
ok(token, w, r) ok(token, w, r)
} }
// UploadSave upload the game save archive to the storage folder
func UploadSave(w http.ResponseWriter, r *http.Request) { func UploadSave(w http.ResponseWriter, r *http.Request) {
userId, err := userIdFromContext(r.Context()) userId, err := userIdFromContext(r.Context())
if err != nil { if err != nil {
@@ -120,6 +142,11 @@ func UploadSave(w http.ResponseWriter, r *http.Request) {
return return
} }
defer database.UnlockGame(gameId) defer database.UnlockGame(gameId)
hash := r.Header.Get("X-Game-Save-Hash")
if utf8.RuneCountInString(hash) == 0 {
badRequest("The header X-Game-Save-Hash is missing", w, r)
return
}
game, err := database.GameInfoById(userId, gameId) game, err := database.GameInfoById(userId, gameId)
if err != nil { if err != nil {
internalServerError(w, r) internalServerError(w, r)
@@ -139,7 +166,7 @@ func UploadSave(w http.ResponseWriter, r *http.Request) {
log.Println(err) log.Println(err)
return return
} }
err = database.UpdateGameRevision(game) err = database.UpdateGameRevision(game, hash)
if err != nil { if err != nil {
internalServerError(w, r) internalServerError(w, r)
log.Println(err) log.Println(err)
@@ -153,6 +180,7 @@ func UploadSave(w http.ResponseWriter, r *http.Request) {
ok(payload, w, r) ok(payload, w, r)
} }
// Download send the game save archive to the client
func Download(w http.ResponseWriter, r *http.Request) { func Download(w http.ResponseWriter, r *http.Request) {
userId, err := userIdFromContext(r.Context()) userId, err := userIdFromContext(r.Context())
if err != nil { if err != nil {

View File

@@ -3,6 +3,7 @@ package server
import ( import (
"context" "context"
"errors" "errors"
"fmt"
"github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware" "github.com/go-chi/chi/v5/middleware"
"log" "log"
@@ -37,6 +38,7 @@ func Serve() {
r.Route("/game", func(secureRouter chi.Router) { r.Route("/game", func(secureRouter chi.Router) {
secureRouter.Use(authMiddleware) secureRouter.Use(authMiddleware)
secureRouter.Post("/create", CreateGame) secureRouter.Post("/create", CreateGame)
secureRouter.Get("/all", AllGamesInformation)
secureRouter.Get("/info/{id}", GameInfoByID) secureRouter.Get("/info/{id}", GameInfoByID)
secureRouter.Post("/upload/init", AskForUpload) secureRouter.Post("/upload/init", AskForUpload)
secureRouter.Group(func(uploadRouter chi.Router) { secureRouter.Group(func(uploadRouter chi.Router) {
@@ -49,7 +51,7 @@ func Serve() {
}) })
}) })
log.Println("Server is listening...") log.Println("Server is listening...")
err := http.ListenAndServe(":8080", router) err := http.ListenAndServe(fmt.Sprintf(":%d", config.Server().Port), router)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }