refakt
This commit is contained in:
+21
-18
@@ -5,36 +5,39 @@ import (
|
||||
)
|
||||
|
||||
type Domain struct {
|
||||
SoftwareRepository SoftwareRepository
|
||||
ReleaseRepository ReleaseRepository
|
||||
DownloadService DownloadService
|
||||
SoftwareUpdaterService SoftwareUpdaterService
|
||||
SoftwareService SoftwareService
|
||||
SoftwareRepository SoftwareRepositoryInterface
|
||||
ReleaseRepository ReleaseRepositoryInterface
|
||||
FileRepository FileRepositoryInterface
|
||||
DownloadService DownloadServiceInterface
|
||||
SoftwareUpdaterService SoftwareUpdaterServiceInterface
|
||||
SoftwareService SoftwareServiceInterface
|
||||
}
|
||||
|
||||
func NewDomain() Domain {
|
||||
DB := mysql_utils.Init()
|
||||
MigrateGoDatabase(DB)
|
||||
|
||||
softwareRepository := &softwareRepository{db: DB}
|
||||
releaseRepository := &releaseRepository{db: DB}
|
||||
softwareService := &softwareService{softwareRepository: softwareRepository}
|
||||
tic80Updater := &softwareUpdaterTIC80Service{
|
||||
softwareRepository: softwareRepository,
|
||||
releaseRepository: releaseRepository,
|
||||
}
|
||||
softwareRepository := &SoftwareRepository{db: DB}
|
||||
releaseRepository := &ReleaseRepository{db: DB}
|
||||
fileRepository := NewFileRepository()
|
||||
|
||||
softwareUpdaterService := &softwareUpdaterService{tic80Updater: tic80Updater}
|
||||
softwareService := NewSoftwareService(softwareRepository)
|
||||
|
||||
tic80Updater := NewSoftwareUpdaterTIC80Service(
|
||||
softwareRepository,
|
||||
releaseRepository,
|
||||
fileRepository,
|
||||
)
|
||||
|
||||
downloadServiceInstance := &downloadService{
|
||||
softwareRepository: softwareRepository,
|
||||
releaseRepository: releaseRepository,
|
||||
}
|
||||
softwareUpdaterService := NewSoftwareUpdaterService(tic80Updater)
|
||||
|
||||
downloadService := NewDownloadService(softwareRepository, releaseRepository)
|
||||
|
||||
return Domain{
|
||||
SoftwareRepository: softwareRepository,
|
||||
ReleaseRepository: releaseRepository,
|
||||
DownloadService: downloadServiceInstance,
|
||||
FileRepository: fileRepository,
|
||||
DownloadService: downloadService,
|
||||
SoftwareUpdaterService: softwareUpdaterService,
|
||||
SoftwareService: softwareService,
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import "gorm.io/gorm"
|
||||
|
||||
type Release struct {
|
||||
gorm.Model
|
||||
ID uint `gorm:"primaryKey" json:"id"`
|
||||
SoftwareID uint `gorm:"index" json:"software_id"`
|
||||
Version string `gorm:"size:64" json:"version"`
|
||||
CartridgePath string `gorm:"size:255" json:"-"`
|
||||
|
||||
@@ -4,7 +4,6 @@ import "gorm.io/gorm"
|
||||
|
||||
type Software struct {
|
||||
gorm.Model
|
||||
ID uint `gorm:"primaryKey" json:"id"`
|
||||
Name string `gorm:"uniqueIndex;size:128" json:"name"`
|
||||
Title string `gorm:"size:255" json:"title"`
|
||||
Author string `gorm:"size:255" json:"author"`
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
package domain
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type FileRepositoryInterface interface {
|
||||
FileExists(fileName, basePath string) bool
|
||||
CreateDir(dirPath string) error
|
||||
DeleteFile(fileName, basePath string) error
|
||||
MoveFile(srcFileName, destPath, basePath string) error
|
||||
UnzipHTMLContent(zipFileName, baseName, basePath string) error
|
||||
GetSoftwareDir(softwareName, basePath string) string
|
||||
GetCartridgePath(softwareName, version, basePath string) string
|
||||
GetSourcePath(softwareName, version, basePath string) string
|
||||
ReadMetaFromFile(fileName, basePath string) (map[string]string, error)
|
||||
}
|
||||
|
||||
type FileRepository struct{}
|
||||
|
||||
func NewFileRepository() *FileRepository {
|
||||
return &FileRepository{}
|
||||
}
|
||||
|
||||
func (r *FileRepository) FileExists(fileName, basePath string) bool {
|
||||
filePath := filepath.Join(basePath, fileName)
|
||||
_, err := os.Stat(filePath)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func (r *FileRepository) CreateDir(dirPath string) error {
|
||||
return os.MkdirAll(dirPath, os.ModePerm)
|
||||
}
|
||||
|
||||
func (r *FileRepository) DeleteFile(fileName, basePath string) error {
|
||||
filePath := filepath.Join(basePath, fileName)
|
||||
return os.Remove(filePath)
|
||||
}
|
||||
|
||||
func (r *FileRepository) MoveFile(srcFileName, destPath, basePath string) error {
|
||||
srcPath := filepath.Join(basePath, srcFileName)
|
||||
return os.Rename(srcPath, destPath)
|
||||
}
|
||||
|
||||
func (r *FileRepository) UnzipHTMLContent(zipFileName, baseName, basePath string) error {
|
||||
zipFilePath := filepath.Join(basePath, zipFileName)
|
||||
destDir := filepath.Join(basePath, "html", baseName)
|
||||
|
||||
fmt.Printf("FileRepository: Unzipping %s to %s\n", zipFilePath, destDir)
|
||||
|
||||
reader, err := zip.OpenReader(zipFilePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer reader.Close()
|
||||
|
||||
destDir, err = filepath.Abs(destDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, f := range reader.File {
|
||||
fpath := filepath.Join(destDir, f.Name)
|
||||
|
||||
if !strings.HasPrefix(fpath, destDir) {
|
||||
return fmt.Errorf("%s: illegal file path", fpath)
|
||||
}
|
||||
|
||||
if f.FileInfo().IsDir() {
|
||||
os.MkdirAll(fpath, os.ModePerm)
|
||||
continue
|
||||
}
|
||||
|
||||
if err = os.MkdirAll(filepath.Dir(fpath), os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
outFile, err := os.OpenFile(fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rc, err := f.Open()
|
||||
if err != nil {
|
||||
outFile.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = io.Copy(outFile, rc)
|
||||
outFile.Close()
|
||||
rc.Close()
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *FileRepository) GetSoftwareDir(softwareName, basePath string) string {
|
||||
return filepath.Join(basePath, softwareName)
|
||||
}
|
||||
|
||||
func (r *FileRepository) GetCartridgePath(softwareName, version, basePath string) string {
|
||||
softwareDir := r.GetSoftwareDir(softwareName, basePath)
|
||||
return filepath.Join(softwareDir, fmt.Sprintf("%s-%s.tic", softwareName, version))
|
||||
}
|
||||
|
||||
func (r *FileRepository) GetSourcePath(softwareName, version, basePath string) string {
|
||||
softwareDir := r.GetSoftwareDir(softwareName, basePath)
|
||||
return filepath.Join(softwareDir, fmt.Sprintf("%s-%s.lua", softwareName, version))
|
||||
}
|
||||
|
||||
func (r *FileRepository) ReadMetaFromFile(fileName, basePath string) (map[string]string, error) {
|
||||
filePath := filepath.Join(basePath, fileName)
|
||||
file, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
metaData := make(map[string]string)
|
||||
scanner := bufio.NewScanner(file)
|
||||
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
if strings.HasPrefix(line, "--") {
|
||||
parts := strings.SplitN(strings.TrimPrefix(line, "--"), ":", 2)
|
||||
if len(parts) != 2 {
|
||||
continue
|
||||
}
|
||||
key := strings.TrimSpace(parts[0])
|
||||
val := strings.TrimSpace(parts[1])
|
||||
metaData[strings.ToLower(key)] = val
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return metaData, nil
|
||||
}
|
||||
@@ -2,32 +2,27 @@ package domain
|
||||
|
||||
import "gorm.io/gorm"
|
||||
|
||||
type ReleaseRepository interface {
|
||||
type ReleaseRepositoryInterface interface {
|
||||
Create(release *Release) error
|
||||
FindLatestBySoftwareID(softwareID uint) (*Release, error)
|
||||
FindBySoftwareIDAndVersion(softwareID uint, version string) (*Release, error)
|
||||
}
|
||||
|
||||
type releaseRepository struct {
|
||||
type ReleaseRepository struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// NewReleaseRepository creates a new instance of ReleaseRepository.
|
||||
/* func NewReleaseRepository(db *gorm.DB) ReleaseRepository {
|
||||
return &releaseRepository{db}
|
||||
} */
|
||||
|
||||
func (r *releaseRepository) Create(release *Release) error {
|
||||
func (r *ReleaseRepository) Create(release *Release) error {
|
||||
return r.db.Create(release).Error
|
||||
}
|
||||
|
||||
func (r *releaseRepository) FindLatestBySoftwareID(softwareID uint) (*Release, error) {
|
||||
func (r *ReleaseRepository) FindLatestBySoftwareID(softwareID uint) (*Release, error) {
|
||||
var release Release
|
||||
err := r.db.Where("software_id = ?", softwareID).Order("created_at desc").First(&release).Error
|
||||
return &release, err
|
||||
}
|
||||
|
||||
func (r *releaseRepository) FindBySoftwareIDAndVersion(softwareID uint, version string) (*Release, error) {
|
||||
func (r *ReleaseRepository) FindBySoftwareIDAndVersion(softwareID uint, version string) (*Release, error) {
|
||||
var release Release
|
||||
err := r.db.Where("software_id = ? AND version = ?", softwareID, version).First(&release).Error
|
||||
return &release, err
|
||||
|
||||
@@ -2,17 +2,17 @@ package domain
|
||||
|
||||
import "gorm.io/gorm"
|
||||
|
||||
type SoftwareRepository interface {
|
||||
type SoftwareRepositoryInterface interface {
|
||||
List() ([]Software, error)
|
||||
GetByName(name string) (*Software, error)
|
||||
UpdateOrCreate(software *Software) error
|
||||
}
|
||||
|
||||
type softwareRepository struct {
|
||||
type SoftwareRepository struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
func (r *softwareRepository) GetByName(name string) (*Software, error) {
|
||||
func (r *SoftwareRepository) GetByName(name string) (*Software, error) {
|
||||
var software Software
|
||||
if err := r.db.Preload("Releases").Where("name = ?", name).First(&software).Error; err != nil {
|
||||
return nil, err
|
||||
@@ -20,7 +20,7 @@ func (r *softwareRepository) GetByName(name string) (*Software, error) {
|
||||
return &software, nil
|
||||
}
|
||||
|
||||
func (r *softwareRepository) List() ([]Software, error) {
|
||||
func (r *SoftwareRepository) List() ([]Software, error) {
|
||||
var softwares []Software
|
||||
if err := r.db.Preload("Releases").Find(&softwares).Error; err != nil {
|
||||
return nil, err
|
||||
@@ -28,7 +28,7 @@ func (r *softwareRepository) List() ([]Software, error) {
|
||||
return softwares, nil
|
||||
}
|
||||
|
||||
func (r *softwareRepository) UpdateOrCreate(software *Software) error {
|
||||
func (r *SoftwareRepository) UpdateOrCreate(software *Software) error {
|
||||
var existing Software
|
||||
if err := r.db.Where("name = ?", software.Name).First(&existing).Error; err == nil {
|
||||
software.ID = existing.ID
|
||||
|
||||
@@ -10,17 +10,24 @@ var (
|
||||
ErrReleaseNotFound = errors.New("release not found for software and version")
|
||||
)
|
||||
|
||||
type DownloadService interface {
|
||||
type DownloadServiceInterface interface {
|
||||
GetLatestRelease(softwareName string) (*Release, error)
|
||||
GetSpecificRelease(softwareName string, version string) (*Release, error)
|
||||
}
|
||||
|
||||
type downloadService struct {
|
||||
softwareRepository SoftwareRepository
|
||||
releaseRepository ReleaseRepository
|
||||
type DownloadService struct {
|
||||
softwareRepository SoftwareRepositoryInterface
|
||||
releaseRepository ReleaseRepositoryInterface
|
||||
}
|
||||
|
||||
func (s *downloadService) GetLatestRelease(softwareName string) (*Release, error) {
|
||||
func NewDownloadService(softwareRepository SoftwareRepositoryInterface, releaseRepository ReleaseRepositoryInterface) *DownloadService {
|
||||
return &DownloadService{
|
||||
softwareRepository: softwareRepository,
|
||||
releaseRepository: releaseRepository,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *DownloadService) GetLatestRelease(softwareName string) (*Release, error) {
|
||||
software, err := s.softwareRepository.GetByName(softwareName)
|
||||
if err != nil {
|
||||
return nil, ErrSoftwareNotFound
|
||||
@@ -34,7 +41,7 @@ func (s *downloadService) GetLatestRelease(softwareName string) (*Release, error
|
||||
return release, nil
|
||||
}
|
||||
|
||||
func (s *downloadService) GetSpecificRelease(softwareName string, version string) (*Release, error) {
|
||||
func (s *DownloadService) GetSpecificRelease(softwareName string, version string) (*Release, error) {
|
||||
software, err := s.softwareRepository.GetByName(softwareName)
|
||||
if err != nil {
|
||||
return nil, ErrSoftwareNotFound
|
||||
|
||||
@@ -7,17 +7,21 @@ type SoftwareListDTO struct {
|
||||
LatestRelease *Release
|
||||
}
|
||||
|
||||
type SoftwareService interface {
|
||||
type SoftwareServiceInterface interface {
|
||||
List() ([]SoftwareListDTO, error)
|
||||
GetByNameWithReleases(name string) (*Software, error)
|
||||
}
|
||||
|
||||
type softwareService struct {
|
||||
softwareRepository SoftwareRepository
|
||||
type SoftwareService struct {
|
||||
repository SoftwareRepositoryInterface
|
||||
}
|
||||
|
||||
func (s *softwareService) List() ([]SoftwareListDTO, error) {
|
||||
softwares, err := s.softwareRepository.List()
|
||||
func NewSoftwareService(repository SoftwareRepositoryInterface) *SoftwareService {
|
||||
return &SoftwareService{repository: repository}
|
||||
}
|
||||
|
||||
func (s *SoftwareService) List() ([]SoftwareListDTO, error) {
|
||||
softwares, err := s.repository.List()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -26,7 +30,6 @@ func (s *softwareService) List() ([]SoftwareListDTO, error) {
|
||||
for _, sw := range softwares {
|
||||
dto := SoftwareListDTO{Software: sw}
|
||||
if len(sw.Releases) > 0 {
|
||||
// Sort releases by CreatedAt in descending order to find the latest
|
||||
sort.Slice(sw.Releases, func(i, j int) bool {
|
||||
return sw.Releases[i].CreatedAt.After(sw.Releases[j].CreatedAt)
|
||||
})
|
||||
@@ -38,6 +41,6 @@ func (s *softwareService) List() ([]SoftwareListDTO, error) {
|
||||
return dtos, nil
|
||||
}
|
||||
|
||||
func (s *softwareService) GetByNameWithReleases(name string) (*Software, error) {
|
||||
return s.softwareRepository.GetByName(name)
|
||||
func (s *SoftwareService) GetByNameWithReleases(name string) (*Software, error) {
|
||||
return s.repository.GetByName(name)
|
||||
}
|
||||
|
||||
@@ -4,19 +4,21 @@ import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Keep os for getting GAME_PATH in TIC80 service if needed
|
||||
|
||||
type SoftwareUpdaterService interface {
|
||||
UpdateSoftware(platform, name string) error
|
||||
type SoftwareUpdaterServiceInterface interface {
|
||||
Update(platform, name string) error
|
||||
}
|
||||
|
||||
type softwareUpdaterService struct {
|
||||
tic80Updater SoftwareUpdaterTIC80Service
|
||||
type SoftwareUpdaterService struct {
|
||||
tic80Updater SoftwareUpdaterTIC80ServiceInterface
|
||||
}
|
||||
|
||||
func (s *softwareUpdaterService) UpdateSoftware(platform, name string) error {
|
||||
func NewSoftwareUpdaterService(tic80Updater SoftwareUpdaterTIC80ServiceInterface) *SoftwareUpdaterService {
|
||||
return &SoftwareUpdaterService{tic80Updater: tic80Updater}
|
||||
}
|
||||
|
||||
func (s *SoftwareUpdaterService) Update(platform, name string) error {
|
||||
if platform == "tic80" {
|
||||
return s.tic80Updater.UpdateTIC80Software(name)
|
||||
return s.tic80Updater.Update(name)
|
||||
}
|
||||
return fmt.Errorf("unsupported platform: %s", platform)
|
||||
}
|
||||
|
||||
@@ -1,193 +1,153 @@
|
||||
package domain
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const TIC80_GAME_EXTENSION = ".tic" // Assuming .tic is the extension for TIC-80 games
|
||||
|
||||
type SoftwareUpdaterTIC80Service interface {
|
||||
UpdateTIC80Software(name string) error
|
||||
type SoftwareUpdaterTIC80ServiceInterface interface {
|
||||
Update(name string) error
|
||||
}
|
||||
|
||||
type softwareUpdaterTIC80Service struct {
|
||||
softwareRepository SoftwareRepository
|
||||
releaseRepository ReleaseRepository
|
||||
type SoftwareUpdaterTIC80Service struct {
|
||||
softwareRepository SoftwareRepositoryInterface
|
||||
releaseRepository ReleaseRepositoryInterface
|
||||
fileRepository FileRepositoryInterface
|
||||
}
|
||||
|
||||
func (s *softwareUpdaterTIC80Service) UpdateTIC80Software(name string) error {
|
||||
func NewSoftwareUpdaterTIC80Service(
|
||||
softwareRepository SoftwareRepositoryInterface,
|
||||
releaseRepository ReleaseRepositoryInterface,
|
||||
fileRepository FileRepositoryInterface,
|
||||
) *SoftwareUpdaterTIC80Service {
|
||||
return &SoftwareUpdaterTIC80Service{
|
||||
softwareRepository: softwareRepository,
|
||||
releaseRepository: releaseRepository,
|
||||
fileRepository: fileRepository,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SoftwareUpdaterTIC80Service) Update(name string) error {
|
||||
fmt.Printf("TIC80 Updater: Starting update for name: %s\n", name)
|
||||
gamePath, _ := os.LookupEnv("GAMES_DIR")
|
||||
// Let's assume 'name' could be "mygame" which means "mygame.html.zip" or "mygame.lua" / "mygame.tic"
|
||||
contentsPath, _ := os.LookupEnv("CONTENTS_DIR")
|
||||
|
||||
// Case 1: HTML game (name.html.zip)
|
||||
zipFileName := fmt.Sprintf("%s.html.zip", name)
|
||||
zipFilePath := filepath.Join(gamePath, zipFileName)
|
||||
|
||||
if _, err := os.Stat(zipFilePath); err == nil { // If the zip file exists
|
||||
baseName := name
|
||||
destDir := filepath.Join(gamePath, "html", baseName) // Destination is GAMES_DIR/html/name
|
||||
fmt.Printf("TIC80 Updater: Unzipping %s to %s\n", zipFilePath, destDir)
|
||||
|
||||
if err := s.unzipSource(zipFilePath, destDir); err != nil {
|
||||
return fmt.Errorf("error unzipping %s: %w", zipFileName, err)
|
||||
}
|
||||
|
||||
if err := os.Remove(zipFilePath); err != nil {
|
||||
fmt.Printf("TIC80 Updater: Deleting zip file: %s\n", zipFilePath)
|
||||
return fmt.Errorf("error deleting zip file %s: %w", zipFileName, err)
|
||||
}
|
||||
fmt.Printf("TIC80 Updater: Successfully processed HTML game: %s\n", name)
|
||||
// Case 1: HTML content (name.html.zip)
|
||||
if err := s.handleHTMLContent(name, contentsPath); err == nil {
|
||||
fmt.Printf("TIC80 Updater: Successfully processed HTML content: %s\n", name)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Case 2: TIC-80 Lua game (name.lua and name.tic)
|
||||
luaFileName := fmt.Sprintf("%s.lua", name)
|
||||
cartridgeFileName := fmt.Sprintf("%s.tic", name)
|
||||
luaFilePath := filepath.Join(gamePath, luaFileName)
|
||||
cartridgeFilePath := filepath.Join(gamePath, cartridgeFileName)
|
||||
|
||||
if _, err := os.Stat(luaFilePath); err == nil { // If the lua file exists
|
||||
if _, err := os.Stat(cartridgeFilePath); os.IsNotExist(err) {
|
||||
return fmt.Errorf("missing cartridge file '%s' for '%s'", cartridgeFileName, luaFileName)
|
||||
}
|
||||
|
||||
software, version := s.parseMeta(luaFilePath, name)
|
||||
if version == "" {
|
||||
return fmt.Errorf("missing version info in '%s'", luaFileName)
|
||||
}
|
||||
|
||||
if err := s.softwareRepository.UpdateOrCreate(&software); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create software directory
|
||||
softwareDir := filepath.Join(gamePath, software.Name)
|
||||
fmt.Printf("TIC80 Updater: Creating software directory: %s\n", softwareDir)
|
||||
os.MkdirAll(softwareDir, os.ModePerm)
|
||||
|
||||
// New file paths
|
||||
newCartridgePath := filepath.Join(softwareDir, fmt.Sprintf("%s-%s.tic", software.Name, version))
|
||||
newSourcePath := filepath.Join(softwareDir, fmt.Sprintf("%s-%s.lua", software.Name, version))
|
||||
|
||||
// Move files
|
||||
fmt.Printf("TIC80 Updater: Moving cartridge from %s to %s\n", cartridgeFilePath, newCartridgePath)
|
||||
if err := os.Rename(cartridgeFilePath, newCartridgePath); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("TIC80 Updater: Moving Lua source from %s to %s\n", luaFilePath, newSourcePath)
|
||||
if err := os.Rename(luaFilePath, newSourcePath); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create Release
|
||||
release := &Release{
|
||||
SoftwareID: software.ID,
|
||||
Version: version,
|
||||
CartridgePath: newCartridgePath,
|
||||
SourcePath: newSourcePath,
|
||||
}
|
||||
if err := s.releaseRepository.Create(release); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("TIC80 Updater: Successfully processed Lua game: %s, version: %s\n", name, version)
|
||||
return nil // Lua game processed
|
||||
}
|
||||
|
||||
return fmt.Errorf("no recognizable game file found for '%s' in '%s'", name, gamePath)
|
||||
}
|
||||
|
||||
// unzipSource extracts a zip archive to a destination directory.
|
||||
func (s *softwareUpdaterTIC80Service) unzipSource(source, destination string) error {
|
||||
reader, err := zip.OpenReader(source)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer reader.Close()
|
||||
|
||||
destination, err = filepath.Abs(destination)
|
||||
if err != nil {
|
||||
// Case 2: TIC-80 Lua cartridge (name.lua and name.tic)
|
||||
if err := s.handleLuaCartridge(name, contentsPath); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, f := range reader.File {
|
||||
fpath := filepath.Join(destination, f.Name)
|
||||
|
||||
if !strings.HasPrefix(fpath, destination) {
|
||||
return fmt.Errorf("%s: illegal file path", fpath)
|
||||
}
|
||||
|
||||
if f.FileInfo().IsDir() {
|
||||
os.MkdirAll(fpath, os.ModePerm)
|
||||
continue
|
||||
}
|
||||
|
||||
if err = os.MkdirAll(filepath.Dir(fpath), os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
outFile, err := os.OpenFile(fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rc, err := f.Open()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = io.Copy(outFile, rc)
|
||||
|
||||
outFile.Close()
|
||||
rc.Close()
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
fmt.Printf("TIC80 Updater: Successfully processed Lua cartridge: %s\n", name)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *softwareUpdaterTIC80Service) parseMeta(path string, name string) (Software, string) {
|
||||
file, _ := os.Open(path)
|
||||
defer file.Close()
|
||||
func (s *SoftwareUpdaterTIC80Service) handleHTMLContent(name, contentsPath string) error {
|
||||
zipFileName := fmt.Sprintf("%s.html.zip", name)
|
||||
baseName := name
|
||||
|
||||
var g Software
|
||||
if err := s.fileRepository.UnzipHTMLContent(zipFileName, baseName, contentsPath); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return s.fileRepository.DeleteFile(zipFileName, contentsPath)
|
||||
}
|
||||
|
||||
func (s *SoftwareUpdaterTIC80Service) handleLuaCartridge(name, contentsPath string) error {
|
||||
luaFileName := fmt.Sprintf("%s.lua", name)
|
||||
cartridgeFileName := fmt.Sprintf("%s.tic", name)
|
||||
|
||||
if !s.fileRepository.FileExists(luaFileName, contentsPath) {
|
||||
return fmt.Errorf("no recognizable content file found for '%s' in '%s'", name, contentsPath)
|
||||
}
|
||||
|
||||
if !s.fileRepository.FileExists(cartridgeFileName, contentsPath) {
|
||||
return fmt.Errorf("missing cartridge file '%s' for '%s'", cartridgeFileName, luaFileName)
|
||||
}
|
||||
|
||||
software, version, err := s.parseMeta(luaFileName, name, contentsPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if version == "" {
|
||||
return fmt.Errorf("missing version info in '%s'", luaFileName)
|
||||
}
|
||||
|
||||
if err := s.softwareRepository.UpdateOrCreate(&software); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := s.moveCartridgeFiles(name, software, version, luaFileName, cartridgeFileName, contentsPath); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
release := &Release{
|
||||
SoftwareID: software.ID,
|
||||
Version: version,
|
||||
CartridgePath: s.fileRepository.GetCartridgePath(software.Name, version, contentsPath),
|
||||
SourcePath: s.fileRepository.GetSourcePath(software.Name, version, contentsPath),
|
||||
}
|
||||
|
||||
return s.releaseRepository.Create(release)
|
||||
}
|
||||
|
||||
func (s *SoftwareUpdaterTIC80Service) moveCartridgeFiles(name string, software Software, version, luaFileName, cartridgeFileName, contentsPath string) error {
|
||||
softwareDir := s.fileRepository.GetSoftwareDir(software.Name, contentsPath)
|
||||
|
||||
fmt.Printf("TIC80 Updater: Creating software directory: %s\n", softwareDir)
|
||||
if err := s.fileRepository.CreateDir(softwareDir); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
newCartridgePath := s.fileRepository.GetCartridgePath(software.Name, version, contentsPath)
|
||||
newSourcePath := s.fileRepository.GetSourcePath(software.Name, version, contentsPath)
|
||||
|
||||
fmt.Printf("TIC80 Updater: Moving cartridge from %s to %s\n", cartridgeFileName, newCartridgePath)
|
||||
if err := s.fileRepository.MoveFile(cartridgeFileName, newCartridgePath, contentsPath); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("TIC80 Updater: Moving Lua source from %s to %s\n", luaFileName, newSourcePath)
|
||||
if err := s.fileRepository.MoveFile(luaFileName, newSourcePath, contentsPath); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SoftwareUpdaterTIC80Service) parseMeta(luaFileName, name, contentsPath string) (Software, string, error) {
|
||||
var software Software
|
||||
var version string
|
||||
g.Name = name
|
||||
scanner := bufio.NewScanner(file)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
if strings.HasPrefix(line, "--") {
|
||||
parts := strings.SplitN(strings.TrimPrefix(line, "--"), ":", 2)
|
||||
if len(parts) != 2 {
|
||||
continue
|
||||
}
|
||||
key := strings.TrimSpace(parts[0])
|
||||
val := strings.TrimSpace(parts[1])
|
||||
|
||||
switch strings.ToLower(key) {
|
||||
case "title":
|
||||
g.Title = val
|
||||
case "author":
|
||||
g.Author = val
|
||||
case "desc":
|
||||
g.Desc = val
|
||||
case "site":
|
||||
g.Site = val
|
||||
case "license":
|
||||
g.License = val
|
||||
case "version":
|
||||
version = val
|
||||
}
|
||||
} else {
|
||||
break
|
||||
metaData, err := s.fileRepository.ReadMetaFromFile(luaFileName, contentsPath)
|
||||
if err != nil {
|
||||
return software, "", err
|
||||
}
|
||||
|
||||
software.Name = name
|
||||
software.Platform = "tic80"
|
||||
|
||||
for key, val := range metaData {
|
||||
switch key {
|
||||
case "title":
|
||||
software.Title = val
|
||||
case "author":
|
||||
software.Author = val
|
||||
case "desc":
|
||||
software.Desc = val
|
||||
case "site":
|
||||
software.Site = val
|
||||
case "license":
|
||||
software.License = val
|
||||
case "version":
|
||||
version = val
|
||||
}
|
||||
}
|
||||
return g, version
|
||||
|
||||
return software, version, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user