diff --git a/application/dependency/options.go b/application/dependency/options.go index 2f052527..81c22074 100644 --- a/application/dependency/options.go +++ b/application/dependency/options.go @@ -160,6 +160,13 @@ func WithShareClient(s inventory.ShareClient) Option { }) } +// WithActivityClient Set the default activity client +func WithActivityClient(s inventory.ActivityClient) Option { + return optionFunc(func(o *dependency) { + o.activityClient = s + }) +} + // WithSearchIndexer Set the default search indexer func WithSearchIndexer(s searcher.SearchIndexer) Option { return optionFunc(func(o *dependency) { diff --git a/middleware/auth.go b/middleware/auth.go index 135f115d..9f08295b 100644 --- a/middleware/auth.go +++ b/middleware/auth.go @@ -9,6 +9,7 @@ import ( "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/activity" "github.com/cloudreve/Cloudreve/v4/pkg/filemanager/driver/oss" "github.com/cloudreve/Cloudreve/v4/pkg/filemanager/fs" "github.com/cloudreve/Cloudreve/v4/pkg/filemanager/manager" @@ -162,6 +163,8 @@ func WebDAVAuth() gin.HandlerFunc { } } + activity.Record(c, dep.SettingProvider(), dep.ActivityClient(), types.EventWebdavLoginFailed, + activity.Extra(map[string]any{"username": username})) l.Debug("WebDAVAuth: failed to get user %q with provided credential: %s", username, err) c.Status(http.StatusUnauthorized) c.Abort() diff --git a/middleware/davbearer_test.go b/middleware/davbearer_test.go index c3619193..5fb022d2 100644 --- a/middleware/davbearer_test.go +++ b/middleware/davbearer_test.go @@ -13,11 +13,19 @@ import ( "github.com/cloudreve/Cloudreve/v4/pkg/auth" "github.com/cloudreve/Cloudreve/v4/pkg/boolset" "github.com/cloudreve/Cloudreve/v4/pkg/cache" + "github.com/cloudreve/Cloudreve/v4/pkg/conf" "github.com/cloudreve/Cloudreve/v4/pkg/logging" + "github.com/cloudreve/Cloudreve/v4/pkg/setting" "github.com/cloudreve/Cloudreve/v4/pkg/util" "github.com/gin-gonic/gin" ) +type stubSettingProvider struct { + setting.Provider +} + +func (stubSettingProvider) AuditLogEnabled(context.Context, int) bool { return true } + type stubTokenAuth struct { uid int scopes []string @@ -68,6 +76,8 @@ func newDavAuthDep(t *testing.T, groupPerms *boolset.BooleanSet, scopes []string dependency.WithLogger(logging.NewConsoleLogger(logging.LevelDebug)), dependency.WithUserClient(inventory.NewUserClient(client)), dependency.WithTokenAuth(stubTokenAuth{uid: u.ID, scopes: scopes}), + dependency.WithSettingProvider(stubSettingProvider{}), + dependency.WithActivityClient(inventory.NewActivityClient(client, conf.SQLiteDB)), ) } diff --git a/pkg/activity/activity.go b/pkg/activity/activity.go index b6f34ee2..f4fc94c8 100644 --- a/pkg/activity/activity.go +++ b/pkg/activity/activity.go @@ -20,6 +20,12 @@ func File(id int) Opt { return func(p *inventory.RecordActivityParams) { p.FileID = id } } +// Actor overrides the actor ID when ctx carries no user (e.g. token +// refresh, activation) or the actor differs from the ctx user. +func Actor(id int) Opt { + return func(p *inventory.RecordActivityParams) { p.ActorID = id } +} + // Share attaches the subject share ID. func Share(id int) Opt { return func(p *inventory.RecordActivityParams) { p.ShareID = id } diff --git a/pkg/filemanager/fs/dbfs/manage.go b/pkg/filemanager/fs/dbfs/manage.go index 8482d39c..ce322e9b 100644 --- a/pkg/filemanager/fs/dbfs/manage.go +++ b/pkg/filemanager/fs/dbfs/manage.go @@ -708,8 +708,11 @@ func (f *DBFS) MoveOrCopy(ctx context.Context, path []*fs.URI, dst *fs.URI, isCo for _, target := range targets { if isCopy { - f.emitFileCreated(ctx, newFile(destination, copiedNewTargetsMap[target.ID()])) - f.record(ctx, types.EventCopyTo, activity.File(target.ID()), activity.Extra(map[string]any{"dst": destination.Uri(false).String()})) + copied := copiedNewTargetsMap[target.ID()] + f.emitFileCreated(ctx, newFile(destination, copied)) + dst := destination.Uri(false).String() + f.record(ctx, types.EventCopyTo, activity.File(copied.ID), activity.Extra(map[string]any{"dst": dst})) + f.record(ctx, types.EventCopyFrom, activity.File(target.ID()), activity.Extra(map[string]any{"dst": dst})) } else { f.emitFileMoved(ctx, target, destination) f.record(ctx, types.EventMoveTo, activity.File(target.ID()), activity.Extra(map[string]any{"dst": destination.Uri(false).String()})) diff --git a/pkg/filemanager/fs/dbfs/upload.go b/pkg/filemanager/fs/dbfs/upload.go index bda8bfe3..75385ef8 100644 --- a/pkg/filemanager/fs/dbfs/upload.go +++ b/pkg/filemanager/fs/dbfs/upload.go @@ -392,7 +392,11 @@ func (f *DBFS) CompleteUpload(ctx context.Context, session *fs.UploadSession) (f } f.emitFileModified(ctx, filePrivate) - f.record(ctx, types.EventEntityUploaded, activity.File(filePrivate.Model.ID), activity.Extra(map[string]any{"uri": session.Props.Uri.String(), "size": session.Props.Size})) + uploadEvent := types.EventEntityUploaded + if entityType == types.EntityTypeLivePhoto { + uploadEvent = types.EventLivePhotoUploaded + } + f.record(ctx, uploadEvent, activity.File(filePrivate.Model.ID), activity.Extra(map[string]any{"uri": session.Props.Uri.String(), "size": session.Props.Size})) file, err = f.Get(ctx, session.Props.Uri, WithFileEntities(), WithNotRoot()) if err != nil { diff --git a/pkg/filemanager/manager/thumbnail.go b/pkg/filemanager/manager/thumbnail.go index bb30d0bb..eb6c1c2a 100644 --- a/pkg/filemanager/manager/thumbnail.go +++ b/pkg/filemanager/manager/thumbnail.go @@ -12,6 +12,7 @@ import ( "github.com/cloudreve/Cloudreve/v4/ent" "github.com/cloudreve/Cloudreve/v4/ent/task" "github.com/cloudreve/Cloudreve/v4/inventory/types" + "github.com/cloudreve/Cloudreve/v4/pkg/activity" "github.com/cloudreve/Cloudreve/v4/pkg/filemanager/driver/local" "github.com/cloudreve/Cloudreve/v4/pkg/filemanager/fs" "github.com/cloudreve/Cloudreve/v4/pkg/filemanager/fs/dbfs" @@ -220,6 +221,8 @@ func (m *manager) generateThumb(ctx context.Context, uri *fs.URI, ext string, es return nil, fmt.Errorf("failed to find thumb entity") } + activity.Record(ctx, m.settings, m.dep.ActivityClient(), types.EventThumbGenerated, + activity.File(file.ID()), activity.Extra(map[string]any{"uri": uri.String()})) } if m.settings.ThumbGCAfterGen(ctx) { diff --git a/pkg/filemanager/workflows/import.go b/pkg/filemanager/workflows/import.go index 6a7a8817..39e9183c 100644 --- a/pkg/filemanager/workflows/import.go +++ b/pkg/filemanager/workflows/import.go @@ -13,6 +13,7 @@ import ( "github.com/cloudreve/Cloudreve/v4/ent/task" "github.com/cloudreve/Cloudreve/v4/inventory" "github.com/cloudreve/Cloudreve/v4/inventory/types" + "github.com/cloudreve/Cloudreve/v4/pkg/activity" "github.com/cloudreve/Cloudreve/v4/pkg/filemanager/fs" "github.com/cloudreve/Cloudreve/v4/pkg/filemanager/manager" "github.com/cloudreve/Cloudreve/v4/pkg/hashid" @@ -203,6 +204,9 @@ func (m *ImportTask) processBatch(ctx context.Context, dep dependency.Dep, user } m.l.Error("Failed to import file %s: %s, skipping", physicalFile.RelativePath, err) failed++ + } else { + activity.Record(ctx, dep.SettingProvider(), dep.ActivityClient(), types.EventFileImported, + activity.Extra(map[string]any{"uri": dst.Join(physicalFile.RelativePath).String(), "src": physicalFile.Source})) } } } diff --git a/service/admin/user.go b/service/admin/user.go index 319eb2d6..7facdea7 100644 --- a/service/admin/user.go +++ b/service/admin/user.go @@ -10,6 +10,7 @@ import ( "github.com/cloudreve/Cloudreve/v4/ent/user" "github.com/cloudreve/Cloudreve/v4/inventory" "github.com/cloudreve/Cloudreve/v4/inventory/types" + "github.com/cloudreve/Cloudreve/v4/pkg/activity" "github.com/cloudreve/Cloudreve/v4/pkg/boolset" "github.com/cloudreve/Cloudreve/v4/pkg/filemanager/manager" "github.com/cloudreve/Cloudreve/v4/pkg/hashid" @@ -265,6 +266,17 @@ func (s *UpsertUserService) Update(c *gin.Context) (*GetUserResponse, error) { return nil, serializer.NewError(serializer.CodeDBError, "Failed to update user", err) } + subject := activity.Extra(map[string]any{"user_id": newUser.ID}) + activity.Record(c, dep.SettingProvider(), dep.ActivityClient(), types.EventUserChanged, subject) + if existing.GroupUsers != newUser.GroupUsers { + activity.Record(c, dep.SettingProvider(), dep.ActivityClient(), types.EventGroupChanged, + subject, activity.Extra(map[string]any{"from": existing.GroupUsers, "to": newUser.GroupUsers})) + } + if existing.Storage != newUser.Storage { + activity.Record(c, dep.SettingProvider(), dep.ActivityClient(), types.EventStorageAdded, + subject, activity.Extra(map[string]any{"from": existing.Storage, "to": newUser.Storage})) + } + service := &SingleUserService{ID: newUser.ID} return service.Get(c) } diff --git a/service/explorer/entity.go b/service/explorer/entity.go index 2b0a2c6c..afd52d59 100644 --- a/service/explorer/entity.go +++ b/service/explorer/entity.go @@ -4,6 +4,8 @@ import ( "fmt" "github.com/cloudreve/Cloudreve/v4/application/dependency" "github.com/cloudreve/Cloudreve/v4/inventory" + "github.com/cloudreve/Cloudreve/v4/inventory/types" + "github.com/cloudreve/Cloudreve/v4/pkg/activity" "github.com/cloudreve/Cloudreve/v4/pkg/cluster/routes" "github.com/cloudreve/Cloudreve/v4/pkg/filemanager/fs" "github.com/cloudreve/Cloudreve/v4/pkg/filemanager/manager" @@ -82,6 +84,12 @@ func (s *SetCurrentVersionService) Set(c *gin.Context) error { return fmt.Errorf("failed to set current version: %w", err) } + opts := []activity.Opt{activity.Extra(map[string]any{"version": versionId})} + if target, err := m.Get(c, uri); err == nil { + opts = append(opts, activity.File(target.ID())) + } + activity.Record(c, dep.SettingProvider(), dep.ActivityClient(), types.EventSetCurrentVersion, opts...) + return nil } @@ -114,5 +122,11 @@ func (s *DeleteVersionService) Delete(c *gin.Context) error { return fmt.Errorf("failed to delete version: %w", err) } + opts := []activity.Opt{activity.Extra(map[string]any{"version": versionId})} + if target, err := m.Get(c, uri); err == nil { + opts = append(opts, activity.File(target.ID())) + } + activity.Record(c, dep.SettingProvider(), dep.ActivityClient(), types.EventDeleteVersion, opts...) + return nil } diff --git a/service/explorer/file.go b/service/explorer/file.go index bf0faeaf..479c2f1e 100644 --- a/service/explorer/file.go +++ b/service/explorer/file.go @@ -784,6 +784,12 @@ func (s *PatchViewService) Patch(c *gin.Context) error { return err } + opts := []activity.Opt{activity.Extra(map[string]any{"view": s.View})} + if target, err := m.Get(c, uri); err == nil { + opts = append(opts, activity.File(target.ID())) + } + activity.Record(c, dep.SettingProvider(), dep.ActivityClient(), types.EventUpdateView, opts...) + return nil } diff --git a/service/explorer/metadata.go b/service/explorer/metadata.go index e6ac3476..70bfba36 100644 --- a/service/explorer/metadata.go +++ b/service/explorer/metadata.go @@ -3,6 +3,8 @@ package explorer import ( "github.com/cloudreve/Cloudreve/v4/application/dependency" "github.com/cloudreve/Cloudreve/v4/inventory" + "github.com/cloudreve/Cloudreve/v4/inventory/types" + "github.com/cloudreve/Cloudreve/v4/pkg/activity" "github.com/cloudreve/Cloudreve/v4/pkg/filemanager/fs" "github.com/cloudreve/Cloudreve/v4/pkg/filemanager/manager" "github.com/cloudreve/Cloudreve/v4/pkg/serializer" @@ -33,5 +35,21 @@ func (s *PatchMetadataService) Patch(c *gin.Context) error { return serializer.NewError(serializer.CodeParamErr, "unknown uri", err) } - return m.PatchMedata(c, uris, s.Patches...) + if err := m.PatchMedata(c, uris, s.Patches...); err != nil { + return err + } + + keys := make([]string, 0, len(s.Patches)) + for _, p := range s.Patches { + keys = append(keys, p.Key) + } + for _, uri := range uris { + opts := []activity.Opt{activity.Extra(map[string]any{"keys": keys})} + if target, err := m.Get(c, uri); err == nil { + opts = append(opts, activity.File(target.ID())) + } + activity.Record(c, dep.SettingProvider(), dep.ActivityClient(), types.EventUpdateMetadata, opts...) + } + + return nil } diff --git a/service/oauth/oauth.go b/service/oauth/oauth.go index 24023314..e99a7d9a 100644 --- a/service/oauth/oauth.go +++ b/service/oauth/oauth.go @@ -10,6 +10,7 @@ import ( "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/activity" "github.com/cloudreve/Cloudreve/v4/pkg/auth" "github.com/cloudreve/Cloudreve/v4/pkg/cluster/routes" "github.com/cloudreve/Cloudreve/v4/pkg/hashid" @@ -102,6 +103,8 @@ func (s *GrantService) Issue(c *gin.Context) (*GrantResponse, error) { if err := oAuthClient.UpsertGrant(c, user.ID, app.ID, requestedScopes); err != nil { return nil, serializer.NewError(serializer.CodeDBError, "Failed to create grant", err) } + activity.Record(c, dep.SettingProvider(), dep.ActivityClient(), types.EventOAuthGrantCreate, + activity.Extra(map[string]any{"client_id": s.ClientID, "scopes": requestedScopes})) // 4. Generate code and save required state into KV for future token exchange request. code := util.RandStringRunesCrypto(128) @@ -221,6 +224,8 @@ func (s *ExchangeTokenService) Exchange(c *gin.Context) (*TokenResponse, error) if err := oAuthClient.UpdateGrantLastUsedAt(c, user.ID, app.ID); err != nil { dep.Logger().Warning("Failed to update grant last used at: %s", err) } + activity.Record(c, dep.SettingProvider(), dep.ActivityClient(), types.EventOAuthTokenExchange, + activity.Actor(user.ID), activity.Extra(map[string]any{"client_id": s.ClientID})) // 10. @@ -318,6 +323,8 @@ func (s *DeleteOAuthGrantService) Delete(c *gin.Context) error { if !deleted { return serializer.NewError(serializer.CodeNotFound, "OAuth grant not found", nil) } + activity.Record(c, dep.SettingProvider(), dep.ActivityClient(), types.EventOAuthGrantRevoke, + activity.Extra(map[string]any{"app_id": s.AppID})) return nil } diff --git a/service/setting/webdav.go b/service/setting/webdav.go index 9d194996..b7b0ddee 100644 --- a/service/setting/webdav.go +++ b/service/setting/webdav.go @@ -6,6 +6,7 @@ import ( "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/activity" "github.com/cloudreve/Cloudreve/v4/pkg/boolset" "github.com/cloudreve/Cloudreve/v4/pkg/filemanager/fs" "github.com/cloudreve/Cloudreve/v4/pkg/hashid" @@ -114,6 +115,8 @@ func (service *CreateDavAccountService) Create(c *gin.Context) (*DavAccount, err if err != nil { return nil, serializer.NewError(serializer.CodeDBError, "Failed to create dav account", err) } + activity.Record(c, dep.SettingProvider(), dep.ActivityClient(), types.EventWebdavAccountCreate, + activity.Extra(map[string]any{"name": service.Name, "uri": service.Uri})) accountRes := BuildDavAccount(account, dep.HashIDEncoder()) return &accountRes, nil @@ -146,6 +149,8 @@ func (service *CreateDavAccountService) Update(c *gin.Context) (*DavAccount, err if err != nil { return nil, serializer.NewError(serializer.CodeDBError, "Failed to update dav account", err) } + activity.Record(c, dep.SettingProvider(), dep.ActivityClient(), types.EventWebdavAccountUpdate, + activity.Extra(map[string]any{"name": service.Name, "uri": service.Uri})) accountRes := BuildDavAccount(account, dep.HashIDEncoder()) return &accountRes, nil @@ -197,6 +202,8 @@ func DeleteDavAccount(c *gin.Context) error { if err := davAccountClient.Delete(c, accountId); err != nil { return serializer.NewError(serializer.CodeDBError, "Failed to delete dav account", err) } + activity.Record(c, dep.SettingProvider(), dep.ActivityClient(), types.EventWebdavAccountDelete, + activity.Extra(map[string]any{"account_id": accountId})) return nil } diff --git a/service/share/visit.go b/service/share/visit.go index 22f96fc8..70a48696 100644 --- a/service/share/visit.go +++ b/service/share/visit.go @@ -8,6 +8,7 @@ import ( "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/activity" "github.com/cloudreve/Cloudreve/v4/pkg/cluster/routes" "github.com/cloudreve/Cloudreve/v4/pkg/filemanager/fs" "github.com/cloudreve/Cloudreve/v4/pkg/filemanager/manager" @@ -78,6 +79,8 @@ func (s *ShareInfoService) Get(c *gin.Context) (*explorer.Share, error) { if s.CountViews { _ = shareClient.Viewed(c, share) + activity.Record(c, dep.SettingProvider(), dep.ActivityClient(), types.EventShareLinkViewed, + activity.Share(share.ID), activity.File(share.Edges.File.ID)) } unlocked := true diff --git a/service/user/email_change.go b/service/user/email_change.go index e0c3817c..2b200e1c 100644 --- a/service/user/email_change.go +++ b/service/user/email_change.go @@ -7,6 +7,7 @@ import ( "github.com/cloudreve/Cloudreve/v4/application/dependency" "github.com/cloudreve/Cloudreve/v4/inventory" "github.com/cloudreve/Cloudreve/v4/inventory/types" + "github.com/cloudreve/Cloudreve/v4/pkg/activity" "github.com/cloudreve/Cloudreve/v4/pkg/auth" "github.com/cloudreve/Cloudreve/v4/pkg/cluster/routes" "github.com/cloudreve/Cloudreve/v4/pkg/email" @@ -82,6 +83,8 @@ func (s *RequestEmailChangeService) Request(c *gin.Context) error { if err := dep.EmailClient(c).Send(c, s.NewEmail, title, body); err != nil { return serializer.NewError(serializer.CodeFailedSendEmail, "Failed to send confirmation email", err) } + activity.Record(c, dep.SettingProvider(), dep.ActivityClient(), types.EventEmailSent, + activity.Extra(map[string]any{"kind": "email_change"})) return nil } diff --git a/service/user/login.go b/service/user/login.go index dc62ad12..debd4a5f 100644 --- a/service/user/login.go +++ b/service/user/login.go @@ -124,6 +124,8 @@ func (service *UserResetEmailService) Reset(c *gin.Context) error { if err := dep.EmailClient(c).Send(c, u.Email, title, body); err != nil { return serializer.NewError(serializer.CodeFailedSendEmail, "Failed to send activation email", err) } + activity.Record(c, dep.SettingProvider(), dep.ActivityClient(), types.EventEmailSent, + activity.Actor(u.ID), activity.Extra(map[string]any{"kind": "password_reset"})) return nil } @@ -206,11 +208,20 @@ type RefreshTokenService struct { func (s *RefreshTokenService) Refresh(c *gin.Context) (*auth.Token, error) { dep := dependency.FromContext(c) + claims, _ := dep.TokenAuth().Claims(c, s.RefreshToken) token, err := dep.TokenAuth().Refresh(c, s.RefreshToken) if err != nil { return nil, serializer.NewError(serializer.CodeCredentialInvalid, "Failed to issue token pair", err) } + opts := []activity.Opt{} + if claims != nil { + if uid, err := dep.HashIDEncoder().Decode(claims.Subject, hashid.UserID); err == nil { + opts = append(opts, activity.Actor(uid)) + } + } + activity.Record(c, dep.SettingProvider(), dep.ActivityClient(), types.EventUserTokenRefresh, opts...) + return token, nil } diff --git a/service/user/passkey.go b/service/user/passkey.go index 4fa928db..9d3ab16f 100644 --- a/service/user/passkey.go +++ b/service/user/passkey.go @@ -9,6 +9,8 @@ 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/activity" "github.com/cloudreve/Cloudreve/v4/pkg/hashid" "github.com/cloudreve/Cloudreve/v4/pkg/serializer" "github.com/cloudreve/Cloudreve/v4/pkg/util" @@ -246,6 +248,8 @@ func (s *FinishPasskeyRegisterService) FinishPasskeyRegister(c *gin.Context) (*P if err != nil { return nil, serializer.NewError(serializer.CodeDBError, "Failed to add passkey", err) } + activity.Record(c, dep.SettingProvider(), dep.ActivityClient(), types.EventAddPasskey, + activity.Extra(map[string]any{"name": name})) res := BuildPasskey(passkey) return &res, nil @@ -283,6 +287,8 @@ func (s *DeletePasskeyService) DeletePasskey(c *gin.Context) error { if err := userClient.RemovePasskey(c, u.ID, s.ID); err != nil { return serializer.NewError(serializer.CodeDBError, "Failed to delete passkey", err) } + activity.Record(c, dep.SettingProvider(), dep.ActivityClient(), types.EventRemovePasskey, + activity.Extra(map[string]any{"credential_id": s.ID})) return nil } diff --git a/service/user/register.go b/service/user/register.go index 99f37ac7..563e1af6 100644 --- a/service/user/register.go +++ b/service/user/register.go @@ -145,6 +145,8 @@ func sendActivationEmail(ctx context.Context, dep dependency.Dep, newUser *ent.U if err := dep.EmailClient(ctx).Send(ctx, newUser.Email, title, body); err != nil { return serializer.NewError(serializer.CodeFailedSendEmail, "Failed to send activation email", err) } + activity.Record(ctx, dep.SettingProvider(), dep.ActivityClient(), types.EventEmailSent, + activity.Actor(newUser.ID), activity.Extra(map[string]any{"kind": "activation"})) return nil } @@ -173,5 +175,7 @@ func ActivateUser(c *gin.Context) serializer.Response { } util.WithValue(c, inventory.UserCtx{}, activeUser) + activity.Record(c, dep.SettingProvider(), dep.ActivityClient(), types.EventUserActivated, + activity.Actor(activeUser.ID)) return serializer.Response{Data: BuildUser(activeUser, dep.HashIDEncoder())} } diff --git a/service/user/setting.go b/service/user/setting.go index 591a74cb..08c1632b 100644 --- a/service/user/setting.go +++ b/service/user/setting.go @@ -16,6 +16,7 @@ import ( "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/activity" "github.com/cloudreve/Cloudreve/v4/pkg/auth" "github.com/cloudreve/Cloudreve/v4/pkg/hashid" "github.com/cloudreve/Cloudreve/v4/pkg/request" @@ -177,6 +178,7 @@ func UpdateUserAvatar(c *gin.Context) error { if _, err := dep.UserClient().UpdateAvatar(c, u, GravatarAvatar); err != nil { return serializer.NewError(serializer.CodeDBError, "Failed to update user avatar", err) } + activity.Record(c, dep.SettingProvider(), dep.ActivityClient(), types.EventChangeAvatar) return nil } @@ -218,6 +220,7 @@ func updateAvatarFile(ctx context.Context, u *ent.User, contentType string, file if _, err := dep.UserClient().UpdateAvatar(ctx, u, FileAvatar); err != nil { return serializer.NewError(serializer.CodeDBError, "Failed to update user avatar", err) } + activity.Record(ctx, dep.SettingProvider(), dep.ActivityClient(), types.EventChangeAvatar) return nil } @@ -282,6 +285,7 @@ func (s *PatchUserSetting) Patch(c *gin.Context) error { if _, err := userClient.UpdateNickname(c, u, *s.Nick); err != nil { return serializer.NewError(serializer.CodeDBError, "Failed to update user nick", err) } + activity.Record(c, dep.SettingProvider(), dep.ActivityClient(), types.EventChangeNick) } if s.Language != nil { @@ -382,6 +386,7 @@ func (s *PatchUserSetting) Patch(c *gin.Context) error { if _, err := userClient.UpdatePassword(c, u, *s.NewPassword); err != nil { return serializer.NewError(serializer.CodeDBError, "Failed to update user password", err) } + activity.Record(c, dep.SettingProvider(), dep.ActivityClient(), types.EventChangePassword) } if s.TwoFAEnabled != nil { @@ -403,6 +408,7 @@ func (s *PatchUserSetting) Patch(c *gin.Context) error { if _, err := userClient.UpdateTwoFASecret(c, u, secret.(string)); err != nil { return serializer.NewError(serializer.CodeDBError, "Failed to update user 2FA", err) } + activity.Record(c, dep.SettingProvider(), dep.ActivityClient(), types.EventEnable2FA) } else { if !totp.Validate(*s.TwoFACode, u.TwoFactorSecret) { @@ -412,6 +418,7 @@ func (s *PatchUserSetting) Patch(c *gin.Context) error { if _, err := userClient.UpdateTwoFASecret(c, u, ""); err != nil { return serializer.NewError(serializer.CodeDBError, "Failed to update user 2FA", err) } + activity.Record(c, dep.SettingProvider(), dep.ActivityClient(), types.EventDisable2FA) } }