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() {
http.HandleFunc("/api/apps.json", s.handleApps)
http.HandleFunc("/api/releases.json", s.handleReleases)
http.HandleFunc("/api/release/", s.handleReleaseMirror)
http.HandleFunc("/api/apps/zip/", s.handleAppZip)
log.Printf("Listening on %s...", flagListen)
http.ListenAndServe(flagListen, nil)
}
package main
import (
"archive/zip"
"bytes"
"context"
"encoding/json"
"fmt"
......@@ -35,6 +37,9 @@ type appInfo struct {
version int
commit string
stars int
commitObj *object.Commit
zip []byte
}
type GLProject struct {
......@@ -72,6 +77,57 @@ func (s *server) getStars(ctx context.Context, repo string) (int, error) {
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) {
p := path.Join(pathInRepo, "flow3r.toml")
f, err := obj.File(p)
......@@ -132,6 +188,7 @@ func (s *server) parseAppToml(ctx context.Context, pathInRepo string, obj *objec
description: data.Metadata.Description,
version: data.Metadata.Version,
commit: obj.Hash.String(),
commitObj: obj,
}, nil
}
......@@ -204,8 +261,14 @@ func (s *server) getAppInfo(ctx context.Context, pathInRepo, repo string) (*appI
if err != nil {
return nil, fmt.Errorf("getting stars failed: %w", err)
}
firstTime[highestVer].stars = stars
return firstTime[highestVer], nil
app := firstTime[highestVer]
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) {
......@@ -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)
continue
}
log.Printf("%+v", dat)
n := reAppRepo.FindStringSubmatch(dat.Repo)
if n == nil {
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) {
continue
}
// 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{
RepoURL: "https://git.flow3r.garden/" + a.repository,
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,
Menu: a.appInfo.menu,
Author: a.appInfo.author,
......@@ -355,3 +414,34 @@ func (s *server) handleApps(w http.ResponseWriter, r *http.Request) {
j := json.NewEncoder(w)
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 (
"io"
"log"
"net/http"
"regexp"
"strings"
)
......@@ -98,6 +99,10 @@ func (s *server) serveMirroredFile(w http.ResponseWriter, r *http.Request, rel *
http.NotFound(w, r)
}
var (
reMirrorURL = regexp.MustCompile("^/api/release/([^/]+)/([^/]+.bin)$")
)
func (s *server) handleReleaseMirror(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
......
......@@ -6,14 +6,9 @@ import (
"fmt"
"net/http"
"net/url"
"regexp"
"time"
)
var (
reMirrorURL = regexp.MustCompile("^/api/release/([^/]+)/([^/]+.bin)$")
)
type GLAssetLink struct {
ID int64 `json:"id"`
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