صور OG في Ruby on Rails
قم بإنشاء صور OG في Ruby on Rails باستخدام واجهة برمجة التطبيقات الديناميكية (Dynamic API) القائمة على الاشتراك من FastOG — مع اشتراك واجهة برمجة التطبيقات الديناميكية (أنشئ مفتاح API الخاص بك في لوحة التحكم). يمكنك المعاينة في أداة اختبار صور OG المجانية، ثم عرض صور Open Graph الديناميكية من تطبيق Rails الخاص بك باستخدام عناوين URL موقعة بـ HMAC.
بدء الاستخدام السريع
1. إنشاء خدمة التوقيع
# app/services/fastog_signer.rb
class QuickogSigner
def self.generate_signed_og_url(params, options = {})
base_url = options[:base_url] || ENV.fetch("FASTOG_BASE_URL", "https://fastog.com/api/v1/og")
api_key = options[:api_key] || ENV.fetch("FASTOG_API_KEY")
hmac_secret = options[:hmac_secret] || ENV.fetch("FASTOG_HMAC_SECRET")
raise "Missing FASTOG_API_KEY or FASTOG_HMAC_SECRET" unless api_key && hmac_secret
all = params.merge(key: api_key)
pairs = all.flat_map do |k, v|
Array(v).sort.map { |vv| "#{enc(k)}=#{enc(vv)}" }
end
canonical = pairs.sort.join("&")
signature = OpenSSL::HMAC.hexdigest("SHA256", hmac_secret, "GET/api/v1/og\n#{canonical}")
"#{base_url}?#{URI.encode_www_form(all.merge(s: signature))}"
end
def self.enc(s)
s.to_s.gsub(/[^A-Za-z0-9\-._~]/) { |c| c.bytes.map { |b| "%%%02X" % b }.join }
end
private_class_method :enc
end
لماذا خدمة توقيع مخصصة enc؟ لأنها تقوم بترميز النسبة المئوية بأحرف سداسية عشرية كبيرة، مع إبقاء -._~ فقط غير محجوز — لذا تتحول المسافات إلى %20، وليس + أبدًا، بما يتوافق مع rawurlencode الخاص بالخادم. إذا استبدلتها بـ CGI.escape أو URI.encode_www_form_component، فكلاهما يُنتج + للمسافات: قم بالتطبيع باستخدام .gsub('+', '%20') أولًا.
2. الإعداد
# config/application.rb
config.fastog = ActiveSupport::OrderedOptions.new
config.fastog.api_key = ENV.fetch("FASTOG_API_KEY", nil)
config.fastog.hmac_secret = ENV.fetch("FASTOG_HMAC_SECRET", nil)
config.fastog.base_url = ENV.fetch("FASTOG_BASE_URL", "https://fastog.com/api/v1/og")
# .env
FASTOG_API_KEY=sk_your_full_api_key_here
FASTOG_HMAC_SECRET=your_64_char_hex_secret_here
وحدة التحكم (Controller)
# app/controllers/blog_controller.rb
class BlogController < ApplicationController
def show
@post = Post.find_by!(slug: params[:slug])
@og_image_url = QuickogSigner.generate_signed_og_url(
template: "blog",
title: @post.title,
subtitle: @post.excerpt,
author: @post.author.name
)
end
end
العرض
<!-- app/views/blog/show.html.erb -->
<% content_for :head do %>
<meta property="og:image" content="<%= @og_image_url %>" />
<meta property="og:title" content="<%= @post.title %>" />
<meta property="og:description" content="<%= @post.excerpt %>" />
<% end %>
<article>
<h1><%= @post.title %></h1>
<p><%= @post.excerpt %></p>
</article>
التخطيط
<!-- app/views/layouts/application.html.erb -->
<head>
<%= yield :head %>
</head>
المساعد
# app/helpers/fastog_helper.rb
module QuickogHelper
def og_image_url(template:, **params)
QuickogSigner.generate_signed_og_url({ template: template }.merge(params))
end
end
<!-- In view -->
<meta property="og:image" content="<%= og_image_url(template: 'blog', title: @post.title) %>" />
مثال التجارة الإلكترونية
# app/controllers/products_controller.rb
class ProductsController < ApplicationController
def show
@product = Product.find(params[:id])
@og_image_url = QuickogSigner.generate_signed_og_url(
template: "ecommerce",
title: @product.name,
price: @product.formatted_price,
image: @product.image_url
)
end
end
مثال على منشور مدونة
# app/controllers/users_controller.rb
class UsersController < ApplicationController
def show
@user = User.find_by!(username: params[:username])
@og_image_url = QuickogSigner.generate_signed_og_url(
template: "blog",
title: @user.name,
subtitle: @user.bio,
author: @user.name
)
end
end
نقطة نهاية API
# config/routes.rb
get "api/og", to: "api/og#generate"
# app/controllers/api/og_controller.rb
module Api
class OgController < ApplicationController
skip_before_action :verify_authenticity_token
def generate
title = params[:title]
template = params[:template] || "default"
unless title
return render json: { error: "Missing title" }, status: :bad_request
end
og_image_url = QuickogSigner.generate_signed_og_url(
template: template,
title: title
)
render json: { ogImageUrl: og_image_url }
end
end
end
Turbo/Hotwire
<!-- app/views/blog/_post.html.erb -->
<%= turbo_frame_tag "post_#{@post.id}" do %>
<meta property="og:image" content="<%= og_image_url(template: 'blog', title: @post.title) %>" />
<h2><%= link_to @post.title, blog_path(@post) %></h2>
<% end %>
الأخطاء الشائعة
توقيع الرسالة الخاطئة
❌ خطأ: "GET/api/v1/og#{canonical}" — فقدان فاصل السطر الجديد.
✅ صحيح: "GET/api/v1/og\n#{canonical}" — الطريقة + المسار، ثم سطر جديد، ثم الاستعلام الأساسي (canonical query).
استبعاد key من التوقيع
❌ خطأ: توقيع معاملات المحتوى فقط وإلحاق key بعنوان URL لاحقًا.
✅ صحيح: يتم دمج key في المعاملات قبل بناء الاستعلام الأساسي، ويظهر في عنوان URL النهائي.
401 missing_signature / invalid_signature
missing_signature (401) تعني أن key أو s مفقود من عنوان URL. invalid_signature (401) تعني أن الاستعلام الأساسي لم يتطابق — تحقق من وجود سطر جديد في الرسالة الموقعة، ومن استخدام نفس hmac_secret على كلا الجانبين، ومن أن enc يقوم بترميز المسافات كنسبة مئوية كـ %20 (وليس + أبدًا).
كشف سر HMAC
لا تكشف أبدًا عن FASTOG_HMAC_SECRET في JavaScript الخاص بالواجهة الأمامية أو في أي كود يعمل على جانب العميل.