Skip to content

Commit

Permalink
Merge pull request #503 from containerish/feat/image-analytics
Browse files Browse the repository at this point in the history
feat: APIs to add repositories to favorites and store repo pull count
  • Loading branch information
guacamole authored Dec 8, 2023
2 parents b0bf038 + 084b9d3 commit e8fdb56
Show file tree
Hide file tree
Showing 15 changed files with 233 additions and 46 deletions.
5 changes: 4 additions & 1 deletion auth/server/webauthn_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,10 @@ func (wa *webauthn_server) BeginLogin(ctx echo.Context) error {
},
}

defer func() {
ctx.Request().Body.Close()
}()

credentialAssertion, err := wa.webauthn.BeginLogin(ctx.Request().Context(), opts)
if err != nil {
if werr := wa.webauthn.RemoveSessionData(ctx.Request().Context(), user.ID); werr != nil {
Expand All @@ -366,7 +370,6 @@ func (wa *webauthn_server) BeginLogin(ctx echo.Context) error {
wa.logger.Log(ctx, err).Send()
return echoErr
}
defer ctx.Request().Body.Close()

echoErr := ctx.JSON(http.StatusOK, echo.Map{
"options": credentialAssertion,
Expand Down
2 changes: 1 addition & 1 deletion auth/signup.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import (
func (a *auth) parseSignUpRequest(ctx echo.Context) (*types.User, error) {
var user types.User
if err := json.NewDecoder(ctx.Request().Body).Decode(&user); err != nil {
return nil, err
return nil, fmt.Errorf("error parsing signup request: %w", err)
}
defer ctx.Request().Body.Close()

Expand Down
1 change: 1 addition & 0 deletions orgmode/admin.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ func (o *orgMode) AllowOrgAdmin() echo.MiddlewareFunc {
o.logger.Log(ctx, err).Send()
return echoErr
}
defer ctx.Request().Body.Close()

// only allow self-migrate
if !strings.EqualFold(user.ID.String(), body.UserID.String()) {
Expand Down
73 changes: 73 additions & 0 deletions registry/v2/extensions/analytics.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package extensions

import (
"net/http"
"time"

"github.com/containerish/OpenRegistry/store/v1/types"
"github.com/google/uuid"
"github.com/labstack/echo/v4"
)

type FavoriteRepositoryRequest struct {
RepositoryID uuid.UUID `json:"repository_id" query:"repository_id"`
UserID uuid.UUID `json:"user_id" query:"user_id"`
}

func (ext *extension) AddRepositoryToFavorites(ctx echo.Context) error {
ctx.Set(types.HandlerStartTime, time.Now())

var body FavoriteRepositoryRequest
if err := ctx.Bind(&body); err != nil {
echoErr := ctx.JSON(http.StatusBadRequest, echo.Map{
"error": err.Error(),
})
ext.logger.Log(ctx, err).Send()
return echoErr
}
defer ctx.Request().Body.Close()

err := ext.store.AddRepositoryToFavorites(ctx.Request().Context(), body.RepositoryID, body.UserID)
if err != nil {
echoErr := ctx.JSON(http.StatusBadRequest, echo.Map{
"error": err.Error(),
})
ext.logger.Log(ctx, err).Send()
return echoErr
}

echoErr := ctx.JSON(http.StatusOK, echo.Map{
"message": "repository added to favorites",
})
ext.logger.Log(ctx, nil).Send()
return echoErr
}

func (ext *extension) RemoveRepositoryFromFavorites(ctx echo.Context) error {
ctx.Set(types.HandlerStartTime, time.Now())

var body FavoriteRepositoryRequest
if err := ctx.Bind(&body); err != nil {
echoErr := ctx.JSON(http.StatusBadRequest, echo.Map{
"error": err.Error(),
})
ext.logger.Log(ctx, err).Send()
return echoErr
}
defer ctx.Request().Body.Close()

err := ext.store.RemoveRepositoryFromFavorites(ctx.Request().Context(), body.RepositoryID, body.UserID)
if err != nil {
echoErr := ctx.JSON(http.StatusBadRequest, echo.Map{
"error": err.Error(),
})
ext.logger.Log(ctx, err).Send()
return echoErr
}

echoErr := ctx.JSON(http.StatusOK, echo.Map{
"message": "repository removed from favorites",
})
ext.logger.Log(ctx, nil).Send()
return echoErr
}
2 changes: 2 additions & 0 deletions registry/v2/extensions/catalog_detail.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ type Extenion interface {
ChangeContainerImageVisibility(ctx echo.Context) error
PublicCatalog(ctx echo.Context) error
GetUserCatalog(ctx echo.Context) error
AddRepositoryToFavorites(ctx echo.Context) error
RemoveRepositoryFromFavorites(ctx echo.Context) error
}

type extension struct {
Expand Down
11 changes: 10 additions & 1 deletion registry/v2/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,8 @@ func (r *registry) PullManifest(ctx echo.Context) error {

manifest, err := r.store.GetManifestByReference(ctx.Request().Context(), namespace, ref)
if err != nil {
errMsg := common.RegistryErrorResponse(RegistryErrorCodeManifestUnknown, err.Error(), echo.Map{
errMsg := common.RegistryErrorResponse(RegistryErrorCodeManifestUnknown, "manifest not found", echo.Map{
"error": err.Error(),
"namespace": namespace,
"ref": ref,
})
Expand All @@ -235,6 +236,14 @@ func (r *registry) PullManifest(ctx echo.Context) error {
return echoErr
}

defer func() {
err = r.store.IncrementRepositoryPullCounter(ctx.Request().Context(), manifest.RepositoryID)
// silently fail
if err != nil {
r.logger.DebugWithContext(ctx).Err(err).Send()
}
}()

trimmedMf := manifest.ToOCISubject()
ctx.Response().Header().Set("Docker-Content-Digest", manifest.Digest)
ctx.Response().Header().Set("Content-Type", manifest.MediaType)
Expand Down
1 change: 1 addition & 0 deletions registry/v2/repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ func (r *registry) CreateRepository(ctx echo.Context) error {
"message": "error parsing request input",
})
}
defer ctx.Request().Body.Close()

if err = body.Validate(); err != nil {
return ctx.JSON(http.StatusBadRequest, echo.Map{
Expand Down
2 changes: 1 addition & 1 deletion router/middlewares.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ func registryNamespaceValidator(logger telemetry.Logger) echo.MiddlewareFunc {
}

namespace := ctx.Param("username") + "/" + ctx.Param("imagename")
if !nsRegex.MatchString(namespace) {
if namespace != "/" && !nsRegex.MatchString(namespace) {
registryErr := common.RegistryErrorResponse(
registry.RegistryErrorCodeNameInvalid,
"invalid user namespace",
Expand Down
2 changes: 2 additions & 0 deletions router/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,4 +104,6 @@ func RegisterExtensionsRoutes(
group.Add(http.MethodGet, UserCatalog, ext.GetUserCatalog, middlewares...)
group.Add(http.MethodPost, ChangeRepositoryVisibility, ext.ChangeContainerImageVisibility, middlewares...)
group.Add(http.MethodPost, CreateRepository, reg.CreateRepository, middlewares...)
group.Add(http.MethodPost, RepositoryFavorites, ext.AddRepositoryToFavorites, middlewares...)
group.Add(http.MethodDelete, RepositoryFavorites, ext.RemoveRepositoryFromFavorites, middlewares...)
}
1 change: 1 addition & 0 deletions router/route_names.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,5 @@ const (

ChangeRepositoryVisibility = Ext + "/repository/visibility"
CreateRepository = Ext + "/repository/create"
RepositoryFavorites = Ext + "/repository/favorites"
)
10 changes: 6 additions & 4 deletions router/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,7 @@ func Register(
usersStore users_store.UserStore,
automationStore automation.BuildAutomationStore,
) *echo.Echo {
e := echo.New()

setDefaultEchoOptions(e, cfg.WebAppConfig, healthCheckApi)
e := setDefaultEchoOptions(cfg.WebAppConfig, healthCheckApi)

baseAPIRouter := e.Group("/api")
githubRouter := e.Group("/github")
Expand All @@ -63,6 +61,7 @@ func Register(
RegisterExtensionsRoutes(ociRouter, registryApi, extensionsApi)
RegisterWebauthnRoutes(webauthnRouter, webauthnApi)
RegisterOrgModeRoutes(orgModeRouter, orgModeApi)

if cfg.Integrations.GetGithubConfig() != nil && cfg.Integrations.GetGithubConfig().Enabled {
RegisterGitHubRoutes(
githubRouter,
Expand Down Expand Up @@ -96,7 +95,8 @@ func Register(
return e
}

func setDefaultEchoOptions(e *echo.Echo, webConfig config.WebAppConfig, healthCheck http.HandlerFunc) {
func setDefaultEchoOptions(webConfig config.WebAppConfig, healthCheck http.HandlerFunc) *echo.Echo {
e := echo.New()
e.HideBanner = true

e.Use(middleware.Recover())
Expand All @@ -123,4 +123,6 @@ func setDefaultEchoOptions(e *echo.Echo, webConfig config.WebAppConfig, healthCh
p.Use(e)

e.Add(http.MethodGet, "/health", echo.WrapHandler(healthCheck))

return e
}
Loading

0 comments on commit e8fdb56

Please sign in to comment.