درس تطبيقي حول الترميز في إطارات عمل Firebase Angular Web

1. ما ستنشئه

في هذا الدرس العملي، ستنشئ مدوّنة سفر تتضمّن خريطة تعاونية في الوقت الفعلي باستخدام أحدث إصدار من مكتبة Angular: AngularFire. سيتألف تطبيق الويب النهائي من مدونة سفر يمكنك فيها تحميل صور لكل موقع جغرافي سافرت إليه.

سيتم استخدام AngularFire لإنشاء تطبيق الويب، وEmulator Suite للاختبار المحلي، وAuthentication لتتبُّع بيانات المستخدمين، وFirestore وStorage للاحتفاظ بالبيانات والوسائط، وكل ذلك باستخدام Cloud Functions، وأخيرًا Firebase Hosting لنشر التطبيق.

أهداف الدورة التعليمية

  • كيفية التطوير باستخدام منتجات Firebase محليًا من خلال "مجموعة أدوات المحاكاة"
  • كيفية تحسين تطبيق الويب باستخدام AngularFire
  • كيفية الاحتفاظ ببياناتك في Firestore
  • كيفية الاحتفاظ بالوسائط في "وحدة التخزين"
  • كيفية نشر تطبيقك على "استضافة Firebase"
  • كيفية استخدام Cloud Functions للتفاعل مع قواعد البيانات وواجهات برمجة التطبيقات

المتطلبات

  • الإصدار 10 من Node.js أو إصدار أحدث
  • حساب Google لإنشاء مشروعك على Firebase وإدارته
  • الإصدار 11.14.2 أو الإصدارات الأحدث من Firebase CLI
  • متصفّح من اختيارك، مثل Chrome
  • فهم أساسي لـ Angular وJavaScript

2. الحصول على الرمز النموذجي

استنسِخ مستودع GitHub الخاص بالدرس العملي من سطر الأوامر:

git clone https://github.com/firebase/codelab-friendlychat-web

بدلاً من ذلك، إذا لم يكن git مثبّتًا لديك، يمكنك تنزيل المستودع كملف ZIP.

يحتوي مستودع Github على نماذج مشاريع لمنصات متعددة.

يستخدم هذا الدرس التطبيقي العملي مستودع webframework فقط:

  • ‫📁 webframework: الرمز الأولي الذي ستستند إليه أثناء هذا الدرس التطبيقي حول الترميز.

تثبيت الحِزم التابعة

بعد الاستنساخ، ثبِّت التبعيات في المجلد الجذر والمجلد functions قبل إنشاء تطبيق الويب.

cd webframework && npm install
cd functions && npm install

تثبيت Firebase CLI

ثبِّت واجهة سطر الأوامر (CLI) في Firebase باستخدام الأمر التالي في الوحدة الطرفية:

npm install -g firebase-tools

تأكَّد من أنّ إصدار Firebase CLI أكبر من 11.14.2 باستخدام الأمر التالي:

firebase  --version

إذا كان إصدارك أقل من 11.14.2، يُرجى التحديث باستخدام:

npm update firebase-tools

3- إنشاء مشروع Firebase وإعداده

إنشاء مشروع على Firebase

  1. سجِّل الدخول إلى وحدة تحكّم Firebase باستخدام حسابك على Google.
  2. انقر على الزر لإنشاء مشروع جديد، ثم أدخِل اسم المشروع (على سبيل المثال، FriendlyChat).
  3. انقر على متابعة.
  4. إذا طُلب منك ذلك، راجِع بنود Firebase واقبلها، ثم انقر على متابعة.
  5. (اختياري) فعِّل ميزة "المساعدة المستندة إلى الذكاء الاصطناعي" في وحدة تحكّم Firebase (المعروفة باسم "Gemini في Firebase").
  6. في هذا الدرس العملي، لا تحتاج إلى "إحصاءات Google"، لذا أوقِف خيار "إحصاءات Google".
  7. انقر على إنشاء مشروع، وانتظِر إلى أن يتم توفير مشروعك، ثم انقر على متابعة.

إضافة تطبيق ويب على Firebase إلى المشروع

  1. انقر على رمز الويب لإنشاء تطبيق ويب جديد على Firebase.
  2. في الخطوة التالية، سيظهر لك عنصر إعداد. انسخ محتوى هذا العنصر إلى ملف environments/environment.ts.

إعداد منتجات Firebase

يستخدم التطبيق الذي سننشئه منتجات Firebase المتاحة لتطبيقات الويب:

  • مصادقة Firebase للسماح للمستخدمين بتسجيل الدخول إلى تطبيقك بسهولة
  • ‫Cloud Firestore لحفظ البيانات المنظَّمة على السحابة الإلكترونية وتلقّي إشعار فوري عند تغيُّر البيانات
  • مساحة تخزين سحابية لـ Firebase لحفظ الملفات في السحابة الإلكترونية
  • استضافة Firebase لاستضافة مواد العرض وعرضها
  • الدوال للتفاعل مع واجهات برمجة التطبيقات الداخلية والخارجية

تتطلّب بعض هذه المنتجات إعدادات خاصة أو يجب تفعيلها باستخدام وحدة تحكّم Firebase.

تفعيل تسجيل الدخول باستخدام Google في خدمة "مصادقة Firebase"

للسماح للمستخدمين بتسجيل الدخول إلى تطبيق الويب باستخدام حساباتهم على Google، سنستخدم طريقة تسجيل الدخول عبر Google.

لتفعيل ميزة تسجيل الدخول باستخدام حساب Google، اتّبِع الخطوات التالية:

  1. في وحدة تحكّم Firebase، ابحث عن قسم الإنشاء في اللوحة اليمنى.
  2. انقر على المصادقة، ثم انقر على علامة التبويب طريقة تسجيل الدخول (أو انقر هنا للانتقال مباشرةً إلى هناك).
  3. فعِّل موفّر تسجيل الدخول Google، ثم انقر على حفظ.
  4. اضبط الاسم المعروض للجميع لتطبيقك على <your-project-name> واختَر عنوان البريد الإلكتروني المخصّص للدعم في المشروع من القائمة المنسدلة.

تفعيل Cloud Firestore

  1. في قسم إنشاء في وحدة تحكّم Firebase، انقر على قاعدة بيانات Firestore.
  2. انقر على إنشاء قاعدة بيانات في لوحة Cloud Firestore.
  3. اضبط الموقع الجغرافي الذي يتم فيه تخزين بيانات Cloud Firestore. يمكنك ترك هذا الخيار كإعداد تلقائي أو اختيار منطقة قريبة منك.

تفعيل Cloud Storage

يستخدم تطبيق الويب "مساحة تخزين سحابية لـ Firebase" لتخزين الصور وتحميلها ومشاركتها.

  1. في قسم إنشاء في وحدة تحكّم Firebase، انقر على Storage.
  2. إذا لم يظهر زر البدء، يعني ذلك أنّ ميزة "التخزين السحابي"

مفعَّلة، ولن تحتاج إلى اتّباع الخطوات أدناه.

  1. انقر على البدء.
  2. اقرأ بيان إخلاء المسؤولية عن قواعد الأمان لمشروعك على Firebase، ثم انقر على التالي.
  3. يتم تحديد موقع Cloud Storage مسبقًا باستخدام المنطقة نفسها التي اخترتها لقاعدة بيانات Cloud Firestore. انقر على تم لإكمال عملية الإعداد.

باستخدام قواعد الأمان التلقائية، يمكن لأي مستخدم مصادَق عليه كتابة أي شيء في Cloud Storage. سنعمل على زيادة أمان مساحة التخزين لاحقًا في هذا الدرس البرمجي.

4. الربط بمشروعك على Firebase

تتيح لك واجهة سطر الأوامر (CLI) في Firebase استخدام ميزة "استضافة Firebase" لعرض تطبيق الويب محليًا، بالإضافة إلى نشر تطبيق الويب في مشروعك على Firebase.

تأكَّد من أنّ سطر الأوامر يصل إلى دليل webframework المحلي لتطبيقك.

ربط رمز تطبيق الويب بمشروعك على Firebase أولاً، سجِّل الدخول إلى واجهة سطر الأوامر في Firebase من خلال سطر الأوامر:

firebase login

بعد ذلك، شغِّل الأمر التالي لإنشاء اسم مستعار للمشروع. استبدِل $YOUR_PROJECT_ID بمعرّف مشروعك على Firebase.

firebase  use  $YOUR_PROJECT_ID

إضافة AngularFire

لإضافة AngularFire إلى التطبيق، شغِّل الأمر:

ng add @angular/fire

بعد ذلك، اتّبِع التعليمات الواردة في سطر الأوامر، واختَر الميزات المتوفّرة في مشروعك على Firebase.

إعداد Firebase

لبدء مشروع Firebase، نفِّذ الأمر التالي:

firebase init

بعد ذلك، اتّبِع التعليمات التي تظهر في سطر الأوامر، واختَر الميزات والمحاكيات التي تم استخدامها في مشروعك على Firebase.

بدء المحاكيات

من دليل webframework، نفِّذ الأمر التالي لبدء المحاكيات:

firebase  emulators:start

في النهاية، من المفترض أن يظهر لك محتوى مشابه لما يلي:

$  firebase  emulators:start

i  emulators:  Starting  emulators:  auth,  functions,  firestore,  hosting,  functions

i  firestore:  Firestore  Emulator  logging  to  firestore-debug.log

i  hosting:  Serving  hosting  files  from:  public

  hosting:  Local  server:  http://localhost:5000

i  ui:  Emulator  UI  logging  to  ui-debug.log

i  functions:  Watching  "/functions"  for  Cloud  Functions...

  functions[updateMap]:  firestore  function  initialized.

  

┌─────────────────────────────────────────────────────────────┐

    All  emulators  ready!  It  is  now  safe  to  connect  your  app.  

  i  View  Emulator  UI  at  http://localhost:4000  

└─────────────────────────────────────────────────────────────┘

  

┌────────────────┬────────────────┬─────────────────────────────────┐

  Emulator    Host:Port    View  in  Emulator  UI  

├────────────────┼────────────────┼─────────────────────────────────┤

  Authentication    localhost:9099    http://localhost:4000/auth  

├────────────────┼────────────────┼─────────────────────────────────┤

  Functions    localhost:5001    http://localhost:4000/functions  

├────────────────┼────────────────┼─────────────────────────────────┤

  Firestore    localhost:8080    http://localhost:4000/firestore  

├────────────────┼────────────────┼─────────────────────────────────┤

  Hosting    localhost:5000    n/a  

└────────────────┴────────────────┴─────────────────────────────────┘

Emulator  Hub  running  at  localhost:4400

Other  reserved  ports:  4500

  

Issues?  Report  them  at  https://github.com/firebase/firebase-tools/issues  and  attach  the  *-debug.log  files.

بعد ظهور الرسالة ✔All emulators ready!، تكون المحاكيات جاهزة للاستخدام.

من المفترض أن تظهر لك واجهة مستخدم تطبيق السفر، ولكنها لن تعمل (حتى الآن):

لنبدأ الآن في إنشاء التطبيق.

5- ربط تطبيق الويب بالمحاكيات

استنادًا إلى الجدول في سجلات المحاكي، يستمع محاكي Cloud Firestore إلى المنفذ 8080 ويستمع محاكي Authentication إلى المنفذ 9099.

فتح EmulatorUI

في متصفّح الويب، انتقِل إلى http://127.0.0.1:4000/‎. من المفترض أن تظهر لك واجهة مستخدم Emulator Suite.

توجيه التطبيق لاستخدام المحاكيات

في src/app/app.module.ts، أضِف الرمز التالي إلى قائمة عمليات الاستيراد في AppModule:

@NgModule({
	declarations: [...],
	imports: [
		provideFirebaseApp(() =>  initializeApp(environment.firebase)),

		provideAuth(() => {
			const  auth = getAuth();
			if (location.hostname === 'localhost') {
				connectAuthEmulator(auth, 'http://127.0.0.1:9099', { disableWarnings:  true });
			}
			return  auth;
		}),

		provideFirestore(() => {
			const  firestore = getFirestore();
			if (location.hostname === 'localhost') {
				connectFirestoreEmulator(firestore, '127.0.0.1', 8080);
			}
			return  firestore;
		}),

		provideFunctions(() => {
			const  functions = getFunctions();
			if (location.hostname === 'localhost') {
				connectFunctionsEmulator(functions, '127.0.0.1', 5001);
			}
			return  functions;
		}),

		provideStorage(() => {
			const  storage = getStorage();
			if (location.hostname === 'localhost') {
				connectStorageEmulator(storage, '127.0.0.1', 5001);
			}
			return  storage;
		}),
		...
	]

تم الآن إعداد التطبيق لاستخدام المحاكيات المحلية، ما يتيح إجراء الاختبارات وعمليات التطوير محليًا.

6. إضافة مصادقة

بعد إعداد المحاكي للتطبيق، يمكننا إضافة ميزات "المصادقة" لضمان تسجيل دخول كل مستخدم قبل نشر الرسائل.

لإجراء ذلك، يمكننا استيراد دوال signin مباشرةً من AngularFire، وتتبُّع حالة مصادقة المستخدم باستخدام الدالة authState. عدِّل وظائف صفحة تسجيل الدخول لكي تتحقّق الصفحة من حالة مصادقة المستخدم عند التحميل.

إدخال AngularFire Auth

في src/app/pages/login-page/login-page.component.ts، استورِد Auth من @angular/fire/auth، وأدرِجه في LoginPageComponent. يمكن أيضًا استيراد موفّري المصادقة، مثل Google، ووظائف مثلsignin وsignout مباشرةً من الحزمة نفسها واستخدامها في التطبيق.

import { Auth, GoogleAuthProvider, signInWithPopup, signOut, user } from  '@angular/fire/auth';

export  class  LoginPageComponent  implements  OnInit {
	private  auth: Auth = inject(Auth);
	private  provider = new  GoogleAuthProvider();
	user$ = user(this.auth);
	constructor() {}  

	ngOnInit(): void {} 

	login() {
		signInWithPopup(this.auth, this.provider).then((result) => {
			const  credential = GoogleAuthProvider.credentialFromResult(result);
			return  credential;
		})
	}

	logout() {
		signOut(this.auth).then(() => {
			console.log('signed out');}).catch((error) => {
				console.log('sign out error: ' + error);
		})
	}
}

أصبحت صفحة تسجيل الدخول تعمل الآن. جرِّب تسجيل الدخول، واطّلِع على النتائج في "محاكي المصادقة".

7. إعداد Firestore

في هذه الخطوة، ستضيف وظائف لنشر وتعديل مشاركات مدونة السفر المخزّنة في Firestore.

على غرار خدمة المصادقة، تأتي دوال Firestore مُجمَّعة مسبقًا من AngularFire. ينتمي كل مستند إلى مجموعة، ويمكن أن يتضمّن كل مستند أيضًا مجموعات متداخلة. يجب معرفة path المستند في Firestore لإنشاء مشاركة في مدوّنة سفر وتعديلها.

تنفيذ TravelService

بما أنّ العديد من الصفحات المختلفة ستحتاج إلى قراءة مستندات Firestore وتعديلها في تطبيق الويب، يمكننا تنفيذ الدوال في src/app/services/travel.service.ts لتجنُّب إعادة إدخال دوال AngularFire نفسها بشكل متكرّر في كل صفحة.

ابدأ بإدخال Auth، على غرار الخطوة السابقة، بالإضافة إلى Firestore في خدمتنا. من المفيد أيضًا تحديد عنصر user$ قابل للمراقبة يستمع إلى حالة المصادقة الحالية.

import { doc, docData, DocumentReference, Firestore, getDoc, setDoc, updateDoc, collection, addDoc, deleteDoc, collectionData, Timestamp } from  "@angular/fire/firestore";

export  class  TravelService {
	firestore: Firestore = inject(Firestore);
	auth: Auth = inject(Auth);
	user$ = authState(this.auth).pipe(filter(user  =>  user !== null), map(user  =>  user!));
	router: Router = inject(Router);

إضافة مشاركة سفر

ستتوفّر مشاركات السفر كمستندات مخزَّنة في Firestore، وبما أنّ المستندات يجب أن تكون ضمن مجموعات، سيتم تسمية المجموعة التي تحتوي على جميع مشاركات السفر travels. وبالتالي، سيكون مسار أي مشاركة سفر هو travels/

باستخدام الدالة addDoc من AngularFire، يمكن إدراج عنصر في مجموعة:

async  addEmptyTravel(userId: String) {
	...
	addDoc(collection(this.firestore, 'travels'), travelData).then((travelRef) => {
		collection(this.firestore, `travels/${travelRef.id}/stops`);
		setDoc(travelRef, {... travelData, id:  travelRef.id})
		this.router.navigate(['edit', `${travelRef.id}`]);
		return  travelRef;

	})
}

تعديل البيانات وحذفها

بإمكان أي شخص معرفة مسار المستند المخزّن في Firestore من خلال معرّف uid الخاص بأي مشاركة سفر، ويمكن بعد ذلك قراءة هذا المستند أو تعديله أو حذفه باستخدام الدالتَين updateFoc وdeleteDoc في AngularFire:

async  updateData(path: string, data: Partial<Travel | Stop>) {
	await  updateDoc(doc(this.firestore, path), data)
}

async  deleteData(path: string) {
	const  ref = doc(this.firestore, path);
	await  deleteDoc(ref)
}

قراءة البيانات كعنصر قابل للملاحظة

بما أنّه يمكن تعديل المشاركات المتعلقة بالسفر والمحطات على طول الطريق بعد إنشائها، سيكون من المفيد أكثر الحصول على عناصر المستندات كعناصر قابلة للمراقبة، وذلك للاشتراك في أي تغييرات يتم إجراؤها. تتوفّر هذه الوظيفة من خلال الدالتَين docData وcollectionData من @angular/fire/firestore.

getDocData(path: string) {
	return  docData(doc(this.firestore, path), {idField:  'id'}) as  Observable<Travel | Stop>
}

  
getCollectionData(path: string) {
	return  collectionData(collection(this.firestore, path), {idField:  'id'}) as  Observable<Travel[] | Stop[]>
}

إضافة محطّات توقّف إلى مشاركة سفر

بعد إعداد عمليات المشاركات المتعلقة بالسفر، حان الوقت للتفكير في المحطات التي ستظهر ضمن مجموعة فرعية من مشاركة متعلقة بالسفر على النحو التالي: travels//stops/

هذه الخطوات مماثلة تقريبًا لإنشاء منشور سفر، لذا ننصحك بتجربة تنفيذها بنفسك أو الاطّلاع على خطوات التنفيذ أدناه:

async  addStop(travelId: string) {
	...
	const  ref = await  addDoc(collection(this.firestore, `travels/${travelId}/stops`), stopData)
	setDoc(ref, {...stopData, id:  ref.id})
}

رائع! تم تنفيذ وظائف Firestore في خدمة السفر، وبالتالي يمكنك الآن رؤيتها أثناء العمل.

استخدام دوال Firestore في التطبيق

انتقِل إلى src/app/pages/my-travels/my-travels.component.ts وأدرِج TravelService لاستخدام وظائفه.

travelService = inject(TravelService);
travelsData$: Observable<Travel[]>;
stopsList$!: Observable<Stop[]>;
constructor() {
	this.travelsData$ = this.travelService.getCollectionData(`travels`) as  Observable<Travel[]>
}

يتم استدعاء TravelService في الدالة الإنشائية للحصول على مصفوفة Observable لجميع الرحلات.

في حال الحاجة إلى رحلات المستخدم الحالي فقط، استخدِم query الدالة.

تشمل الطرق الأخرى لضمان الأمان تنفيذ قواعد الأمان أو استخدام Cloud Functions مع Firestore كما هو موضّح في الخطوات الاختيارية أدناه.

بعد ذلك، ما عليك سوى استدعاء الدوال التي تم تنفيذها في TravelService.

async  createTravel(userId: String) {
	this.travelService.addEmptyTravel(userId);
}

deleteTravel(travelId: String) {
	this.travelService.deleteData(`travels/${travelId}`)
}

من المفترض أن تعمل صفحة "رحلاتي" الآن. اطّلِع على ما يحدث في محاكي Firestore عند إنشاء منشور جديد عن السفر.

بعد ذلك، كرِّر الخطوات لوظائف التعديل في /src/app/pages/edit-travels/edit-travels.component.ts :

travelService: TravelService = inject(TravelService)
travelId = this.activatedRoute.snapshot.paramMap.get('travelId');
travelData$: Observable<Travel>;
stopsData$: Observable<Stop[]>;

constructor() {
	this.travelData$ = this.travelService.getDocData(`travels/${this.travelId}`) as  Observable<Travel>
	this.stopsData$ = this.travelService.getCollectionData(`travels/${this.travelId}/stops`) as  Observable<Stop[]>
}

updateCurrentTravel(travel: Partial<Travel>) {
	this.travelService.updateData(`travels${this.travelId}`, travel)
}

  

updateCurrentStop(stop: Partial<Stop>) {
	stop.type = stop.type?.toString();
	this.travelService.updateData(`travels${this.travelId}/stops/${stop.id}`, stop)
}

  

addStop() {
	if (!this.travelId) return;
	this.travelService.addStop(this.travelId);
}

deleteStop(stopId: string) {
	if (!this.travelId || !stopId) {
		return;
	}
	this.travelService.deleteData(`travels${this.travelId}/stops/${stopId}`)
	this.stopsData$ = this.travelService.getCollectionData(`travels${this.travelId}/stops`) as  Observable<Stop[]>

}

8. ضبط إعدادات مساحة التخزين

عليك الآن تنفيذ Storage لتخزين الصور وأنواع الوسائط الأخرى.

يُستخدَم Cloud Firestore على أفضل وجه لتخزين البيانات المنظَّمة، مثل عناصر JSON. تم تصميم Cloud Storage لتخزين الملفات أو الملفات الثنائية الكبيرة. في هذا التطبيق، ستستخدمها للسماح للمستخدمين بمشاركة صور سفرهم.

كما هو الحال مع Firestore، يتطلّب تخزين الملفات وتعديلها باستخدام Storage معرّفًا فريدًا لكل ملف.

لننفّذ الدوال في TraveService:

تحميل ملف

انتقِل إلى src/app/services/travel.service.ts وأدرِج Storage من AngularFire:

export  class  TravelService {
firestore: Firestore = inject(Firestore);
auth: Auth = inject(Auth);
storage: Storage = inject(Storage);

وتنفيذ دالة التحميل:

async  uploadToStorage(path: string, input: HTMLInputElement, contentType: any) {
	if (!input.files) return  null
	const  files: FileList = input.files;
		for (let  i = 0; i  <  files.length; i++) {
			const  file = files.item(i);
			if (file) {
				const  imagePath = `${path}/${file.name}`
				const  storageRef = ref(this.storage, imagePath);
				await  uploadBytesResumable(storageRef, file, contentType);
				return  await  getDownloadURL(storageRef);
			}
		}
	return  null;
}

الفرق الأساسي بين الوصول إلى المستندات من Firestore والملفات من Cloud Storage هو أنّه على الرغم من أنّ كلاهما يتّبع مسارات منظَّمة في مجلدات، يتم الحصول على تركيبة عنوان URL الأساسي والمسار من خلال getDownloadURL، والتي يمكن بعد ذلك تخزينها واستخدامها في ملف .

استخدام الوظيفة في التطبيق

انتقِل إلى src/app/components/edit-stop/edit-stop.component.ts واستدعِ دالة التحميل باستخدام:

	async  uploadFile(file: HTMLInputElement, stop: Partial<Stop>) {
	const  path = `/travels/${this.travelId}/stops/${stop.id}`
	const  url = await  this.travelService.uploadToStorage(path, file, {contentType:  'image/png'});
	stop.image = url ? url : '';
	this.travelService.updateData(path, stop);
}

عند تحميل الصورة، سيتم تحميل ملف الوسائط نفسه إلى مساحة التخزين، وسيتم تخزين عنوان URL وفقًا لذلك في المستند في Firestore.

9. نشر التطبيق

أصبحنا الآن جاهزين لنشر التطبيق.

انسخ إعدادات firebase من src/environments/environment.ts إلى src/environments/environment.prod.ts ونفِّذ ما يلي:

firebase deploy

ينبغي أن تظهر لك على النحو التالي:

 Browser application bundle generation complete.
 Copying assets complete.
 Index html generation complete.

=== Deploying to 'friendly-travels-b6a4b'...

i  deploying storage, firestore, hosting
i  firebase.storage: checking storage.rules for compilation errors...
  firebase.storage: rules file storage.rules compiled successfully
i  firestore: reading indexes from firestore.indexes.json...
i  cloud.firestore: checking firestore.rules for compilation errors...
  cloud.firestore: rules file firestore.rules compiled successfully
i  storage: latest version of storage.rules already up to date, skipping upload...
i  firestore: deploying indexes...
i  firestore: latest version of firestore.rules already up to date, skipping upload...
  firestore: deployed indexes in firestore.indexes.json successfully for (default) database
i  hosting[friendly-travels-b6a4b]: beginning deploy...
i  hosting[friendly-travels-b6a4b]: found 6 files in .firebase/friendly-travels-b6a4b/hosting
  hosting[friendly-travels-b6a4b]: file upload complete
  storage: released rules storage.rules to firebase.storage
  firestore: released rules firestore.rules to cloud.firestore
i  hosting[friendly-travels-b6a4b]: finalizing version...
  hosting[friendly-travels-b6a4b]: version finalized
i  hosting[friendly-travels-b6a4b]: releasing new version...
  hosting[friendly-travels-b6a4b]: release complete

  Deploy complete!

Project Console: https://console.firebase.google.com/project/friendly-travels-b6a4b/overview
Hosting URL: https://friendly-travels-b6a4b.web.app

10. تهانينا!

من المفترض أن يكون تطبيقك الآن مكتملاً وتم نشره على Firebase Hosting. سيصبح بإمكانك الآن الوصول إلى جميع البيانات والإحصاءات في "وحدة تحكّم Firebase".

للحصول على المزيد من الميزات المتعلقة بـ AngularFire وFunctions وقواعد الأمان، لا تنسَ الاطّلاع على الخطوات الاختيارية أدناه، بالإضافة إلى Codelabs الأخرى من Firebase.

11. اختياري: أدوات الحماية من AngularFire

بالإضافة إلى خدمة Firebase Authentication، توفّر AngularFire أيضًا أدوات حماية مستندة إلى المصادقة على المسارات، ما يتيح إعادة توجيه المستخدمين الذين لا تتوفّر لهم أذونات وصول كافية. يساعد ذلك في حماية التطبيق من وصول المستخدمين إلى البيانات المحمية.

في src/app/app-routing.module.ts، استورِد

import {AuthGuard, redirectLoggedInTo, redirectUnauthorizedTo} from  '@angular/fire/auth-guard'

يمكنك بعد ذلك تحديد وظائف لتحديد وقت ومكان إعادة توجيه المستخدمين إلى صفحات معيّنة:

const  redirectUnauthorizedToLogin = () =>  redirectUnauthorizedTo(['signin']);
const  redirectLoggedInToTravels = () =>  redirectLoggedInTo(['my-travels']);

بعد ذلك، ما عليك سوى إضافتها إلى مساراتك:

const  routes: Routes = [
	{path:  '', component:  LoginPageComponent, canActivate: [AuthGuard], data: {authGuardPipe:  redirectLoggedInToTravels}},
	{path:  'signin', component:  LoginPageComponent, canActivate: [AuthGuard], data: {authGuardPipe:  redirectLoggedInToTravels}},
	{path:  'my-travels', component:  MyTravelsComponent, canActivate: [AuthGuard], data: {authGuardPipe:  redirectUnauthorizedToLogin}},
	{path:  'edit/:travelId', component:  EditTravelsComponent, canActivate: [AuthGuard], data: {authGuardPipe:  redirectUnauthorizedToLogin}},
];

12. اختياري: قواعد الأمان

يستخدم كل من Firestore وCloud Storage قواعد الأمان (firestore.rules وsecurity.rules على التوالي) لفرض الأمان والتحقّق من صحة البيانات.

في الوقت الحالي، يمكن الوصول إلى بيانات Firestore وStorage وقراءتها وكتابتها، ولكن لا تريد أن يتمكّن المستخدمون من تغيير مشاركات الآخرين. يمكنك استخدام قواعد الأمان لتقييد الوصول إلى مجموعاتك ومستنداتك.

قواعد Firestore

للسماح للمستخدمين الذين تمّت مصادقتهم فقط بالاطّلاع على المشاركات المتعلّقة بالسفر، انتقِل إلى ملف firestore.rules وأضِف ما يلي:

rules_version  =  '2';
service  cloud.firestore  {
	match  /databases/{database}/travels  {
		allow  read:  if  request.auth.uid  !=  null;
		allow  write:
		if  request.auth.uid  ==  request.resource.data.userId;
	}
}

يمكن أيضًا استخدام قواعد الأمان للتحقّق من صحة البيانات:

rules_version  =  '2';
service  cloud.firestore  {
	match  /databases/{database}/posts  {
		allow  read:  if  request.auth.uid  !=  null;
		allow  write:
		if  request.auth.uid  ==  request.resource.data.userId;
		&&  "author"  in  request.resource.data
		&&  "text"  in  request.resource.data
		&&  "timestamp"  in  request.resource.data;
	}
}

قواعد التخزين

وبالمثل، يمكننا استخدام قواعد الأمان لفرض الوصول إلى قواعد بيانات التخزين في storage.rules. يُرجى العِلم أنّه يمكننا أيضًا استخدام الدوال لإجراء عمليات تحقّق أكثر تعقيدًا:

rules_version  =  '2';

function  isImageBelowMaxSize(maxSizeMB)  {
	return  request.resource.size  <  maxSizeMB  *  1024  *  1024
		&&  request.resource.contentType.matches('image/.*');
}

 service  firebase.storage  {
	match  /b/{bucket}/o  {
		match  /{userId}/{postId}/{filename}  {
			allow  write:  if  request.auth  !=  null
			&&  request.auth.uid  ==  userId  &&  isImageBelowMaxSize(5);
			allow  read;
		}
	}
}