github.com/cozy/cozy-stack@v0.0.0-20240603063001-31110fa4cae1/web/registry/registry.go (about) 1 package registry 2 3 import ( 4 "encoding/json" 5 "net/http" 6 7 "github.com/cozy/cozy-stack/model/app" 8 "github.com/cozy/cozy-stack/model/permission" 9 "github.com/cozy/cozy-stack/pkg/consts" 10 "github.com/cozy/cozy-stack/pkg/registry" 11 "github.com/cozy/cozy-stack/web/middlewares" 12 "github.com/labstack/echo/v4" 13 "github.com/labstack/echo/v4/middleware" 14 ) 15 16 type authType int 17 18 const ( 19 authed authType = iota 20 perms 21 ) 22 23 type clientCacheControl int 24 25 const ( 26 noClientCache clientCacheControl = iota 27 shortClientCache 28 permanentClientCache 29 ) 30 31 func proxyReq(auth authType, clientCache clientCacheControl, proxyCacheControl registry.CacheControl) echo.HandlerFunc { 32 return func(c echo.Context) error { 33 i := middlewares.GetInstance(c) 34 switch auth { 35 case authed: 36 if !middlewares.IsLoggedIn(c) { 37 if err := middlewares.AllowWholeType(c, permission.GET, consts.Apps); err != nil { 38 return echo.NewHTTPError(http.StatusForbidden) 39 } 40 } 41 case perms: 42 pdoc, err := middlewares.GetPermission(c) 43 if err != nil { 44 return echo.NewHTTPError(http.StatusForbidden) 45 } 46 if pdoc.Type != permission.TypeWebapp && pdoc.Type != permission.TypeOauth { 47 return echo.NewHTTPError(http.StatusForbidden) 48 } 49 default: 50 panic("unknown authType") 51 } 52 req := c.Request() 53 proxyResp, err := registry.Proxy(req, i.Registries(), proxyCacheControl) 54 if err != nil { 55 return err 56 } 57 defer proxyResp.Body.Close() 58 switch clientCache { 59 case permanentClientCache: 60 c.Response().Header().Set("Cache-Control", "max-age=31536000, immutable") 61 case shortClientCache: 62 c.Response().Header().Set("Cache-Control", "max-age=86400") 63 } 64 contentType := proxyResp.Header.Get(echo.HeaderContentType) 65 return c.Stream(proxyResp.StatusCode, contentType, proxyResp.Body) 66 } 67 } 68 69 func proxyListReq(c echo.Context) error { 70 i := middlewares.GetInstance(c) 71 pdoc, err := middlewares.GetPermission(c) 72 if err != nil { 73 return err 74 } 75 if pdoc.Type != permission.TypeWebapp && pdoc.Type != permission.TypeOauth { 76 return echo.NewHTTPError(http.StatusForbidden) 77 } 78 req := c.Request() 79 list, err := registry.ProxyList(req, i.Registries()) 80 if err != nil { 81 return err 82 } 83 maintenance, err := app.ListMaintenance() 84 if err != nil { 85 return err 86 } 87 for _, app := range list.Apps { 88 slug := registry.ParseSlug(app["slug"]) 89 for _, item := range maintenance { 90 if item["slug"] == slug { 91 app["maintenance_activated"] = json.RawMessage("true") 92 if opts, err := json.Marshal(item["maintenance_options"]); err == nil { 93 app["maintenance_options"] = json.RawMessage(opts) 94 } 95 } 96 } 97 } 98 return c.JSON(http.StatusOK, list) 99 } 100 101 func proxyAppReq(c echo.Context) error { 102 i := middlewares.GetInstance(c) 103 pdoc, err := middlewares.GetPermission(c) 104 if err != nil { 105 return err 106 } 107 if pdoc.Type != permission.TypeWebapp && pdoc.Type != permission.TypeOauth { 108 return echo.NewHTTPError(http.StatusForbidden) 109 } 110 req := c.Request() 111 proxyResp, err := registry.Proxy(req, i.Registries(), registry.WithCache) 112 if err != nil { 113 return err 114 } 115 defer proxyResp.Body.Close() 116 var doc map[string]interface{} 117 if err := json.NewDecoder(proxyResp.Body).Decode(&doc); err != nil { 118 return err 119 } 120 opts, err := app.GetMaintenanceOptions(c.Param("app")) 121 if err != nil { 122 return err 123 } 124 if opts != nil { 125 doc["maintenance_activated"] = true 126 doc["maintenance_options"] = opts 127 } 128 return c.JSON(http.StatusOK, doc) 129 } 130 131 func proxyMaintenanceReq(c echo.Context) error { 132 i := middlewares.GetInstance(c) 133 pdoc, err := middlewares.GetPermission(c) 134 if err != nil { 135 return err 136 } 137 if pdoc.Type != permission.TypeWebapp && pdoc.Type != permission.TypeOauth { 138 return echo.NewHTTPError(http.StatusForbidden) 139 } 140 apps, err := registry.ListMaintenance(i.Registries()) 141 if err != nil { 142 return err 143 } 144 maintenance, err := app.ListMaintenance() 145 if err != nil { 146 return err 147 } 148 list := make([]interface{}, 0, len(apps)+len(maintenance)) 149 for _, item := range maintenance { 150 list = append(list, item) 151 } 152 for _, doc := range apps { 153 item, err := doc.MarshalJSON() 154 if err != nil { 155 return err 156 } 157 list = append(list, json.RawMessage(item)) 158 } 159 return c.JSON(http.StatusOK, list) 160 } 161 162 // Routes sets the routing for the registry 163 func Routes(router *echo.Group) { 164 gzip := middleware.Gzip() 165 router.GET("", proxyListReq, gzip) 166 router.GET("/", proxyListReq, gzip) 167 router.GET("/maintenance", proxyMaintenanceReq, gzip) 168 router.GET("/:app", proxyAppReq, gzip) 169 router.GET("/:app/icon", proxyReq(authed, shortClientCache, registry.NoCache)) 170 router.GET("/:app/partnership_icon", proxyReq(authed, shortClientCache, registry.NoCache)) 171 router.GET("/:app/screenshots/*", proxyReq(authed, shortClientCache, registry.NoCache)) 172 router.GET("/:app/:version/icon", proxyReq(authed, permanentClientCache, registry.NoCache)) 173 router.GET("/:app/:version/partnership_icon", proxyReq(authed, permanentClientCache, registry.NoCache)) 174 router.GET("/:app/:version/screenshots/*", proxyReq(authed, permanentClientCache, registry.NoCache)) 175 router.GET("/:app/:version", proxyReq(perms, permanentClientCache, registry.WithCache)) 176 router.GET("/:app/:channel/latest", proxyReq(perms, noClientCache, registry.WithCache)) 177 }