diff --git a/assets b/assets index 8b2c8a7..eb2cfac 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 8b2c8a7bdbde43a1f95ddaa4555e4304215c1e7c +Subproject commit eb2cfac37d73e5bd3000eb66a3a0062509efe122 diff --git a/inventory/types/types.go b/inventory/types/types.go index 479574f..bc06985 100644 --- a/inventory/types/types.go +++ b/inventory/types/types.go @@ -7,17 +7,20 @@ import ( // UserSetting 用户其他配置 type ( UserSetting struct { - ProfileOff bool `json:"profile_off,omitempty"` - PreferredTheme string `json:"preferred_theme,omitempty"` - VersionRetention bool `json:"version_retention,omitempty"` - VersionRetentionExt []string `json:"version_retention_ext,omitempty"` - VersionRetentionMax int `json:"version_retention_max,omitempty"` - Pined []PinedFile `json:"pined,omitempty"` - Language string `json:"email_language,omitempty"` - DisableViewSync bool `json:"disable_view_sync,omitempty"` - FsViewMap map[string]ExplorerView `json:"fs_view_map,omitempty"` + ProfileOff bool `json:"profile_off,omitempty"` + PreferredTheme string `json:"preferred_theme,omitempty"` + VersionRetention bool `json:"version_retention,omitempty"` + VersionRetentionExt []string `json:"version_retention_ext,omitempty"` + VersionRetentionMax int `json:"version_retention_max,omitempty"` + Pined []PinedFile `json:"pined,omitempty"` + Language string `json:"email_language,omitempty"` + DisableViewSync bool `json:"disable_view_sync,omitempty"` + FsViewMap map[string]ExplorerView `json:"fs_view_map,omitempty"` + ShareLinksInProfile ShareLinksInProfileLevel `json:"share_links_in_profile,omitempty"` } + ShareLinksInProfileLevel string + PinedFile struct { Uri string `json:"uri"` Name string `json:"name,omitempty"` @@ -334,3 +337,9 @@ const ( CustomPropsTypeLink = "link" CustomPropsTypeRating = "rating" ) + +const ( + ProfilePublicShareOnly = ShareLinksInProfileLevel("") + ProfileAllShare = ShareLinksInProfileLevel("all_share") + ProfileHideShare = ShareLinksInProfileLevel("hide_share") +) diff --git a/service/explorer/response.go b/service/explorer/response.go index 755e96e..302a227 100644 --- a/service/explorer/response.go +++ b/service/explorer/response.go @@ -271,19 +271,20 @@ type Entity struct { } type Share struct { - ID string `json:"id"` - Name string `json:"name,omitempty"` - RemainDownloads *int `json:"remain_downloads,omitempty"` - Visited int `json:"visited"` - Downloaded int `json:"downloaded,omitempty"` - Expires *time.Time `json:"expires,omitempty"` - Unlocked bool `json:"unlocked"` - SourceType *types.FileType `json:"source_type,omitempty"` - Owner user.User `json:"owner"` - CreatedAt time.Time `json:"created_at,omitempty"` - Expired bool `json:"expired"` - Url string `json:"url"` - ShowReadMe bool `json:"show_readme,omitempty"` + ID string `json:"id"` + Name string `json:"name,omitempty"` + RemainDownloads *int `json:"remain_downloads,omitempty"` + Visited int `json:"visited"` + Downloaded int `json:"downloaded,omitempty"` + Expires *time.Time `json:"expires,omitempty"` + Unlocked bool `json:"unlocked"` + PasswordProtected bool `json:"password_protected,omitempty"` + SourceType *types.FileType `json:"source_type,omitempty"` + Owner user.User `json:"owner"` + CreatedAt time.Time `json:"created_at,omitempty"` + Expired bool `json:"expired"` + Url string `json:"url"` + ShowReadMe bool `json:"show_readme,omitempty"` // Only viewable by owner IsPrivate bool `json:"is_private,omitempty"` @@ -301,15 +302,16 @@ func BuildShare(s *ent.Share, base *url.URL, hasher hashid.Encoder, requester *e redactLevel = user.RedactLevelUser } res := Share{ - Name: name, - ID: hashid.EncodeShareID(hasher, s.ID), - Unlocked: unlocked, - Owner: user.BuildUserRedacted(owner, redactLevel, hasher), - Expired: inventory.IsShareExpired(s) != nil || expired, - Url: BuildShareLink(s, hasher, base), - CreatedAt: s.CreatedAt, - Visited: s.Views, - SourceType: util.ToPtr(t), + Name: name, + ID: hashid.EncodeShareID(hasher, s.ID), + Unlocked: unlocked, + Owner: user.BuildUserRedacted(owner, redactLevel, hasher), + Expired: inventory.IsShareExpired(s) != nil || expired, + Url: BuildShareLink(s, hasher, base, unlocked), + CreatedAt: s.CreatedAt, + Visited: s.Views, + SourceType: util.ToPtr(t), + PasswordProtected: s.Password != "", } if unlocked { @@ -436,9 +438,12 @@ func BuildEntity(extendedInfo *fs.FileExtendedInfo, e fs.Entity, hasher hashid.E } } -func BuildShareLink(s *ent.Share, hasher hashid.Encoder, base *url.URL) string { +func BuildShareLink(s *ent.Share, hasher hashid.Encoder, base *url.URL, unlocked bool) string { shareId := hashid.EncodeShareID(hasher, s.ID) - return routes.MasterShareUrl(base, shareId, s.Password).String() + if unlocked { + return routes.MasterShareUrl(base, shareId, s.Password).String() + } + return routes.MasterShareUrl(base, shareId, "").String() } func BuildStoragePolicy(sp *ent.StoragePolicy, hasher hashid.Encoder) *StoragePolicy { diff --git a/service/share/manage.go b/service/share/manage.go index 335f346..b8ad4d1 100644 --- a/service/share/manage.go +++ b/service/share/manage.go @@ -66,7 +66,7 @@ func (service *ShareCreateService) Upsert(c *gin.Context, existed int) (string, } base := dep.SettingProvider().SiteURL(c) - return explorer.BuildShareLink(share, dep.HashIDEncoder(), base), nil + return explorer.BuildShareLink(share, dep.HashIDEncoder(), base, true), nil } func DeleteShare(c *gin.Context, shareId int) error { diff --git a/service/share/visit.go b/service/share/visit.go index e5a756b..cf07622 100644 --- a/service/share/visit.go +++ b/service/share/visit.go @@ -137,6 +137,16 @@ func (s *ListShareService) ListInUserProfile(c *gin.Context, uid int) (*ListShar hasher := dep.HashIDEncoder() shareClient := dep.ShareClient() + targetUser, err := dep.UserClient().GetActiveByID(c, uid) + if err != nil { + return nil, serializer.NewError(serializer.CodeDBError, "Failed to get user", err) + } + + if targetUser.Settings != nil && targetUser.Settings.ShareLinksInProfile == types.ProfileHideShare { + return nil, serializer.NewError(serializer.CodeParamErr, "User has disabled share links in profile", nil) + } + + publicOnly := targetUser.Settings == nil || targetUser.Settings.ShareLinksInProfile == types.ProfilePublicShareOnly args := &inventory.ListShareArgs{ PaginationArgs: &inventory.PaginationArgs{ UseCursorPagination: true, @@ -146,7 +156,7 @@ func (s *ListShareService) ListInUserProfile(c *gin.Context, uid int) (*ListShar OrderBy: s.OrderBy, }, UserID: uid, - PublicOnly: true, + PublicOnly: publicOnly, } ctx := context.WithValue(c, inventory.LoadShareUser{}, true) diff --git a/service/user/response.go b/service/user/response.go index e83e99e..6c30a2c 100644 --- a/service/user/response.go +++ b/service/user/response.go @@ -29,6 +29,7 @@ type UserSettings struct { TwoFAEnabled bool `json:"two_fa_enabled"` Passkeys []Passkey `json:"passkeys,omitempty"` DisableViewSync bool `json:"disable_view_sync"` + ShareLinksInProfile string `json:"share_links_in_profile"` } func BuildUserSettings(u *ent.User, passkeys []*ent.Passkey, parser *uaparser.Parser) *UserSettings { @@ -41,7 +42,8 @@ func BuildUserSettings(u *ent.User, passkeys []*ent.Passkey, parser *uaparser.Pa Passkeys: lo.Map(passkeys, func(item *ent.Passkey, index int) Passkey { return BuildPasskey(item) }), - DisableViewSync: u.Settings.DisableViewSync, + DisableViewSync: u.Settings.DisableViewSync, + ShareLinksInProfile: string(u.Settings.ShareLinksInProfile), } } @@ -97,18 +99,19 @@ type BuiltinLoginResponse struct { // User 用户序列化器 type User struct { - ID string `json:"id"` - Email string `json:"email,omitempty"` - Nickname string `json:"nickname"` - Status user.Status `json:"status,omitempty"` - Avatar string `json:"avatar,omitempty"` - CreatedAt time.Time `json:"created_at"` - PreferredTheme string `json:"preferred_theme,omitempty"` - Anonymous bool `json:"anonymous,omitempty"` - Group *Group `json:"group,omitempty"` - Pined []types.PinedFile `json:"pined,omitempty"` - Language string `json:"language,omitempty"` - DisableViewSync bool `json:"disable_view_sync,omitempty"` + ID string `json:"id"` + Email string `json:"email,omitempty"` + Nickname string `json:"nickname"` + Status user.Status `json:"status,omitempty"` + Avatar string `json:"avatar,omitempty"` + CreatedAt time.Time `json:"created_at"` + PreferredTheme string `json:"preferred_theme,omitempty"` + Anonymous bool `json:"anonymous,omitempty"` + Group *Group `json:"group,omitempty"` + Pined []types.PinedFile `json:"pined,omitempty"` + Language string `json:"language,omitempty"` + DisableViewSync bool `json:"disable_view_sync,omitempty"` + ShareLinksInProfile types.ShareLinksInProfileLevel `json:"share_links_in_profile,omitempty"` } type Group struct { @@ -153,18 +156,19 @@ func BuildWebAuthnList(credentials []webauthn.Credential) []WebAuthnCredentials // BuildUser 序列化用户 func BuildUser(user *ent.User, idEncoder hashid.Encoder) User { return User{ - ID: hashid.EncodeUserID(idEncoder, user.ID), - Email: user.Email, - Nickname: user.Nick, - Status: user.Status, - Avatar: user.Avatar, - CreatedAt: user.CreatedAt, - PreferredTheme: user.Settings.PreferredTheme, - Anonymous: user.ID == 0, - Group: BuildGroup(user.Edges.Group, idEncoder), - Pined: user.Settings.Pined, - Language: user.Settings.Language, - DisableViewSync: user.Settings.DisableViewSync, + ID: hashid.EncodeUserID(idEncoder, user.ID), + Email: user.Email, + Nickname: user.Nick, + Status: user.Status, + Avatar: user.Avatar, + CreatedAt: user.CreatedAt, + PreferredTheme: user.Settings.PreferredTheme, + Anonymous: user.ID == 0, + Group: BuildGroup(user.Edges.Group, idEncoder), + Pined: user.Settings.Pined, + Language: user.Settings.Language, + DisableViewSync: user.Settings.DisableViewSync, + ShareLinksInProfile: user.Settings.ShareLinksInProfile, } } @@ -193,10 +197,11 @@ func BuildUserRedacted(u *ent.User, level int, idEncoder hashid.Encoder) User { userRaw := BuildUser(u, idEncoder) user := User{ - ID: userRaw.ID, - Nickname: userRaw.Nickname, - Avatar: userRaw.Avatar, - CreatedAt: userRaw.CreatedAt, + ID: userRaw.ID, + Nickname: userRaw.Nickname, + Avatar: userRaw.Avatar, + CreatedAt: userRaw.CreatedAt, + ShareLinksInProfile: userRaw.ShareLinksInProfile, } if userRaw.Group != nil { diff --git a/service/user/setting.go b/service/user/setting.go index c60085e..1c2181b 100644 --- a/service/user/setting.go +++ b/service/user/setting.go @@ -14,6 +14,7 @@ import ( "github.com/cloudreve/Cloudreve/v4/application/dependency" "github.com/cloudreve/Cloudreve/v4/ent" "github.com/cloudreve/Cloudreve/v4/inventory" + "github.com/cloudreve/Cloudreve/v4/inventory/types" "github.com/cloudreve/Cloudreve/v4/pkg/hashid" "github.com/cloudreve/Cloudreve/v4/pkg/request" "github.com/cloudreve/Cloudreve/v4/pkg/serializer" @@ -221,6 +222,7 @@ type ( TwoFAEnabled *bool `json:"two_fa_enabled" binding:"omitempty"` TwoFACode *string `json:"two_fa_code" binding:"omitempty"` DisableViewSync *bool `json:"disable_view_sync" binding:"omitempty"` + ShareLinksInProfile *string `json:"share_links_in_profile" binding:"omitempty"` } PatchUserSettingParamsCtx struct{} ) @@ -267,6 +269,11 @@ func (s *PatchUserSetting) Patch(c *gin.Context) error { saveSetting = true } + if s.ShareLinksInProfile != nil { + u.Settings.ShareLinksInProfile = types.ShareLinksInProfileLevel(*s.ShareLinksInProfile) + saveSetting = true + } + if s.CurrentPassword != nil && s.NewPassword != nil { if err := inventory.CheckPassword(u, *s.CurrentPassword); err != nil { return serializer.NewError(serializer.CodeIncorrectPassword, "Incorrect password", err)