Codelab בנושא Firebase Angular Web Frameworks

1. מה יוצרים?

בקודלאב הזה תלמדו ליצור בלוג נסיעות עם מפה שיתופית בזמן אמת, באמצעות הגרסה העדכנית ביותר של ספריית Angular: AngularFire. אפליקציית האינטרנט הסופית תכלול בלוג בנושא טיולים, שבו תוכלו להעלות תמונות לכל מקום שביקרתם בו.

AngularFire ישמש לפיתוח אפליקציית האינטרנט, חבילת אמולטור לבדיקה מקומית, אימות למעקב אחרי נתוני משתמשים, Firestore ו-Storage כדי לשמור נתונים ומדיה, יופעל על ידי Cloud Functions, ולבסוף, אירוח ב-Firebase לפריסת האפליקציה.

מה תלמדו

  • איך לפתח עם מוצרי Firebase באופן מקומי באמצעות חבילת Emulator
  • איך לשפר את אפליקציית האינטרנט באמצעות AngularFire
  • איך לשמור את הנתונים ב-Firestore
  • איך לשמור מדיה באחסון
  • איך פורסים את האפליקציה ב-Firebase Hosting
  • איך משתמשים ב-Cloud Functions כדי ליצור אינטראקציה עם מסדי הנתונים וממשקי ה-API

מה צריך להכין

  • Node.js מגרסה 10 ואילך
  • חשבון Google ליצירה ולניהול של פרויקט Firebase
  • Firebase CLI בגרסה 11.14.2 ואילך
  • דפדפן לבחירתכם, כמו Chrome
  • הבנה בסיסית של Angular ו-JavaScript

2. קבלת קוד לדוגמה

משכפלים את מאגר GitHub של Codelab משורת הפקודה:

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

לחלופין, אם לא התקנתם את git, תוכלו להוריד את המאגר כקובץ ZIP.

מאגר GitHub מכיל פרויקטים לדוגמה לכמה פלטפורמות.

ה-Codelab הזה משתמש רק במאגר ה-webframework:

  • 📁 webframework: הקוד הבסיסי שעליו תבנו במהלך ה-Codelab הזה.

התקנת יחסי תלות

אחרי היצירה באמצעות קלון, מתקינים את יחסי התלות ברמה הבסיסית (root) ובתיקייה functions לפני שמפתחים את אפליקציית האינטרנט.

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

התקנת Firebase CLI

מתקינים את Firebase CLI באמצעות הפקודה הבאה בטרמינל:

npm install -g firebase-tools

מוודאים שגרסת ה-CLI של Firebase גדולה מ-11.14.2 באמצעות:

firebase  --version

אם הגרסה שלך נמוכה מ-11.14.2, עליך לעדכן אותה באמצעות:

npm update firebase-tools

3. יצירה והגדרה של פרויקט Firebase

יצירת פרויקט Firebase

  1. נכנסים ל-Firebase.
  2. במסוף Firebase, לוחצים על הוספת פרויקט ונותנים את השם <your-project> לפרויקט Firebase. חשוב לזכור את מזהה הפרויקט של פרויקט Firebase.
  3. לוחצים על Create Project.

חשוב: שם הפרויקט ב-Firebase יהיה <your-project>, אבל מערכת Firebase תקצה לו באופן אוטומטי מזהה פרויקט ייחודי בפורמט <your-project>-1234. המזהה הייחודי הזה הוא האופן שבו הפרויקט מזוהה בפועל (כולל ב-CLI), ואילו <your-project> הוא פשוט שם לתצוגה.

האפליקציה שנשיק עכשיו משתמשת במוצרי Firebase שזמינים לאפליקציות אינטרנט:

  • אימות ב-Firebase כדי לאפשר למשתמשים להיכנס בקלות לאפליקציה שלכם.
  • Cloud Firestore כדי לשמור נתונים מובְנים בענן ולקבל התראות מיידיות על שינויים בנתונים.
  • Cloud Storage for Firebase לשמירת קבצים בענן.
  • אירוח ב-Firebase לאירוח ולהצגה של הנכסים שלכם.
  • פונקציות לאינטראקציה עם ממשקי API פנימיים וחיצוניים.

לחלק מהמוצרים האלה צריך לקבוע הגדרות מיוחדות או להפעיל אותם באמצעות מסוף Firebase.

הוספת אפליקציית אינטרנט של Firebase לפרויקט

  1. לוחצים על סמל האינטרנט כדי ליצור אפליקציית אינטרנט חדשה ב-Firebase.
  2. בשלב הבא יוצג אובייקט של הגדרה. מעתיקים את התוכן של האובייקט הזה לקובץ environments/environment.ts.

מפעילים את האפשרות כניסה באמצעות חשבון Google ל-Firebase.

כדי לאפשר למשתמשים להיכנס לאפליקציית האינטרנט באמצעות חשבונות Google שלהם, נשתמש בשיטת הכניסה של Google.

כדי להפעיל כניסה באמצעות חשבון Google:

  1. במסוף Firebase, מאתרים את הקטע Build בחלונית השמאלית.
  2. לוחצים על אימות ולאחר מכן לוחצים על הכרטיסייה שיטת כניסה (אפשר גם ללחוץ כאן כדי לעבור ישירות לשם).
  3. מפעילים את ספק הכניסה של Google ולוחצים על שמירה.
  4. מגדירים את השם הציבורי של האפליקציה כ-<your-project-name> ובוחרים מהתפריט הנפתח באפשרות Project support email (אימייל תמיכה בפרויקט).

הפעלת Cloud Firestore

  1. בקטע Build במסוף Firebase, לוחצים על Firestore Database.
  2. לוחצים על Create dataset בחלונית Cloud Firestore.
  3. הגדרת המיקום שבו יאוחסנו הנתונים שלכם ב-Cloud Firestore. אפשר להשאיר את האפשרות הזו כברירת המחדל או לבחור אזור קרוב אליכם.

הפעלת Cloud Storage

אפליקציית האינטרנט משתמשת ב-Cloud Storage for Firebase כדי לאחסן, להעלות ולשתף תמונות.

  1. בקטע Build במסוף Firebase, לוחצים על Storage.
  2. אם הלחצן תחילת העבודה לא מופיע, סימן ש-Cloud Storage כבר

מופעלת, ואין צורך לבצע את השלבים הבאים.

  1. לוחצים על Get Started.
  2. קוראים את כתב הוויתור לגבי כללי האבטחה של הפרויקט ב-Firebase ולוחצים על הבא.
  3. המיקום של Cloud Storage נבחר מראש באותו אזור שבחרתם למסד הנתונים שלכם ב-Cloud Firestore. לוחצים על Done כדי להשלים את ההגדרה.

לפי כללי האבטחה שמוגדרים כברירת מחדל, כל משתמש מאומת יכול לכתוב כל דבר ב-Cloud Storage. נפח האחסון שלנו יהיה מאובטח יותר בהמשך ב-Codelab הזה.

4. מקשרים את פרויקט Firebase

ממשק שורת הפקודה של Firebase (CLI) מאפשר לכם להשתמש באירוח ב-Firebase כדי לשרת את אפליקציית האינטרנט שלכם באופן מקומי, וגם לפרוס את אפליקציית האינטרנט שלכם לפרויקט Firebase.

חשוב לוודא ששורת הפקודה ניגשת לספריית webframework המקומית של האפליקציה.

מחברים את הקוד של אפליקציית האינטרנט לפרויקט Firebase. קודם כול, מתחברים ל-CLI של 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 ישירות מ-AgularFire ולעקוב אחרי מצב האימות של המשתמש באמצעות הפונקציה 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 מגיעות מ-AgularFire כחבילה מראש. כל מסמך שייך לאוסף, וכל מסמך יכול לכלול גם אוספים בתצוגת עץ. כדי ליצור ולעדכן פוסט בבלוג בנושא נסיעות, צריך לדעת 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;

	})
}

עדכון ומחיקה של נתונים

לפי uid של כל פוסט בנושא נסיעות, אפשר להסיק את הנתיב של המסמך שנשמר ב-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 יושמו בשירות Travel, כך שאפשר לראות אותן בפעולה.

שימוש בפונקציות של 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 נקרא ב-constructor כדי לקבל מערך 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. הגדרת האחסון

מעכשיו המערכת תטמיע את 'אחסון' כדי לאחסן תמונות וסוגים אחרים של מדיה.

הכי כדאי להשתמש ב-Cloud Firestore לאחסון נתונים מובְנים, כמו אובייקטים של JSON. Cloud Storage נועד לאחסן קבצים או blobs. באפליקציה הזו, תשתמשו בה כדי לאפשר למשתמשים לשתף את תמונות הנסיעה שלהם.

בדומה ל-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, פונקציות וכללי אבטחה, אל תשכחו לבדוק את השלבים האופציונליים שבהמשך, וגם את 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;
		}
	}
}