service.go 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. package service
  2. import (
  3. "fmt"
  4. "github.com/gin-contrib/static"
  5. "github.com/gin-gonic/gin"
  6. "github.com/yddeng/utils/task"
  7. "net/http"
  8. "reflect"
  9. "strings"
  10. "sync"
  11. "time"
  12. "github.com/swaggo/gin-swagger"
  13. ginSwaggerFiles "github.com/swaggo/files"
  14. "filecloud/docs"
  15. "filecloud/internal/handler"
  16. )
  17. var (
  18. app *gin.Engine
  19. taskQueue *task.TaskPool
  20. )
  21. func Launch() {
  22. taskQueue = task.NewTaskPool(1, 1024)
  23. // 初始化任务队列
  24. logger.Info("Task queue initialized")
  25. // 初始化 Swagger 信息
  26. docs.SwaggerInfo.Title = "FileCloud API"
  27. docs.SwaggerInfo.Description = "FileCloud API documentation"
  28. docs.SwaggerInfo.Version = "1.0"
  29. docs.SwaggerInfo.BasePath = "/api"
  30. docs.SwaggerInfo.Host = config.WebAddr
  31. app = gin.New()
  32. app.Use(gin.Logger(), gin.Recovery())
  33. // 跨域
  34. app.Use(func(ctx *gin.Context) {
  35. ctx.Header("Access-Control-Allow-Origin", "*")
  36. ctx.Header("Access-Control-Allow-Headers", "*")
  37. ctx.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, PATCH")
  38. ctx.Header("Access-Control-Allow-Credentials", "true")
  39. ctx.Header("Access-Control-Expose-Headers", "*")
  40. if ctx.Request.Method == "OPTIONS" {
  41. // 处理浏览器的options请求时,返回200状态即可
  42. ctx.JSON(http.StatusOK, "")
  43. ctx.Abort()
  44. return
  45. }
  46. ctx.Next()
  47. })
  48. // if config.StaticFS {
  49. // // 静态资源浏览
  50. // app.StaticFS("/static", gin.Dir(config.FilePath, true))
  51. // }
  52. // 前端
  53. if config.WebIndex != "" {
  54. app.Use(static.Serve("/static", static.LocalFile(config.WebIndex, false)))
  55. app.NoRoute(func(ctx *gin.Context) {
  56. ctx.File(config.WebIndex + "/index.html")
  57. })
  58. } else {
  59. // 使用内置的 public 目录
  60. app.Use(static.Serve("/static", static.LocalFile("public", false)))
  61. app.NoRoute(func(ctx *gin.Context) {
  62. ctx.File("public/index.html")
  63. })
  64. }
  65. // 注册 Swagger 路由
  66. app.GET("/swagger/*any", ginSwagger.WrapHandler(ginSwaggerFiles.Handler))
  67. // 静态资源嵌入与服务
  68. app.StaticFS("/", http.FS(embeddedFiles))
  69. initHandler(app)
  70. port := strings.Split(config.WebAddr, ":")[1]
  71. webAddr := fmt.Sprintf("0.0.0.0:%s", port)
  72. // logger.Infof("start web service on %s", config.WebAddr)
  73. if err := app.Run(webAddr); err != nil {
  74. logger.Error(err)
  75. }
  76. }
  77. func Stop() {
  78. }
  79. // 应答结构
  80. type Result struct {
  81. Success bool `json:"success"`
  82. Message string `json:"message"`
  83. Data interface{} `json:"data"`
  84. }
  85. type WaitConn struct {
  86. code int
  87. ctx *gin.Context
  88. route string
  89. result Result
  90. done chan struct{}
  91. doneOnce sync.Once
  92. }
  93. func newWaitConn(ctx *gin.Context, route string) *WaitConn {
  94. return &WaitConn{
  95. ctx: ctx,
  96. code: http.StatusOK,
  97. route: route,
  98. done: make(chan struct{}),
  99. }
  100. }
  101. func (this *WaitConn) Done(code ...int) {
  102. this.doneOnce.Do(func() {
  103. if this.result.Message == "" {
  104. this.result.Success = true
  105. }
  106. if len(code) > 0 {
  107. this.code = code[0]
  108. }
  109. close(this.done)
  110. })
  111. }
  112. func (this *WaitConn) GetRoute() string {
  113. return this.route
  114. }
  115. func (this *WaitConn) Context() *gin.Context {
  116. return this.ctx
  117. }
  118. func (this *WaitConn) SetResult(message string, data interface{}) {
  119. this.result.Message = message
  120. this.result.Data = data
  121. }
  122. func (this *WaitConn) Wait() {
  123. <-this.done
  124. }
  125. type webTask func()
  126. func (t webTask) Do() {
  127. t()
  128. }
  129. func transBegin(ctx *gin.Context, fn interface{}, args ...reflect.Value) {
  130. val := reflect.ValueOf(fn)
  131. if val.Kind() != reflect.Func {
  132. panic("value not func")
  133. }
  134. typ := val.Type()
  135. if typ.NumIn() != len(args)+1 {
  136. panic("func argument error")
  137. }
  138. route := getCurrentRoute(ctx)
  139. wait := newWaitConn(ctx, route)
  140. if err := taskQueue.SubmitTask(webTask(func() {
  141. ok := checkToken(ctx, route)
  142. if !ok {
  143. wait.SetResult("Token验证失败", nil)
  144. wait.Done(401)
  145. return
  146. }
  147. val.Call(append([]reflect.Value{reflect.ValueOf(wait)}, args...))
  148. })); err != nil {
  149. wait.SetResult("访问人数过多", nil)
  150. wait.Done()
  151. }
  152. wait.Wait()
  153. ctx.JSON(wait.code, wait.result)
  154. }
  155. func getCurrentRoute(ctx *gin.Context) string {
  156. return ctx.FullPath()
  157. }
  158. func getJsonBody(ctx *gin.Context, inType reflect.Type) (inValue reflect.Value, err error) {
  159. if inType.Kind() == reflect.Ptr {
  160. inValue = reflect.New(inType.Elem())
  161. } else {
  162. inValue = reflect.New(inType)
  163. }
  164. if err = ctx.ShouldBindJSON(inValue.Interface()); err != nil {
  165. return
  166. }
  167. if inType.Kind() != reflect.Ptr {
  168. inValue = inValue.Elem()
  169. }
  170. return
  171. }
  172. func WarpHandle(fn interface{}) gin.HandlerFunc {
  173. val := reflect.ValueOf(fn)
  174. if val.Kind() != reflect.Func {
  175. panic("value not func")
  176. }
  177. typ := val.Type()
  178. switch typ.NumIn() {
  179. case 1: // func(done *WaitConn)
  180. return func(ctx *gin.Context) {
  181. transBegin(ctx, fn)
  182. }
  183. case 2: // func(done *WaitConn, req struct)
  184. return func(ctx *gin.Context) {
  185. inValue, err := getJsonBody(ctx, typ.In(1))
  186. if err != nil {
  187. ctx.JSON(http.StatusBadRequest, gin.H{
  188. "message": "Json unmarshal failed!",
  189. "error": err.Error(),
  190. })
  191. return
  192. }
  193. transBegin(ctx, fn, inValue)
  194. }
  195. default:
  196. panic("func symbol error")
  197. }
  198. }
  199. var (
  200. // 需要验证token的路由
  201. routeNeedToken = map[string]struct{}{
  202. "/file/list": {},
  203. "/file/mkdir": {},
  204. "/file/remove": {},
  205. "/file/rename": {},
  206. "/file/mvcp": {},
  207. "/file/download": {},
  208. "/upload/check": {},
  209. "/upload/upload": {},
  210. "/shared/create": {},
  211. "/shared/list": {},
  212. "/shared/cancel": {},
  213. }
  214. )
  215. func checkToken(ctx *gin.Context, route string) bool {
  216. if _, ok := routeNeedToken[route]; !ok {
  217. return true
  218. }
  219. tkn := ctx.GetHeader("Access-Token")
  220. if tkn == "" {
  221. return false
  222. }
  223. if accessTokenExpire.IsZero() || time.Now().After(accessTokenExpire) {
  224. accessToken = ""
  225. accessTokenExpire = time.Time{}
  226. return false
  227. }
  228. return tkn == accessToken
  229. }
  230. func initHandler(app *gin.Engine) {
  231. authHandle := new(authHandler)
  232. authGroup := app.Group("/auth")
  233. authGroup.POST("/login", WarpHandle(authHandle.login))
  234. taskHandle := new(taskHandler)
  235. fileGroup := app.Group("/file")
  236. fileGroup.GET("/task", WarpHandle(taskHandle.taskInfo))
  237. // 注册文件相关 API
  238. apiGroup := app.Group("/api/files")
  239. apiGroup.POST("", handler.UploadFile)
  240. apiGroup.GET("", handler.ListFiles)
  241. }