mirror of https://github.com/cloudreve/Cloudreve
feat(profile): options to select why kind of share links to show in user's profile (#2453)
parent
bb3db2e326
commit
b0057fe92f
2
assets
2
assets
|
@ -1 +1 @@
|
||||||
Subproject commit 8b2c8a7bdbde43a1f95ddaa4555e4304215c1e7c
|
Subproject commit eb2cfac37d73e5bd3000eb66a3a0062509efe122
|
|
@ -7,17 +7,20 @@ import (
|
||||||
// UserSetting 用户其他配置
|
// UserSetting 用户其他配置
|
||||||
type (
|
type (
|
||||||
UserSetting struct {
|
UserSetting struct {
|
||||||
ProfileOff bool `json:"profile_off,omitempty"`
|
ProfileOff bool `json:"profile_off,omitempty"`
|
||||||
PreferredTheme string `json:"preferred_theme,omitempty"`
|
PreferredTheme string `json:"preferred_theme,omitempty"`
|
||||||
VersionRetention bool `json:"version_retention,omitempty"`
|
VersionRetention bool `json:"version_retention,omitempty"`
|
||||||
VersionRetentionExt []string `json:"version_retention_ext,omitempty"`
|
VersionRetentionExt []string `json:"version_retention_ext,omitempty"`
|
||||||
VersionRetentionMax int `json:"version_retention_max,omitempty"`
|
VersionRetentionMax int `json:"version_retention_max,omitempty"`
|
||||||
Pined []PinedFile `json:"pined,omitempty"`
|
Pined []PinedFile `json:"pined,omitempty"`
|
||||||
Language string `json:"email_language,omitempty"`
|
Language string `json:"email_language,omitempty"`
|
||||||
DisableViewSync bool `json:"disable_view_sync,omitempty"`
|
DisableViewSync bool `json:"disable_view_sync,omitempty"`
|
||||||
FsViewMap map[string]ExplorerView `json:"fs_view_map,omitempty"`
|
FsViewMap map[string]ExplorerView `json:"fs_view_map,omitempty"`
|
||||||
|
ShareLinksInProfile ShareLinksInProfileLevel `json:"share_links_in_profile,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ShareLinksInProfileLevel string
|
||||||
|
|
||||||
PinedFile struct {
|
PinedFile struct {
|
||||||
Uri string `json:"uri"`
|
Uri string `json:"uri"`
|
||||||
Name string `json:"name,omitempty"`
|
Name string `json:"name,omitempty"`
|
||||||
|
@ -334,3 +337,9 @@ const (
|
||||||
CustomPropsTypeLink = "link"
|
CustomPropsTypeLink = "link"
|
||||||
CustomPropsTypeRating = "rating"
|
CustomPropsTypeRating = "rating"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ProfilePublicShareOnly = ShareLinksInProfileLevel("")
|
||||||
|
ProfileAllShare = ShareLinksInProfileLevel("all_share")
|
||||||
|
ProfileHideShare = ShareLinksInProfileLevel("hide_share")
|
||||||
|
)
|
||||||
|
|
|
@ -271,19 +271,20 @@ type Entity struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Share struct {
|
type Share struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Name string `json:"name,omitempty"`
|
Name string `json:"name,omitempty"`
|
||||||
RemainDownloads *int `json:"remain_downloads,omitempty"`
|
RemainDownloads *int `json:"remain_downloads,omitempty"`
|
||||||
Visited int `json:"visited"`
|
Visited int `json:"visited"`
|
||||||
Downloaded int `json:"downloaded,omitempty"`
|
Downloaded int `json:"downloaded,omitempty"`
|
||||||
Expires *time.Time `json:"expires,omitempty"`
|
Expires *time.Time `json:"expires,omitempty"`
|
||||||
Unlocked bool `json:"unlocked"`
|
Unlocked bool `json:"unlocked"`
|
||||||
SourceType *types.FileType `json:"source_type,omitempty"`
|
PasswordProtected bool `json:"password_protected,omitempty"`
|
||||||
Owner user.User `json:"owner"`
|
SourceType *types.FileType `json:"source_type,omitempty"`
|
||||||
CreatedAt time.Time `json:"created_at,omitempty"`
|
Owner user.User `json:"owner"`
|
||||||
Expired bool `json:"expired"`
|
CreatedAt time.Time `json:"created_at,omitempty"`
|
||||||
Url string `json:"url"`
|
Expired bool `json:"expired"`
|
||||||
ShowReadMe bool `json:"show_readme,omitempty"`
|
Url string `json:"url"`
|
||||||
|
ShowReadMe bool `json:"show_readme,omitempty"`
|
||||||
|
|
||||||
// Only viewable by owner
|
// Only viewable by owner
|
||||||
IsPrivate bool `json:"is_private,omitempty"`
|
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
|
redactLevel = user.RedactLevelUser
|
||||||
}
|
}
|
||||||
res := Share{
|
res := Share{
|
||||||
Name: name,
|
Name: name,
|
||||||
ID: hashid.EncodeShareID(hasher, s.ID),
|
ID: hashid.EncodeShareID(hasher, s.ID),
|
||||||
Unlocked: unlocked,
|
Unlocked: unlocked,
|
||||||
Owner: user.BuildUserRedacted(owner, redactLevel, hasher),
|
Owner: user.BuildUserRedacted(owner, redactLevel, hasher),
|
||||||
Expired: inventory.IsShareExpired(s) != nil || expired,
|
Expired: inventory.IsShareExpired(s) != nil || expired,
|
||||||
Url: BuildShareLink(s, hasher, base),
|
Url: BuildShareLink(s, hasher, base, unlocked),
|
||||||
CreatedAt: s.CreatedAt,
|
CreatedAt: s.CreatedAt,
|
||||||
Visited: s.Views,
|
Visited: s.Views,
|
||||||
SourceType: util.ToPtr(t),
|
SourceType: util.ToPtr(t),
|
||||||
|
PasswordProtected: s.Password != "",
|
||||||
}
|
}
|
||||||
|
|
||||||
if unlocked {
|
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)
|
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 {
|
func BuildStoragePolicy(sp *ent.StoragePolicy, hasher hashid.Encoder) *StoragePolicy {
|
||||||
|
|
|
@ -66,7 +66,7 @@ func (service *ShareCreateService) Upsert(c *gin.Context, existed int) (string,
|
||||||
}
|
}
|
||||||
|
|
||||||
base := dep.SettingProvider().SiteURL(c)
|
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 {
|
func DeleteShare(c *gin.Context, shareId int) error {
|
||||||
|
|
|
@ -137,6 +137,16 @@ func (s *ListShareService) ListInUserProfile(c *gin.Context, uid int) (*ListShar
|
||||||
hasher := dep.HashIDEncoder()
|
hasher := dep.HashIDEncoder()
|
||||||
shareClient := dep.ShareClient()
|
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{
|
args := &inventory.ListShareArgs{
|
||||||
PaginationArgs: &inventory.PaginationArgs{
|
PaginationArgs: &inventory.PaginationArgs{
|
||||||
UseCursorPagination: true,
|
UseCursorPagination: true,
|
||||||
|
@ -146,7 +156,7 @@ func (s *ListShareService) ListInUserProfile(c *gin.Context, uid int) (*ListShar
|
||||||
OrderBy: s.OrderBy,
|
OrderBy: s.OrderBy,
|
||||||
},
|
},
|
||||||
UserID: uid,
|
UserID: uid,
|
||||||
PublicOnly: true,
|
PublicOnly: publicOnly,
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := context.WithValue(c, inventory.LoadShareUser{}, true)
|
ctx := context.WithValue(c, inventory.LoadShareUser{}, true)
|
||||||
|
|
|
@ -29,6 +29,7 @@ type UserSettings struct {
|
||||||
TwoFAEnabled bool `json:"two_fa_enabled"`
|
TwoFAEnabled bool `json:"two_fa_enabled"`
|
||||||
Passkeys []Passkey `json:"passkeys,omitempty"`
|
Passkeys []Passkey `json:"passkeys,omitempty"`
|
||||||
DisableViewSync bool `json:"disable_view_sync"`
|
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 {
|
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 {
|
Passkeys: lo.Map(passkeys, func(item *ent.Passkey, index int) Passkey {
|
||||||
return BuildPasskey(item)
|
return BuildPasskey(item)
|
||||||
}),
|
}),
|
||||||
DisableViewSync: u.Settings.DisableViewSync,
|
DisableViewSync: u.Settings.DisableViewSync,
|
||||||
|
ShareLinksInProfile: string(u.Settings.ShareLinksInProfile),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,18 +99,19 @@ type BuiltinLoginResponse struct {
|
||||||
|
|
||||||
// User 用户序列化器
|
// User 用户序列化器
|
||||||
type User struct {
|
type User struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Email string `json:"email,omitempty"`
|
Email string `json:"email,omitempty"`
|
||||||
Nickname string `json:"nickname"`
|
Nickname string `json:"nickname"`
|
||||||
Status user.Status `json:"status,omitempty"`
|
Status user.Status `json:"status,omitempty"`
|
||||||
Avatar string `json:"avatar,omitempty"`
|
Avatar string `json:"avatar,omitempty"`
|
||||||
CreatedAt time.Time `json:"created_at"`
|
CreatedAt time.Time `json:"created_at"`
|
||||||
PreferredTheme string `json:"preferred_theme,omitempty"`
|
PreferredTheme string `json:"preferred_theme,omitempty"`
|
||||||
Anonymous bool `json:"anonymous,omitempty"`
|
Anonymous bool `json:"anonymous,omitempty"`
|
||||||
Group *Group `json:"group,omitempty"`
|
Group *Group `json:"group,omitempty"`
|
||||||
Pined []types.PinedFile `json:"pined,omitempty"`
|
Pined []types.PinedFile `json:"pined,omitempty"`
|
||||||
Language string `json:"language,omitempty"`
|
Language string `json:"language,omitempty"`
|
||||||
DisableViewSync bool `json:"disable_view_sync,omitempty"`
|
DisableViewSync bool `json:"disable_view_sync,omitempty"`
|
||||||
|
ShareLinksInProfile types.ShareLinksInProfileLevel `json:"share_links_in_profile,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Group struct {
|
type Group struct {
|
||||||
|
@ -153,18 +156,19 @@ func BuildWebAuthnList(credentials []webauthn.Credential) []WebAuthnCredentials
|
||||||
// BuildUser 序列化用户
|
// BuildUser 序列化用户
|
||||||
func BuildUser(user *ent.User, idEncoder hashid.Encoder) User {
|
func BuildUser(user *ent.User, idEncoder hashid.Encoder) User {
|
||||||
return User{
|
return User{
|
||||||
ID: hashid.EncodeUserID(idEncoder, user.ID),
|
ID: hashid.EncodeUserID(idEncoder, user.ID),
|
||||||
Email: user.Email,
|
Email: user.Email,
|
||||||
Nickname: user.Nick,
|
Nickname: user.Nick,
|
||||||
Status: user.Status,
|
Status: user.Status,
|
||||||
Avatar: user.Avatar,
|
Avatar: user.Avatar,
|
||||||
CreatedAt: user.CreatedAt,
|
CreatedAt: user.CreatedAt,
|
||||||
PreferredTheme: user.Settings.PreferredTheme,
|
PreferredTheme: user.Settings.PreferredTheme,
|
||||||
Anonymous: user.ID == 0,
|
Anonymous: user.ID == 0,
|
||||||
Group: BuildGroup(user.Edges.Group, idEncoder),
|
Group: BuildGroup(user.Edges.Group, idEncoder),
|
||||||
Pined: user.Settings.Pined,
|
Pined: user.Settings.Pined,
|
||||||
Language: user.Settings.Language,
|
Language: user.Settings.Language,
|
||||||
DisableViewSync: user.Settings.DisableViewSync,
|
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)
|
userRaw := BuildUser(u, idEncoder)
|
||||||
|
|
||||||
user := User{
|
user := User{
|
||||||
ID: userRaw.ID,
|
ID: userRaw.ID,
|
||||||
Nickname: userRaw.Nickname,
|
Nickname: userRaw.Nickname,
|
||||||
Avatar: userRaw.Avatar,
|
Avatar: userRaw.Avatar,
|
||||||
CreatedAt: userRaw.CreatedAt,
|
CreatedAt: userRaw.CreatedAt,
|
||||||
|
ShareLinksInProfile: userRaw.ShareLinksInProfile,
|
||||||
}
|
}
|
||||||
|
|
||||||
if userRaw.Group != nil {
|
if userRaw.Group != nil {
|
||||||
|
|
|
@ -14,6 +14,7 @@ import (
|
||||||
"github.com/cloudreve/Cloudreve/v4/application/dependency"
|
"github.com/cloudreve/Cloudreve/v4/application/dependency"
|
||||||
"github.com/cloudreve/Cloudreve/v4/ent"
|
"github.com/cloudreve/Cloudreve/v4/ent"
|
||||||
"github.com/cloudreve/Cloudreve/v4/inventory"
|
"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/hashid"
|
||||||
"github.com/cloudreve/Cloudreve/v4/pkg/request"
|
"github.com/cloudreve/Cloudreve/v4/pkg/request"
|
||||||
"github.com/cloudreve/Cloudreve/v4/pkg/serializer"
|
"github.com/cloudreve/Cloudreve/v4/pkg/serializer"
|
||||||
|
@ -221,6 +222,7 @@ type (
|
||||||
TwoFAEnabled *bool `json:"two_fa_enabled" binding:"omitempty"`
|
TwoFAEnabled *bool `json:"two_fa_enabled" binding:"omitempty"`
|
||||||
TwoFACode *string `json:"two_fa_code" binding:"omitempty"`
|
TwoFACode *string `json:"two_fa_code" binding:"omitempty"`
|
||||||
DisableViewSync *bool `json:"disable_view_sync" binding:"omitempty"`
|
DisableViewSync *bool `json:"disable_view_sync" binding:"omitempty"`
|
||||||
|
ShareLinksInProfile *string `json:"share_links_in_profile" binding:"omitempty"`
|
||||||
}
|
}
|
||||||
PatchUserSettingParamsCtx struct{}
|
PatchUserSettingParamsCtx struct{}
|
||||||
)
|
)
|
||||||
|
@ -267,6 +269,11 @@ func (s *PatchUserSetting) Patch(c *gin.Context) error {
|
||||||
saveSetting = true
|
saveSetting = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if s.ShareLinksInProfile != nil {
|
||||||
|
u.Settings.ShareLinksInProfile = types.ShareLinksInProfileLevel(*s.ShareLinksInProfile)
|
||||||
|
saveSetting = true
|
||||||
|
}
|
||||||
|
|
||||||
if s.CurrentPassword != nil && s.NewPassword != nil {
|
if s.CurrentPassword != nil && s.NewPassword != nil {
|
||||||
if err := inventory.CheckPassword(u, *s.CurrentPassword); err != nil {
|
if err := inventory.CheckPassword(u, *s.CurrentPassword); err != nil {
|
||||||
return serializer.NewError(serializer.CodeIncorrectPassword, "Incorrect password", err)
|
return serializer.NewError(serializer.CodeIncorrectPassword, "Incorrect password", err)
|
||||||
|
|
Loading…
Reference in New Issue