Skip to content
Snippets Groups Projects
Commit 24d5a5c1 authored by q3k's avatar q3k
Browse files

api: repack zips

parent 3eac2494
No related branches found
No related tags found
No related merge requests found
...@@ -31,6 +31,7 @@ func main() { ...@@ -31,6 +31,7 @@ func main() {
http.HandleFunc("/api/apps.json", s.handleApps) http.HandleFunc("/api/apps.json", s.handleApps)
http.HandleFunc("/api/releases.json", s.handleReleases) http.HandleFunc("/api/releases.json", s.handleReleases)
http.HandleFunc("/api/release/", s.handleReleaseMirror) http.HandleFunc("/api/release/", s.handleReleaseMirror)
http.HandleFunc("/api/apps/zip/", s.handleAppZip)
log.Printf("Listening on %s...", flagListen) log.Printf("Listening on %s...", flagListen)
http.ListenAndServe(flagListen, nil) http.ListenAndServe(flagListen, nil)
} }
package main package main
import ( import (
"archive/zip"
"bytes"
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
...@@ -35,6 +37,9 @@ type appInfo struct { ...@@ -35,6 +37,9 @@ type appInfo struct {
version int version int
commit string commit string
stars int stars int
commitObj *object.Commit
zip []byte
} }
type GLProject struct { type GLProject struct {
...@@ -72,6 +77,57 @@ func (s *server) getStars(ctx context.Context, repo string) (int, error) { ...@@ -72,6 +77,57 @@ func (s *server) getStars(ctx context.Context, repo string) (int, error) {
return project.StarCount, nil return project.StarCount, nil
} }
func (s *server) zipApp(ctx context.Context, name, pathInRepo, repo string, obj *object.Commit) ([]byte, error) {
fi, err := obj.Files()
if err != nil {
return nil, fmt.Errorf("listing files: %w", err)
}
buf := bytes.NewBuffer(nil)
w := zip.NewWriter(buf)
for {
f, err := fi.Next()
if err == io.EOF {
break
}
if err != nil {
return nil, fmt.Errorf("fi.Next: %w", err)
}
prefix := ""
if pathInRepo != "" {
prefix = pathInRepo + "/"
}
if !strings.HasPrefix(f.Name, prefix) {
continue
}
if !f.Mode.IsFile() {
continue
}
p := f.Name[len(prefix):]
prefix = strings.ReplaceAll(repo, "/", "-")
outPath := path.Join(prefix, p)
fo, err := w.Create(outPath)
if err != nil {
return nil, fmt.Errorf("Create(%q): %w", outPath, err)
}
if f.Size+int64(buf.Len()) > 10<<20 {
return nil, fmt.Errorf("archive too large")
}
rdr, err := f.Blob.Reader()
if err != nil {
return nil, fmt.Errorf("Blob.Reader: %w", err)
}
_, err = io.Copy(fo, rdr)
rdr.Close()
if err != nil {
return nil, fmt.Errorf("when copying: %w", err)
}
}
if err := w.Close(); err != nil {
return nil, fmt.Errorf("when closing: %w", err)
}
return buf.Bytes(), nil
}
func (s *server) parseAppToml(ctx context.Context, pathInRepo string, obj *object.Commit) (*appInfo, error) { func (s *server) parseAppToml(ctx context.Context, pathInRepo string, obj *object.Commit) (*appInfo, error) {
p := path.Join(pathInRepo, "flow3r.toml") p := path.Join(pathInRepo, "flow3r.toml")
f, err := obj.File(p) f, err := obj.File(p)
...@@ -132,6 +188,7 @@ func (s *server) parseAppToml(ctx context.Context, pathInRepo string, obj *objec ...@@ -132,6 +188,7 @@ func (s *server) parseAppToml(ctx context.Context, pathInRepo string, obj *objec
description: data.Metadata.Description, description: data.Metadata.Description,
version: data.Metadata.Version, version: data.Metadata.Version,
commit: obj.Hash.String(), commit: obj.Hash.String(),
commitObj: obj,
}, nil }, nil
} }
...@@ -204,8 +261,14 @@ func (s *server) getAppInfo(ctx context.Context, pathInRepo, repo string) (*appI ...@@ -204,8 +261,14 @@ func (s *server) getAppInfo(ctx context.Context, pathInRepo, repo string) (*appI
if err != nil { if err != nil {
return nil, fmt.Errorf("getting stars failed: %w", err) return nil, fmt.Errorf("getting stars failed: %w", err)
} }
firstTime[highestVer].stars = stars app := firstTime[highestVer]
return firstTime[highestVer], nil app.stars = stars
zbytes, err := s.zipApp(ctx, app.name, pathInRepo, repo, app.commitObj)
if err != nil {
return nil, fmt.Errorf("zipping failed: %w", err)
}
app.zip = zbytes
return app, nil
} }
func (s *server) getAppRegistry(ctx context.Context) ([]*appDescriptor, error) { func (s *server) getAppRegistry(ctx context.Context) ([]*appDescriptor, error) {
...@@ -276,7 +339,6 @@ func (s *server) getAppRegistry(ctx context.Context) ([]*appDescriptor, error) { ...@@ -276,7 +339,6 @@ func (s *server) getAppRegistry(ctx context.Context) ([]*appDescriptor, error) {
log.Printf("App %q: couldn't parse TOML: %v", m[1], err) log.Printf("App %q: couldn't parse TOML: %v", m[1], err)
continue continue
} }
log.Printf("%+v", dat)
n := reAppRepo.FindStringSubmatch(dat.Repo) n := reAppRepo.FindStringSubmatch(dat.Repo)
if n == nil { if n == nil {
log.Printf("App %q: invalid repo %q", m[1], dat.Repo) log.Printf("App %q: invalid repo %q", m[1], dat.Repo)
...@@ -335,13 +397,10 @@ func (s *server) handleApps(w http.ResponseWriter, r *http.Request) { ...@@ -335,13 +397,10 @@ func (s *server) handleApps(w http.ResponseWriter, r *http.Request) {
continue continue
} }
// Guarenteed to be exactly 2 parts because of the regex // Guarenteed to be exactly 2 parts because of the regex
parts := strings.Split(a.repository, "/")
orga := parts[0]
repo := parts[1]
resp.Apps = append(resp.Apps, app{ resp.Apps = append(resp.Apps, app{
RepoURL: "https://git.flow3r.garden/" + a.repository, RepoURL: "https://git.flow3r.garden/" + a.repository,
Commit: a.appInfo.commit, Commit: a.appInfo.commit,
DownloadURL: fmt.Sprintf("https://git.flow3r.garden/%s/%s/-/archive/%s/%s-%s.zip", orga, repo, a.appInfo.commit, repo, a.appInfo.commit), DownloadURL: fmt.Sprintf("%sapps/zip/%s.zip", flagBaseURL, a.repository),
Name: a.appInfo.name, Name: a.appInfo.name,
Menu: a.appInfo.menu, Menu: a.appInfo.menu,
Author: a.appInfo.author, Author: a.appInfo.author,
...@@ -355,3 +414,34 @@ func (s *server) handleApps(w http.ResponseWriter, r *http.Request) { ...@@ -355,3 +414,34 @@ func (s *server) handleApps(w http.ResponseWriter, r *http.Request) {
j := json.NewEncoder(w) j := json.NewEncoder(w)
j.Encode(resp) j.Encode(resp)
} }
var (
reAppZipURL = regexp.MustCompile("^/api/apps/zip/([^/]+)/([^/]+).zip$")
)
func (s *server) handleAppZip(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
matches := reAppZipURL.FindStringSubmatch(r.URL.Path)
if matches == nil {
http.NotFound(w, r)
return
}
orga := matches[1]
repo := matches[2]
apps, err := s.getApps(ctx)
if err != nil {
return
}
for _, app := range apps {
if app.repository == fmt.Sprintf("%s/%s", orga, repo) {
w.Header().Add("Content-Type", "application/zip")
w.Write(app.appInfo.zip)
return
}
}
http.NotFound(w, r)
}
...@@ -8,6 +8,7 @@ import ( ...@@ -8,6 +8,7 @@ import (
"io" "io"
"log" "log"
"net/http" "net/http"
"regexp"
"strings" "strings"
) )
...@@ -98,6 +99,10 @@ func (s *server) serveMirroredFile(w http.ResponseWriter, r *http.Request, rel * ...@@ -98,6 +99,10 @@ func (s *server) serveMirroredFile(w http.ResponseWriter, r *http.Request, rel *
http.NotFound(w, r) http.NotFound(w, r)
} }
var (
reMirrorURL = regexp.MustCompile("^/api/release/([^/]+)/([^/]+.bin)$")
)
func (s *server) handleReleaseMirror(w http.ResponseWriter, r *http.Request) { func (s *server) handleReleaseMirror(w http.ResponseWriter, r *http.Request) {
ctx := r.Context() ctx := r.Context()
......
...@@ -6,14 +6,9 @@ import ( ...@@ -6,14 +6,9 @@ import (
"fmt" "fmt"
"net/http" "net/http"
"net/url" "net/url"
"regexp"
"time" "time"
) )
var (
reMirrorURL = regexp.MustCompile("^/api/release/([^/]+)/([^/]+.bin)$")
)
type GLAssetLink struct { type GLAssetLink struct {
ID int64 `json:"id"` ID int64 `json:"id"`
Name string `json:"name"` Name string `json:"name"`
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment