Do not use ActiveStorage variants, process original avatar
Variants are currently broken. So we process the original file with the most common avatar dimensions and stripping metadata, then hash and upload only that version.
This commit is contained in:
parent
1884f082ee
commit
417e346074
@ -5,22 +5,20 @@ class AvatarsController < ApplicationController
|
|||||||
|
|
||||||
sha256_hash = params[:hash]
|
sha256_hash = params[:hash]
|
||||||
format = params[:format]&.to_sym || :png
|
format = params[:format]&.to_sym || :png
|
||||||
size = params[:size]&.to_sym || :original
|
# size = params[:size]&.to_sym || :original
|
||||||
|
|
||||||
unless user.avatar.filename.to_s == "#{sha256_hash}.#{format}"
|
unless user.avatar.filename.to_s == "#{sha256_hash}.#{format}"
|
||||||
http_status :not_found and return
|
http_status :not_found and return
|
||||||
end
|
end
|
||||||
|
|
||||||
blob = if size == :original
|
# TODO See note for avatar_variant in user model
|
||||||
user.avatar.blob
|
# blob = if size == :original
|
||||||
else
|
# user.avatar.blob
|
||||||
# TODO Variants use the same custom storage key/path, which
|
# else
|
||||||
# makes blob downloads always fetch the original version instead
|
# user.avatar_variant(size: size)&.blob
|
||||||
# of the variant. Needs to be fixed/added in Rails.
|
# end
|
||||||
user.avatar_variant(size: size)&.blob
|
|
||||||
end
|
|
||||||
|
|
||||||
data = blob.download
|
data = user.avatar.blob.download
|
||||||
send_data data, type: "image/#{format}", disposition: "inline"
|
send_data data, type: "image/#{format}", disposition: "inline"
|
||||||
else
|
else
|
||||||
http_status :not_found
|
http_status :not_found
|
||||||
|
@ -191,9 +191,14 @@ class SettingsController < ApplicationController
|
|||||||
end
|
end
|
||||||
|
|
||||||
def store_user_avatar
|
def store_user_avatar
|
||||||
data = @user.avatar_new.tempfile.read
|
io = @user.avatar_new.tempfile
|
||||||
@user.avatar_new.tempfile.rewind
|
img_data = process_avatar(io)
|
||||||
hash = Digest::SHA256.hexdigest(data)
|
tempfile = Tempfile.create
|
||||||
|
tempfile.binmode
|
||||||
|
tempfile.write(img_data)
|
||||||
|
tempfile.rewind
|
||||||
|
|
||||||
|
hash = Digest::SHA256.hexdigest(img_data)
|
||||||
ext = @user.avatar_new.content_type == "image/png" ? "png" : "jpg"
|
ext = @user.avatar_new.content_type == "image/png" ? "png" : "jpg"
|
||||||
filename = "#{hash}.#{ext}"
|
filename = "#{hash}.#{ext}"
|
||||||
|
|
||||||
@ -202,8 +207,22 @@ class SettingsController < ApplicationController
|
|||||||
false
|
false
|
||||||
else
|
else
|
||||||
key = "users/#{@user.cn}/avatars/#{filename}"
|
key = "users/#{@user.cn}/avatars/#{filename}"
|
||||||
@user.avatar.attach io: @user.avatar_new.tempfile, key: key, filename: filename
|
@user.avatar.attach io: tempfile, key: key, filename: filename
|
||||||
@user.save
|
@user.save
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def process_avatar(io)
|
||||||
|
processed = ImageProcessing::Vips
|
||||||
|
.source(io)
|
||||||
|
.resize_to_fill(400, 400)
|
||||||
|
.saver(strip: true)
|
||||||
|
.call
|
||||||
|
io.rewind
|
||||||
|
processed.read
|
||||||
|
rescue Vips::Error => e
|
||||||
|
Sentry.capture_exception(e) if Setting.sentry_enabled?
|
||||||
|
Rails.logger.error { "Image processing failed for avatar: #{e.message}" }
|
||||||
|
nil
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
@ -56,6 +56,7 @@ class User < ApplicationRecord
|
|||||||
validates_length_of :display_name, minimum: 3, maximum: 35, allow_blank: true,
|
validates_length_of :display_name, minimum: 3, maximum: 35, allow_blank: true,
|
||||||
if: -> { defined?(@display_name) }
|
if: -> { defined?(@display_name) }
|
||||||
|
|
||||||
|
|
||||||
validate :acceptable_avatar
|
validate :acceptable_avatar
|
||||||
|
|
||||||
validate :acceptable_pgp_key_format, if: -> { defined?(@pgp_pubkey) && @pgp_pubkey.present? }
|
validate :acceptable_pgp_key_format, if: -> { defined?(@pgp_pubkey) && @pgp_pubkey.present? }
|
||||||
@ -83,6 +84,10 @@ class User < ApplicationRecord
|
|||||||
:timeoutable,
|
:timeoutable,
|
||||||
:rememberable
|
:rememberable
|
||||||
|
|
||||||
|
#
|
||||||
|
# Methods
|
||||||
|
#
|
||||||
|
|
||||||
def ldap_before_save
|
def ldap_before_save
|
||||||
self.email = Devise::LDAP::Adapter.get_ldap_param(self.cn, "mail").first
|
self.email = Devise::LDAP::Adapter.get_ldap_param(self.cn, "mail").first
|
||||||
self.ou = dn.split(',')
|
self.ou = dn.split(',')
|
||||||
@ -165,23 +170,19 @@ class User < ApplicationRecord
|
|||||||
@display_name ||= ldap_entry[:display_name]
|
@display_name ||= ldap_entry[:display_name]
|
||||||
end
|
end
|
||||||
|
|
||||||
def avatar_base64(size: :medium)
|
# TODO Variant keys are currently broken for some reason
|
||||||
return nil unless avatar.attached?
|
# (They use the same key as the main blob, when it should be
|
||||||
variant = avatar_variant(size: size)
|
# "/variants/#{key)"
|
||||||
data = ActiveStorage::Blob.service.download(variant.key)
|
# def avatar_variant(size: :medium)
|
||||||
Base64.strict_encode64(data)
|
# dimensions = case size
|
||||||
end
|
# when :large then [400, 400]
|
||||||
|
# when :medium then [256, 256]
|
||||||
def avatar_variant(size: :medium)
|
# when :small then [64, 64]
|
||||||
dimensions = case size
|
# else [256, 256]
|
||||||
when :large then [400, 400]
|
# end
|
||||||
when :medium then [256, 256]
|
# format = avatar.content_type == "image/png" ? :png : :jpeg
|
||||||
when :small then [64, 64]
|
# avatar.variant(resize_to_fill: dimensions, format: format)
|
||||||
else [256, 256]
|
# end
|
||||||
end
|
|
||||||
format = avatar.content_type == "image/png" ? :png : :jpeg
|
|
||||||
avatar.variant(resize_to_fill: dimensions, format: format)
|
|
||||||
end
|
|
||||||
|
|
||||||
def nostr_pubkey
|
def nostr_pubkey
|
||||||
@nostr_pubkey ||= ldap_entry[:nostr_key]
|
@nostr_pubkey ||= ldap_entry[:nostr_key]
|
||||||
|
@ -14,15 +14,16 @@ module LdapManager
|
|||||||
end
|
end
|
||||||
|
|
||||||
img_data = @user.avatar.blob.download
|
img_data = @user.avatar.blob.download
|
||||||
jpg_data = process(img_data)
|
jpg_data = process_avatar
|
||||||
|
|
||||||
|
Rails.logger.debug { "Storing new jpegPhoto for user #{@user.cn} in LDAP" }
|
||||||
result = replace_attribute(@dn, :jpegPhoto, jpg_data)
|
result = replace_attribute(@dn, :jpegPhoto, jpg_data)
|
||||||
result == 0
|
result == 0
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def process(data)
|
def process_avatar
|
||||||
@user.avatar.blob.open do |file|
|
@user.avatar.blob.open do |file|
|
||||||
processed = ImageProcessing::Vips
|
processed = ImageProcessing::Vips
|
||||||
.source(file)
|
.source(file)
|
||||||
|
@ -38,8 +38,7 @@
|
|||||||
<div class="flex items-center gap-6">
|
<div class="flex items-center gap-6">
|
||||||
<% if @user.avatar.attached? %>
|
<% if @user.avatar.attached? %>
|
||||||
<p class="flex-none">
|
<p class="flex-none">
|
||||||
<%= image_tag @user.avatar_variant(size: :medium),
|
<%= image_tag image_url_for(@user.avatar), class: "h-24 w-24 rounded-lg" %>
|
||||||
class: "h-24 w-24 rounded-lg" %>
|
|
||||||
</p>
|
</p>
|
||||||
<% end %>
|
<% end %>
|
||||||
<div class="grow">
|
<div class="grow">
|
||||||
|
Loading…
x
Reference in New Issue
Block a user