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

1- المحتوى الذي ستنشئه

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

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

المعلومات التي ستطّلع عليها

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

المتطلبات

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

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

استنسِخ مستودع GitHub في الدرس التطبيقي حول الترميز من سطر الأوامر:

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

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

يحتوي مستودع جيت هب على نماذج مشروعات لأنظمة أساسية متعددة.

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

  • 📁 إطار الويب: رمز البدء الذي ستعتمد عليه خلال هذا الدرس التطبيقي حول الترميز.

تثبيت الملحقات

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

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

تثبيت واجهة سطر الأوامر بنظام Firebase

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

npm install -g firebase-tools

يُرجى التحقّق من أنّ إصدار واجهة سطر الأوامر في Firebase أكبر من 11.14.2 باستخدام:

firebase  --version

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

npm update firebase-tools

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

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

  1. سجِّل الدخول إلى Firebase.
  2. في "وحدة تحكُّم Firebase"، انقر على إضافة مشروع، ثم أدخِل اسمًا لمشروع Firebase <your-project>. تذكّر رقم تعريف مشروعك على Firebase.
  3. انقر على إنشاء مشروع.

ملاحظة مهمّة: ستتم تسمية مشروعك في Firebase باسم <your-project>، ولكن سيحدّد له Firebase رقم تعريف مشروع فريد تلقائيًا بالتنسيق <your-project>-1234. هذا المعرّف الفريد هو كيفية تحديد مشروعك فعليًا (بما في ذلك واجهة سطر الأوامر)، في حين أن <your-project> هو مجرد اسم معروض.

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

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

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

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

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

تفعيل تسجيل الدخول إلى حساب 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

يستخدم تطبيق الويب Cloud Storage for Firebase لتخزين الصور وتحميلها ومشاركتها.

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

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

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

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

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

تتيح لك واجهة سطر أوامر Firebase (CLI) استخدام ميزة "استضافة 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 ويستجيب محاكي المصادقة عبر المنفذ 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

في "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، والذي يمكن قراءته أو تحديثه أو حذفه باستخدام الدالتين 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 في الدالة الإنشائية للحصول على صفيفة قابلة للملاحظة لجميع الانتقالات.

في حال الحاجة إلى رحلات المستخدم الحالي فقط، يمكنك استخدام الدالة 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- ضبط مساحة التخزين

ستتمكن الآن من استخدام مساحة التخزين لتخزين الصور وأنواع أخرى من الوسائط.

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

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

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

تحميل ملف

انتقِل إلى src/app/services/travel.service.ts وأدخِل مساحة تخزين من 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. سيمكنك الآن الوصول إلى جميع البيانات والإحصاءات في "وحدة تحكُّم Firebase".

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

11- اختياري: وسائل حماية مصادقات AngularFire

إلى جانب مصادقة Firebase، يوفِّر 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;
		}
	}
}