Cloud Firestore Web Codelab

۱. مرور کلی

اهداف

در این آزمایشگاه کد، شما یک برنامه وب توصیه رستوران با پشتیبانی Cloud Firestore خواهید ساخت.

img5.png

آنچه یاد خواهید گرفت

  • خواندن و نوشتن داده‌ها در Cloud Firestore از یک برنامه وب
  • به تغییرات در داده‌های Cloud Firestore در زمان واقعی گوش دهید
  • استفاده از احراز هویت فایربیس و قوانین امنیتی برای ایمن‌سازی داده‌های کلود فایراستور
  • کوئری‌های پیچیده Cloud Firestore بنویسید

آنچه نیاز دارید

قبل از شروع این codelab، مطمئن شوید که موارد زیر را نصب کرده‌اید:

  • npm که معمولاً با Node.js همراه است - Node 16+ توصیه می‌شود
  • IDE/ویرایشگر متن مورد نظر شما، مانند WebStorm ، VS Code یا Sublime

۲. ایجاد و راه‌اندازی یک پروژه Firebase

ایجاد یک پروژه فایربیس

  1. با استفاده از حساب گوگل خود وارد کنسول فایربیس شوید.
  2. برای ایجاد یک پروژه جدید، روی دکمه کلیک کنید و سپس نام پروژه را وارد کنید (برای مثال، FriendlyEats ).
  3. روی ادامه کلیک کنید.
  4. در صورت درخواست، شرایط Firebase را مرور و قبول کنید و سپس روی ادامه کلیک کنید.
  5. (اختیاری) دستیار هوش مصنوعی را در کنسول Firebase (با نام "Gemini در Firebase") فعال کنید.
  6. برای این codelab، به گوگل آنالیتیکس نیاز ندارید ، بنابراین گزینه گوگل آنالیتیکس را غیرفعال کنید .
  7. روی ایجاد پروژه کلیک کنید، منتظر بمانید تا پروژه شما آماده شود و سپس روی ادامه کلیک کنید.

راه اندازی محصولات فایربیس

برنامه‌ای که قرار است بسازیم از چند سرویس Firebase موجود در وب استفاده می‌کند:

  • احراز هویت فایربیس برای شناسایی آسان کاربران شما
  • Cloud Firestore برای ذخیره داده‌های ساختاریافته روی فضای ابری و دریافت اعلان فوری هنگام به‌روزرسانی داده‌ها
  • میزبانی فایربیس برای میزبانی و ارائه دارایی‌های استاتیک شما

برای این codelab خاص، ما قبلاً Firebase Hosting را پیکربندی کرده‌ایم. با این حال، برای Firebase Auth و Cloud Firestore، شما را در پیکربندی و فعال‌سازی سرویس‌ها با استفاده از کنسول Firebase راهنمایی خواهیم کرد.

فعال کردن احراز هویت ناشناس

اگرچه تمرکز این آزمایشگاه کد روی احراز هویت نیست، اما داشتن نوعی احراز هویت در برنامه ما مهم است. ما از ورود ناشناس استفاده خواهیم کرد - به این معنی که کاربر بدون هیچ گونه درخواستی و به طور مخفیانه وارد سیستم می‌شود.

شما باید ورود ناشناس (Anonymous login) را فعال کنید.

  1. در کنسول فایربیس، بخش ساخت (Build) را در منوی سمت چپ پیدا کنید.
  2. روی تأیید هویت کلیک کنید، سپس روی برگه روش ورود کلیک کنید (یا برای رفتن مستقیم به آنجا اینجا کلیک کنید ).
  3. ارائه دهنده ورود ناشناس را فعال کنید، سپس روی ذخیره کلیک کنید.

img7.png

این به برنامه اجازه می‌دهد تا هنگام دسترسی کاربران به برنامه وب، آنها را بی‌صدا وارد سیستم کند. برای کسب اطلاعات بیشتر، می‌توانید مستندات احراز هویت ناشناس را مطالعه کنید.

فعال کردن کلود فایر استور

این برنامه از Cloud Firestore برای ذخیره و دریافت اطلاعات و رتبه‌بندی رستوران‌ها استفاده می‌کند.

شما باید Cloud Firestore را فعال کنید. در بخش Build کنسول Firebase، روی Firestore Database کلیک کنید. در پنل Cloud Firestore روی Create database کلیک کنید.

دسترسی به داده‌ها در Cloud Firestore توسط قوانین امنیتی کنترل می‌شود. بعداً در این آزمایشگاه کد بیشتر در مورد قوانین صحبت خواهیم کرد، اما ابتدا باید برای شروع، چند قانون اساسی روی داده‌های خود تنظیم کنیم. در تب قوانین کنسول Firebase، قوانین زیر را اضافه کنید و سپس روی انتشار کلیک کنید.

rules_version = '2';
service cloud.firestore {

  // Determine if the value of the field "key" is the same
  // before and after the request.
  function unchanged(key) {
    return (key in resource.data)
      && (key in request.resource.data)
      && (resource.data[key] == request.resource.data[key]);
  }

  match /databases/{database}/documents {
    // Restaurants:
    //   - Authenticated user can read
    //   - Authenticated user can create/update (for demo purposes only)
    //   - Updates are allowed if no fields are added and name is unchanged
    //   - Deletes are not allowed (default)
    match /restaurants/{restaurantId} {
      allow read: if request.auth != null;
      allow create: if request.auth != null;
      allow update: if request.auth != null
                    && (request.resource.data.keys() == resource.data.keys())
                    && unchanged("name");

      // Ratings:
      //   - Authenticated user can read
      //   - Authenticated user can create if userId matches
      //   - Deletes and updates are not allowed (default)
      match /ratings/{ratingId} {
        allow read: if request.auth != null;
        allow create: if request.auth != null
                      && request.resource.data.userId == request.auth.uid;
      }
    }
  }
}

ما در ادامه در آزمایشگاه کد، در مورد این قوانین و نحوه عملکرد آنها بحث خواهیم کرد.

۳. کد نمونه را دریافت کنید

مخزن گیت‌هاب را از خط فرمان کلون کنید:

git clone https://github.com/firebase/friendlyeats-web

کد نمونه باید در دایرکتوری 📁 friendlyeats-web کپی می‌شد. از این به بعد، مطمئن شوید که تمام دستورات خود را از این دایرکتوری اجرا می‌کنید:

cd friendlyeats-web/vanilla-js

برنامه شروع کننده را وارد کنید

با استفاده از IDE خود (WebStorm، Atom، Sublime، Visual Studio Code...) پوشه 📁 friendlyeats-web را باز یا ایمپورت کنید. این پوشه شامل کد اولیه برای codelab است که شامل یک برنامه توصیه رستوران است که هنوز کاربردی نشده است. ما آن را در طول این codelab کاربردی خواهیم کرد، بنابراین شما باید به زودی کد را در آن پوشه ویرایش کنید.

۴. رابط خط فرمان فایربیس را نصب کنید

رابط خط فرمان فایربیس (CLI) به شما امکان می‌دهد تا برنامه وب خود را به صورت محلی ارائه دهید و برنامه وب خود را در Firebase Hosting مستقر کنید.

  1. با اجرای دستور npm زیر، رابط خط فرمان (CLI) را نصب کنید:
npm -g install firebase-tools
  1. با اجرای دستور زیر، تأیید کنید که CLI به درستی نصب شده است:
firebase --version

مطمئن شوید که نسخه Firebase CLI نسخه ۷.۴.۰ یا بالاتر است.

  1. با اجرای دستور زیر، Firebase CLI را مجاز کنید:
firebase login

ما قالب برنامه وب را طوری تنظیم کرده‌ایم که پیکربندی برنامه شما را برای Firebase Hosting از دایرکتوری و فایل‌های محلی برنامه دریافت کند. اما برای انجام این کار، باید برنامه شما را با پروژه Firebase شما مرتبط کنیم.

  1. مطمئن شوید که خط فرمان شما به دایرکتوری محلی برنامه‌تان دسترسی دارد.
  2. با اجرای دستور زیر، برنامه خود را با پروژه Firebase خود مرتبط کنید:
firebase use --add
  1. وقتی از شما خواسته شد، شناسه پروژه خود را انتخاب کنید، سپس به پروژه Firebase خود یک نام مستعار بدهید.

اگر چندین محیط (محیط تولید، محیط مرحله‌بندی و غیره) دارید، یک نام مستعار مفید است. با این حال، برای این آزمایشگاه کد، بیایید فقط از نام مستعار default استفاده کنیم.

  1. دستورالعمل‌های باقی‌مانده را در خط فرمان خود دنبال کنید.

۵. سرور محلی را اجرا کنید

ما آماده‌ایم تا کار روی برنامه‌مان را شروع کنیم! بیایید برنامه‌مان را به صورت محلی اجرا کنیم!

  1. دستور Firebase CLI زیر را اجرا کنید:
firebase emulators:start --only hosting
  1. خط فرمان شما باید پاسخ زیر را نمایش دهد:
hosting: Local server: http://localhost:5000

ما از شبیه‌ساز Firebase Hosting برای ارائه برنامه خود به صورت محلی استفاده می‌کنیم. برنامه وب اکنون باید از طریق آدرس http://localhost:5000 در دسترس باشد.

  1. برنامه خود را از طریق آدرس http://localhost:5000 باز کنید.

شما باید کپی FriendlyEats خود را که به پروژه Firebase شما متصل شده است، ببینید.

این برنامه به طور خودکار به پروژه Firebase شما متصل شده و شما را به عنوان یک کاربر ناشناس به سیستم وارد کرده است.

img2.png

۶. نوشتن داده‌ها در Cloud Firestore

در این بخش، مقداری داده در Cloud Firestore می‌نویسیم تا بتوانیم رابط کاربری برنامه را پر کنیم. این کار را می‌توان به صورت دستی از طریق کنسول Firebase انجام داد، اما ما این کار را در خود برنامه انجام خواهیم داد تا یک نوشتن اولیه در Cloud Firestore را نشان دهیم.

مدل داده

داده‌های Firestore به مجموعه‌ها، اسناد، فیلدها و زیرمجموعه‌ها تقسیم می‌شوند. ما هر رستوران را به عنوان یک سند در یک مجموعه سطح بالا به نام restaurants ذخیره خواهیم کرد.

img3.png

بعداً، هر نقد و بررسی را در زیرمجموعه‌ای به نام ratings ، زیر هر رستوران ذخیره خواهیم کرد.

img4.png

رستوران‌ها را به Firestore اضافه کنید

شیء مدل اصلی در برنامه ما یک رستوران است. بیایید کدی بنویسیم که یک سند رستوران را به مجموعه restaurants اضافه کند.

  1. از فایل‌های دانلود شده، scripts/FriendlyEats.Data.js را باز کنید.
  2. تابع FriendlyEats.prototype.addRestaurant را پیدا کنید.
  3. کل تابع را با کد زیر جایگزین کنید.

FriendlyEats.Data.js

FriendlyEats.prototype.addRestaurant = function(data) {
  var collection = firebase.firestore().collection('restaurants');
  return collection.add(data);
};

کد بالا یک سند جدید به مجموعه restaurants اضافه می‌کند. داده‌های سند از یک شیء ساده جاوا اسکریپت می‌آیند. ما این کار را با دریافت ارجاع به مجموعه restaurants Cloud Firestore و سپس add کردن داده‌ها انجام می‌دهیم.

بیایید رستوران‌ها را اضافه کنیم!

  1. به برنامه FriendlyEats در مرورگر خود برگردید و آن را رفرش کنید.
  2. روی افزودن داده‌های شبیه‌سازی‌شده کلیک کنید.

برنامه به طور خودکار مجموعه‌ای تصادفی از اشیاء رستوران‌ها را تولید می‌کند، سپس تابع addRestaurant شما را فراخوانی می‌کند. با این حال، شما هنوز داده‌ها را در برنامه وب واقعی خود مشاهده نخواهید کرد زیرا ما هنوز نیاز به پیاده‌سازی بازیابی داده‌ها داریم (بخش بعدی codelab).

اگر در کنسول فایربیس به تب Cloud Firestore بروید، اکنون باید اسناد جدیدی را در مجموعه restaurants مشاهده کنید!

img6.png

تبریک می‌گویم، شما همین الان داده‌ها را از یک برنامه وب در Cloud Firestore نوشتید!

در بخش بعدی، نحوه بازیابی داده‌ها از Cloud Firestore و نمایش آنها در برنامه خود را خواهید آموخت.

۷. نمایش داده‌ها از Cloud Firestore

در این بخش، یاد خواهید گرفت که چگونه داده‌ها را از Cloud Firestore بازیابی کرده و در برنامه خود نمایش دهید. دو مرحله کلیدی، ایجاد یک پرس‌وجو و اضافه کردن یک شنونده snapshot است. این شنونده از تمام داده‌های موجود که با پرس‌وجو مطابقت دارند مطلع می‌شود و به‌روزرسانی‌ها را به صورت بلادرنگ دریافت می‌کند.

ابتدا، بیایید پرس‌وجویی بسازیم که لیست پیش‌فرض و فیلتر نشده رستوران‌ها را نمایش دهد.

  1. به فایل scripts/FriendlyEats.Data.js برگردید.
  2. تابع FriendlyEats.prototype.getAllRestaurants را پیدا کنید.
  3. کل تابع را با کد زیر جایگزین کنید.

FriendlyEats.Data.js

FriendlyEats.prototype.getAllRestaurants = function(renderer) {
  var query = firebase.firestore()
      .collection('restaurants')
      .orderBy('avgRating', 'desc')
      .limit(50);

  this.getDocumentsInQuery(query, renderer);
};

در کد بالا، ما یک کوئری می‌سازیم که تا ۵۰ رستوران را از مجموعه سطح بالا به نام restaurants بازیابی می‌کند، که بر اساس میانگین امتیاز (در حال حاضر همه صفر) مرتب شده‌اند. پس از اعلام این کوئری، آن را به متد getDocumentsInQuery() ارسال می‌کنیم که مسئول بارگذاری و رندر داده‌ها است.

ما این کار را با اضافه کردن یک شنونده‌ی snapshot انجام خواهیم داد.

  1. به فایل scripts/FriendlyEats.Data.js برگردید.
  2. تابع FriendlyEats.prototype.getDocumentsInQuery را پیدا کنید.
  3. کل تابع را با کد زیر جایگزین کنید.

FriendlyEats.Data.js

FriendlyEats.prototype.getDocumentsInQuery = function(query, renderer) {
  query.onSnapshot(function(snapshot) {
    if (!snapshot.size) return renderer.empty(); // Display "There are no restaurants".

    snapshot.docChanges().forEach(function(change) {
      if (change.type === 'removed') {
        renderer.remove(change.doc);
      } else {
        renderer.display(change.doc);
      }
    });
  });
};

در کد بالا، query.onSnapshot هر بار که تغییری در نتیجه‌ی کوئری ایجاد شود، فراخوانی مجدد خود را آغاز می‌کند.

  • بار اول، تابع فراخوانی با کل مجموعه نتایج کوئری - یعنی کل مجموعه restaurants از Cloud Firestore - اجرا می‌شود. سپس تمام اسناد جداگانه را به تابع renderer.display ارسال می‌کند.
  • وقتی یک سند حذف می‌شود، change.type برابر با removed می‌شود. بنابراین در این حالت، تابعی را فراخوانی می‌کنیم که رستوران را از رابط کاربری حذف می‌کند.

حالا که هر دو روش را پیاده‌سازی کرده‌ایم، برنامه را رفرش کنید و تأیید کنید که رستوران‌هایی که قبلاً در کنسول Firebase دیدیم، اکنون در برنامه قابل مشاهده هستند. اگر این بخش را با موفقیت تکمیل کرده باشید، برنامه شما اکنون در حال خواندن و نوشتن داده‌ها با Cloud Firestore است!

با تغییر فهرست رستوران‌های شما، این شنونده به‌طور خودکار به‌روزرسانی می‌شود. سعی کنید به کنسول Firebase بروید و به‌صورت دستی یک رستوران را حذف کنید یا نام آن را تغییر دهید - خواهید دید که تغییرات بلافاصله در سایت شما نمایش داده می‌شوند!

img5.png

۸. دریافت () داده

تا اینجا، نحوه‌ی استفاده از onSnapshot برای بازیابی به‌روزرسانی‌ها به صورت بلادرنگ را نشان دادیم؛ با این حال، این همیشه چیزی نیست که ما می‌خواهیم. گاهی اوقات منطقی‌تر است که داده‌ها را فقط یک بار دریافت کنیم.

ما می‌خواهیم متدی را پیاده‌سازی کنیم که وقتی کاربر روی یک رستوران خاص در برنامه شما کلیک می‌کند، فعال شود.

  1. به فایل scripts/FriendlyEats.Data.js خود برگردید.
  2. تابع FriendlyEats.prototype.getRestaurant را پیدا کنید.
  3. کل تابع را با کد زیر جایگزین کنید.

FriendlyEats.Data.js

FriendlyEats.prototype.getRestaurant = function(id) {
  return firebase.firestore().collection('restaurants').doc(id).get();
};

بعد از اینکه این روش را پیاده‌سازی کردید، می‌توانید صفحات مربوط به هر رستوران را مشاهده کنید. کافیست روی یک رستوران در لیست کلیک کنید تا صفحه جزئیات رستوران را ببینید:

img1.png

فعلاً نمی‌توانید رتبه‌بندی اضافه کنید، زیرا هنوز باید اضافه کردن رتبه‌بندی را بعداً در آزمایشگاه کد پیاده‌سازی کنیم.

۹. مرتب‌سازی و فیلتر کردن داده‌ها

در حال حاضر، برنامه ما لیستی از رستوران‌ها را نمایش می‌دهد، اما هیچ راهی برای فیلتر کردن بر اساس نیاز کاربر وجود ندارد. در این بخش، از پرس‌وجوی پیشرفته Cloud Firestore برای فعال کردن فیلتر استفاده خواهید کرد.

در اینجا مثالی از یک پرس و جوی ساده برای دریافت همه رستوران‌های Dim Sum آورده شده است:

var filteredQuery = query.where('category', '==', 'Dim Sum')

همانطور که از نامش پیداست، متد where() باعث می‌شود کوئری ما فقط اعضایی از مجموعه را دانلود کند که فیلدهایشان محدودیت‌های تعیین‌شده توسط ما را داشته باشند. در این حالت، فقط رستوران‌هایی را دانلود می‌کند که category آنها Dim Sum باشد.

در برنامه ما، کاربر می‌تواند چندین فیلتر را به صورت زنجیره‌ای برای ایجاد جستجوهای خاص، مانند «پیتزا در سانفرانسیسکو» یا «غذاهای دریایی در لس‌آنجلس بر اساس محبوبیت» ایجاد کند.

ما متدی ایجاد خواهیم کرد که یک کوئری ایجاد می‌کند که رستوران‌های ما را بر اساس معیارهای چندگانه انتخاب شده توسط کاربرانمان فیلتر می‌کند.

  1. به فایل scripts/FriendlyEats.Data.js خود برگردید.
  2. تابع FriendlyEats.prototype.getFilteredRestaurants را پیدا کنید.
  3. کل تابع را با کد زیر جایگزین کنید.

FriendlyEats.Data.js

FriendlyEats.prototype.getFilteredRestaurants = function(filters, renderer) {
  var query = firebase.firestore().collection('restaurants');

  if (filters.category !== 'Any') {
    query = query.where('category', '==', filters.category);
  }

  if (filters.city !== 'Any') {
    query = query.where('city', '==', filters.city);
  }

  if (filters.price !== 'Any') {
    query = query.where('price', '==', filters.price.length);
  }

  if (filters.sort === 'Rating') {
    query = query.orderBy('avgRating', 'desc');
  } else if (filters.sort === 'Reviews') {
    query = query.orderBy('numRatings', 'desc');
  }

  this.getDocumentsInQuery(query, renderer);
};

کد بالا چندین فیلتر where و یک عبارت orderBy اضافه می‌کند تا یک کوئری ترکیبی بر اساس ورودی کاربر ایجاد کند. اکنون کوئری ما فقط رستوران‌هایی را برمی‌گرداند که با نیازهای کاربر مطابقت دارند.

برنامه FriendlyEats خود را در مرورگرتان به‌روزرسانی کنید، سپس تأیید کنید که می‌توانید بر اساس قیمت، شهر و دسته‌بندی فیلتر کنید. هنگام آزمایش، خطاهایی مانند این را در کنسول جاوا اسکریپت مرورگر خود مشاهده خواهید کرد:

The query requires an index. You can create it here: https://console.firebase.google.com/project/project-id/database/firestore/indexes?create_composite=...

این خطاها به این دلیل است که Cloud Firestore برای اکثر کوئری‌های ترکیبی به ایندکس نیاز دارد. نیاز به ایندکس در کوئری‌ها، Cloud Firestore را در مقیاس بزرگ، سریع نگه می‌دارد.

باز کردن لینک از پیام خطا، به طور خودکار رابط کاربری ایجاد شاخص را در کنسول Firebase با پارامترهای صحیح پر شده باز می‌کند. در بخش بعدی، شاخص‌های مورد نیاز برای این برنامه را خواهیم نوشت و مستقر خواهیم کرد.

۱۰. استقرار ایندکس‌ها

اگر نخواهیم هر مسیر را در برنامه خود بررسی کنیم و تک تک لینک‌های ایجاد ایندکس را دنبال کنیم، می‌توانیم به راحتی با استفاده از Firebase CLI، تعداد زیادی ایندکس را به طور همزمان مستقر کنیم.

  1. در دایرکتوری محلی دانلود شده‌ی برنامه‌تان، فایلی به firestore.indexes.json پیدا خواهید کرد.

این فایل تمام شاخص‌های مورد نیاز برای تمام ترکیبات ممکن فیلترها را شرح می‌دهد.

فایل firestore.indexs.json

{
 "indexes": [
   {
     "collectionGroup": "restaurants",
     "queryScope": "COLLECTION",
     "fields": [
       { "fieldPath": "city", "order": "ASCENDING" },
       { "fieldPath": "avgRating", "order": "DESCENDING" }
     ]
   },

   ...

 ]
}
  1. این ایندکس‌ها را با دستور زیر مستقر کنید:
firebase deploy --only firestore:indexes

بعد از چند دقیقه، ایندکس‌های شما فعال می‌شوند و پیام‌های خطا از بین می‌روند.

۱۱. نوشتن داده‌ها در یک تراکنش

در این بخش، قابلیتی را اضافه خواهیم کرد که کاربران بتوانند نظرات خود را در مورد رستوران‌ها ارسال کنند. تاکنون، تمام نوشته‌های ما اتمیک و نسبتاً ساده بوده‌اند. اگر هر یک از آنها خطا داشته باشد، احتمالاً فقط از کاربر می‌خواهیم که آنها را دوباره امتحان کند یا برنامه ما به طور خودکار نوشتن را دوباره امتحان می‌کند.

برنامه ما کاربران زیادی خواهد داشت که می‌خواهند برای یک رستوران امتیاز اضافه کنند، بنابراین باید چندین عملیات خواندن و نوشتن را هماهنگ کنیم. ابتدا باید خود نظر ارسال شود، سپس count امتیاز و average rating رستوران باید به‌روزرسانی شود. اگر یکی از این موارد با شکست مواجه شود و دیگری نه، ما در یک حالت ناسازگار قرار می‌گیریم که در آن داده‌های یک بخش از پایگاه داده ما با داده‌های بخش دیگر مطابقت ندارد.

خوشبختانه، Cloud Firestore قابلیت تراکنش را فراهم می‌کند که به ما امکان می‌دهد چندین عملیات خواندن و نوشتن را در یک عملیات اتمی انجام دهیم و از ثبات داده‌های خود اطمینان حاصل کنیم.

  1. به فایل scripts/FriendlyEats.Data.js خود برگردید.
  2. تابع FriendlyEats.prototype.addRating را پیدا کنید.
  3. کل تابع را با کد زیر جایگزین کنید.

FriendlyEats.Data.js

FriendlyEats.prototype.addRating = function(restaurantID, rating) {
  var collection = firebase.firestore().collection('restaurants');
  var document = collection.doc(restaurantID);
  var newRatingDocument = document.collection('ratings').doc();

  return firebase.firestore().runTransaction(function(transaction) {
    return transaction.get(document).then(function(doc) {
      var data = doc.data();

      var newAverage =
          (data.numRatings * data.avgRating + rating.rating) /
          (data.numRatings + 1);

      transaction.update(document, {
        numRatings: data.numRatings + 1,
        avgRating: newAverage
      });
      return transaction.set(newRatingDocument, rating);
    });
  });
};

در بلوک بالا، ما یک تراکنش را برای به‌روزرسانی مقادیر عددی avgRating و numRatings در سند رستوران آغاز می‌کنیم. همزمان، rating جدید را به زیرمجموعه ratings اضافه می‌کنیم.

۱۲. داده‌های خود را ایمن کنید

در ابتدای این آزمایشگاه کد، ما قوانین امنیتی برنامه خود را برای محدود کردن دسترسی به برنامه خود تنظیم کردیم.

قوانین فروشگاه آتش‌نشانی

rules_version = '2';
service cloud.firestore {

  // Determine if the value of the field "key" is the same
  // before and after the request.
  function unchanged(key) {
    return (key in resource.data)
      && (key in request.resource.data)
      && (resource.data[key] == request.resource.data[key]);
  }

  match /databases/{database}/documents {
    // Restaurants:
    //   - Authenticated user can read
    //   - Authenticated user can create/update (for demo purposes only)
    //   - Updates are allowed if no fields are added and name is unchanged
    //   - Deletes are not allowed (default)
    match /restaurants/{restaurantId} {
      allow read: if request.auth != null;
      allow create: if request.auth != null;
      allow update: if request.auth != null
                    && (request.resource.data.keys() == resource.data.keys())
                    && unchanged("name");

      // Ratings:
      //   - Authenticated user can read
      //   - Authenticated user can create if userId matches
      //   - Deletes and updates are not allowed (default)
      match /ratings/{ratingId} {
        allow read: if request.auth != null;
        allow create: if request.auth != null
                      && request.resource.data.userId == request.auth.uid;
      }
    }
  }
}

این قوانین دسترسی را محدود می‌کنند تا اطمینان حاصل شود که کلاینت‌ها فقط تغییرات ایمن را انجام می‌دهند. برای مثال:

  • به‌روزرسانی‌های سند رستوران فقط می‌توانند رتبه‌بندی‌ها را تغییر دهند، نه نام یا هیچ داده تغییرناپذیر دیگری را.
  • رتبه‌بندی‌ها فقط در صورتی می‌توانند ایجاد شوند که شناسه کاربری با کاربر وارد شده مطابقت داشته باشد، که این امر از جعل هویت جلوگیری می‌کند.

به عنوان یک جایگزین برای استفاده از کنسول Firebase، می‌توانید از رابط خط فرمان Firebase برای استقرار قوانین در پروژه Firebase خود استفاده کنید. فایل firestore.rules در دایرکتوری کاری شما از قبل حاوی قوانین فوق است. برای استقرار این قوانین از سیستم فایل محلی خود (به جای استفاده از کنسول Firebase)، دستور زیر را اجرا کنید:

firebase deploy --only firestore:rules

۱۳. نتیجه‌گیری

در این آزمایشگاه کد، شما یاد گرفتید که چگونه عملیات خواندن و نوشتن مقدماتی و پیشرفته را با Cloud Firestore انجام دهید، و همچنین چگونه دسترسی به داده‌ها را با قوانین امنیتی ایمن کنید. می‌توانید راه‌حل کامل را در مخزن quickstarts-js پیدا کنید.

برای کسب اطلاعات بیشتر در مورد Cloud Firestore، به منابع زیر مراجعه کنید:

۱۴. [اختیاری] اجرا با بررسی برنامه

Firebase App Check با کمک به اعتبارسنجی و جلوگیری از ترافیک ناخواسته به برنامه شما، محافظت ایجاد می‌کند. در این مرحله، با اضافه کردن App Check with reCAPTCHA Enterprise ، دسترسی به سرویس‌های خود را ایمن خواهید کرد.

ابتدا، باید App Check و reCaptcha را فعال کنید.

فعال کردن reCaptcha Enterprise

  1. در کنسول Cloud، در قسمت Security، گزینه reCaptcha Enterprise را پیدا کرده و انتخاب کنید.
  2. سرویس را طبق دستورالعمل فعال کنید و روی ایجاد کلید (Create Key) کلیک کنید.
  3. یک نام نمایشی مطابق درخواست وارد کنید و وب‌سایت را به عنوان نوع پلتفرم خود انتخاب کنید.
  4. آدرس‌های اینترنتی (URL) مستقر شده خود را به لیست دامنه اضافه کنید و مطمئن شوید که گزینه "استفاده از چالش کادر انتخاب" علامت نخورده باشد.
  5. روی ایجاد کلید (Create Key) کلیک کنید و کلید تولید شده را در جایی برای نگهداری امن ذخیره کنید. در ادامه‌ی این مرحله به آن نیاز خواهید داشت.

فعال کردن بررسی برنامه

  1. در کنسول Firebase، بخش Build را در پنل سمت چپ پیدا کنید.
  2. روی «بررسی برنامه» کلیک کنید، سپس روی دکمه «شروع به کار» کلیک کنید (یا مستقیماً به کنسول هدایت شوید).
  3. روی «ثبت نام» کلیک کنید و در صورت درخواست، کلید reCaptcha Enterprise خود را وارد کنید، سپس روی «ذخیره» کلیک کنید.
  4. در نمای APIها، Storage را انتخاب کرده و روی Enforce کلیک کنید. همین کار را برای Cloud Firestore انجام دهید.

اکنون باید App Check اجرا شده باشد! برنامه خود را رفرش کنید و سعی کنید یک رستوران ایجاد/مشاهده کنید. باید پیام خطای زیر را دریافت کنید:

Uncaught Error in snapshot listener: FirebaseError: [code=permission-denied]: Missing or insufficient permissions.

این یعنی App Check به طور پیش‌فرض درخواست‌های اعتبارسنجی نشده را مسدود می‌کند. حالا بیایید اعتبارسنجی را به برنامه خود اضافه کنیم.

به فایل FriendlyEats.View.js بروید و تابع initAppCheck را به‌روزرسانی کنید و کلید reCaptcha خود را برای راه‌اندازی اولیه App Check اضافه کنید.

FriendlyEats.prototype.initAppCheck = function() {
    var appCheck = firebase.appCheck();
    appCheck.activate(
    new firebase.appCheck.ReCaptchaEnterpriseProvider(
      /* reCAPTCHA Enterprise site key */
    ),
    true // Set to true to allow auto-refresh.
  );
};

نمونه appCheck با یک ReCaptchaEnterpriseProvider به همراه کلید شما مقداردهی اولیه می‌شود و isTokenAutoRefreshEnabled به توکن‌ها اجازه می‌دهد تا در برنامه شما به صورت خودکار به‌روزرسانی شوند.

برای فعال کردن تست محلی، بخشی را که برنامه در آن مقداردهی اولیه شده است در فایل FriendlyEats.js پیدا کنید و خط زیر را به تابع FriendlyEats.prototype.initAppCheck اضافه کنید:

if(isLocalhost) {
  self.FIREBASE_APPCHECK_DEBUG_TOKEN = true;
}

این دستور یک توکن اشکال‌زدایی (debug token) را در کنسول برنامه وب محلی شما، مشابه کد زیر، ثبت می‌کند:

App Check debug token: 8DBDF614-649D-4D22-B0A3-6D489412838B. You will need to add it to your app's App Check settings in the Firebase console for it to work.

حالا، در کنسول فایربیس به نمای برنامه‌ها (Apps View) از بخش بررسی برنامه‌ها (App Check) بروید.

روی منوی سرریز کلیک کنید و مدیریت توکن‌های اشکال‌زدایی را انتخاب کنید.

سپس، روی افزودن توکن اشکال‌زدایی کلیک کنید و توکن اشکال‌زدایی را از کنسول خود، همانطور که از شما خواسته شد، جایگذاری کنید.

تبریک! اکنون App Check باید در برنامه شما کار کند.