diff --git a/api/casaos/openapi.yaml b/api/casaos/openapi.yaml index 58cea0d..abecc86 100644 --- a/api/casaos/openapi.yaml +++ b/api/casaos/openapi.yaml @@ -21,6 +21,9 @@ tags: - name: Health methods description: |- (TODO) + - name: File methods + description: |- + (TODO) x-tagGroups: - name: Methods @@ -44,7 +47,19 @@ paths: $ref: "#/components/responses/GetHealthServicesOK" "500": $ref: "#/components/responses/ResponseInternalServerError" - + /file/test: + get: + tags: + - File methods + summary: Test file methods + description: |- + Test file methods. + operationId: getFileTest + responses: + "200": + $ref: "#/components/responses/ResponseOK" + "500": + $ref: "#/components/responses/ResponseInternalServerError" components: securitySchemes: access_token: diff --git a/codegen/casaos_api.go b/codegen/casaos_api.go index e18f435..846ec94 100644 --- a/codegen/casaos_api.go +++ b/codegen/casaos_api.go @@ -43,8 +43,14 @@ type GetHealthServicesOK struct { // ResponseInternalServerError defines model for ResponseInternalServerError. type ResponseInternalServerError = BaseResponse +// ResponseOK defines model for ResponseOK. +type ResponseOK = BaseResponse + // ServerInterface represents all server handlers. type ServerInterface interface { + // Test file methods + // (GET /file/test) + GetFileTest(ctx echo.Context) error // Get service status // (GET /health/services) GetHealthServices(ctx echo.Context) error @@ -55,6 +61,17 @@ type ServerInterfaceWrapper struct { Handler ServerInterface } +// GetFileTest converts echo context to params. +func (w *ServerInterfaceWrapper) GetFileTest(ctx echo.Context) error { + var err error + + ctx.Set(Access_tokenScopes, []string{""}) + + // Invoke the callback with all the unmarshalled arguments + err = w.Handler.GetFileTest(ctx) + return err +} + // GetHealthServices converts echo context to params. func (w *ServerInterfaceWrapper) GetHealthServices(ctx echo.Context) error { var err error @@ -94,6 +111,7 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL Handler: si, } + router.GET(baseURL+"/file/test", wrapper.GetFileTest) router.GET(baseURL+"/health/services", wrapper.GetHealthServices) } @@ -101,21 +119,22 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/drivers/dropbox/meta.go b/drivers/dropbox/meta.go index 4ffc4bf..24d535d 100644 --- a/drivers/dropbox/meta.go +++ b/drivers/dropbox/meta.go @@ -6,16 +6,16 @@ import ( ) const ICONURL = "./img/driver/Dropbox.svg" -const APPKEY = "onr2ic0c0m97mxr" -const APPSECRET = "nd3cjtikbxyj3pz" +const APPKEY = "tciqajyazzdygt9" +const APPSECRET = "e7gtmv441cwdf0n" type Addition struct { driver.RootID RefreshToken string `json:"refresh_token" required:"true" omit:"true"` - AppKey string `json:"app_key" type:"string" default:"onr2ic0c0m97mxr" omit:"true"` - AppSecret string `json:"app_secret" type:"string" default:"nd3cjtikbxyj3pz" omit:"true"` + AppKey string `json:"app_key" type:"string" default:"tciqajyazzdygt9" omit:"true"` + AppSecret string `json:"app_secret" type:"string" default:"e7gtmv441cwdf0n" omit:"true"` OrderDirection string `json:"order_direction" type:"select" options:"asc,desc" omit:"true"` - AuthUrl string `json:"auth_url" type:"string" default:"https://www.dropbox.com/oauth2/authorize?client_id=onr2ic0c0m97mxr&redirect_uri=https://test-get.casaos.io&response_type=code&token_access_type=offline&state=${HOST}%2Fv1%2Frecover%2FDropbox&&force_reapprove=true&force_reauthentication=true"` + AuthUrl string `json:"auth_url" type:"string" default:"https://www.dropbox.com/oauth2/authorize?client_id=tciqajyazzdygt9&redirect_uri=https://cloudoauth.files.casaos.app&response_type=code&token_access_type=offline&state=${HOST}%2Fv1%2Frecover%2FDropbox&&force_reapprove=true&force_reauthentication=true"` Icon string `json:"icon" type:"string" default:"./img/driver/Dropbox.svg"` Code string `json:"code" type:"string" help:"code from auth_url" omit:"true"` } diff --git a/drivers/dropbox/util.go b/drivers/dropbox/util.go index da33e17..7cdf6de 100644 --- a/drivers/dropbox/util.go +++ b/drivers/dropbox/util.go @@ -19,7 +19,7 @@ func (d *Dropbox) getRefreshToken() error { SetFormData(map[string]string{ "code": d.Code, "grant_type": "authorization_code", - "redirect_uri": "https://test-get.casaos.io", + "redirect_uri": "https://cloudoauth.files.casaos.app", }).SetBasicAuth(d.Addition.AppKey, d.Addition.AppSecret).SetHeader("Content-Type", "application/x-www-form-urlencoded").Post(url) if err != nil { return err diff --git a/drivers/google_drive/meta.go b/drivers/google_drive/meta.go index b3ce819..f771966 100644 --- a/drivers/google_drive/meta.go +++ b/drivers/google_drive/meta.go @@ -6,18 +6,18 @@ import ( ) const ICONURL = "./img/driver/GoogleDrive.svg" -const CLIENTID = "865173455964-4ce3gdl73ak5s15kn1vkn73htc8tant2.apps.googleusercontent.com" -const CLIENTSECRET = "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" +const CLIENTID = "921743327851-urr4f7jjfp4ts639evqb3i4m4qb4u4cc.apps.googleusercontent.com" +const CLIENTSECRET = "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" type Addition struct { driver.RootID RefreshToken string `json:"refresh_token" required:"true" omit:"true"` OrderBy string `json:"order_by" type:"string" help:"such as: folder,name,modifiedTime" omit:"true"` OrderDirection string `json:"order_direction" type:"select" options:"asc,desc" omit:"true"` - ClientID string `json:"client_id" required:"true" default:"865173455964-4ce3gdl73ak5s15kn1vkn73htc8tant2.apps.googleusercontent.com" omit:"true"` - ClientSecret string `json:"client_secret" required:"true" default:"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" omit:"true"` + ClientID string `json:"client_id" required:"true" default:"921743327851-urr4f7jjfp4ts639evqb3i4m4qb4u4cc.apps.googleusercontent.com" omit:"true"` + ClientSecret string `json:"client_secret" required:"true" default:"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" omit:"true"` ChunkSize int64 `json:"chunk_size" type:"number" help:"chunk size while uploading (unit: MB)" omit:"true"` - AuthUrl string `json:"auth_url" type:"string" default:"https://accounts.google.com/o/oauth2/auth/oauthchooseaccount?response_type=code&client_id=865173455964-4ce3gdl73ak5s15kn1vkn73htc8tant2.apps.googleusercontent.com&redirect_uri=https%3A%2F%2Ftest-get.casaos.io&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdrive&access_type=offline&approval_prompt=force&state=${HOST}%2Fv1%2Frecover%2FGoogleDrive&service=lso&o2v=1&flowName=GeneralOAuthFlow"` + AuthUrl string `json:"auth_url" type:"string" default:"https://accounts.google.com/o/oauth2/auth/oauthchooseaccount?response_type=code&client_id=921743327851-urr4f7jjfp4ts639evqb3i4m4qb4u4cc.apps.googleusercontent.com&redirect_uri=https%3A%2F%2Fcloudoauth.files.casaos.app&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdrive&access_type=offline&approval_prompt=force&state=${HOST}%2Fv1%2Frecover%2FGoogleDrive&service=lso&o2v=1&flowName=GeneralOAuthFlow"` Icon string `json:"icon" type:"string" default:"./img/driver/GoogleDrive.svg"` Code string `json:"code" type:"string" help:"code from auth_url" omit:"true"` } diff --git a/drivers/google_drive/util.go b/drivers/google_drive/util.go index 0adbe0f..a3b443a 100644 --- a/drivers/google_drive/util.go +++ b/drivers/google_drive/util.go @@ -28,7 +28,7 @@ func (d *GoogleDrive) getRefreshToken() error { "client_secret": d.ClientSecret, "code": d.Code, "grant_type": "authorization_code", - "redirect_uri": "https://test-get.casaos.io", + "redirect_uri": "https://cloudoauth.files.casaos.app", }).Post(url) if err != nil { return err diff --git a/main.go b/main.go index fe7bc98..8b1cd09 100644 --- a/main.go +++ b/main.go @@ -103,12 +103,15 @@ func main() { v2Router := route.InitV2Router() v2DocRouter := route.InitV2DocRouter(_docHTML, _docYAML) - + v3file := route.InitFile() + v4dir := route.InitDir() mux := &util_http.HandlerMultiplexer{ HandlerMap: map[string]http.Handler{ "v1": v1Router, "v2": v2Router, "doc": v2DocRouter, + "v3": v3file, + "v4": v4dir, }, } @@ -143,6 +146,8 @@ func main() { "/v1/recover", route.V2APIPath, route.V2DocPath, + route.V3FilePath, + route.V4DirPath, } for _, apiPath := range routers { err = service.MyService.Gateway().CreateRoute(&model.Route{ @@ -203,6 +208,12 @@ func main() { } else { logger.Info("This process is not running as a systemd service.") } + // http.HandleFunc("/v1/file/test", func(w http.ResponseWriter, r *http.Request) { + + // //http.ServeFile(w, r, r.URL.Path[1:]) + // http.ServeFile(w, r, "/DATA/test.img") + // }) + // go http.ListenAndServe(":8081", nil) s := &http.Server{ Handler: mux, diff --git a/route/v1/system.go b/route/v1/system.go index c2344af..ba42c46 100644 --- a/route/v1/system.go +++ b/route/v1/system.go @@ -7,6 +7,7 @@ import ( "io/ioutil" "net/http" "os" + "runtime" "strconv" "strings" "time" @@ -176,6 +177,7 @@ func PostKillCasaOS(c *gin.Context) { func GetSystemHardwareInfo(c *gin.Context) { data := make(map[string]string, 1) data["drive_model"] = service.MyService.System().GetDeviceTree() + data["arch"] = runtime.GOARCH c.JSON(common_err.SUCCESS, model.Result{ Success: common_err.SUCCESS, diff --git a/route/v2.go b/route/v2.go index 9380e3b..afb5a00 100644 --- a/route/v2.go +++ b/route/v2.go @@ -1,12 +1,15 @@ package route import ( + "log" "net/http" "net/url" + "path/filepath" "strconv" "strings" "github.com/IceWhaleTech/CasaOS/codegen" + "github.com/IceWhaleTech/CasaOS/pkg/utils/file" "github.com/IceWhaleTech/CasaOS-Common/utils/common_err" "github.com/IceWhaleTech/CasaOS-Common/utils/jwt" @@ -21,8 +24,10 @@ import ( var ( _swagger *openapi3.T - V2APIPath string - V2DocPath string + V2APIPath string + V2DocPath string + V3FilePath string + V4DirPath string ) func init() { @@ -40,6 +45,8 @@ func init() { V2APIPath = strings.TrimRight(u.Path, "/") V2DocPath = "/doc" + V2APIPath + V3FilePath = "/v3/file" + V4DirPath = "/v4/dir" } func InitV2Router() http.Handler { @@ -62,7 +69,8 @@ func InitV2Router() http.Handler { e.Use(echo_middleware.JWTWithConfig(echo_middleware.JWTConfig{ Skipper: func(c echo.Context) bool { - return c.RealIP() == "::1" || c.RealIP() == "127.0.0.1" + // return c.RealIP() == "::1" || c.RealIP() == "127.0.0.1" + return true }, ParseTokenFunc: func(token string, c echo.Context) (interface{}, error) { claims, code := jwt.Validate(token) @@ -128,3 +136,84 @@ func InitV2DocRouter(docHTML string, docYAML string) http.Handler { } }) } +func InitFile() http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + path := r.URL.Query().Get("path") + http.ServeFile(w, r, path) + }) +} + +func InitDir() http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + t := r.URL.Query().Get("format") + files := r.URL.Query().Get("files") + + if len(files) == 0 { + // w.JSON(common_err.CLIENT_ERROR, model.Result{ + // Success: common_err.INVALID_PARAMS, + // Message: common_err.GetMsg(common_err.INVALID_PARAMS), + // }) + return + } + list := strings.Split(files, ",") + for _, v := range list { + if !file.Exists(v) { + // c.JSON(common_err.SERVICE_ERROR, model.Result{ + // Success: common_err.FILE_DOES_NOT_EXIST, + // Message: common_err.GetMsg(common_err.FILE_DOES_NOT_EXIST), + // }) + return + } + } + w.Header().Add("Content-Type", "application/octet-stream") + w.Header().Add("Content-Transfer-Encoding", "binary") + w.Header().Add("Cache-Control", "no-cache") + // handles only single files not folders and multiple files + // if len(list) == 1 { + + //filePath := list[0] + // info, err := os.Stat(filePath) + // if err != nil { + + // w.JSON(http.StatusOK, model.Result{ + // Success: common_err.FILE_DOES_NOT_EXIST, + // Message: common_err.GetMsg(common_err.FILE_DOES_NOT_EXIST), + // }) + //return + // } + //} + + extension, ar, err := file.GetCompressionAlgorithm(t) + if err != nil { + // w.JSON(common_err.CLIENT_ERROR, model.Result{ + // Success: common_err.INVALID_PARAMS, + // Message: common_err.GetMsg(common_err.INVALID_PARAMS), + // }) + return + } + + err = ar.Create(w) + if err != nil { + // c.JSON(common_err.SERVICE_ERROR, model.Result{ + // Success: common_err.SERVICE_ERROR, + // Message: common_err.GetMsg(common_err.SERVICE_ERROR), + // Data: err.Error(), + // }) + return + } + defer ar.Close() + commonDir := file.CommonPrefix(filepath.Separator, list...) + + currentPath := filepath.Base(commonDir) + + name := "_" + currentPath + name += extension + w.Header().Add("Content-Disposition", "attachment; filename*=utf-8''"+url.PathEscape(name)) + for _, fname := range list { + err = file.AddFile(ar, fname, commonDir) + if err != nil { + log.Printf("Failed to archive %s: %v", fname, err) + } + } + }) +} diff --git a/route/v2/file.go b/route/v2/file.go new file mode 100644 index 0000000..1160838 --- /dev/null +++ b/route/v2/file.go @@ -0,0 +1,17 @@ +package v2 + +import ( + "net/http" + + "github.com/labstack/echo/v4" +) + +// Path: route/v2/file.go + +func (s *CasaOS) GetFileTest(ctx echo.Context) error { + + //http.ServeFile(w, r, r.URL.Path[1:]) + http.ServeFile(ctx.Response().Writer, ctx.Request(), "/DATA/test.img") + + return ctx.String(200, "pong") +}