আজকের পোস্টে, আমরা শিখব কিভাবে একটি রেলস অ্যাপ্লিকেশন তৈরি করব যা একাধিক সাবডোমেন সমর্থন করতে পারে। ধরি আমাদের একটি গেমিং ওয়েবসাইট আছে funkygames.co এবং আমরা একক রেলস অ্যাপ্লিকেশনের মাধ্যমে app.funkygames.co, api.funkygames.co, এবং dev.funkygames.co এর মতো একাধিক সাবডোমেন সমর্থন করতে চাই। আমরা নিশ্চিত করতে চাই যে সব সাবডোমেনের জন্য সঠিক প্রমাণীকরণ সম্পন্ন হয় এবং কোনো ডুপ্লিকেট রাউট নেই।
পূর্বশর্ত
এই পোস্টের জন্য, আমি অনুমান করছি যে আপনি সব সাবডোমেনের জন্য উপযুক্ত DNS রেকর্ড সেট আপ করেছেন যাতে সেগুলো রেলস অ্যাপের দিকে নির্দেশ করে। এই পোস্টে আমরা শুধু রেলস পক্ষের বিষয় নিয়ে আলোচনা করব।
একাধিক সাবডোমেন হ্যান্ডলিং
রেলস routes.rb ফাইল ব্যবহার করে ইনকামিং রিকোয়েস্ট হ্যান্ডেল করে এবং সেগুলো নির্দিষ্ট কন্ট্রোলার অ্যাকশনে ম্যাপ করে। একটি সাধারণ অ্যাপে, routes.rb এর প্রতিটি ম্যাপিং একটি রাউটকে কন্ট্রোলার অ্যাকশনের সাথে ম্যাপ করে নিম্নরূপ:
get '/games/:id', to: 'games#show'
এই পদ্ধতিতে, routes.rb ফাইলে সংজ্ঞায়িত সকল এন্ডপয়েন্ট সব সাবডোমেনের জন্য প্রযোজ্য হয়। তাই app.funkygames.co/games/1 এবং api.funkygames.co/games/1 উভয়ই এই রাউট দ্বারা হ্যান্ডেল হবে। কিন্তু আমরা শুধু app সাবডোমেন থেকে আসা রিকোয়েস্ট এই রাউট দ্বারা হ্যান্ডেল হোক তা চাই। api সাবডোমেন শুধু API রাউটের জন্য ব্যবহার করা হবে। আমরা রাউটে কিছু নিয়ম যোগ করব যাতে সেগুলো শুধু নির্দিষ্ট শর্ত পূরণ করলে হ্যান্ডেল হয়।
রেলস রাউটিং একটি constraints হেল্পার মেথড প্রদান করে যা দিয়ে দেওয়া রাউটের জন্য অতিরিক্ত নিয়ম নির্দিষ্ট করা যায়।
get '/games/:id', to: 'games#show', constraints: { subdomain: 'app' }
এটি নিশ্চিত করবে যে যদি রিকোয়েস্ট app.funkygames.co/games/1 থেকে আসে, তবে এটি GamesController-র show অ্যাকশন দ্বারা হ্যান্ডেল হবে। app ছাড়া অন্যান্য সাবডোমেন থেকে আসা যেকোনো রিকোয়েস্ট এই রাউট দ্বারা হ্যান্ডেল হবে না।
প্রতিটি রাউটের জন্য আলাদা আলাদা constraints ডিফাইন করা বেশ কঠিন হবে।
get '/games/:id', to: 'games#show', constraints: { subdomain: 'app' } get '/games/list', to: 'games#list', constraints: { subdomain: 'app' } post '/games/start', to: 'games#start', constraints: { subdomain: 'app' }
আমরা constraints হেল্পারের ব্লক ফর্ম ব্যবহার করে একক সাবডোমেনের জন্য একাধিক রাউট সংজ্ঞায়িত করতে পারি।
constraints subdomain: 'app' do get '/games/:id', to: 'games#show' get '/games/list', to: 'games#list' post '/games/start', to: 'games#start' end
একাধিক সাবডোমেনের জন্য রাউট সংজ্ঞায়িত করতে, আমাদের routes.rb ফাইলে একাধিক constraints ব্লক যোগ করতে হবে।
constraints subdomain: 'app' do ... end constraints subdomain: 'api' do ... end constraints subdomain: 'dev' do ... end
ভেতরের কাহিনি
রেলস রাউটিং রিকোয়েস্ট কনস্ট্রেইন্ট এবং সেগমেন্ট কনস্ট্রেইন্ট প্রদান করে। সেগমেন্ট কনস্ট্রেইন্ট রিকোয়েস্ট পথের ওপর নিয়ম লাগায়, অন্যদিকে রিকোয়েস্ট কনস্ট্রেইন্ট ইনকামিং রিকোয়েস্টের ওপর শর্ত প্রয়োগ করে। একটি রিকোয়েস্ট কনস্ট্রেইন্টের হ্যাশ কী অবশ্যই Request অবজেক্টের একটি মেথড হতে হবে যা স্ট্রিং রিটার্ন করে এবং মানটি হবে প্রত্যাশিত মান।
constraints subdomain: 'app' do ... end
উপরের কেসে, আমরা Request অবজেক্টের subdomain মেথড ব্যবহার করছি এবং সেটিকে app, api বা dev স্ট্রিংয়ের সাথে মিলিয়ে যাচাই করছি।
আরো বিস্তারিত জানতে রেলস রাউটিং গাইড দেখুন।
বহু-স্তরীয় সাবডোমেন হ্যান্ডলিং
ধরি আমরা আমাদের স্টেজিং এনভায়রনমেন্টের জন্য app.staging.funkygames.co ব্যবহার করছি। যদি উপরের সেটআপ থাকে, তবে আমরা দ্রুত লক্ষ্য করব যে app সাবডোমেনে যেতে পারে এমন সব রিকোয়েস্ট 404 রিটার্ন করছে। আরও ডিবাগ করলে দেখা যাবে আমাদের সাবডোমেনের জন্য কনস্ট্রেইন্ট ব্যর্থ হচ্ছে।
request.subdomain #=> app.staging
আমরা আশা করছিলাম সাবডোমেন app রিটার্ন করবে, কিন্তু এটি app.staging রিটার্ন করছে। অবশ্যই আমরা এনভায়রনমেন্ট-নির্দিষ্ট কোড যোগ না করেই এটি সমাধান করতে চাই! রিকোয়েস্টের সাবডোমেন পার্স করা হয় config.action_dispatch.tld_length অপশন দ্বারা পরিচালিত। এই কনফিগারেশনের ডিফল্ট মান ১, যা মূলত এক-স্তরের সাবডোমেন সমর্থন করে। যেহেতু আমাদের দুই-স্তরের সাবডোমেন আছে, তাই config.action_dispatch.tld_length এর মান ২ সেট করতে হবে।
# config/application.rb config.action_dispatch.tld_length = Integer(ENV['TLD_LENGTH'] || 1)
এটি এনভায়রনমেন্ট ভেরিয়েবল ব্যবহার করে সেট করা যায়, যাতে একই কোড স্টেজিং এবং প্রোডাকশন উভয় পরিবেশে কাজ করে। এখন আমাদের রাউটিং সেটআপ app.staging.funkygames.co-র জন্যও কাজ করবে।
সেশন ম্যানেজমেন্ট
এখন যেহেতু রাউটগুলো একাধিক সাবডোমেন থেকে আসা রিকোয়েস্ট হ্যান্ডেল করার জন্য সংজ্ঞায়িত হয়েছে, আমাদের সব সাবডোমেনের জন্য প্রমাণীকরণের যত্ন নিতে হবে। এটি দুটি পদ্ধতিতে করা যায়—আমরা সব সাবডোমেন জুড়ে একই ইউজার সেশন ব্যবহার করার অনুমতি দিতে পারি, অথবা আলাদা আলাদা সাবডোমেনের জন্য আলাদা সেশন রাখতে পারি।
প্রমাণীকরণ সংক্ষেপে
রেলস ডিফল্টভাবে কুকি ব্যবহার করে ইউজার সেশন কী সংরক্ষণ করে। একবার ইউজার লগ ইন করলে, ইউজারের সেশন তথ্য আমাদের পছন্দের সেশন স্টোরে সংরক্ষিত হয় এবং সেশন কী ব্রাউজারে কুকি হিসেবে সংরক্ষিত হয়। পরবর্তীবার ইউজার আমাদের ওয়েবসাইটে আসলে, ব্রাউজার থেকে একই সেশন কুকি সার্ভারে পাঠানো হয় এবং সার্ভার ইনকামিং সেশন কুকির জন্য সেশন আছে কিনা তা যাচাই করে ইউজার লগইন আছে কিনা তা নির্ধারণ করে।
রেলস অ্যাপে সেশনের ডিফল্ট কনফিগারেশন নিম্নরূপ দেখায়:
Rails.application.config.session_store :cookie_store, key: "_funkygames_session"
কী _funkygames_session সেশন কুকির নাম হিসেবে ব্যবহার হবে এবং এর মান হবে সেশন আইডি।
কুকি প্রাইমার
ডিফল্টভাবে, কুকি ব্রাউজার দ্বারা রিকোয়েস্টের ডোমেনে সেট হয়। তাই যদি আমরা app.funkygames.co থেকে অ্যাপ্লিকেশন হিট করি, তবে সেশন কুকি app.funkygames.co ডোমেনে সেট হবে। প্রতিটি সাবডোমেন নিজস্ব সেশন কুকি সেট করবে, তাই ডিফল্টভাবে ইউজার সেশন সাবডোমেন জুড়ে শেয়ার হবে না।
বিভিন্ন সাবডোমেন জুড়ে সেশন শেয়ারিং
যদি আমরা সাবডোমেন জুড়ে ইউজার সেশন শেয়ার করতে চাই, তবে সেশন কুকি funkygames.co ডোমেনে সেট করতে হবে যাতে সব সাবডোমেন সেটি অ্যাক্সেস করতে পারে। এটি সেশন স্টোর সেটিং-এ domain অপশন পাস করে অর্জন করা যায়।
Rails.application.config.session_store :cookie_store, key: "_funkygames_session", domain: :all
domain হিসেবে :all পাস করলে, আমরা মূলত রেলসকে বলছি যে সেশন কুকি অ্যাপ্লিকেশনের টপ-লেভেল ডোমেন (যেমন funkygames.co) এ সেট করুন, রিকোয়েস্ট হোস্টে না (যে ইনডিভিজুয়াল সাবডোমেন থাকতে পারে)। একবার এটি করলে, সেশন বিভিন্ন সাবডোমেন জুড়ে শেয়ার করা যাবে।
আমরা
domainsঅপশনে ডোমেনের একটি তালিকা অ্যারে ফরম্যাটে পাস করতে পারি একাধিক ডোমেন সমর্থন করতে।
একটি আরো অপশন কনফিগার করতে হবে সাবডোমেন জুড়ে কুকি সঠিকভাবে সেট করার জন্য। সেটি হল tld_length অপশন। domain: :all ব্যবহার করার সময়, এই অপশন নির্দিষ্ট করে কিভাবে ডোমেন পার্স করবে TLD ব্যাখ্যা করতে। আমাদের কেসে, app.funkygames.co-র জন্য, tld_length ২ সেট করতে হবে যাতে রেলস কুকি সেট করার সময় TLD কে funkygames.co হিসেবে ব্যাখ্যা করে। তাই একাধিক সাবডোমেনের জন্য চূড়ান্ত সেশন স্টোর কনফিগারেশন হবে নিম্নরূপ:
Rails.application.config.session_store :cookie_store, key: "_funkygames_session", domain: :all, tld_length: 2
সেশন স্টোর থেকে
tld_lengthঅপশনটি আগে আলোচনা করাconfig.action_dispatch.tld_length-এর থেকে আলাদা।
একাধিক সাবডোমেনের জন্য টেস্ট লিখা
যেহেতু রাউটগুলো সাবডোমেন-নির্দিষ্ট, রিকোয়েস্ট স্পেস বা ইন্টিগ্রেশন টেস্ট ৪০৪ এরর দেবে যদি টেস্ট রিকোয়েস্টে উপযুক্ত সাবডোমেন না থাকে। রেলস ইন্টিগ্রেশন টেস্ট একটি host! হেল্পার প্রদান করে যা একটি টেস্ট ফাইলের মধ্যে করা সব রিকোয়েস্টের জন্য উপযুক্ত সাবডোমেন সেট করতে পারে।
# রেলস ইন্টিগ্রেশন টেস্টে সাবডোমেন কনফিগার করা setup do host! 'dev.example.com' end # # RSpec রিকোয়েস্ট স্পেসে সাবডোমেন কনফিগার করা before do host! 'dev.example.com' end
এটির পর, রিকোয়েস্টগুলো routes.rb ফাইলে সাবডোমেন রাউটিং অনুযায়ী সঠিকভাবে কন্ট্রোলার অ্যাকশনে রাউট হবে।
নোট করুন যে ডোমেন এখানে মানে রাখে না, কেবল কোড অনুযায়ী উপযুক্ত সাবডোমেনই গুরুত্বপূর্ণ।
লোকাল ডেভেলপমেন্টের জন্য একাধিক সাবডোমেন সেটআপ
লোকালি সাবডোমেন সেটআপ করার একাধিক পদ্ধতি আছে। সবচেয়ে সহজ হল /etc/hosts ফাইল এডিট করা।
127.0.0.1 dev.funkygames.local 127.0.0.1 app.funkygames.local 127.0.0.1 api.funkygames.local
এটি নিশ্চিত করে যে সাবডোমেন সেটআপ লোকাল এনভায়রনমেন্টে কাজ করবে। আমরা pow এর মত টুলও ব্যবহার করতে পারি লোকালি সাবডোমেন ম্যানেজ করার জন্য।
কনস্ট্রেইন্ট-ভিত্তিক সাবডোমেন রাউটিংয়ের কিছু সমস্যা
যদিও কনস্ট্রেইন্ট-ভিত্তিক সাবডোমেন রাউটিং অধিকাংশ কেসে কাজ করে, নির্দিষ্ট পরিস্থিতিতে এটি সমস্যা সৃষ্টি করতে পারে।
বহির্দেশ API নিয়ে কাজ
যখন আমরা থার্ড-পার্টি API এর সাথে কাজ করি এবং ইন্টিগ্রেশন বিল্ড করি, লোকাল ডেভেলপমেন্ট TLD যেন .local বা .dev অ্যালোওড নয়। আমাদের ngrok এর মত টুল ব্যবহার করতে হয়। সাবডোমেন-ভিত্তিক রাউটিং সে কেসে কাজ করে না এবং কিছু রাউট হোয়াইটলিস্ট করতে হয় যাতে সেগুলো ngrok-এর মাধ্যমে অ্যাক্সেসযোগ্য থাকে।
সাবডোমেন কনস্ট্রেইন্টের বাইরে রাউট
কিছু রাউট সাবডোমেন কনস্ট্রেইন্টের মধ্যে রাখা যাবে না। একটি সাধারণ উদাহরণ হল healthcheck বা ping এন্ডপয়েন্ট। যদি আমরা রেলস অ্যাপের সামনে লোড ব্যালেন্সার ব্যবহার করি, তবে লোড ব্যালেন্সার পিরিয়ডিক্যালি চেক করতে চায় অ্যাপ আপ আছে কিনা। এমন কেসে ব্যবহৃত healthcheck এন্ডপয়েন্ট সাবডোমেন কনস্ট্রেইন্টের অধীনে রাখা যাবে না কারণ লোড ব্যালেন্সারের কাছে রিকোয়েস্ট হোস্টের জানकारी নাও থাকতে পারে।
রুট রাউটের অনুপস্থিতি
রেলসের একটি বিশেষ root রাউট আছে যা অ্যাপ্লিকেশনের ডিফল্ট রাউট। যদি অন্য কোনো রাউট রিকোয়েস্টের সাথে ম্যাচ না করে, তবে root রাউট ব্যবহার হয়। যখন আমাদের সব রাউট যেকোনো একটি সাবডোমেনের অধীনে থাকে, তখন এমন পরিস্থিতি হতে পারে যে কোনো root রাউট সংজ্ঞায়িত থাকে না। কিছু জেম root রাউটের উপস্থিতির ওপর নির্ভর করতে পারে এবং আমাদের সেই অনুযায়ী চেক এন্ড ব্যালেন্স যোগ করতে হয়।
উপসংহার
এই পোস্টে, আমরা খুব কম কনফিগারেশনের সাথে একাধিক সাবডোমেন সহ একটি রেলস অ্যাপ সেট আপ করেছি। আমরা লোকালি এবং ভিন্ন পরিবেশে সাবডোমেন সেটআপ করার পদ্ধতিও দেখেছি, একাধিক সাবডোমেনের জন্য কার্যকর টেস্ট লিখার টিপসসহ। রেলস দ্বারা প্রদান প্লাম্বিং-এর সাহায্যে, একাধিক সাবডোমেন সহ রেলস অ্যাপ সেট আপ এবং টেস্ট করা সহজ হয়ে যায়।
P.S. যদি আপনি Ruby Magic পোস্টগুলো প্রেস থেকে বের होते পড়তে চান, আমাদের Ruby Magic নিউজলেটার সাবস্ক্রাইব করুন এবং কখনো একটি পোস্ট মিস করবেন না!