268 lines
6.1 KiB
Go
268 lines
6.1 KiB
Go
package provider
|
|
|
|
import (
|
|
"archive/zip"
|
|
"conjure-os/lib/config"
|
|
"conjure-os/lib/models"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"net/http"
|
|
"net/url"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"gopkg.in/yaml.v3"
|
|
)
|
|
|
|
var token string
|
|
|
|
func Update() {
|
|
//requestURL := fmt.Sprintf("http://localhost:%d", 8080)
|
|
//login(requestURL)
|
|
//allGames(requestURL)
|
|
}
|
|
|
|
type AuthResponse struct {
|
|
Token string `json:"token"`
|
|
}
|
|
|
|
func login(requestURL string) {
|
|
client := &http.Client{}
|
|
form := url.Values{
|
|
"username": {"test"},
|
|
"password": {"test"}}
|
|
req, _ := http.NewRequest("POST", requestURL+"/login", strings.NewReader(form.Encode()))
|
|
req.Header.Set("API-Version", "1")
|
|
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
|
res, err := client.Do(req)
|
|
check(err)
|
|
var auth AuthResponse
|
|
err = json.NewDecoder(res.Body).Decode(&auth)
|
|
check(err)
|
|
token = auth.Token
|
|
}
|
|
|
|
func allGames(requestURL string) {
|
|
|
|
client := &http.Client{}
|
|
req, _ := http.NewRequest("GET", requestURL+"/games", nil)
|
|
req.Header.Set("Authorization", token)
|
|
req.Header.Set("API-Version", "1")
|
|
res, err := client.Do(req)
|
|
|
|
check(err)
|
|
fmt.Printf("client: status code: %d\n", res.StatusCode)
|
|
}
|
|
|
|
func GetOrSetEnvKey(key string, defaultValue string) string {
|
|
value, exist := os.LookupEnv(key)
|
|
if exist {
|
|
return value
|
|
}
|
|
err := os.Setenv(key, defaultValue)
|
|
if err != nil {
|
|
log.Println(err)
|
|
}
|
|
return defaultValue
|
|
}
|
|
|
|
func extractZipToSiblingFolder(zipPath string) error {
|
|
// Determine destination folder name (same name as zip file, without .zip)
|
|
zipBase := strings.TrimSuffix(filepath.Base(zipPath), ".conj")
|
|
zipBase = strings.TrimSuffix(filepath.Base(zipBase), ".zip")
|
|
destDir := filepath.Join(filepath.Dir(zipPath), zipBase)
|
|
|
|
// Delete destination folder if it exists
|
|
if _, err := os.Stat(destDir); err == nil {
|
|
err = os.RemoveAll(destDir)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to remove existing folder: %v", err)
|
|
}
|
|
}
|
|
|
|
// Open the zip archive
|
|
r, err := zip.OpenReader(zipPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer r.Close()
|
|
|
|
// Create the destination directory
|
|
err = os.MkdirAll(destDir, os.ModePerm)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, f := range r.File {
|
|
destPath := filepath.Join(destDir, f.Name)
|
|
|
|
// ZipSlip protection
|
|
if !strings.HasPrefix(destPath, filepath.Clean(destDir)+string(os.PathSeparator)) {
|
|
return fmt.Errorf("illegal file path: %s", destPath)
|
|
}
|
|
|
|
if f.FileInfo().IsDir() {
|
|
if err := os.MkdirAll(destPath, os.ModePerm); err != nil {
|
|
return err
|
|
}
|
|
continue
|
|
}
|
|
|
|
// Ensure parent directories exist
|
|
if err := os.MkdirAll(filepath.Dir(destPath), os.ModePerm); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Create and copy the file
|
|
srcFile, err := f.Open()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer srcFile.Close()
|
|
|
|
dstFile, err := os.OpenFile(destPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer dstFile.Close()
|
|
|
|
if _, err := io.Copy(dstFile, srcFile); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func extractZipsInFolder(folder string) error {
|
|
entries, err := os.ReadDir(folder)
|
|
if err != nil {
|
|
return fmt.Errorf("reading directory failed: %w", err)
|
|
}
|
|
|
|
for _, entry := range entries {
|
|
if entry.IsDir() {
|
|
continue
|
|
}
|
|
if strings.HasSuffix(strings.ToLower(entry.Name()), ".zip") {
|
|
zipPath := filepath.Join(folder, entry.Name())
|
|
fmt.Println("Extracting:", zipPath)
|
|
if err := extractZipToSiblingFolder(zipPath); err != nil {
|
|
return fmt.Errorf("failed to extract %s: %w", zipPath, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func ExtractGame(game models.Metadata) string {
|
|
info, err := os.Stat(filepath.Join(config.GetDefaultConjureGamesDirectory(), game.Id))
|
|
if err != nil || !info.IsDir() {
|
|
gamePath := filepath.Join(config.GetDefaultConjureGamesDirectory(), fmt.Sprintf("%s.conj", game.Id))
|
|
err = extractZipToSiblingFolder(gamePath)
|
|
check(err)
|
|
}
|
|
gamePath := filepath.Join(config.GetDefaultConjureGamesDirectory(), game.Id)
|
|
|
|
_, err = os.Stat(filepath.Join(gamePath, game.Files))
|
|
if err == nil {
|
|
return filepath.Join(gamePath, game.Files)
|
|
}
|
|
|
|
err = extractZipsInFolder(gamePath)
|
|
check(err)
|
|
gamePath = filepath.Join(gamePath, game.Files)
|
|
return gamePath
|
|
}
|
|
|
|
func GetConjureGameInfo() []models.Metadata {
|
|
gamePath := config.GetDefaultConjureGamesDirectory()
|
|
|
|
fmt.Println("Loading games....")
|
|
fmt.Println(gamePath)
|
|
|
|
entries, err := os.ReadDir(gamePath)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
var games []models.Metadata
|
|
|
|
for _, e := range entries {
|
|
if e.IsDir() {
|
|
continue
|
|
} else if filepath.Ext(e.Name()) == ".conj" {
|
|
conjPath := filepath.Join(gamePath, e.Name())
|
|
conjBase := strings.TrimSuffix(conjPath, ".conj")
|
|
|
|
// Check if the destination folder already exists
|
|
if _, err := os.Stat(conjBase); os.IsNotExist(err) {
|
|
err = extractZipToSiblingFolder(conjPath)
|
|
check(err)
|
|
}
|
|
|
|
// Now read metadata from the extracted directory
|
|
entries, err := os.ReadDir(conjBase)
|
|
check(err)
|
|
|
|
fmt.Println("Contents of", conjPath)
|
|
for _, f := range entries {
|
|
if f.Name() == "metadata.txt" {
|
|
rc, err := os.Open(filepath.Join(conjBase, f.Name()))
|
|
check(err)
|
|
defer rc.Close()
|
|
|
|
fmt.Println("Contents of metadata.txt:")
|
|
metadata, err := io.ReadAll(rc)
|
|
check(err)
|
|
game := parseGameInfo([]byte(escapeBackslashes(string(metadata))))
|
|
fmt.Println(game.ThumbnailPath)
|
|
games = append(games, game)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if len(games) > 0 {
|
|
fmt.Println("Found Conjure Games:", len(games))
|
|
} else {
|
|
fmt.Println("No Conjure games Found")
|
|
}
|
|
return games
|
|
}
|
|
|
|
// Function to escape backslashes in the YAML string
|
|
func escapeBackslashes(input string) string {
|
|
// Replace every single backslash with double backslashes
|
|
return strings.ReplaceAll(input, `\`, `\\`)
|
|
}
|
|
|
|
// Helper to print tree-like structure
|
|
func printIndentedPath(path string) {
|
|
parts := strings.Split(path, "/")
|
|
for i, part := range parts {
|
|
if part == "" {
|
|
continue
|
|
}
|
|
fmt.Print(strings.Repeat(" ", i))
|
|
fmt.Println(part)
|
|
}
|
|
}
|
|
|
|
func parseGameInfo(data []byte) models.Metadata {
|
|
game := models.Metadata{}
|
|
err := yaml.Unmarshal(data, &game)
|
|
check(err)
|
|
return game
|
|
}
|
|
|
|
func check(e error) {
|
|
if e != nil {
|
|
panic(e)
|
|
}
|
|
}
|