Compare commits

..

3 Commits

Author SHA1 Message Date
c3b845b18f
Don't queue job when service isn't enabled
All checks were successful
continuous-integration/drone/push Build is passing
2025-05-17 17:46:22 +04:00
587f940764
Add avatar to admin user page 2025-05-17 17:46:17 +04:00
c2bdf5247e
Sync Mastodon IDs/profiles to local accounts
Add a new service to import some data from Mastodon accounts:

* Find users by username, store Mastodon account ID in local db when
  found
* Import display name (don't overwrite existing)
* Import avatar (don't overwrite existing)
2025-05-17 17:43:23 +04:00
5 changed files with 87 additions and 19 deletions

View File

@ -36,7 +36,7 @@ class SettingsController < ApplicationController
if @user.avatar_new.present?
if store_user_avatar
LdapManager::UpdateAvatar.call(user: @user)
XmppSetAvatarJob.perform_later(user: @user)
XmppSetAvatarJob.perform_later(user: @user) if Setting.ejabberd_enabled?
else
@validation_errors = @user.errors
render :show, status: :unprocessable_entity and return
@ -193,7 +193,11 @@ class SettingsController < ApplicationController
def store_user_avatar
io = @user.avatar_new.tempfile
img_data = process_avatar(io)
img_data = UserManager::ProcessAvatar.call(io: io)
if img_data.blank?
@user.errors.add(:avatar, "failed to process file")
false
end
tempfile = Tempfile.create
tempfile.binmode
tempfile.write(img_data)
@ -212,18 +216,4 @@ class SettingsController < ApplicationController
@user.save
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

View File

@ -8,7 +8,7 @@ module MastodonManager
users = get "v2/admin/accounts?username=#{@username}&origin=local"
users = users.map { |u| u.with_indifferent_access }
# Results may contain partial matches
users.find { |u| u.dig(:username) == @username }
users.find { |u| u.dig(:username).downcase == @username.downcase }
end
end
end

View File

@ -6,7 +6,36 @@ module UserManager
end
def call
if import_remote_avatar
LdapManager::UpdateAvatar.call(user: @user)
XmppSetAvatarJob.perform_later(user: @user) if Setting.ejabberd_enabled?
end
end
def import_remote_avatar
tempfile = Down.download(@avatar_url)
content_type = tempfile.content_type
unless %w[image/jpeg image/png].include?(content_type)
Rails.logger.warn { "Wrong content type of remote avatar for user #{user.cn}: '#{content_type}'" }
return false
end
img_data = UserManager::ProcessAvatar.call(io: tempfile)
tempfile = Tempfile.create
tempfile.binmode
tempfile.write(img_data)
tempfile.rewind
hash = Digest::SHA256.hexdigest(img_data)
ext = content_type == "image/png" ? "png" : "jpg"
filename = "#{hash}.#{ext}"
key = "users/#{@user.cn}/avatars/#{filename}"
@user.avatar.attach io: tempfile, key: key, filename: filename
rescue => e
Sentry.capture_exception(e) if Setting.sentry_enabled?
Rails.logger.warn "Importing remote avatar failed: \"#{e.message}\""
false
end
end
end

View File

@ -0,0 +1,21 @@
module UserManager
class ProcessAvatar < UserManagerService
def initialize(io:)
@io = io
end
def call
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.warn { "Image processing failed for avatar: #{e.message}" }
nil
end
end
end

View File

@ -89,14 +89,42 @@
</section>
<section class="sm:flex-1 sm:pt-0">
<h3>LDAP</h3>
<h3>Avatar</h3>
<% if @user.avatar.attached? %>
<table class="divided">
<tbody>
<tr>
<th class="align-top">Image</th>
<td class="align-top">
<%= image_tag image_url_for(@user.avatar), class: "h-20 w-20 rounded-lg" %>
</td>
</tr>
<tr>
<th>Content type</th>
<td>
<%= @user.avatar.content_type %>
</td>
</tr>
<tr>
<th>Size</th>
<td>
<%= number_to_human_size(@user.avatar.blob.byte_size) %>
</td>
</tr>
</tbody>
</table>
<% else %>
<p class="text-gray-500">No avatar uploaded</p>
<% end %>
<h3 class="mt-12">LDAP</h3>
<table class="divided">
<tbody>
<tr>
<th>Avatar</th>
<td>
<% if @ldap_avatar.present? %>
JPEG size: <%= @ldap_avatar.size %>
JPEG size: <%= number_to_human_size(@ldap_avatar.size) %>
<% else %>
&mdash;
<% end %>