1
0
mirror of https://github.com/Luzifer/tex-api.git synced 2024-09-16 16:18:24 +00:00
tex-api/main.go
Knut Ahlers 95146f88cb
Add format override
Signed-off-by: Knut Ahlers <knut@ahlers.me>
2023-09-07 00:10:58 +02:00

161 lines
3.8 KiB
Go

package main
import (
"fmt"
"io"
"io/fs"
"net/http"
"os"
"time"
"github.com/Luzifer/rconfig/v2"
"github.com/pkg/errors"
"github.com/gofrs/uuid"
"github.com/gorilla/mux"
"github.com/sirupsen/logrus"
)
const (
filenameInput = "input.zip"
filenameStatus = "status.json"
filenameStatusTemp = "status.tmp.json"
filenameOutputDir = "output"
sleepBase = 1.5
)
var (
cfg = struct {
DefaultEnv string `flag:"default-env" default:"" description:"Environment to copy to the job before unpacking"`
Script string `flag:"script" default:"tex-build.sh" description:"Script to execute (needs to generate output directory)"`
Listen string `flag:"listen" default:":3000" description:"IP/Port to listen on"`
StorageDir string `flag:"storage-dir" default:"/storage" description:"Where to store uploaded ZIPs and resulting files"`
VersionAndExit bool `flag:"version" default:"false" description:"Prints current version and exits"`
}{}
version = "dev"
router = mux.NewRouter()
)
func initApp() error {
rconfig.AutoEnv(true)
if err := rconfig.Parse(&cfg); err != nil {
return errors.Wrap(err, "parsing cli options")
}
return nil
}
func main() {
var err error
if err = initApp(); err != nil {
logrus.WithError(err).Fatal("app initialization failed")
}
if cfg.VersionAndExit {
logrus.WithField("version", version).Info("tex-api")
os.Exit(0)
}
router.HandleFunc("/job", startNewJob).
Methods("POST").
Name("startNewJob")
router.HandleFunc("/job/{uid:[0-9a-z-]{36}}", getJobStatus).
Methods("GET").
Name("getJobStatus")
router.HandleFunc("/job/{uid:[0-9a-z-]{36}}/wait", waitForJob).
Methods("GET").
Name("waitForJob")
router.HandleFunc("/job/{uid:[0-9a-z-]{36}}/download", downloadAssets).
Methods("GET").
Name("downloadAssets")
server := &http.Server{
Addr: cfg.Listen,
Handler: router,
ReadHeaderTimeout: time.Second,
}
logrus.WithFields(logrus.Fields{
"addr": cfg.Listen,
"version": version,
}).Info("tex-api started")
if err := server.ListenAndServe(); err != nil {
logrus.WithError(err).Fatal("HTTP server exited with error")
}
}
func chooseDistribution(r *http.Request) string {
if dist := r.URL.Query().Get("format"); dist != "" {
return dist
}
switch r.Header.Get("Accept") {
case "application/tar", "application/x-tar", "application/x-gtar", "multipart/x-tar", "application/x-compress", "application/x-compressed":
return "tar"
case "application/pdf":
return "pdf"
default:
return "zip"
}
}
func downloadAssets(res http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
uid, err := uuid.FromString(vars["uid"])
if err != nil {
http.Error(res, "UUID had unexpected format!", http.StatusBadRequest)
return
}
var (
content io.Reader
contentType = "application/zip"
filename string
)
switch dist := chooseDistribution(r); dist {
case "tar":
contentType = "application/tar"
content, err = buildAssetsTAR(uid)
filename = uid.String() + ".tar"
case "pdf":
contentType = "application/pdf"
filename = uid.String() + ".pdf"
content, err = getAssetsFile(uid, ".pdf")
if errors.Is(err, fs.ErrNotExist) && r.URL.Query().Has("log-on-error") {
contentType = "application/octet-stream"
filename = uid.String() + ".log"
content, err = getAssetsFile(uid, ".log")
}
case "zip":
content, err = buildAssetsZIP(uid)
filename = uid.String() + ".zip"
default:
err = errors.Errorf("unknown distribution %q", dist)
}
if err != nil {
serverErrorf(res, err, "generating downloadable asset")
return
}
res.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%q", filename))
res.Header().Set("Content-Type", contentType)
res.WriteHeader(http.StatusOK)
if _, err = io.Copy(res, content); err != nil {
serverErrorf(res, err, "writing content")
return
}
}