From f2dc157e8a0fadd1fa2b4e7a047bcc8077a102f5 Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Fri, 8 Aug 2025 13:10:57 +0530 Subject: [PATCH 01/14] Support for customDomain flag --- server/cmd/museum/main.go | 4 +- server/ente/remotestore.go | 45 ++++++++++++++----- server/pkg/api/remotestore.go | 14 ++++++ .../pkg/controller/remotestore/controller.go | 34 +++++++++++--- server/pkg/repo/remotestore/repository.go | 9 ++++ 5 files changed, 90 insertions(+), 16 deletions(-) diff --git a/server/cmd/museum/main.go b/server/cmd/museum/main.go index 87d720530a..e3301fe6db 100644 --- a/server/cmd/museum/main.go +++ b/server/cmd/museum/main.go @@ -213,7 +213,6 @@ func main() { commonBillController := commonbilling.NewController(emailNotificationCtrl, storagBonusRepo, userRepo, usageRepo, billingRepo) appStoreController := controller.NewAppStoreController(defaultPlan, billingRepo, fileRepo, userRepo, commonBillController) - remoteStoreController := &remoteStoreCtrl.Controller{Repo: remoteStoreRepository} playStoreController := controller.NewPlayStoreController(defaultPlan, billingRepo, fileRepo, userRepo, storagBonusRepo, commonBillController) stripeController := controller.NewStripeController(plans, stripeClients, @@ -222,6 +221,8 @@ func main() { appStoreController, playStoreController, stripeController, discordController, emailNotificationCtrl, billingRepo, userRepo, usageRepo, storagBonusRepo, commonBillController) + remoteStoreController := &remoteStoreCtrl.Controller{Repo: remoteStoreRepository, BillingCtrl: billingController} + pushController := controller.NewPushController(pushRepo, taskLockingRepo, hostName) mailingListsController := controller.NewMailingListsController() @@ -788,6 +789,7 @@ func main() { remoteStoreHandler := &api.RemoteStoreHandler{Controller: remoteStoreController} privateAPI.POST("/remote-store/update", remoteStoreHandler.InsertOrUpdate) + privateAPI.DELETE("/remote-store/:key", remoteStoreHandler.RemoveKey) privateAPI.GET("/remote-store", remoteStoreHandler.GetKey) privateAPI.GET("/remote-store/feature-flags", remoteStoreHandler.GetFeatureFlags) diff --git a/server/ente/remotestore.go b/server/ente/remotestore.go index 8546fd3cf0..69f29cf543 100644 --- a/server/ente/remotestore.go +++ b/server/ente/remotestore.go @@ -23,15 +23,16 @@ type AdminUpdateKeyValueRequest struct { type FeatureFlagResponse struct { EnableStripe bool `json:"enableStripe"` // If true, the mobile client will stop using CF worker to download files - DisableCFWorker bool `json:"disableCFWorker"` - MapEnabled bool `json:"mapEnabled"` - FaceSearchEnabled bool `json:"faceSearchEnabled"` - PassKeyEnabled bool `json:"passKeyEnabled"` - RecoveryKeyVerified bool `json:"recoveryKeyVerified"` - InternalUser bool `json:"internalUser"` - BetaUser bool `json:"betaUser"` - EnableMobMultiPart bool `json:"enableMobMultiPart"` - CastUrl string `json:"castUrl"` + DisableCFWorker bool `json:"disableCFWorker"` + MapEnabled bool `json:"mapEnabled"` + FaceSearchEnabled bool `json:"faceSearchEnabled"` + PassKeyEnabled bool `json:"passKeyEnabled"` + RecoveryKeyVerified bool `json:"recoveryKeyVerified"` + InternalUser bool `json:"internalUser"` + BetaUser bool `json:"betaUser"` + EnableMobMultiPart bool `json:"enableMobMultiPart"` + CastUrl string `json:"castUrl"` + CustomDomain *string `json:"customDomain,omitempty"` } type FlagKey string @@ -43,8 +44,24 @@ const ( PassKeyEnabled FlagKey = "passKeyEnabled" IsInternalUser FlagKey = "internalUser" IsBetaUser FlagKey = "betaUser" + CustomDomain FlagKey = "customDomain" ) +var validFlagKeys = map[FlagKey]struct{}{ + RecoveryKeyVerified: {}, + MapEnabled: {}, + FaceSearchEnabled: {}, + PassKeyEnabled: {}, + IsInternalUser: {}, + IsBetaUser: {}, + CustomDomain: {}, +} + +func IsValidFlagKey(key string) bool { + _, exists := validFlagKeys[FlagKey(key)] + return exists +} + func (k FlagKey) String() string { return string(k) } @@ -52,13 +69,21 @@ func (k FlagKey) String() string { // UserEditable returns true if the key is user editable func (k FlagKey) UserEditable() bool { switch k { - case RecoveryKeyVerified, MapEnabled, FaceSearchEnabled, PassKeyEnabled: + case RecoveryKeyVerified, MapEnabled, FaceSearchEnabled, PassKeyEnabled, CustomDomain: return true default: return false } } +func (k FlagKey) NeedSubscription() bool { + return k == CustomDomain +} + +func (k FlagKey) CanRemove() bool { + return k == CustomDomain +} + func (k FlagKey) IsAdminEditable() bool { switch k { case RecoveryKeyVerified, MapEnabled, FaceSearchEnabled: diff --git a/server/pkg/api/remotestore.go b/server/pkg/api/remotestore.go index 9f03554de8..7752c990f3 100644 --- a/server/pkg/api/remotestore.go +++ b/server/pkg/api/remotestore.go @@ -33,6 +33,20 @@ func (h *RemoteStoreHandler) InsertOrUpdate(c *gin.Context) { c.Status(http.StatusOK) } +func (h *RemoteStoreHandler) RemoveKey(c *gin.Context) { + key := c.Param("key") + if key == "" { + handler.Error(c, stacktrace.Propagate(ente.NewBadRequestWithMessage("key is missing"), "")) + return + } + err := h.Controller.RemoveKey(c, key) + if err != nil { + handler.Error(c, stacktrace.Propagate(err, "failed to update key's value")) + return + } + c.Status(http.StatusOK) +} + // GetKey handler for fetching a value for particular key func (h *RemoteStoreHandler) GetKey(c *gin.Context) { var request ente.GetValueRequest diff --git a/server/pkg/controller/remotestore/controller.go b/server/pkg/controller/remotestore/controller.go index f031f65033..77d00947d4 100644 --- a/server/pkg/controller/remotestore/controller.go +++ b/server/pkg/controller/remotestore/controller.go @@ -4,6 +4,7 @@ import ( "database/sql" "errors" "fmt" + "github.com/ente-io/museum/pkg/controller" "github.com/spf13/viper" "github.com/ente-io/museum/ente" @@ -15,20 +16,33 @@ import ( // Controller is interface for exposing business logic related to for remote store type Controller struct { - Repo *remotestore.Repository + Repo *remotestore.Repository + BillingCtrl *controller.BillingController } // InsertOrUpdate the key's value func (c *Controller) InsertOrUpdate(ctx *gin.Context, request ente.UpdateKeyValueRequest) error { - if err := _validateRequest(request.Key, request.Value, false); err != nil { + userID := auth.GetUserID(ctx.Request.Header) + if err := c._validateRequest(userID, request.Key, request.Value, false); err != nil { return err } - userID := auth.GetUserID(ctx.Request.Header) return c.Repo.InsertOrUpdate(ctx, userID, request.Key, request.Value) } +// RemoveKey removes the key from remote store +func (c *Controller) RemoveKey(ctx *gin.Context, key string) error { + userID := auth.GetUserID(ctx.Request.Header) + if valid := ente.IsValidFlagKey(key); !valid { + return stacktrace.Propagate(ente.NewBadRequestWithMessage(fmt.Sprintf("key %s is not allowed", key)), "invalid flag key") + } + if !ente.FlagKey(key).CanRemove() { + return stacktrace.Propagate(ente.NewBadRequestWithMessage(fmt.Sprintf("key %s is not removable", key)), "key not removable") + } + return c.Repo.RemoveKey(ctx, userID, key) +} + func (c *Controller) AdminInsertOrUpdate(ctx *gin.Context, request ente.AdminUpdateKeyValueRequest) error { - if err := _validateRequest(request.Key, request.Value, true); err != nil { + if err := c._validateRequest(request.UserID, request.Key, request.Value, true); err != nil { return err } return c.Repo.InsertOrUpdate(ctx, request.UserID, request.Key, request.Value) @@ -80,12 +94,19 @@ func (c *Controller) GetFeatureFlags(ctx *gin.Context) (*ente.FeatureFlagRespons response.InternalUser = value == "true" case ente.IsBetaUser: response.BetaUser = value == "true" + case ente.CustomDomain: + if value != "" { + response.CustomDomain = &value + } } } return response, nil } -func _validateRequest(key, value string, byAdmin bool) error { +func (c *Controller) _validateRequest(userID int64, key, value string, byAdmin bool) error { + if ente.IsValidFlagKey(key) { + return stacktrace.Propagate(ente.NewBadRequestWithMessage(fmt.Sprintf("key %s is not allowed", key)), "invalid flag key") + } flag := ente.FlagKey(key) if !flag.UserEditable() && !byAdmin { return stacktrace.Propagate(ente.NewBadRequestWithMessage(fmt.Sprintf("key %s is not user editable", key)), "key not user editable") @@ -96,5 +117,8 @@ func _validateRequest(key, value string, byAdmin bool) error { if flag.IsBoolType() && value != "true" && value != "false" { return stacktrace.Propagate(ente.NewBadRequestWithMessage(fmt.Sprintf("value %s is not allowed", value)), "value not allowed") } + if flag.NeedSubscription() { + return c.BillingCtrl.HasActiveSelfOrFamilySubscription(userID, true) + } return nil } diff --git a/server/pkg/repo/remotestore/repository.go b/server/pkg/repo/remotestore/repository.go index 2548f49018..d9dd50ac4d 100644 --- a/server/pkg/repo/remotestore/repository.go +++ b/server/pkg/repo/remotestore/repository.go @@ -24,6 +24,15 @@ func (r *Repository) InsertOrUpdate(ctx context.Context, userID int64, key strin return stacktrace.Propagate(err, "failed to insert/update") } +func (r *Repository) RemoveKey(ctx context.Context, userID int64, key string) error { + _, err := r.DB.ExecContext(ctx, `DELETE FROM remote_store + WHERE user_id = $1 AND key_name = $2`, + userID, // $1 + key, // $2 + ) + return stacktrace.Propagate(err, "failed to remove key") +} + // GetValue fetches and return the value for given user_id and key func (r *Repository) GetValue(ctx context.Context, userID int64, key string) (string, error) { rows := r.DB.QueryRowContext(ctx, `SELECT key_value FROM remote_store From 1af834aa7a85b78a226bace02a87f70533155c11 Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Fri, 8 Aug 2025 14:02:36 +0530 Subject: [PATCH 02/14] Remove unused field --- server/pkg/repo/public/collection_link.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/server/pkg/repo/public/collection_link.go b/server/pkg/repo/public/collection_link.go index fafcd4cb11..3271a850b7 100644 --- a/server/pkg/repo/public/collection_link.go +++ b/server/pkg/repo/public/collection_link.go @@ -11,8 +11,6 @@ import ( "github.com/lib/pq" ) -const BaseShareURL = "https://albums.ente.io/?t=%s" - // CollectionLinkRepo defines the methods for inserting, updating and // retrieving entities related to public collections type CollectionLinkRepo struct { From bc1ae62477b3f6c9d9dbb8f45697d00b97b7df16 Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Fri, 8 Aug 2025 14:06:18 +0530 Subject: [PATCH 03/14] Ability to configure cname --- server/cmd/museum/main.go | 1 + server/configurations/local.yaml | 3 +++ server/ente/remotestore.go | 1 + server/pkg/controller/remotestore/controller.go | 1 + 4 files changed, 6 insertions(+) diff --git a/server/cmd/museum/main.go b/server/cmd/museum/main.go index e3301fe6db..cc10a67e8b 100644 --- a/server/cmd/museum/main.go +++ b/server/cmd/museum/main.go @@ -98,6 +98,7 @@ func main() { } viper.SetDefault("apps.public-albums", "https://albums.ente.io") + viper.SetDefault("apps.custom-domain.cname", "https://my.ente.io") viper.SetDefault("apps.public-locker", "https://locker.ente.io") viper.SetDefault("apps.accounts", "https://accounts.ente.io") viper.SetDefault("apps.cast", "https://cast.ente.io") diff --git a/server/configurations/local.yaml b/server/configurations/local.yaml index a16f560e43..806add1428 100644 --- a/server/configurations/local.yaml +++ b/server/configurations/local.yaml @@ -95,6 +95,9 @@ apps: accounts: # Default is https://family.ente.io family: + custom-domain: + # Default is https://my.ente.io + cname: # Database connection parameters db: diff --git a/server/ente/remotestore.go b/server/ente/remotestore.go index 69f29cf543..d08181f72c 100644 --- a/server/ente/remotestore.go +++ b/server/ente/remotestore.go @@ -33,6 +33,7 @@ type FeatureFlagResponse struct { EnableMobMultiPart bool `json:"enableMobMultiPart"` CastUrl string `json:"castUrl"` CustomDomain *string `json:"customDomain,omitempty"` + CustomDomainCNAME string `json:"customDomainCNAME,omitempty"` } type FlagKey string diff --git a/server/pkg/controller/remotestore/controller.go b/server/pkg/controller/remotestore/controller.go index 77d00947d4..bbbe741799 100644 --- a/server/pkg/controller/remotestore/controller.go +++ b/server/pkg/controller/remotestore/controller.go @@ -75,6 +75,7 @@ func (c *Controller) GetFeatureFlags(ctx *gin.Context) (*ente.FeatureFlagRespons // except internal user.rt EnableMobMultiPart: true, CastUrl: viper.GetString("apps.cast"), + CustomDomainCNAME: viper.GetString("apps.custom-domain.cname"), } for key, value := range values { flag := ente.FlagKey(key) From 3167d85f069ee68f5c4348c8913bb8e3e78a63dd Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Fri, 8 Aug 2025 15:25:04 +0530 Subject: [PATCH 04/14] Add index for customDomain --- server/migrations/104_rs_custom_domain.down.sql | 3 +++ server/migrations/104_rs_custom_domain.up.sql | 3 +++ 2 files changed, 6 insertions(+) create mode 100644 server/migrations/104_rs_custom_domain.down.sql create mode 100644 server/migrations/104_rs_custom_domain.up.sql diff --git a/server/migrations/104_rs_custom_domain.down.sql b/server/migrations/104_rs_custom_domain.down.sql new file mode 100644 index 0000000000..848999ea47 --- /dev/null +++ b/server/migrations/104_rs_custom_domain.down.sql @@ -0,0 +1,3 @@ +CREATE UNIQUE INDEX IF NOT EXISTS remote_store_custom_domain_unique_idx + ON remote_store (key_value) + WHERE key_name = 'custom_domain'; \ No newline at end of file diff --git a/server/migrations/104_rs_custom_domain.up.sql b/server/migrations/104_rs_custom_domain.up.sql new file mode 100644 index 0000000000..73da6baf53 --- /dev/null +++ b/server/migrations/104_rs_custom_domain.up.sql @@ -0,0 +1,3 @@ +CREATE UNIQUE INDEX IF NOT EXISTS remote_store_custom_domain_unique_idx + ON remote_store (key_value) + WHERE key_name = 'customDomain'; \ No newline at end of file From 920702c5dd9090bd4df1824157a82daf053fadeb Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Fri, 8 Aug 2025 15:31:02 +0530 Subject: [PATCH 05/14] Add validation --- server/ente/remotestore.go | 38 ++++++++++++++++++- .../pkg/controller/remotestore/controller.go | 13 ++++--- 2 files changed, 44 insertions(+), 7 deletions(-) diff --git a/server/ente/remotestore.go b/server/ente/remotestore.go index d08181f72c..7318d56753 100644 --- a/server/ente/remotestore.go +++ b/server/ente/remotestore.go @@ -1,5 +1,12 @@ package ente +import ( + "fmt" + "github.com/ente-io/stacktrace" + "net/url" + "strings" +) + type GetValueRequest struct { Key string `form:"key" binding:"required"` DefaultValue *string `form:"defaultValue"` @@ -100,7 +107,36 @@ func (k FlagKey) IsBoolType() bool { switch k { case RecoveryKeyVerified, MapEnabled, FaceSearchEnabled, PassKeyEnabled, IsInternalUser, IsBetaUser: return true - default: + case CustomDomain: return false + default: + return false // Explicitly handle unexpected cases } } + +func (k FlagKey) IsValidValue(value string) error { + if k.IsBoolType() && value != "true" && value != "false" { + return stacktrace.Propagate(NewBadRequestWithMessage(fmt.Sprintf("value %s is not allowed", value)), "value not allowed") + } + if k == CustomDomain && value != "" { + if !isValidCustomDomainURL(value) { + return stacktrace.Propagate(NewBadRequestWithMessage(fmt.Sprintf("unexcpeted %s", value)), "url with https://. Also, tt should not end with trailing dash.") + } + // ensure that it's valid domain that starts with https and does not end with trailing dash. + return stacktrace.Propagate(NewBadRequestWithMessage("custom domain cannot be empty"), "custom domain cannot be empty") + } + return nil +} + +func isValidCustomDomainURL(input string) bool { + if !strings.HasPrefix(input, "https://") || strings.HasSuffix(input, "/") { + return false + } + + u, err := url.Parse(input) + if err != nil || u.Scheme != "https" || u.Host == "" { + return false + } + + return true +} diff --git a/server/pkg/controller/remotestore/controller.go b/server/pkg/controller/remotestore/controller.go index bbbe741799..e6769ce443 100644 --- a/server/pkg/controller/remotestore/controller.go +++ b/server/pkg/controller/remotestore/controller.go @@ -26,6 +26,9 @@ func (c *Controller) InsertOrUpdate(ctx *gin.Context, request ente.UpdateKeyValu if err := c._validateRequest(userID, request.Key, request.Value, false); err != nil { return err } + if request.Value == "" && ente.FlagKey(request.Key).CanRemove() { + return c.Repo.RemoveKey(ctx, userID, request.Key) + } return c.Repo.InsertOrUpdate(ctx, userID, request.Key, request.Value) } @@ -79,9 +82,6 @@ func (c *Controller) GetFeatureFlags(ctx *gin.Context) (*ente.FeatureFlagRespons } for key, value := range values { flag := ente.FlagKey(key) - if !flag.IsBoolType() { - continue - } switch flag { case ente.RecoveryKeyVerified: response.RecoveryKeyVerified = value == "true" @@ -109,15 +109,16 @@ func (c *Controller) _validateRequest(userID int64, key, value string, byAdmin b return stacktrace.Propagate(ente.NewBadRequestWithMessage(fmt.Sprintf("key %s is not allowed", key)), "invalid flag key") } flag := ente.FlagKey(key) + if err := flag.IsValidValue(value); err != nil { + return stacktrace.Propagate(err, "") + } if !flag.UserEditable() && !byAdmin { return stacktrace.Propagate(ente.NewBadRequestWithMessage(fmt.Sprintf("key %s is not user editable", key)), "key not user editable") } if byAdmin && !flag.IsAdminEditable() { return stacktrace.Propagate(ente.NewBadRequestWithMessage(fmt.Sprintf("key %s is not admin editable", key)), "key not admin editable") } - if flag.IsBoolType() && value != "true" && value != "false" { - return stacktrace.Propagate(ente.NewBadRequestWithMessage(fmt.Sprintf("value %s is not allowed", value)), "value not allowed") - } + if flag.NeedSubscription() { return c.BillingCtrl.HasActiveSelfOrFamilySubscription(userID, true) } From 23103c3bcc83b20906efb62fd86fe9acc4ab120e Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Fri, 8 Aug 2025 15:31:14 +0530 Subject: [PATCH 06/14] Handle claimed domain error --- server/pkg/repo/remotestore/repository.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/server/pkg/repo/remotestore/repository.go b/server/pkg/repo/remotestore/repository.go index d9dd50ac4d..681942365c 100644 --- a/server/pkg/repo/remotestore/repository.go +++ b/server/pkg/repo/remotestore/repository.go @@ -3,6 +3,9 @@ package remotestore import ( "context" "database/sql" + "errors" + "github.com/ente-io/museum/ente" + "github.com/lib/pq" "github.com/ente-io/stacktrace" ) @@ -21,6 +24,17 @@ func (r *Repository) InsertOrUpdate(ctx context.Context, userID int64, key strin key, // $2 key_name value, // $3 key_value ) + + if err != nil { + // Check for unique violation (PostgreSQL error code 23505) + var pgErr *pq.Error + if errors.As(err, &pgErr) && pgErr.Code == "23505" { + if pgErr.Constraint == "remote_store_custom_domain_unique_idx" { + return ente.NewConflictError("custom domain already exists for another user") + } + } + return stacktrace.Propagate(err, "failed to insert/update") + } return stacktrace.Propagate(err, "failed to insert/update") } From 1c37332f37a49ac148f64cae31bb6c649d6736d0 Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Fri, 8 Aug 2025 15:40:05 +0530 Subject: [PATCH 07/14] Endpoint to check domain claim --- server/cmd/museum/main.go | 1 + server/pkg/api/remotestore.go | 15 +++++++++++++++ .../pkg/controller/remotestore/controller.go | 4 ++++ server/pkg/middleware/rate_limit.go | 3 ++- server/pkg/repo/remotestore/repository.go | 18 ++++++++++++++++++ 5 files changed, 40 insertions(+), 1 deletion(-) diff --git a/server/cmd/museum/main.go b/server/cmd/museum/main.go index cc10a67e8b..86f5537c7b 100644 --- a/server/cmd/museum/main.go +++ b/server/cmd/museum/main.go @@ -793,6 +793,7 @@ func main() { privateAPI.DELETE("/remote-store/:key", remoteStoreHandler.RemoveKey) privateAPI.GET("/remote-store", remoteStoreHandler.GetKey) privateAPI.GET("/remote-store/feature-flags", remoteStoreHandler.GetFeatureFlags) + privateAPI.GET("/custom-domain", remoteStoreHandler.CheckDomain) pushHandler := &api.PushHandler{PushController: pushController} privateAPI.POST("/push/token", pushHandler.AddToken) diff --git a/server/pkg/api/remotestore.go b/server/pkg/api/remotestore.go index 7752c990f3..e83883c2dc 100644 --- a/server/pkg/api/remotestore.go +++ b/server/pkg/api/remotestore.go @@ -73,3 +73,18 @@ func (h *RemoteStoreHandler) GetFeatureFlags(c *gin.Context) { } c.JSON(http.StatusOK, resp) } + +// CheckDomain returns 200 ok if the custom domain is claimed by any ente user +func (h *RemoteStoreHandler) CheckDomain(c *gin.Context) { + domain := c.Query("domain") + if domain == "" { + handler.Error(c, stacktrace.Propagate(ente.NewBadRequestWithMessage("domain is missing"), "")) + return + } + _, err := h.Controller.DomainOwner(c, domain) + if err != nil { + handler.Error(c, stacktrace.Propagate(err, "failed to get feature flags")) + return + } + c.JSON(http.StatusOK, gin.H{}) +} diff --git a/server/pkg/controller/remotestore/controller.go b/server/pkg/controller/remotestore/controller.go index e6769ce443..d6932a5aaf 100644 --- a/server/pkg/controller/remotestore/controller.go +++ b/server/pkg/controller/remotestore/controller.go @@ -104,6 +104,10 @@ func (c *Controller) GetFeatureFlags(ctx *gin.Context) (*ente.FeatureFlagRespons return response, nil } +func (c *Controller) DomainOwner(ctx *gin.Context, domain string) (*int64, error) { + return c.Repo.DomainOwner(ctx, domain) +} + func (c *Controller) _validateRequest(userID int64, key, value string, byAdmin bool) error { if ente.IsValidFlagKey(key) { return stacktrace.Propagate(ente.NewBadRequestWithMessage(fmt.Sprintf("key %s is not allowed", key)), "invalid flag key") diff --git a/server/pkg/middleware/rate_limit.go b/server/pkg/middleware/rate_limit.go index bf4c403cfe..26044ba53c 100644 --- a/server/pkg/middleware/rate_limit.go +++ b/server/pkg/middleware/rate_limit.go @@ -133,7 +133,8 @@ func (r *RateLimitMiddleware) APIRateLimitForUserMiddleware(urlSanitizer func(_ // getLimiter, based on reqPath & reqMethod, return instance of limiter.Limiter which needs to // be applied for a request. It returns nil if the request is not rate limited func (r *RateLimitMiddleware) getLimiter(reqPath string, reqMethod string) *limiter.Limiter { - if reqPath == "/users/public-key" { + if reqPath == "/users/public-key" || + reqPath == "/custom-domain" { return r.limit200ReqPerMin } if reqPath == "/users/ott" || diff --git a/server/pkg/repo/remotestore/repository.go b/server/pkg/repo/remotestore/repository.go index 681942365c..f2bd8d7789 100644 --- a/server/pkg/repo/remotestore/repository.go +++ b/server/pkg/repo/remotestore/repository.go @@ -47,6 +47,24 @@ func (r *Repository) RemoveKey(ctx context.Context, userID int64, key string) er return stacktrace.Propagate(err, "failed to remove key") } +func (r *Repository) DomainOwner(ctx context.Context, domain string) (*int64, error) { + // Check if the domain is already taken by another user + rows := r.DB.QueryRowContext(ctx, `SELECT user_id FROM remote_store + WHERE key_name = $1 AND key_value = $2`, + ente.CustomDomain, // $1 + domain, // $2 + ) + var userID int64 + err := rows.Scan(&userID) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + return nil, stacktrace.Propagate(&ente.ErrNotFoundError, "") + } + return nil, stacktrace.Propagate(err, "failed to fetch domain owner") + } + return &userID, nil +} + // GetValue fetches and return the value for given user_id and key func (r *Repository) GetValue(ctx context.Context, userID int64, key string) (string, error) { rows := r.DB.QueryRowContext(ctx, `SELECT key_value FROM remote_store From 7996ab4a3e8497d36e22c9e8ccb8bbae01555549 Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Fri, 8 Aug 2025 16:36:50 +0530 Subject: [PATCH 08/14] Minor fixes --- server/ente/remotestore.go | 4 +--- server/pkg/controller/remotestore/controller.go | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/server/ente/remotestore.go b/server/ente/remotestore.go index 7318d56753..02bcc52c1d 100644 --- a/server/ente/remotestore.go +++ b/server/ente/remotestore.go @@ -120,10 +120,8 @@ func (k FlagKey) IsValidValue(value string) error { } if k == CustomDomain && value != "" { if !isValidCustomDomainURL(value) { - return stacktrace.Propagate(NewBadRequestWithMessage(fmt.Sprintf("unexcpeted %s", value)), "url with https://. Also, tt should not end with trailing dash.") + return stacktrace.Propagate(NewBadRequestWithMessage(fmt.Sprintf("invalid domain fmt: %s", value)), "url with https://. Also, tt should not end with trailing dash.") } - // ensure that it's valid domain that starts with https and does not end with trailing dash. - return stacktrace.Propagate(NewBadRequestWithMessage("custom domain cannot be empty"), "custom domain cannot be empty") } return nil } diff --git a/server/pkg/controller/remotestore/controller.go b/server/pkg/controller/remotestore/controller.go index d6932a5aaf..131a6e7c2f 100644 --- a/server/pkg/controller/remotestore/controller.go +++ b/server/pkg/controller/remotestore/controller.go @@ -109,7 +109,7 @@ func (c *Controller) DomainOwner(ctx *gin.Context, domain string) (*int64, error } func (c *Controller) _validateRequest(userID int64, key, value string, byAdmin bool) error { - if ente.IsValidFlagKey(key) { + if !ente.IsValidFlagKey(key) { return stacktrace.Propagate(ente.NewBadRequestWithMessage(fmt.Sprintf("key %s is not allowed", key)), "invalid flag key") } flag := ente.FlagKey(key) From 1d649c5a79f16bf9f22d7cd4ef0092af1ba70e79 Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Tue, 12 Aug 2025 10:01:32 +0530 Subject: [PATCH 09/14] Store only domain --- server/cmd/museum/main.go | 2 +- server/ente/remotestore.go | 30 ++++++----- server/ente/remotestore_test.go | 92 +++++++++++++++++++++++++++++++++ 3 files changed, 111 insertions(+), 13 deletions(-) create mode 100644 server/ente/remotestore_test.go diff --git a/server/cmd/museum/main.go b/server/cmd/museum/main.go index 597826ca92..375bef2798 100644 --- a/server/cmd/museum/main.go +++ b/server/cmd/museum/main.go @@ -98,7 +98,7 @@ func main() { } viper.SetDefault("apps.public-albums", "https://albums.ente.io") - viper.SetDefault("apps.custom-domain.cname", "https://my.ente.io") + viper.SetDefault("apps.custom-domain.cname", "my.ente.io") viper.SetDefault("apps.public-locker", "https://locker.ente.io") viper.SetDefault("apps.accounts", "https://accounts.ente.io") viper.SetDefault("apps.cast", "https://cast.ente.io") diff --git a/server/ente/remotestore.go b/server/ente/remotestore.go index 02bcc52c1d..3a9a965bf7 100644 --- a/server/ente/remotestore.go +++ b/server/ente/remotestore.go @@ -3,7 +3,7 @@ package ente import ( "fmt" "github.com/ente-io/stacktrace" - "net/url" + "regexp" "strings" ) @@ -119,22 +119,28 @@ func (k FlagKey) IsValidValue(value string) error { return stacktrace.Propagate(NewBadRequestWithMessage(fmt.Sprintf("value %s is not allowed", value)), "value not allowed") } if k == CustomDomain && value != "" { - if !isValidCustomDomainURL(value) { - return stacktrace.Propagate(NewBadRequestWithMessage(fmt.Sprintf("invalid domain fmt: %s", value)), "url with https://. Also, tt should not end with trailing dash.") + if err := isValidDomainWithoutScheme(value); err != nil { + return stacktrace.Propagate(err, "invalid custom domain") } } return nil } -func isValidCustomDomainURL(input string) bool { - if !strings.HasPrefix(input, "https://") || strings.HasSuffix(input, "/") { - return false - } +var domainRegex = regexp.MustCompile(`^(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$`) - u, err := url.Parse(input) - if err != nil || u.Scheme != "https" || u.Host == "" { - return false +func isValidDomainWithoutScheme(input string) error { + trimmed := strings.TrimSpace(input) + if trimmed != input { + return NewBadRequestWithMessage("domain contains leading or trailing spaces") } - - return true + if trimmed == "" { + return NewBadRequestWithMessage("domain is empty") + } + if strings.Contains(trimmed, "://") { + return NewBadRequestWithMessage("domain should not contain scheme (e.g., http:// or https://)") + } + if !domainRegex.MatchString(trimmed) { + return NewBadRequestWithMessage(fmt.Sprintf("invalid domain format: %s", trimmed)) + } + return nil } diff --git a/server/ente/remotestore_test.go b/server/ente/remotestore_test.go new file mode 100644 index 0000000000..1056e5ca6b --- /dev/null +++ b/server/ente/remotestore_test.go @@ -0,0 +1,92 @@ +package ente + +import "testing" + +func TestIsValidDomainWithoutScheme(t *testing.T) { + tests := []struct { + name string + input string + wantErr bool + }{ + // ✅ Valid cases + {"simple domain", "google.com", false}, + {"multi-level domain", "sub.example.co.in", false}, + {"numeric in label", "a1b2c3.com", false}, + {"long but valid label", "my-very-long-subdomain-name.example.com", false}, + + // ❌ Leading/trailing spaces + {"leading space", " google.com", true}, + {"trailing space", "google.com ", true}, + {"both spaces", " google.com ", true}, + + // ❌ Empty or whitespace + {"empty string", "", true}, + {"only spaces", " ", true}, + + // ❌ Scheme included + {"http scheme", "http://google.com", true}, + {"https scheme", "https://example.com", true}, + {"ftp scheme", "ftp://example.com", true}, + + // ❌ Invalid characters + {"underscore in label", "my_domain.com", true}, + {"invalid symbol", "exa$mple.com", true}, + {"space inside", "exa mple.com", true}, + + // ❌ Wrong format + {"missing dot", "localhost", true}, + {"single label TLD", "com", true}, + {"ends with dot", "example.com.", true}, + {"ends with dash", "example-.com", true}, + {"starts with dash", "-example.com", true}, + + // ❌ Consecutive dots + {"double dots", "example..com", true}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := isValidDomainWithoutScheme(tt.input) + if (err != nil) != tt.wantErr { + t.Errorf("isValidDomainWithoutScheme(%q) error = %v, wantErr %v", tt.input, err, tt.wantErr) + } + }) + } +} + +func TestFlagKey_IsValidValue(t *testing.T) { + tests := []struct { + name string + key FlagKey + value string + wantErr bool + }{ + // ✅ Valid boolean flag values + {"valid true for bool key", MapEnabled, "true", false}, + {"valid false for bool key", FaceSearchEnabled, "false", false}, + + // ❌ Invalid boolean flag values + {"invalid value for bool key", PassKeyEnabled, "yes", true}, + {"empty value for bool key", IsInternalUser, "", true}, + + // ✅ Valid custom domain values + {"valid custom domain", CustomDomain, "example.com", false}, + {"valid subdomain", CustomDomain, "sub.example.com", false}, + + // ❌ Invalid custom domain values + {"empty custom domain", CustomDomain, "", false}, // Allowed as empty + {"custom domain with scheme", CustomDomain, "http://example.com", true}, + {"custom domain with invalid format", CustomDomain, "exa$mple.com", true}, + {"custom domain with leading space", CustomDomain, " example.com", true}, + {"custom domain with trailing space", CustomDomain, "example.com ", true}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := tt.key.IsValidValue(tt.value) + if (err != nil) != tt.wantErr { + t.Errorf("FlagKey(%q).IsValidValue(%q) error = %v, wantErr %v", tt.key, tt.value, err, tt.wantErr) + } + }) + } +} From 5090e16d54d72f9ea9de1bb54c25875892526bc1 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Tue, 12 Aug 2025 10:25:24 +0530 Subject: [PATCH 10/14] Make public --- server/cmd/museum/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/cmd/museum/main.go b/server/cmd/museum/main.go index 375bef2798..f7c291b6d3 100644 --- a/server/cmd/museum/main.go +++ b/server/cmd/museum/main.go @@ -793,7 +793,7 @@ func main() { privateAPI.DELETE("/remote-store/:key", remoteStoreHandler.RemoveKey) privateAPI.GET("/remote-store", remoteStoreHandler.GetKey) privateAPI.GET("/remote-store/feature-flags", remoteStoreHandler.GetFeatureFlags) - privateAPI.GET("/custom-domain", remoteStoreHandler.CheckDomain) + publicAPI.GET("/custom-domain", remoteStoreHandler.CheckDomain) pushHandler := &api.PushHandler{PushController: pushController} privateAPI.POST("/push/token", pushHandler.AddToken) From 29d316349b5898bb691caaaa2f3a9825726efc40 Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Tue, 12 Aug 2025 11:47:46 +0530 Subject: [PATCH 11/14] Allow empty string --- server/ente/remotestore.go | 10 +++++----- server/pkg/controller/remotestore/controller.go | 12 ++++++++---- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/server/ente/remotestore.go b/server/ente/remotestore.go index 3a9a965bf7..ea96d8de1c 100644 --- a/server/ente/remotestore.go +++ b/server/ente/remotestore.go @@ -17,14 +17,14 @@ type GetValueResponse struct { } type UpdateKeyValueRequest struct { - Key string `json:"key" binding:"required"` - Value string `json:"value" binding:"required"` + Key string `json:"key" binding:"required"` + Value *string `json:"value" binding:"required"` } type AdminUpdateKeyValueRequest struct { - UserID int64 `json:"userID" binding:"required"` - Key string `json:"key" binding:"required"` - Value string `json:"value" binding:"required"` + UserID int64 `json:"userID" binding:"required"` + Key string `json:"key" binding:"required"` + Value *string `json:"value" binding:"required"` } type FeatureFlagResponse struct { diff --git a/server/pkg/controller/remotestore/controller.go b/server/pkg/controller/remotestore/controller.go index 131a6e7c2f..eac04534ba 100644 --- a/server/pkg/controller/remotestore/controller.go +++ b/server/pkg/controller/remotestore/controller.go @@ -26,10 +26,10 @@ func (c *Controller) InsertOrUpdate(ctx *gin.Context, request ente.UpdateKeyValu if err := c._validateRequest(userID, request.Key, request.Value, false); err != nil { return err } - if request.Value == "" && ente.FlagKey(request.Key).CanRemove() { + if *request.Value == "" && ente.FlagKey(request.Key).CanRemove() { return c.Repo.RemoveKey(ctx, userID, request.Key) } - return c.Repo.InsertOrUpdate(ctx, userID, request.Key, request.Value) + return c.Repo.InsertOrUpdate(ctx, userID, request.Key, *request.Value) } // RemoveKey removes the key from remote store @@ -48,7 +48,7 @@ func (c *Controller) AdminInsertOrUpdate(ctx *gin.Context, request ente.AdminUpd if err := c._validateRequest(request.UserID, request.Key, request.Value, true); err != nil { return err } - return c.Repo.InsertOrUpdate(ctx, request.UserID, request.Key, request.Value) + return c.Repo.InsertOrUpdate(ctx, request.UserID, request.Key, *request.Value) } func (c *Controller) Get(ctx *gin.Context, req ente.GetValueRequest) (*ente.GetValueResponse, error) { @@ -108,10 +108,14 @@ func (c *Controller) DomainOwner(ctx *gin.Context, domain string) (*int64, error return c.Repo.DomainOwner(ctx, domain) } -func (c *Controller) _validateRequest(userID int64, key, value string, byAdmin bool) error { +func (c *Controller) _validateRequest(userID int64, key string, valuePtr *string, byAdmin bool) error { if !ente.IsValidFlagKey(key) { return stacktrace.Propagate(ente.NewBadRequestWithMessage(fmt.Sprintf("key %s is not allowed", key)), "invalid flag key") } + if valuePtr == nil { + return stacktrace.Propagate(ente.NewBadRequestWithMessage("value is missing"), "value is nil") + } + value := *valuePtr flag := ente.FlagKey(key) if err := flag.IsValidValue(value); err != nil { return stacktrace.Propagate(err, "") From e9c41aed1e755c447b6f61991b494ab0373244e2 Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Tue, 12 Aug 2025 13:03:42 +0530 Subject: [PATCH 12/14] Alert for origin mismatch --- server/cmd/museum/main.go | 1 + server/configurations/local.yaml | 2 +- server/pkg/middleware/collection_link.go | 54 +++++++++++++++++++++-- server/pkg/repo/remotestore/repository.go | 19 ++++++++ 4 files changed, 72 insertions(+), 4 deletions(-) diff --git a/server/cmd/museum/main.go b/server/cmd/museum/main.go index f7c291b6d3..abdd9e58ce 100644 --- a/server/cmd/museum/main.go +++ b/server/cmd/museum/main.go @@ -378,6 +378,7 @@ func main() { Cache: accessTokenCache, BillingCtrl: billingController, DiscordController: discordController, + RemoteStoreRepo: remoteStoreRepository, } fileLinkMiddleware := &middleware.FileLinkMiddleware{ FileLinkRepo: fileLinkRepo, diff --git a/server/configurations/local.yaml b/server/configurations/local.yaml index 806add1428..70d7589280 100644 --- a/server/configurations/local.yaml +++ b/server/configurations/local.yaml @@ -96,7 +96,7 @@ apps: # Default is https://family.ente.io family: custom-domain: - # Default is https://my.ente.io + # Default is my.ente.io cname: # Database connection parameters diff --git a/server/pkg/middleware/collection_link.go b/server/pkg/middleware/collection_link.go index 9c20362559..4bdb2de285 100644 --- a/server/pkg/middleware/collection_link.go +++ b/server/pkg/middleware/collection_link.go @@ -5,7 +5,12 @@ import ( "context" "crypto/sha256" "fmt" + "github.com/ente-io/museum/pkg/repo/remotestore" + "github.com/gin-contrib/requestid" + "github.com/spf13/viper" "net/http" + "net/url" + "strings" public2 "github.com/ente-io/museum/pkg/controller/public" "github.com/ente-io/museum/pkg/repo/public" @@ -35,6 +40,7 @@ type CollectionLinkMiddleware struct { Cache *cache.Cache BillingCtrl *controller.BillingController DiscordController *discord.DiscordController + RemoteStoreRepo *remotestore.Repository } // Authenticate returns a middle ware that extracts the `X-Auth-Access-Token` @@ -65,7 +71,7 @@ func (m *CollectionLinkMiddleware) Authenticate(urlSanitizer func(_ *gin.Context return } // validate if user still has active paid subscription - if err = m.validateOwnersSubscription(publicCollectionSummary.CollectionID); err != nil { + if err = m.validateOwnersSubscription(c, publicCollectionSummary.CollectionID); err != nil { logrus.WithError(err).Warn("failed to verify active paid subscription") c.AbortWithStatusJSON(http.StatusGone, gin.H{"error": "no active subscription"}) return @@ -115,12 +121,17 @@ func (m *CollectionLinkMiddleware) Authenticate(urlSanitizer func(_ *gin.Context c.Next() } } -func (m *CollectionLinkMiddleware) validateOwnersSubscription(cID int64) error { +func (m *CollectionLinkMiddleware) validateOwnersSubscription(c *gin.Context, cID int64) error { userID, err := m.CollectionRepo.GetOwnerID(cID) if err != nil { return stacktrace.Propagate(err, "") } - return m.BillingCtrl.HasActiveSelfOrFamilySubscription(userID, false) + err = m.BillingCtrl.HasActiveSelfOrFamilySubscription(userID, false) + if err != nil { + return stacktrace.Propagate(err, "failed to validate owners subscription") + } + m.validateOrigin(c, userID) + return nil } func (m *CollectionLinkMiddleware) isDeviceLimitReached(ctx context.Context, @@ -177,6 +188,43 @@ func (m *CollectionLinkMiddleware) validatePassword(c *gin.Context, reqPath stri return m.PublicCollectionCtrl.ValidateJWTToken(c, accessTokenJWT, *collectionSummary.PassHash) } +func (m *CollectionLinkMiddleware) validateOrigin(c *gin.Context, ownerID int64) { + origin := c.Request.Header.Get("Origin") + + if origin == "" || origin == viper.GetString("apps.public-albums") { + return + } + reqId := requestid.Get(c) + logger := logrus.WithFields(logrus.Fields{ + "ownerID": ownerID, + "req_id": reqId, + "origin": origin, + }) + alertMessage := fmt.Sprintf("custom domain check failed %s", reqId) + domain, err := m.RemoteStoreRepo.GetDomain(c, ownerID) + if err != nil { + logger.WithError(err).Error("failed to fetch custom domain for owner") + m.DiscordController.NotifyPotentialAbuse(alertMessage) + return + } + if domain == nil || *domain == "" { + logger.Warn("custom domain is nil or empty") + m.DiscordController.NotifyPotentialAbuse(alertMessage) + return + } + parse, err := url.Parse(origin) + if err != nil { + logger.WithError(err).Error("failed to parse origin URL") + m.DiscordController.NotifyPotentialAbuse(alertMessage + " - failed to parse origin URL") + return + } + if !strings.Contains(strings.ToLower(parse.Host), strings.ToLower(*domain)) { + logger.Warnf("custom domain check failed for owner %d, origin %s, domain %s", ownerID, origin, *domain) + m.DiscordController.NotifyPotentialAbuse(alertMessage) + } + return +} + func computeHashKeyForList(list []string, delim string) string { var buffer bytes.Buffer for i := range list { diff --git a/server/pkg/repo/remotestore/repository.go b/server/pkg/repo/remotestore/repository.go index f2bd8d7789..245d05b4eb 100644 --- a/server/pkg/repo/remotestore/repository.go +++ b/server/pkg/repo/remotestore/repository.go @@ -65,6 +65,25 @@ func (r *Repository) DomainOwner(ctx context.Context, domain string) (*int64, er return &userID, nil } +func (r *Repository) GetDomain(ctx context.Context, userID int64) (*string, error) { + // Fetch the custom domain for the user + rows := r.DB.QueryRowContext(ctx, `SELECT key_value FROM remote_store + WHERE user_id = $1 AND key_name = $2`, + userID, // $1 + ente.CustomDomain, // $2 + ) + var domain string + err := rows.Scan(&domain) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + return nil, nil + } + return nil, stacktrace.Propagate(err, "failed to fetch custom domain") + } + return &domain, nil + +} + // GetValue fetches and return the value for given user_id and key func (r *Repository) GetValue(ctx context.Context, userID int64, key string) (string, error) { rows := r.DB.QueryRowContext(ctx, `SELECT key_value FROM remote_store From 21c8af01a2fbf3f42e6b3d5a5b7c4b07522c4122 Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Tue, 12 Aug 2025 13:25:39 +0530 Subject: [PATCH 13/14] Update query --- server/migrations/104_rs_custom_domain.down.sql | 4 +--- server/migrations/104_rs_custom_domain.up.sql | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/server/migrations/104_rs_custom_domain.down.sql b/server/migrations/104_rs_custom_domain.down.sql index 848999ea47..de4e692485 100644 --- a/server/migrations/104_rs_custom_domain.down.sql +++ b/server/migrations/104_rs_custom_domain.down.sql @@ -1,3 +1 @@ -CREATE UNIQUE INDEX IF NOT EXISTS remote_store_custom_domain_unique_idx - ON remote_store (key_value) - WHERE key_name = 'custom_domain'; \ No newline at end of file +DROP INDEX IF EXISTS remote_store_custom_domain_unique_idx; diff --git a/server/migrations/104_rs_custom_domain.up.sql b/server/migrations/104_rs_custom_domain.up.sql index 73da6baf53..4ab6b896f2 100644 --- a/server/migrations/104_rs_custom_domain.up.sql +++ b/server/migrations/104_rs_custom_domain.up.sql @@ -1,3 +1,3 @@ CREATE UNIQUE INDEX IF NOT EXISTS remote_store_custom_domain_unique_idx ON remote_store (key_value) - WHERE key_name = 'customDomain'; \ No newline at end of file + WHERE key_name = 'customDomain'; From da590a643f74c3dbd2b0b86159720b2843111484 Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Tue, 12 Aug 2025 13:28:00 +0530 Subject: [PATCH 14/14] Lint fixes --- server/pkg/api/admin.go | 9 +++++---- server/pkg/middleware/collection_link.go | 8 ++++---- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/server/pkg/api/admin.go b/server/pkg/api/admin.go index 124d21078f..7ae784ab41 100644 --- a/server/pkg/api/admin.go +++ b/server/pkg/api/admin.go @@ -3,13 +3,14 @@ package api import ( "errors" "fmt" - "github.com/ente-io/museum/pkg/controller/emergency" - "github.com/ente-io/museum/pkg/controller/remotestore" - "github.com/ente-io/museum/pkg/repo/authenticator" "net/http" "strconv" "strings" + "github.com/ente-io/museum/pkg/controller/emergency" + "github.com/ente-io/museum/pkg/controller/remotestore" + "github.com/ente-io/museum/pkg/repo/authenticator" + "github.com/ente-io/museum/pkg/controller/family" bonusEntity "github.com/ente-io/museum/ente/storagebonus" @@ -377,7 +378,7 @@ func (h *AdminHandler) UpdateFeatureFlag(c *gin.Context) { return } go h.DiscordController.NotifyAdminAction( - fmt.Sprintf("Admin (%d) updating flag:%s to val:%s for %d", auth.GetUserID(c.Request.Header), request.Key, request.Value, request.UserID)) + fmt.Sprintf("Admin (%d) updating flag:%s to val:%v for %d", auth.GetUserID(c.Request.Header), request.Key, request.Value, request.UserID)) logger := logrus.WithFields(logrus.Fields{ "user_id": request.UserID, diff --git a/server/pkg/middleware/collection_link.go b/server/pkg/middleware/collection_link.go index 4bdb2de285..a0e59df2ca 100644 --- a/server/pkg/middleware/collection_link.go +++ b/server/pkg/middleware/collection_link.go @@ -5,13 +5,14 @@ import ( "context" "crypto/sha256" "fmt" - "github.com/ente-io/museum/pkg/repo/remotestore" - "github.com/gin-contrib/requestid" - "github.com/spf13/viper" "net/http" "net/url" "strings" + "github.com/ente-io/museum/pkg/repo/remotestore" + "github.com/gin-contrib/requestid" + "github.com/spf13/viper" + public2 "github.com/ente-io/museum/pkg/controller/public" "github.com/ente-io/museum/pkg/repo/public" @@ -222,7 +223,6 @@ func (m *CollectionLinkMiddleware) validateOrigin(c *gin.Context, ownerID int64) logger.Warnf("custom domain check failed for owner %d, origin %s, domain %s", ownerID, origin, *domain) m.DiscordController.NotifyPotentialAbuse(alertMessage) } - return } func computeHashKeyForList(list []string, delim string) string {