minify/main.go

204 lines
5.9 KiB
Go

//go:generate npm --prefix frontend run build
package main
import (
"embed"
"github.com/chi-middleware/logrus-logger"
"github.com/go-chi/chi/v5"
"github.com/go-chi/cors"
"github.com/go-chi/httprate"
log "github.com/sirupsen/logrus"
httpSwagger "github.com/swaggo/http-swagger"
"github.com/tdewolff/minify/v2"
"github.com/tdewolff/minify/v2/css"
"github.com/tdewolff/minify/v2/html"
"github.com/tdewolff/minify/v2/js"
"github.com/tdewolff/minify/v2/json"
"github.com/tdewolff/minify/v2/svg"
"github.com/tdewolff/minify/v2/xml"
"io"
"io/fs"
_ "minify.ccatss.dev/docs"
"net/http"
"regexp"
"strings"
"time"
)
var (
//go:embed frontend/dist
frontendFs embed.FS
//go:embed LICENSE.txt
licenseBytes []byte
)
// @title Minifier API
// @version 1.0
// @description An API Implementation of tdewolfe's minify package
// @termsOfService http://swagger.io/terms/
// @contact.name ccatss
// @contact.url https://ccatss.dev
// @contact.email admin@meow.tf
// @license.name ISC License
// @license.url /LICENSE.txt
// @host minify.ccatss.dev
// @BasePath /api
func main() {
r := chi.NewRouter()
r.Use(logger.Logger("router", log.StandardLogger()))
m := minify.New()
m.AddFunc("text/css", css.Minify)
m.AddFunc("text/html", html.Minify)
m.AddFunc("image/svg+xml", svg.Minify)
m.AddFuncRegexp(regexp.MustCompile("^(application|text)/(x-)?(java|ecma)script$"), js.Minify)
m.AddFuncRegexp(regexp.MustCompile("[/+]json$"), json.Minify)
m.AddFuncRegexp(regexp.MustCompile("[/+]xml$"), xml.Minify)
subFs, err := fs.Sub(frontendFs, "frontend/dist")
if err != nil {
log.WithError(err).Fatalln("Unable to load frontend")
}
httpFs := http.FileServer(http.FS(subFs))
r.Handle("/", httpFs)
r.Handle("/js/*", httpFs)
r.Handle("/css/*", httpFs)
r.Get("/LICENSE.txt", func(w http.ResponseWriter, r *http.Request) {
w.Write(licenseBytes)
})
r.Get("/swagger/*", httpSwagger.Handler(
httpSwagger.URL("/swagger/doc.json"), //The url pointing to API definition
))
r.Route("/api", func(r chi.Router) {
r.Use(httprate.LimitByIP(100, 1*time.Minute))
r.Use(cors.Handler(cors.Options{
AllowedOrigins: []string{"https://*", "http://*"},
AllowedMethods: []string{"GET", "POST", "OPTIONS"},
AllowedHeaders: []string{"Accept", "Content-Type"},
ExposedHeaders: []string{"Link"},
AllowCredentials: false,
MaxAge: 300, // Maximum value not ignored by any of major browsers
}))
r.Post("/", minifyContent("", m))
r.Post("/css", minifyCss(m))
r.Post("/html", minifyHtml(m))
r.Post("/svg", minifySVG(m))
r.Post("/js", minifyJS(m))
r.Post("/json", minifyJSON(m))
r.Post("/xml", minifyXML(m))
})
http.ListenAndServe(":3000", r)
}
// Minify godoc
// @Summary Minify content
// @Description Minify content based on input type
// @Param input body string false "Input text"
// @Accept html,text/css,image/svg+xml,application/javascript,json,xml
// @Produce text/plain
// @Success 200 {string} string "Minified content"
// @Router / [post]
func minifyContent(mediaType string, min *minify.M) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if mediaType == "" {
mediaType = r.Header.Get("Content-Type")
if idx := strings.Index(mediaType, ";"); idx != -1 {
mediaType = mediaType[0:idx]
}
}
_, err := io.Copy(w, min.Reader(mediaType, r.Body))
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
}
// Minify CSS godoc
// @Summary Minify CSS
// @Description Minify CSS Input
// @Param input body string false "Input CSS"
// @Accept text/css
// @Produce text/css
// @Success 200 {string} string "Minified CSS"
// @Router /css [post]
func minifyCss(min *minify.M) http.HandlerFunc {
return minifyContent("text/css", min)
}
// Minify HTML godoc
// @Summary Minify HTML
// @Description Minify HTML Input
// @Param input body string false "Input HTML"
// @Accept html
// @Produce html
// @Success 200 {string} string "Minified HTML"
// @Router /html [post]
func minifyHtml(min *minify.M) http.HandlerFunc {
return minifyContent("text/html", min)
}
// Minify SVG godoc
// @Summary Minify SVG
// @Description Minify SVG Input
// @Param input body string false "Input SVG"
// @Accept image/svg+xml
// @Produce image/svg+xml
// @Success 200 {string} string "Minified SVG"
// @Router /svg [post]
func minifySVG(min *minify.M) http.HandlerFunc {
return minifyContent("image/svg+xml", min)
}
// Minify JS godoc
// @Summary Minify JavaScript
// @Description Minify JavaScript Input
// @Param input body string false "Input JavaScript"
// @Accept application/javascript
// @Produce application/javascript
// @Success 200 {string} string "Minified JavaScript"
// @Router /js [post]
func minifyJS(min *minify.M) http.HandlerFunc {
return minifyContent("application/javascript", min)
}
// Minify JSON godoc
// @Summary Minify JSON
// @Description Minify JSON Input
// @Param input body string false "Input JSON"
// @Accept application/json
// @Produce application/json
// @Success 200 {string} string "Minified JSON"
// @Router /json [post]
func minifyJSON(min *minify.M) http.HandlerFunc {
return minifyContent("application/json", min)
}
// Minify XML godoc
// @Summary Minify XML
// @Description Minify XML Input
// @Param input body string false "Input XML"
// @Accept xml
// @Produce xml
// @Success 200 {string} string "Minified XML"
// @Router /xml [post]
func minifyXML(min *minify.M) http.HandlerFunc {
return minifyContent("text/xml", min)
}