v2.go 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. package route
  2. import (
  3. "crypto/ecdsa"
  4. "log"
  5. "net/http"
  6. "net/url"
  7. "path"
  8. "path/filepath"
  9. "strconv"
  10. "strings"
  11. "github.com/IceWhaleTech/CasaOS/codegen"
  12. "github.com/IceWhaleTech/CasaOS/pkg/config"
  13. "github.com/IceWhaleTech/CasaOS/pkg/utils/file"
  14. "github.com/IceWhaleTech/CasaOS-Common/external"
  15. "github.com/IceWhaleTech/CasaOS-Common/utils/jwt"
  16. v2Route "github.com/IceWhaleTech/CasaOS/route/v2"
  17. "github.com/deepmap/oapi-codegen/pkg/middleware"
  18. "github.com/getkin/kin-openapi/openapi3"
  19. "github.com/getkin/kin-openapi/openapi3filter"
  20. "github.com/labstack/echo/v4"
  21. echo_middleware "github.com/labstack/echo/v4/middleware"
  22. )
  23. var (
  24. _swagger *openapi3.T
  25. V2APIPath string
  26. V2DocPath string
  27. V3FilePath string
  28. )
  29. func init() {
  30. swagger, err := codegen.GetSwagger()
  31. if err != nil {
  32. panic(err)
  33. }
  34. _swagger = swagger
  35. u, err := url.Parse(_swagger.Servers[0].URL)
  36. if err != nil {
  37. panic(err)
  38. }
  39. V2APIPath = strings.TrimRight(u.Path, "/")
  40. V2DocPath = "/doc" + V2APIPath
  41. V3FilePath = "/v3/file"
  42. }
  43. func InitV2Router() http.Handler {
  44. appManagement := v2Route.NewCasaOS()
  45. e := echo.New()
  46. e.Use((echo_middleware.CORSWithConfig(echo_middleware.CORSConfig{
  47. AllowOrigins: []string{"*"},
  48. AllowMethods: []string{echo.POST, echo.GET, echo.OPTIONS, echo.PUT, echo.DELETE},
  49. AllowHeaders: []string{echo.HeaderAuthorization, echo.HeaderContentLength, echo.HeaderXCSRFToken, echo.HeaderContentType, echo.HeaderAccessControlAllowOrigin, echo.HeaderAccessControlAllowHeaders, echo.HeaderAccessControlAllowMethods, echo.HeaderConnection, echo.HeaderOrigin, echo.HeaderXRequestedWith},
  50. ExposeHeaders: []string{echo.HeaderContentLength, echo.HeaderAccessControlAllowOrigin, echo.HeaderAccessControlAllowHeaders},
  51. MaxAge: 172800,
  52. AllowCredentials: true,
  53. })))
  54. e.Use(echo_middleware.Gzip())
  55. e.Use(echo_middleware.Logger())
  56. e.Use(echo_middleware.JWTWithConfig(echo_middleware.JWTConfig{
  57. Skipper: func(c echo.Context) bool {
  58. return c.RealIP() == "::1" || c.RealIP() == "127.0.0.1"
  59. //return true
  60. },
  61. ParseTokenFunc: func(token string, c echo.Context) (interface{}, error) {
  62. valid, claims, err := jwt.Validate(token, func() (*ecdsa.PublicKey, error) { return external.GetPublicKey(config.CommonInfo.RuntimePath) })
  63. if err != nil || !valid {
  64. return nil, echo.ErrUnauthorized
  65. }
  66. c.Request().Header.Set("user_id", strconv.Itoa(claims.ID))
  67. return claims, nil
  68. },
  69. TokenLookupFuncs: []echo_middleware.ValuesExtractor{
  70. func(c echo.Context) ([]string, error) {
  71. if len(c.Request().Header.Get(echo.HeaderAuthorization)) > 0 {
  72. return []string{c.Request().Header.Get(echo.HeaderAuthorization)}, nil
  73. }
  74. return []string{c.QueryParam("token")}, nil
  75. },
  76. },
  77. }))
  78. // e.Use(func(next echo.HandlerFunc) echo.HandlerFunc {
  79. // return func(c echo.Context) error {
  80. // switch c.Request().Header.Get(echo.HeaderContentType) {
  81. // case common.MIMEApplicationYAML: // in case request contains a compose content in YAML
  82. // return middleware.OapiRequestValidatorWithOptions(_swagger, &middleware.Options{
  83. // Options: openapi3filter.Options{
  84. // AuthenticationFunc: openapi3filter.NoopAuthenticationFunc,
  85. // // ExcludeRequestBody: true,
  86. // // ExcludeResponseBody: true,
  87. // },
  88. // })(next)(c)
  89. // default:
  90. // return middleware.OapiRequestValidatorWithOptions(_swagger, &middleware.Options{
  91. // Options: openapi3filter.Options{
  92. // AuthenticationFunc: openapi3filter.NoopAuthenticationFunc,
  93. // },
  94. // })(next)(c)
  95. // }
  96. // }
  97. // })
  98. e.Use(middleware.OapiRequestValidatorWithOptions(_swagger, &middleware.Options{
  99. Options: openapi3filter.Options{AuthenticationFunc: openapi3filter.NoopAuthenticationFunc},
  100. }))
  101. codegen.RegisterHandlersWithBaseURL(e, appManagement, V2APIPath)
  102. return e
  103. }
  104. func InitV2DocRouter(docHTML string, docYAML string) http.Handler {
  105. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  106. if r.URL.Path == V2DocPath {
  107. if _, err := w.Write([]byte(docHTML)); err != nil {
  108. w.WriteHeader(http.StatusInternalServerError)
  109. }
  110. return
  111. }
  112. if r.URL.Path == V2DocPath+"/openapi.yaml" {
  113. if _, err := w.Write([]byte(docYAML)); err != nil {
  114. w.WriteHeader(http.StatusInternalServerError)
  115. }
  116. }
  117. })
  118. }
  119. func InitFile() http.Handler {
  120. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  121. token := r.URL.Query().Get("token")
  122. if len(token) == 0 {
  123. w.Header().Set("Content-Type", "application/json")
  124. w.WriteHeader(http.StatusUnauthorized)
  125. w.Write([]byte(`{"message": "token not found"}`))
  126. return
  127. }
  128. valid, _, errs := jwt.Validate(token, func() (*ecdsa.PublicKey, error) { return external.GetPublicKey(config.CommonInfo.RuntimePath) })
  129. if errs != nil || !valid {
  130. w.Header().Set("Content-Type", "application/json")
  131. w.WriteHeader(http.StatusUnauthorized)
  132. w.Write([]byte(`{"message": "validation failure"}`))
  133. return
  134. }
  135. filePath := r.URL.Query().Get("path")
  136. fileName := path.Base(filePath)
  137. w.Header().Add("Content-Disposition", "attachment; filename*=utf-8''"+url.PathEscape(fileName))
  138. http.ServeFile(w, r, filePath)
  139. //http.ServeFile(w, r, filePath)
  140. })
  141. }
  142. func InitDir() http.Handler {
  143. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  144. token := r.URL.Query().Get("token")
  145. if len(token) == 0 {
  146. w.Header().Set("Content-Type", "application/json")
  147. w.WriteHeader(http.StatusUnauthorized)
  148. w.Write([]byte(`{"message": "token not found"}`))
  149. return
  150. }
  151. valid, _, errs := jwt.Validate(token, func() (*ecdsa.PublicKey, error) { return external.GetPublicKey(config.CommonInfo.RuntimePath) })
  152. if errs != nil || !valid {
  153. w.Header().Set("Content-Type", "application/json")
  154. w.WriteHeader(http.StatusUnauthorized)
  155. w.Write([]byte(`{"message": "validation failure"}`))
  156. return
  157. }
  158. t := r.URL.Query().Get("format")
  159. files := r.URL.Query().Get("files")
  160. if len(files) == 0 {
  161. // w.JSON(common_err.CLIENT_ERROR, model.Result{
  162. // Success: common_err.INVALID_PARAMS,
  163. // Message: common_err.GetMsg(common_err.INVALID_PARAMS),
  164. // })
  165. return
  166. }
  167. list := strings.Split(files, ",")
  168. for _, v := range list {
  169. if !file.Exists(v) {
  170. // c.JSON(common_err.SERVICE_ERROR, model.Result{
  171. // Success: common_err.FILE_DOES_NOT_EXIST,
  172. // Message: common_err.GetMsg(common_err.FILE_DOES_NOT_EXIST),
  173. // })
  174. return
  175. }
  176. }
  177. w.Header().Add("Content-Type", "application/octet-stream")
  178. w.Header().Add("Content-Transfer-Encoding", "binary")
  179. w.Header().Add("Cache-Control", "no-cache")
  180. // handles only single files not folders and multiple files
  181. // if len(list) == 1 {
  182. // filePath := list[0]
  183. // info, err := os.Stat(filePath)
  184. // if err != nil {
  185. // w.JSON(http.StatusOK, model.Result{
  186. // Success: common_err.FILE_DOES_NOT_EXIST,
  187. // Message: common_err.GetMsg(common_err.FILE_DOES_NOT_EXIST),
  188. // })
  189. //return
  190. // }
  191. //}
  192. extension, ar, err := file.GetCompressionAlgorithm(t)
  193. if err != nil {
  194. // w.JSON(common_err.CLIENT_ERROR, model.Result{
  195. // Success: common_err.INVALID_PARAMS,
  196. // Message: common_err.GetMsg(common_err.INVALID_PARAMS),
  197. // })
  198. return
  199. }
  200. err = ar.Create(w)
  201. if err != nil {
  202. // c.JSON(common_err.SERVICE_ERROR, model.Result{
  203. // Success: common_err.SERVICE_ERROR,
  204. // Message: common_err.GetMsg(common_err.SERVICE_ERROR),
  205. // Data: err.Error(),
  206. // })
  207. return
  208. }
  209. defer ar.Close()
  210. commonDir := file.CommonPrefix(filepath.Separator, list...)
  211. currentPath := filepath.Base(commonDir)
  212. name := "_" + currentPath
  213. name += extension
  214. w.Header().Add("Content-Disposition", "attachment; filename*=utf-8''"+url.PathEscape(name))
  215. for _, fname := range list {
  216. err = file.AddFile(ar, fname, commonDir)
  217. if err != nil {
  218. log.Printf("Failed to archive %s: %v", fname, err)
  219. }
  220. }
  221. })
  222. }