file.rb 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. # Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
  2. class Store < ApplicationModel
  3. class File < ApplicationModel
  4. include ApplicationLib
  5. after_destroy :destroy_provider
  6. =begin
  7. add new file to store
  8. store_file_id = Store::File.add(binary_data)
  9. do also verify of written data
  10. store_file_id = Store::File.add(binary_data, true)
  11. =end
  12. def self.add(data, verify = true)
  13. sha = checksum(data)
  14. file = Store::File.find_by(sha: sha)
  15. if file.nil?
  16. # load backend based on config
  17. adapter_name = Setting.get('storage_provider') || 'DB'
  18. if !adapter_name
  19. raise __("The setting 'storage_provider' was not configured.")
  20. end
  21. adapter = "Store::Provider::#{adapter_name}".constantize
  22. adapter.add(data, sha)
  23. file = Store::File.create(
  24. provider: adapter_name,
  25. sha: sha,
  26. )
  27. # verify
  28. if verify
  29. read_data = adapter.get(sha)
  30. read_sha = checksum(read_data)
  31. if sha != read_sha
  32. raise "Content not written correctly (provider #{adapter_name})."
  33. end
  34. end
  35. end
  36. file
  37. end
  38. =begin
  39. read content of a file
  40. store = Store::File.find(123)
  41. store.content # returns binary
  42. =end
  43. def content
  44. "Store::Provider::#{provider}".constantize.get(sha)
  45. end
  46. =begin
  47. file system check of store, check data and sha (in case fix it)
  48. Store::File.verify
  49. read each file which should be in backend and verify against sha hash
  50. in case of fixing sha hash use:
  51. Store::File.verify(true)
  52. =end
  53. def self.verify(fix_it = false)
  54. success = true
  55. Store::File.find_each(batch_size: 10) do |item|
  56. begin
  57. logger.info "CHECK: Store::File.find(#{item.id})"
  58. sha = checksum(item.content)
  59. next if sha == item.sha
  60. success = false
  61. logger.error "DIFF: sha diff of Store::File.find(#{item.id}) current:#{sha}/db:#{item.sha}/provider:#{item.provider}"
  62. store = Store.find_by(store_file_id: item.id)
  63. logger.error "STORE: #{store.inspect}"
  64. item.update_attribute(:sha, sha) if fix_it # rubocop:disable Rails/SkipsModelValidations
  65. rescue => e
  66. success = false
  67. logger.error { e.message }
  68. next
  69. end
  70. end
  71. success
  72. end
  73. =begin
  74. move file from one to other provider
  75. move files from file backend to db
  76. Store::File.move('File', 'DB')
  77. move files from db backend to fs
  78. Store::File.move('DB', 'File')
  79. nice move to keep system responsive
  80. Store::File.move('DB', 'File', delay_in_sec) # e. g. 1
  81. =end
  82. def self.move(source, target, delay = nil)
  83. adapter_source = "Store::Provider::#{source}".constantize
  84. adapter_target = "Store::Provider::#{target}".constantize
  85. succeeded = true
  86. Store::File.where(provider: source).find_each(batch_size: 10) do |item|
  87. begin
  88. adapter_target.add(item.content, item.sha)
  89. item.update_attribute(:provider, target) # rubocop:disable Rails/SkipsModelValidations
  90. adapter_source.delete(item.sha)
  91. rescue => e
  92. succeeded = false
  93. logger.error "File #{item.sha} could not be moved from #{source} to #{target}: #{e.message}"
  94. next
  95. end
  96. logger.info "Moved file #{item.sha} from #{source} to #{target}"
  97. sleep delay if delay
  98. end
  99. succeeded
  100. end
  101. =begin
  102. generate a checksum for the given content
  103. Store::File.checksum(binary_data)
  104. =end
  105. def self.checksum(content)
  106. Digest::SHA256.hexdigest(content)
  107. end
  108. private
  109. def destroy_provider
  110. "Store::Provider::#{provider}".constantize.delete(sha)
  111. end
  112. end
  113. end