first version
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"rpirelay/config"
|
||||
"time"
|
||||
|
||||
"github.com/gin-contrib/cors"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
*gin.Engine
|
||||
cfg *config.Config
|
||||
}
|
||||
|
||||
func New(cfg *config.Config) *Server {
|
||||
e := gin.New()
|
||||
|
||||
e.Use(gin.Logger())
|
||||
e.Use(gin.Recovery())
|
||||
|
||||
corsCfg := cors.DefaultConfig()
|
||||
corsCfg.AllowOriginFunc = func(origin string) bool { return origin != "" }
|
||||
corsCfg.AllowCredentials = true
|
||||
corsCfg.AddAllowHeaders("Authorization")
|
||||
|
||||
e.Use(cors.New(corsCfg))
|
||||
|
||||
if os.Getenv("RELEASE") != "1" {
|
||||
e.Use(SwaggerServe("/api-docs", nil))
|
||||
e.GET("/api-docs.json", SwaggerSpecServe())
|
||||
}
|
||||
|
||||
// set healthcheck path
|
||||
e.GET("/", func(c *gin.Context) {
|
||||
c.String(http.StatusOK, "ok")
|
||||
})
|
||||
|
||||
return &Server{
|
||||
Engine: e,
|
||||
cfg: cfg,
|
||||
}
|
||||
}
|
||||
|
||||
func (w *Server) Start(ctx context.Context) {
|
||||
s := &http.Server{
|
||||
Handler: w,
|
||||
Addr: fmt.Sprintf(":%d", w.cfg.Server.Port),
|
||||
}
|
||||
|
||||
go func() {
|
||||
if err := s.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||
s.ErrorLog.Fatal(err)
|
||||
}
|
||||
}()
|
||||
|
||||
<-ctx.Done()
|
||||
|
||||
c, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
||||
defer cancel()
|
||||
|
||||
if err := s.Shutdown(c); err != nil {
|
||||
s.ErrorLog.Fatal(err)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/textproto"
|
||||
"regexp"
|
||||
rpiRelay "rpirelay"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type SwaggerOptions struct {
|
||||
Root string
|
||||
}
|
||||
|
||||
var regex = regexp.MustCompile(`(src|href)="./`)
|
||||
var specRegex = regexp.MustCompile(`url:.+`)
|
||||
|
||||
func SwaggerSpecServe() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
b := rpiRelay.GetSpec()
|
||||
|
||||
var j map[string]interface{}
|
||||
if err := json.Unmarshal(b, &j); err != nil {
|
||||
c.String(http.StatusInternalServerError, "internal error")
|
||||
return
|
||||
}
|
||||
|
||||
if _, ok := j["host"]; ok {
|
||||
j["host"] = c.Request.Host
|
||||
}
|
||||
if xfp := c.Request.Header.Get(textproto.CanonicalMIMEHeaderKey("X-Forwarded-Proto")); xfp != "" && xfp == "https" {
|
||||
j["schemes"] = []string{xfp}
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, j)
|
||||
}
|
||||
}
|
||||
|
||||
func SwaggerServe(prefix string, opts *SwaggerOptions) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
fp := c.Request.URL.Path
|
||||
|
||||
if prefix != "" {
|
||||
if !strings.HasPrefix(fp, prefix) {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
if fp = strings.Replace(fp, prefix, "", 1); fp == "" {
|
||||
fp = "/"
|
||||
}
|
||||
}
|
||||
|
||||
// end of slash replace to index.html file
|
||||
if strings.HasSuffix(fp, "/") {
|
||||
fp += "index.html"
|
||||
}
|
||||
|
||||
if b, mime, err := rpiRelay.GetWebFile(fp); err == nil {
|
||||
// 如果是 index.html 要更新內部的連結 轉換成絕對路徑
|
||||
if fp == "/index.html" {
|
||||
replPrefix := prefix
|
||||
if !strings.HasSuffix(replPrefix, "/") {
|
||||
replPrefix += "/"
|
||||
}
|
||||
|
||||
b = regex.ReplaceAll(b, []byte(fmt.Sprintf(`$1="%s`, replPrefix)))
|
||||
b = specRegex.ReplaceAll(b, []byte(fmt.Sprintf(`url: "%s.json",`, prefix)))
|
||||
}
|
||||
|
||||
c.Writer.Header().Set(textproto.CanonicalMIMEHeaderKey("Content-Type"), mime)
|
||||
c.Status(http.StatusOK)
|
||||
c.Writer.Write(b)
|
||||
c.Abort()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user