api_metadata.go 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. package httpd
  2. import (
  3. "fmt"
  4. "net/http"
  5. "sync"
  6. "time"
  7. "github.com/go-chi/render"
  8. "github.com/drakkan/sftpgo/v2/dataprovider"
  9. "github.com/drakkan/sftpgo/v2/logger"
  10. "github.com/drakkan/sftpgo/v2/util"
  11. )
  12. var (
  13. activeMetadataChecks metadataChecks
  14. )
  15. type metadataCheck struct {
  16. // Username to which the metadata check refers
  17. Username string `json:"username"`
  18. // check start time as unix timestamp in milliseconds
  19. StartTime int64 `json:"start_time"`
  20. }
  21. // metadataChecks holds the active metadata checks
  22. type metadataChecks struct {
  23. sync.RWMutex
  24. checks []metadataCheck
  25. }
  26. func (c *metadataChecks) get() []metadataCheck {
  27. c.RLock()
  28. defer c.RUnlock()
  29. checks := make([]metadataCheck, len(c.checks))
  30. copy(checks, c.checks)
  31. return checks
  32. }
  33. func (c *metadataChecks) add(username string) bool {
  34. c.Lock()
  35. defer c.Unlock()
  36. for idx := range c.checks {
  37. if c.checks[idx].Username == username {
  38. return false
  39. }
  40. }
  41. c.checks = append(c.checks, metadataCheck{
  42. Username: username,
  43. StartTime: util.GetTimeAsMsSinceEpoch(time.Now()),
  44. })
  45. return true
  46. }
  47. func (c *metadataChecks) remove(username string) bool {
  48. c.Lock()
  49. defer c.Unlock()
  50. for idx := range c.checks {
  51. if c.checks[idx].Username == username {
  52. lastIdx := len(c.checks) - 1
  53. c.checks[idx] = c.checks[lastIdx]
  54. c.checks = c.checks[:lastIdx]
  55. return true
  56. }
  57. }
  58. return false
  59. }
  60. func getMetadataChecks(w http.ResponseWriter, r *http.Request) {
  61. r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
  62. render.JSON(w, r, activeMetadataChecks.get())
  63. }
  64. func startMetadataCheck(w http.ResponseWriter, r *http.Request) {
  65. r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
  66. user, err := dataprovider.UserExists(getURLParam(r, "username"))
  67. if err != nil {
  68. sendAPIResponse(w, r, err, "", getRespStatus(err))
  69. return
  70. }
  71. if !activeMetadataChecks.add(user.Username) {
  72. sendAPIResponse(w, r, err, fmt.Sprintf("Another check is already in progress for user %#v", user.Username),
  73. http.StatusConflict)
  74. return
  75. }
  76. go doMetadataCheck(user) //nolint:errcheck
  77. sendAPIResponse(w, r, err, "Check started", http.StatusAccepted)
  78. }
  79. func doMetadataCheck(user dataprovider.User) error {
  80. defer activeMetadataChecks.remove(user.Username)
  81. err := user.CheckMetadataConsistency()
  82. if err != nil {
  83. logger.Warn(logSender, "", "error checking metadata for user %#v: %v", user.Username, err)
  84. return err
  85. }
  86. logger.Debug(logSender, "", "metadata check completed for user: %#v", user.Username)
  87. return nil
  88. }