Firebase Angular Web Frameworks Codelab

1. ما ستقوم بإنشائه

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

سيتم استخدام AngularFire لإنشاء تطبيق الويب، وEmulator Suite للاختبار المحلي، والمصادقة لتتبع بيانات المستخدم، وFirestore والتخزين لاستمرار البيانات والوسائط، مدعومًا بالوظائف السحابية، وأخيرًا، Firebase Hosting لنشر التطبيق.

ما ستتعلمه

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

ماذا ستحتاج

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

2. احصل على نموذج التعليمات البرمجية

قم باستنساخ مستودع GitHub الخاص بـ Codelab من سطر الأوامر:

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

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

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

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

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

تثبيت التبعيات

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

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

قم بتثبيت Firebase CLI

قم بتثبيت Firebase CLI باستخدام هذا الأمر في المحطة الطرفية:

npm install -g firebase-tools

تأكد مرة أخرى من أن إصدار Firebase CLI الخاص بك أكبر من 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 لحفظ البيانات المنظمة على السحابة والحصول على إشعار فوري عند تغيير البيانات.
  • التخزين السحابي لـ Firebase لحفظ الملفات في السحابة.
  • استضافة Firebase لاستضافة الأصول الخاصة بك وخدمتها.
  • وظائف للتفاعل مع واجهات برمجة التطبيقات الداخلية والخارجية.

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

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

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

تمكين تسجيل الدخول إلى Google لمصادقة Firebase

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

لتمكين تسجيل الدخول إلى Google :

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

تمكين سحابة Firestore

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

تمكين التخزين السحابي

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

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

تم تمكينه، ولا تحتاج إلى اتباع الخطوات أدناه.

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

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

4. اتصل بمشروع Firebase الخاص بك

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

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

قم بتوصيل رمز تطبيق الويب بمشروع Firebase الخاص بك. أولاً، قم بتسجيل الدخول إلى Firebase CLI في سطر الأوامر:

firebase login

قم بعد ذلك بتشغيل الأمر التالي لإنشاء اسم مستعار للمشروع. استبدل $YOUR_PROJECT_ID بمعرف مشروع Firebase الخاص بك.

firebase  use  $YOUR_PROJECT_ID

أضف أنجولار فاير

لإضافة 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 لإنشاء منشور مدونة السفر وتحديثه.

تنفيذ خدمة السفر

نظرًا لأن العديد من الصفحات المختلفة ستحتاج إلى قراءة مستندات 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/ 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 Hosting! سيتم الآن الوصول إلى جميع البيانات والتحليلات في وحدة تحكم Firebase الخاصة بك.

لمزيد من الميزات المتعلقة بـ AngularFire والوظائف وقواعد الأمان، لا تنس مراجعة الخطوات الاختيارية أدناه، بالإضافة إلى مختبرات Firebase Codelabs الأخرى!

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;
		}
	}
}