html.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. package resources
  2. import (
  3. "embed"
  4. "errors"
  5. "fmt"
  6. "github.com/gin-gonic/gin"
  7. "io/fs"
  8. "net/http"
  9. "os"
  10. "path"
  11. "path/filepath"
  12. "strings"
  13. )
  14. //go:embed assets/index.html
  15. var index []byte
  16. //go:embed assets/assets
  17. var Static embed.FS
  18. //go:embed images
  19. var Images embed.FS
  20. //go:embed assets/favicon.ico
  21. var ICO embed.FS
  22. //go:embed template/defaultAvatar.jpg
  23. var DefaultAvatar embed.FS
  24. //go:embed app/index.html
  25. var app []byte
  26. //go:embed app
  27. var APP embed.FS
  28. const (
  29. apiBase = "/api/v1"
  30. adminApi = "/admin-api/"
  31. assetsProxy = "assets"
  32. )
  33. func HtmlRouteHandler() gin.HandlerFunc {
  34. return func(context *gin.Context) {
  35. var uri = context.Request.URL.Path
  36. var ext = path.Ext(uri)
  37. if find := strings.Contains(uri, apiBase); find {
  38. return
  39. } else if find = ext == ".txt"; find {
  40. //打开文件
  41. file, err := os.Open("." + uri)
  42. defer file.Close()
  43. if err != nil {
  44. fmt.Println("打开文件错误", err)
  45. }
  46. context.File("." + uri)
  47. context.Abort()
  48. return
  49. }
  50. accept := context.Request.Header.Get("Accept")
  51. if strings.Contains(accept, "text/html") {
  52. writeIndex(context)
  53. }
  54. }
  55. }
  56. func writeIndex(context *gin.Context) {
  57. agent := context.Request.Header.Get("User-Agent")
  58. if strings.Contains(strings.ToLower(agent), "mobile") {
  59. context.Redirect(http.StatusFound, "/app")
  60. return
  61. }
  62. context.Writer.WriteHeader(200)
  63. context.Writer.Header().Add("Accept", "text/html")
  64. _, _ = context.Writer.Write(index)
  65. context.Writer.Flush()
  66. context.Abort()
  67. }
  68. type Resource struct {
  69. fs embed.FS
  70. path string
  71. }
  72. func NewResource() *Resource {
  73. return &Resource{
  74. fs: Static,
  75. path: "assets",
  76. }
  77. }
  78. func NewAppResource() *Resource {
  79. return &Resource{
  80. fs: APP,
  81. path: "app/",
  82. }
  83. }
  84. func NewImagesResource() *Resource {
  85. return &Resource{
  86. fs: Images,
  87. path: "images/",
  88. }
  89. }
  90. func (r *Resource) Open(name string) (fs.File, error) {
  91. if filepath.Separator != '/' && strings.ContainsRune(name, filepath.Separator) {
  92. return nil, errors.New("http: invalid character in file path")
  93. }
  94. fullName := r.getFullName(name)
  95. file, err := r.fs.Open(fullName)
  96. return file, err
  97. }
  98. func (r *Resource) getFullName(name string) string {
  99. if r.path == "assets" {
  100. return r.path + "/assets/" + name
  101. }
  102. return r.path + name
  103. }
  104. func InitResource(engine *gin.Engine) {
  105. resourceRouter(engine)
  106. engine.StaticFS("/assets", http.FS(NewResource()))
  107. engine.StaticFS("/app", http.FS(NewAppResource()))
  108. engine.StaticFS("/images", http.FS(NewImagesResource()))
  109. }
  110. type HtmlHandler struct{}
  111. func NewHtmlHandler() *HtmlHandler {
  112. return &HtmlHandler{}
  113. }
  114. // RedirectIndex 404 重定向
  115. func (h *HtmlHandler) RedirectIndex(context *gin.Context) {
  116. var uri = context.Request.URL.Path
  117. if strings.Contains(uri, adminApi) {
  118. context.Abort()
  119. return
  120. }
  121. accept := context.Request.Header.Get("Accept")
  122. if strings.Contains(accept, "text/html") {
  123. writeIndex(context)
  124. }
  125. }
  126. func (h *HtmlHandler) Index(context *gin.Context) {
  127. writeIndex(context)
  128. }
  129. func (h *HtmlHandler) App(context *gin.Context) {
  130. context.Writer.WriteHeader(200)
  131. context.Writer.Header().Add("Accept", "text/html")
  132. _, _ = context.Writer.Write(app)
  133. context.Writer.Flush()
  134. context.Abort()
  135. }
  136. // resourceRouter 静态资源配置
  137. func resourceRouter(engine *gin.Engine) {
  138. html := NewHtmlHandler()
  139. group := engine.Group("/")
  140. {
  141. group.GET("", html.Index)
  142. group.GET("/app", html.App)
  143. group.GET("/defaultAvatar", func(context *gin.Context) {
  144. context.Request.URL.Path = "template/defaultAvatar.jpg"
  145. staticServer := http.FileServer(http.FS(DefaultAvatar))
  146. staticServer.ServeHTTP(context.Writer, context.Request)
  147. context.Abort()
  148. })
  149. group.GET("favicon.ico", func(context *gin.Context) {
  150. context.Request.URL.Path = assetsProxy + context.Request.URL.Path
  151. staticServer := http.FileServer(http.FS(ICO))
  152. staticServer.ServeHTTP(context.Writer, context.Request)
  153. context.Abort()
  154. })
  155. }
  156. // 解决刷新 404 问题
  157. engine.NoRoute(html.RedirectIndex)
  158. }