Lớp học mã khung web góc của Firebase

1. Những gì bạn sẽ tạo ra

Trong lớp học lập trình này, bạn sẽ xây dựng một blog du lịch với bản đồ cộng tác theo thời gian thực bằng bản đồ mới nhất từ ​​thư viện Angular của chúng tôi: AngularFire . Ứng dụng web cuối cùng sẽ bao gồm một blog du lịch nơi bạn có thể tải hình ảnh lên từng địa điểm mà bạn đã đến.

AngularFire sẽ được sử dụng để xây dựng ứng dụng web, Bộ mô phỏng để thử nghiệm cục bộ, Xác thực để theo dõi dữ liệu người dùng, Firestore và Storage để duy trì dữ liệu và phương tiện, được cung cấp bởi Cloud Functions và cuối cùng là Firebase Hosting để triển khai ứng dụng.

Bạn sẽ học được gì

  • Cách phát triển cục bộ các sản phẩm Firebase bằng Emulator Suite
  • Cách nâng cao ứng dụng web của bạn với AngularFire
  • Cách lưu giữ dữ liệu của bạn trong Firestore
  • Cách duy trì phương tiện trong Bộ lưu trữ
  • Cách triển khai ứng dụng của bạn lên Firebase Hosting
  • Cách sử dụng Chức năng đám mây để tương tác với cơ sở dữ liệu và API của bạn

Những gì bạn cần

  • Node.js phiên bản 10 trở lên
  • Tài khoản Google để tạo và quản lý Dự án Firebase của bạn
  • Firebase CLI phiên bản 11.14.2 trở lên
  • Trình duyệt bạn chọn, chẳng hạn như Chrome
  • Hiểu biết cơ bản về Angular và Javascript

2. Lấy mã mẫu

Sao chép kho lưu trữ GitHub của lớp học lập trình từ dòng lệnh:

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

Ngoài ra, nếu bạn chưa cài đặt git, bạn có thể tải xuống kho lưu trữ dưới dạng tệp ZIP .

Kho lưu trữ Github chứa các dự án mẫu cho nhiều nền tảng.

Lớp học lập trình này chỉ sử dụng kho lưu trữ webframework:

  • 📁 webframework : Mã khởi đầu mà bạn sẽ xây dựng trong lớp học lập trình này.

Cài đặt phụ thuộc

Sau khi nhân bản, hãy cài đặt các phần phụ thuộc trong thư mục gốc và functions trước khi xây dựng ứng dụng web.

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

Cài đặt Firebase CLI

Cài đặt Firebase CLI bằng lệnh này trong thiết bị đầu cuối:

npm install -g firebase-tools

Kiểm tra kỹ xem phiên bản Firebase CLI của bạn có lớn hơn 11.14.2 hay không bằng cách sử dụng:

firebase  --version

Nếu phiên bản của bạn thấp hơn 11.14.2, vui lòng cập nhật bằng cách sử dụng:

npm update firebase-tools

3. Tạo và thiết lập dự án Firebase

Tạo dự án Firebase

  1. Đăng nhập vào Firebase .
  2. Trong bảng điều khiển Firebase, hãy nhấp vào Thêm dự án rồi đặt tên cho dự án Firebase của bạn là <your-project> . Hãy nhớ ID dự án cho dự án Firebase của bạn.
  3. Nhấp vào Tạo dự án .

Quan trọng : Dự án Firebase của bạn sẽ được đặt tên là <your-project> , nhưng Firebase sẽ tự động gán cho dự án đó một ID dự án duy nhất ở dạng <your-project>-1234 . Mã định danh duy nhất này là cách dự án của bạn thực sự được xác định (bao gồm cả trong CLI), trong khi <your-project> chỉ đơn giản là tên hiển thị.

Ứng dụng mà chúng tôi sắp xây dựng sử dụng các sản phẩm Firebase có sẵn cho ứng dụng web:

  • Xác thực Firebase để dễ dàng cho phép người dùng đăng nhập vào ứng dụng của bạn.
  • Cloud Firestore để lưu dữ liệu có cấu trúc trên đám mây và nhận thông báo ngay lập tức khi dữ liệu thay đổi.
  • Cloud Storage cho Firebase để lưu tệp trên đám mây.
  • Firebase Hosting để lưu trữ và phục vụ nội dung của bạn.
  • Chức năng tương tác với API bên trong và bên ngoài.

Một số sản phẩm này cần có cấu hình đặc biệt hoặc cần được bật bằng bảng điều khiển Firebase.

Thêm ứng dụng web Firebase vào dự án

  1. Nhấp vào biểu tượng web để tạo ứng dụng web Firebase mới.
  2. Ở bước tiếp theo, bạn sẽ thấy một đối tượng cấu hình. Sao chép nội dung của đối tượng này vào tệp environments/environment.ts .

Cho phép đăng nhập Google để xác thực Firebase

Để cho phép người dùng đăng nhập vào ứng dụng web bằng tài khoản Google của họ, chúng tôi sẽ sử dụng phương thức đăng nhập Google .

Để bật đăng nhập bằng Google :

  1. Trong bảng điều khiển Firebase, tìm phần Xây dựng ở bảng điều khiển bên trái.
  2. Nhấp vào Xác thực , sau đó nhấp vào tab Phương thức đăng nhập (hoặc nhấp vào đây để truy cập trực tiếp vào đó).
  3. Kích hoạt nhà cung cấp dịch vụ đăng nhập Google , sau đó nhấp vào Lưu .
  4. Đặt tên công khai cho ứng dụng của bạn thành <your-project-name> và chọn email hỗ trợ Dự án từ menu thả xuống.

Kích hoạt Cloud Firestore

  1. Trong phần Build của bảng điều khiển Firebase, nhấp vào Cơ sở dữ liệu Firestore .
  2. Nhấp vào Tạo cơ sở dữ liệu trong ngăn Cloud Firestore.
  3. Đặt vị trí lưu trữ dữ liệu Cloud Firestore của bạn. Bạn có thể để mặc định hoặc chọn khu vực gần bạn.

Kích hoạt bộ nhớ đám mây

Ứng dụng web sử dụng Cloud Storage cho Firebase để lưu trữ, tải lên và chia sẻ hình ảnh.

  1. Trong phần Build của bảng điều khiển Firebase, hãy nhấp vào Storage .
  2. Nếu không có nút Bắt đầu nghĩa là Cloud storage đã sẵn sàng.

đã được bật và bạn không cần phải làm theo các bước bên dưới.

  1. Nhấp vào Bắt đầu .
  2. Đọc tuyên bố từ chối trách nhiệm về các quy tắc bảo mật cho dự án Firebase của bạn, sau đó nhấp vào Tiếp theo .
  3. Vị trí Cloud Storage được chọn trước cùng khu vực bạn đã chọn cho cơ sở dữ liệu Cloud Firestore của mình. Nhấn Done để hoàn tất việc thiết lập.

Với các quy tắc bảo mật mặc định, bất kỳ người dùng được xác thực nào cũng có thể ghi bất cứ thứ gì vào Cloud Storage. Chúng tôi sẽ làm cho bộ nhớ của chúng tôi an toàn hơn sau này trong lớp học lập trình này.

4. Kết nối với dự án Firebase của bạn

Giao diện dòng lệnh Firebase (CLI) cho phép bạn sử dụng Firebase Hosting để phân phát ứng dụng web cục bộ cũng như triển khai ứng dụng web cho dự án Firebase của bạn.

Đảm bảo rằng dòng lệnh của bạn đang truy cập vào thư mục webframework cục bộ của ứng dụng.

Kết nối mã ứng dụng web với dự án Firebase của bạn. Đầu tiên, đăng nhập vào Firebase CLI bằng dòng lệnh:

firebase login

Tiếp theo chạy lệnh sau để tạo bí danh dự án. Thay thế $YOUR_PROJECT_ID bằng ID dự án Firebase của bạn.

firebase  use  $YOUR_PROJECT_ID

Thêm AngularFire

Để thêm AngularFire vào ứng dụng, hãy chạy lệnh:

ng add @angular/fire

Sau đó, làm theo hướng dẫn dòng lệnh và chọn các tính năng có trong dự án Firebase của bạn.

Khởi tạo Firebase

Để khởi tạo dự án Firebase, hãy chạy:

firebase init

Sau đó, làm theo lời nhắc dòng lệnh, chọn các tính năng và trình mô phỏng đã được sử dụng trong dự án Firebase của bạn.

Khởi động trình giả lập

Từ thư mục webframework , hãy chạy lệnh sau để khởi động trình mô phỏng:

firebase  emulators:start

Cuối cùng bạn sẽ thấy một cái gì đó như thế này:

$  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.

Khi bạn thấy ✔All emulators ready! thông báo, trình mô phỏng đã sẵn sàng để sử dụng.

Bạn sẽ thấy giao diện người dùng của ứng dụng du lịch của mình, giao diện này chưa (chưa!) hoạt động:

Bây giờ chúng ta hãy bắt tay vào xây dựng!

5. Kết nối ứng dụng web với trình giả lập

Dựa trên bảng trong nhật ký trình mô phỏng, trình mô phỏng Cloud Firestore đang nghe trên cổng 8080 và trình mô phỏng Xác thực đang nghe trên cổng 9099.

Mở EmulatorUI

Trong trình duyệt web của bạn, điều hướng đến http://127.0.0.1:4000/ . Bạn sẽ thấy giao diện người dùng Emulator Suite.

Định tuyến ứng dụng để sử dụng trình mô phỏng

Trong src/app/app.module.ts , thêm mã sau vào danh sách nhập của 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;
		}),
		...
	]

Ứng dụng hiện được định cấu hình để sử dụng trình mô phỏng cục bộ, cho phép thực hiện thử nghiệm và phát triển cục bộ.

6. Thêm xác thực

Giờ đây, trình mô phỏng đã được thiết lập cho ứng dụng, chúng tôi có thể thêm các tính năng Xác thực để đảm bảo rằng mỗi người dùng đều đăng nhập trước khi họ đăng tin nhắn.

Để làm như vậy, chúng tôi có thể nhập các hàm signin trực tiếp từ AngularFire và theo dõi trạng thái xác thực của người dùng của bạn bằng hàm authState . Sửa đổi các chức năng của trang đăng nhập để trang kiểm tra trạng thái xác thực người dùng khi tải.

Tiêm AngularFire Auth

Trong src/app/pages/login-page/login-page.component.ts , nhập Auth từ @angular/fire/auth và đưa nó vào LoginPageComponent . Các nhà cung cấp xác thực, chẳng hạn như Google và các chức năng như signin , signout cũng có thể được nhập trực tiếp từ cùng một gói và sử dụng trong ứng dụng.

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

Bây giờ trang đăng nhập đã hoạt động! Hãy thử đăng nhập và kiểm tra kết quả trong Trình mô phỏng xác thực.

7. Định cấu hình Firestore

Trong bước này, bạn sẽ thêm chức năng đăng và cập nhật các bài đăng trên blog du lịch được lưu trữ trong Firestore.

Tương tự như Xác thực, các chức năng của Firestore được đóng gói sẵn từ AngularFire. Mỗi tài liệu thuộc về một bộ sưu tập và mỗi tài liệu cũng có thể có các bộ sưu tập lồng nhau. Cần phải biết path của tài liệu trong Firestore để tạo và cập nhật bài đăng trên blog du lịch.

Triển khai dịch vụ du lịch

Vì nhiều trang khác nhau sẽ cần đọc và cập nhật tài liệu Firestore trong ứng dụng web, nên chúng tôi có thể triển khai các chức năng trong src/app/services/travel.service.ts để hạn chế việc tiêm liên tục các chức năng AngularFire giống nhau vào mỗi trang.

Bắt đầu bằng việc đưa Auth , tương tự như bước trước, cũng như Firestore vào dịch vụ của chúng tôi. Việc xác định một đối tượng user$ có thể quan sát được để lắng nghe trạng thái xác thực hiện tại cũng rất hữu ích.

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

Thêm bài viết du lịch

Các bài đăng về du lịch sẽ tồn tại dưới dạng tài liệu được lưu trữ trong Firestore và vì tài liệu phải tồn tại trong các bộ sưu tập nên bộ sưu tập chứa tất cả các bài đăng về du lịch sẽ được đặt tên là travels . Vì vậy, đường đi của bất kỳ điểm du lịch nào sẽ là travels/

Sử dụng hàm addDoc từ AngularFire, một đối tượng có thể được chèn vào bộ sưu tập:

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;

	})
}

Cập nhật và xóa dữ liệu

Với uid của bất kỳ bài đăng du lịch nào, người ta có thể suy ra đường dẫn của tài liệu được lưu trữ trong Firestore, sau đó có thể đọc, cập nhật hoặc xóa bằng cách sử dụng các hàm updateFocdeleteDoc của 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)
}

Đọc dữ liệu dưới dạng có thể quan sát được

Vì các bài đăng du lịch và điểm dừng trên đường đi có thể được sửa đổi sau khi tạo, nên sẽ hữu ích hơn nếu lấy các đối tượng tài liệu dưới dạng có thể quan sát được, để đăng ký bất kỳ thay đổi nào được thực hiện. Chức năng này được cung cấp bởi các hàm docDatacollectionData từ @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[]>
}

Thêm điểm dừng vào bài viết du lịch

Bây giờ, các hoạt động đăng bài du lịch đã được thiết lập, đã đến lúc xem xét các điểm dừng, sẽ tồn tại trong một tập hợp con của một bài đăng du lịch như sau: travels/ /stops/ travels/ /stops/

Điều này gần giống với việc tạo một bài đăng về du lịch, vì vậy hãy thử thách bản thân thực hiện nó hoặc xem cách thực hiện bên dưới:

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

Đẹp! Các chức năng của Firestore đã được triển khai trong dịch vụ Du lịch, vì vậy bây giờ bạn có thể thấy chúng hoạt động.

Sử dụng các chức năng của Firestore trong ứng dụng

Điều hướng đến src/app/pages/my-travels/my-travels.component.ts và đưa TravelService vào để sử dụng các chức năng của nó.

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

TravelService được gọi trong hàm tạo để lấy một mảng có thể quan sát được của tất cả các chuyến đi.

Trong trường hợp chỉ cần di chuyển của người dùng hiện tại, hãy sử dụng chức năng query .

Các phương pháp khác để đảm bảo bảo mật bao gồm triển khai các quy tắc bảo mật hoặc sử dụng Chức năng đám mây với Firestore như được khám phá trong các bước tùy chọn bên dưới

Sau đó, chỉ cần gọi các hàm được triển khai trong TravelService .

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

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

Bây giờ trang Chuyến đi của tôi sẽ hoạt động bình thường! Hãy xem điều gì xảy ra trong trình mô phỏng Firestore khi bạn tạo một bài đăng du lịch mới.

Sau đó, lặp lại các chức năng cập nhật trong /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. Cấu hình bộ nhớ

Bây giờ bạn sẽ triển khai Bộ nhớ để lưu trữ hình ảnh và các loại phương tiện khác.

Cloud Firestore được sử dụng tốt nhất để lưu trữ dữ liệu có cấu trúc, chẳng hạn như các đối tượng JSON. Cloud Storage được thiết kế để lưu trữ tệp hoặc đốm màu. Trong ứng dụng này, bạn sẽ sử dụng nó để cho phép người dùng chia sẻ những bức ảnh du lịch của họ.

Tương tự như vậy với Firestore, việc lưu trữ và cập nhật tệp bằng Bộ lưu trữ yêu cầu mã định danh duy nhất cho mỗi tệp.

Hãy triển khai các chức năng trong TraveService :

Đang tải lên một tập tin

Điều hướng đến src/app/services/travel.service.ts và thêm Bộ nhớ từ AngularFire:

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

Và thực hiện chức năng tải lên:

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

Sự khác biệt chính giữa việc truy cập tài liệu từ Firestore và tệp từ Cloud Storage là, mặc dù cả hai đều tuân theo các đường dẫn có cấu trúc thư mục, nhưng kết hợp url cơ sở và đường dẫn được lấy thông qua getDownloadURL , sau đó có thể được lưu trữ và sử dụng trong một tài liệu.

Sử dụng chức năng trong ứng dụng

Điều hướng đến src/app/components/edit-stop/edit-stop.component.ts và gọi hàm tải lên bằng cách sử dụng:

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

Khi hình ảnh được tải lên, tệp phương tiện sẽ được tải lên bộ lưu trữ và url được lưu trữ tương ứng trong tài liệu trong Firestore.

9. Triển khai ứng dụng

Bây giờ chúng ta đã sẵn sàng triển khai ứng dụng!

Sao chép cấu hình firebase từ src/environments/environment.ts sang src/environments/environment.prod.ts và chạy:

firebase deploy

Bạn sẽ thấy một cái gì đó như thế này:

✔ 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. Xin chúc mừng!

Bây giờ ứng dụng của bạn đã hoàn tất và được triển khai lên Firebase Hosting! Giờ đây, tất cả dữ liệu và phân tích sẽ có thể truy cập được trong Bảng điều khiển Firebase của bạn.

Để biết thêm các tính năng liên quan đến AngularFire, Hàm, quy tắc bảo mật, đừng quên xem các bước tùy chọn bên dưới, cũng như các Phòng học lập trình Firebase khác!

11. Tùy chọn: Bảo vệ xác thực AngularFire

Cùng với Xác thực Firebase, AngularFire cũng cung cấp các biện pháp bảo vệ dựa trên xác thực trên các tuyến đường, để người dùng không có đủ quyền truy cập có thể được chuyển hướng. Điều này giúp bảo vệ ứng dụng khỏi việc người dùng truy cập vào dữ liệu được bảo vệ.

Trong src/app/app-routing.module.ts , nhập

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

Sau đó, bạn có thể xác định các chức năng về thời điểm và nơi người dùng sẽ được chuyển hướng đến trên một số trang nhất định:

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

Sau đó, chỉ cần thêm chúng vào tuyến đường của bạn:

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. Tùy chọn: quy tắc bảo mật

Cả Firestore và Cloud Storage đều sử dụng các quy tắc bảo mật ( firestore.rulessecurity.rules tương ứng) để thực thi bảo mật và xác thực dữ liệu.

Hiện tại, dữ liệu Firestore và Storage có quyền truy cập mở để đọc và ghi, nhưng bạn không muốn mọi người thay đổi bài đăng của người khác! Bạn có thể sử dụng các quy tắc bảo mật để hạn chế quyền truy cập vào bộ sưu tập và tài liệu của mình.

Quy tắc của lò sưởi

Để chỉ cho phép người dùng được xác thực xem các bài đăng về du lịch, hãy truy cập tệp firestore.rules và thêm:

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

Các quy tắc bảo mật cũng có thể được sử dụng để xác thực dữ liệu:

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

Quy tắc lưu trữ

Tương tự, chúng ta có thể sử dụng các quy tắc bảo mật để thực thi quyền truy cập vào cơ sở dữ liệu lưu trữ trong storage.rules . Lưu ý rằng chúng ta cũng có thể sử dụng các hàm để kiểm tra phức tạp hơn:

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