Skip to content

Commit

Permalink
Placeholder to download zoo remote links.
Browse files Browse the repository at this point in the history
  • Loading branch information
bengarrett committed Feb 3, 2024
1 parent 587d07c commit 0e9eeae
Show file tree
Hide file tree
Showing 7 changed files with 172 additions and 9 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ require (
github.com/jackc/pgx/v5 v5.5.2
github.com/labstack/echo-contrib v0.15.0
github.com/labstack/echo/v4 v4.11.4
github.com/labstack/gommon v0.4.2
github.com/lib/pq v1.10.9
github.com/rosedblabs/rosedb/v2 v2.3.4
github.com/stretchr/testify v1.8.4
Expand Down Expand Up @@ -53,7 +54,6 @@ require (
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 // indirect
github.com/jackc/puddle/v2 v2.2.1 // indirect
github.com/labstack/gommon v0.4.2 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
Expand Down
7 changes: 4 additions & 3 deletions handler/app/web.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,15 @@ func EditDownloadGET(filename, filesize, demozoo any) template.HTML {
return ""
}
}
var zooID int64
if val, ok := demozoo.(null.Int64); ok {
if !val.Valid || val.Int64 == 0 {
return ""
}
zooID = val.Int64
}
// zoo := demozoo.(null.Int64).Int64
// fmt.Sprintf("%d", zoo)
return template.HTML(`<a class="card-link" href="">GET a remote download</a>`)
s := fmt.Sprintf("<a class=\"card-link\" href=\"/editor/new-for-approval/%d\">GET a remote download</a>", zooID)
return template.HTML(s)
}

// Web is the configuration and status of the web app.
Expand Down
44 changes: 44 additions & 0 deletions handler/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,20 @@ import (
"embed"
"fmt"
"net/http"
"os"
"path/filepath"
"strconv"
"strings"

"github.com/Defacto2/releaser"
"github.com/Defacto2/server/handler/app"
"github.com/Defacto2/server/internal/config"
"github.com/Defacto2/server/internal/helper"
"github.com/Defacto2/server/internal/zoo"
"github.com/gorilla/sessions"
"github.com/labstack/echo-contrib/session"
"github.com/labstack/echo/v4"
"github.com/labstack/gommon/log"
"go.uber.org/zap"
)

Expand Down Expand Up @@ -271,9 +277,47 @@ func (c Configuration) Routes(z *zap.SugaredLogger, e *echo.Echo, public embed.F
// Editor pages to update the database records.
editor := e.Group("/editor")
editor.Use(c.ReadOnlyLock, c.SessionLock)

editor.GET("/new-for-approval", func(x echo.Context) error {
return app.FilesWaiting(z, x, "1")
})
editor.GET("/new-for-approval/:id", func(x echo.Context) error {
// todo, placeholder
sid := x.Param("id")
id, err := strconv.Atoi(sid)
if err != nil {
return app.StatusErr(z, x, http.StatusNotFound, sid)
}
dz := zoo.Demozoo{}
if err = dz.Get(id); err != nil {
return app.StatusErr(z, x, http.StatusNotFound, sid)
}
var file string
for _, link := range dz.DownloadLinks {
if link.URL != "" {
file, err = helper.DownloadFile(link.URL)
if err != nil {
log.Debug(err)
continue
}
if file != "" {
base := filepath.Base(link.URL)
home, _ := os.UserHomeDir()
dst := filepath.Join(home, base)
if err = helper.RenameFile(file, dst); err != nil {
return app.StatusErr(z, x, http.StatusInternalServerError, sid)
}
return x.String(http.StatusOK,
fmt.Sprintf("File downloaded to %s", dst))
}
}
}
if file == "" {
return app.StatusErr(z, x, http.StatusNotFound, sid)
}
return x.JSON(http.StatusOK, dz)
})

online := editor.Group("/online")
online.POST("/true", func(x echo.Context) error {
return app.RecordToggle(z, x, true)
Expand Down
106 changes: 106 additions & 0 deletions internal/helper/get.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package helper

import (
"context"
"errors"
"fmt"
"io"
"net/http"
"os"
"time"
)

const (
// Timeout is the HTTP client timeout.
Timeout = 5 * time.Second
// User-Agent to send with the HTTP request.
UserAgent = "Defacto2 2024 app under construction (thanks!)"
)

// DownloadFile downloads a file from a remote URL and saves it to the default temp directory.
// It returns the path to the downloaded file.
func DownloadFile(url string) (string, error) {
// Get the remote file
client := http.Client{
Timeout: Timeout,
}
ctx := context.Background()
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return "", err
}
req.Header.Set("User-Agent", UserAgent)
res, err := client.Do(req)
if err != nil {
return "", err
}
defer res.Body.Close()

// Create the file in the default temp directory
tmpFile, err := os.CreateTemp("", "downloadfile-*")
if err != nil {
return "", err
}
defer tmpFile.Close()

// Write the body to file
if _, err := io.Copy(tmpFile, res.Body); err != nil {
defer os.Remove(tmpFile.Name())
return "", err
}
return tmpFile.Name(), nil
}

// RenameFile renames a file from oldpath to newpath.
// It returns an error if the oldpath does not exist or is a directory,
// newpath already exists, or the rename fails.
func RenameFile(oldpath, newpath string) error {
st, err := os.Stat(oldpath)
if err != nil {
return err
}
if st.IsDir() {
return fmt.Errorf("oldpath %w: %s", ErrFilePath, oldpath)
}
if _, err = os.Stat(newpath); err == nil {
return fmt.Errorf("newpath %w: %s", ErrExistPath, newpath)
}
if err := os.Rename(oldpath, newpath); err != nil {
var linkErr *os.LinkError
if errors.As(err, &linkErr) && linkErr.Err.Error() == "invalid cross-device link" {
return RenameCrossDevice(oldpath, newpath)
}
return err
}
return nil
}

// RenameCrossDevice is a workaround for renaming files across different devices.
// A cross device can also be a different file system such as a Docker volume.
func RenameCrossDevice(oldpath, newpath string) error {
src, err := os.Open(oldpath)
if err != nil {
return err
}
defer src.Close()
dst, err := os.Create(newpath)
if err != nil {
return err
}
defer dst.Close()

if _, err = io.Copy(dst, src); err != nil {
return err
}
fi, err := os.Stat(oldpath)
if err != nil {
defer os.Remove(newpath)
return err
}
if err = os.Chmod(newpath, fi.Mode()); err != nil {
defer os.Remove(newpath)
return err
}
defer os.Remove(oldpath)
return nil
}
6 changes: 6 additions & 0 deletions internal/helper/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"crypto/sha512"
"embed"
"encoding/base64"
"errors"
"fmt"
"net"
"os"
Expand All @@ -19,6 +20,11 @@ const (
byteUnits = "kMGTPE"
)

var (
ErrFilePath = errors.New("file path is a directory")
ErrExistPath = errors.New("path ready exists and will not overwrite")
)

// GetLocalIPs returns a list of local IP addresses.
// credit: https://gosamples.dev/local-ip-address/
func GetLocalIPs() ([]net.IP, error) {
Expand Down
6 changes: 3 additions & 3 deletions internal/pouet/pouet.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import (
"strconv"
"strings"
"time"

"github.com/Defacto2/server/internal/helper"
)

const (
Expand All @@ -21,8 +23,6 @@ const (
Timeout = 5 * time.Second
// StarRounder is the rounding value for the stars rating.
StarRounder = 0.5
// User-Agent to send with the HTTP request.
UserAgent = "Defacto2 2024 app, under construction (thanks!)"
// firstID is the first production ID on Pouet.
firstID = 1
)
Expand Down Expand Up @@ -191,7 +191,7 @@ func (r *Response) Get(id int) error {
if err != nil {
return err
}
req.Header.Set("User-Agent", UserAgent)
req.Header.Set("User-Agent", helper.UserAgent)
res, err := client.Do(req)
if err != nil {
return err
Expand Down
10 changes: 8 additions & 2 deletions internal/zoo/zoo.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import (
"net/http"
"strconv"
"time"

"github.com/Defacto2/server/internal/helper"
)

const (
Expand Down Expand Up @@ -43,6 +45,11 @@ type Demozoo struct {
Name string `json:"name"`
ID int `json:"id"`
} `json:"types"`
// Download links to the remotely hosted files.
DownloadLinks []struct {
LinkClass string `json:"link_class"`
URL string `json:"url"`
} `json:"download_links"`
// ID is the production ID.
ID int `json:"id"`
}
Expand All @@ -67,8 +74,7 @@ func (d *Demozoo) Get(id int) error {
if err != nil {
return err
}
req.Header.Set("User-Agent",
"Defacto2 2023 app under construction (thanks!)")
req.Header.Set("User-Agent", helper.UserAgent)
res, err := client.Do(req)
if err != nil {
return err
Expand Down

0 comments on commit 0e9eeae

Please sign in to comment.