Merge branch 'feature/user_avatars' into live
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Râu Cao 2025-05-14 14:44:14 +04:00
commit 9a406b8381
Signed by: raucao
GPG Key ID: 37036C356E56CC51
5 changed files with 53 additions and 32 deletions

View File

@ -5,19 +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
user.avatar_variant(size: size)&.blob # else
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

View File

@ -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

View File

@ -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]

View File

@ -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)

View 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">