Codelab do Firebase Angular Web Frameworks

1. O que você criará

Neste codelab, você criará um blog de viagem com um mapa colaborativo em tempo real com as novidades da nossa biblioteca Angular: AngularFire . O aplicativo web final consistirá em um blog de viagens onde você poderá fazer upload de imagens de cada local para onde viajou.

AngularFire será usado para construir o aplicativo da web, Emulator Suite para testes locais, Autenticação para rastrear os dados do usuário, Firestore e Storage para persistir dados e mídia, alimentado por Cloud Functions e, finalmente, Firebase Hosting para implantar o aplicativo.

O que você aprenderá

  • Como desenvolver produtos Firebase localmente com o Emulator Suite
  • Como aprimorar seu aplicativo web com AngularFire
  • Como persistir seus dados no Firestore
  • Como persistir mídia no armazenamento
  • Como implantar seu aplicativo no Firebase Hosting
  • Como usar o Cloud Functions para interagir com seus bancos de dados e APIs

O que você precisará

  • Node.js versão 10 ou superior
  • Uma Conta Google para a criação e gerenciamento do seu projeto Firebase
  • A CLI do Firebase versão 11.14.2 ou posterior
  • Um navegador de sua preferência, como o Chrome
  • Compreensão básica de Angular e Javascript

2. Obtenha o código de exemplo

Clone o repositório GitHub do codelab na linha de comando:

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

Alternativamente, se você não tiver o git instalado, poderá baixar o repositório como um arquivo ZIP .

O repositório Github contém exemplos de projetos para múltiplas plataformas.

Este codelab usa apenas o repositório do webframework:

  • 📁 webframework : o código inicial que você usará durante este codelab.

Instalar dependências

Após a clonagem, instale dependências na pasta raiz e functions antes de construir o aplicativo web.

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

Instale a CLI do Firebase

Instale o Firebase CLI usando este comando em um terminal:

npm install -g firebase-tools

Verifique novamente se sua versão do Firebase CLI é superior a 11.14.2 usando:

firebase  --version

Se sua versão for inferior a 11.14.2, atualize usando:

npm update firebase-tools

3. Crie e configure um projeto Firebase

Crie um projeto do Firebase

  1. Faça login no Firebase .
  2. No console do Firebase, clique em Adicionar projeto e nomeie seu projeto do Firebase como <seu-projeto> . Lembre-se do ID do seu projeto do Firebase.
  3. Clique em Criar projeto .

Importante : seu projeto do Firebase será nomeado <seu-projeto> , mas o Firebase atribuirá a ele automaticamente um ID de projeto exclusivo no formato <seu-projeto>-1234 . Este identificador exclusivo é como seu projeto é realmente identificado (inclusive na CLI), enquanto <your-project> é simplesmente um nome de exibição.

O aplicativo que criaremos usa produtos Firebase disponíveis para aplicativos da web:

  • Firebase Authentication para permitir que seus usuários façam login facilmente em seu aplicativo.
  • Cloud Firestore para salvar dados estruturados na nuvem e receber notificações instantâneas quando os dados forem alterados.
  • Cloud Storage para Firebase para salvar arquivos na nuvem.
  • Firebase Hosting para hospedar e servir seus ativos.
  • Funções para interagir com APIs internas e externas.

Alguns desses produtos precisam de configurações especiais ou precisam ser habilitados usando o console do Firebase.

Adicione um aplicativo da Web do Firebase ao projeto

  1. Clique no ícone da web para criar um novo aplicativo da web do Firebase.
  2. Na próxima etapa, você verá um objeto de configuração. Copie o conteúdo deste objeto no arquivo environments/environment.ts .

Ative o login do Google para Firebase Authentication

Para permitir que os usuários façam login no aplicativo da web com suas contas do Google, usaremos o método de login do Google .

Para ativar o login do Google :

  1. No console do Firebase, localize a seção Build no painel esquerdo.
  2. Clique em Autenticação e, em seguida, clique na guia Método de login (ou clique aqui para ir diretamente para lá).
  3. Ative o provedor de login do Google e clique em Salvar .
  4. Defina o nome público do seu aplicativo como <nome do seu projeto> e escolha um e-mail de suporte do projeto no menu suspenso.

Ativar o Cloud Firestore

  1. Na seção Build do console do Firebase, clique em Firestore Database .
  2. Clique em Criar banco de dados no painel Cloud Firestore.
  3. Defina o local onde seus dados do Cloud Firestore são armazenados. Você pode deixar isso como padrão ou escolher uma região mais próxima de você.

Habilitar armazenamento em nuvem

O aplicativo da web usa Cloud Storage for Firebase para armazenar, fazer upload e compartilhar imagens.

  1. Na seção Build do console do Firebase, clique em Storage .
  2. Se não houver um botão Começar , significa que o armazenamento em nuvem já está

ativado e você não precisa seguir as etapas abaixo.

  1. Clique em Começar .
  2. Leia a isenção de responsabilidade sobre as regras de segurança do seu projeto do Firebase e clique em Avançar .
  3. O local do Cloud Storage é pré-selecionado com a mesma região escolhida para seu banco de dados do Cloud Firestore. Clique em Concluído para concluir a configuração.

Com as regras de segurança padrão, qualquer usuário autenticado pode gravar qualquer coisa no Cloud Storage. Tornaremos nosso armazenamento mais seguro posteriormente neste codelab.

4. Conecte-se ao seu projeto Firebase

A interface de linha de comando (CLI) do Firebase permite que você use o Firebase Hosting para servir seu aplicativo da Web localmente, bem como para implantar seu aplicativo da Web em seu projeto do Firebase.

Certifique-se de que sua linha de comando esteja acessando o diretório webframework local do seu aplicativo.

Conecte o código do aplicativo da web ao seu projeto do Firebase. Primeiro, faça login na CLI do Firebase na linha de comando:

firebase login

Em seguida, execute o seguinte comando para criar um alias de projeto. Substitua $YOUR_PROJECT_ID pelo ID do seu projeto do Firebase.

firebase  use  $YOUR_PROJECT_ID

Adicionar Angular Fire

Para adicionar AngularFire ao aplicativo, execute o comando:

ng add @angular/fire

Em seguida, siga as instruções da linha de comando e selecione os recursos existentes em seu projeto Firebase.

Inicializar Firebase

Para inicializar o projeto Firebase, execute:

firebase init

Em seguida, seguindo os prompts da linha de comando, selecione os recursos e emuladores que foram usados ​​no seu projeto do Firebase.

Inicie os emuladores

No diretório webframework , execute o seguinte comando para iniciar os emuladores:

firebase  emulators:start

Eventualmente você deverá ver algo assim:

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

Depois de ver os ✔All emulators ready! mensagem, os emuladores estão prontos para uso.

Você deverá ver a IU do seu aplicativo de viagens, que (ainda!) não está funcionando:

Agora vamos construir!

5. Conecte o aplicativo web aos emuladores

Com base na tabela nos registros do emulador, o emulador do Cloud Firestore está escutando na porta 8080 e o emulador do Authentication está escutando na porta 9099.

Abra o emuladorUI

No seu navegador da web, navegue até http://127.0.0.1:4000/ . Você deverá ver a IU do Emulator Suite.

Roteie o aplicativo para usar os emuladores

Em src/app/app.module.ts , adicione o seguinte código à lista de importações do 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;
		}),
		...
	]

O aplicativo agora está configurado para usar emuladores locais, permitindo que testes e desenvolvimento sejam feitos localmente.

6. Adicionando autenticação

Agora que os emuladores estão configurados para o aplicativo, podemos adicionar recursos de autenticação para garantir que cada usuário esteja conectado antes de postar mensagens.

Para fazer isso, podemos importar funções signin diretamente do AngularFire e rastrear o estado de autenticação do seu usuário com a função authState . Modifique as funções da página de login para que a página verifique o estado de autenticação do usuário durante o carregamento.

Injetando autenticação AngularFire

Em src/app/pages/login-page/login-page.component.ts , importe Auth de @angular/fire/auth e injete-o no LoginPageComponent . Provedores de autenticação, como Google, e funções como signin e signout também podem ser importados diretamente do mesmo pacote e usados ​​no aplicativo.

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

Agora a página de login está funcional! Tente fazer login e confira os resultados no emulador de autenticação.

7. Configurando o Firestore

Nesta etapa, você adicionará funcionalidade para postar e atualizar postagens de blogs de viagens armazenadas no Firestore.

Semelhante ao Authentication, as funções do Firestore vêm pré-empacotadas do AngularFire. Cada documento pertence a uma coleção e cada documento também pode ter coleções aninhadas. É necessário conhecer o path do documento no Firestore para criar e atualizar uma postagem no blog de viagens.

Implementando TravelService

Como muitas páginas diferentes precisarão ler e atualizar documentos do Firestore no aplicativo da web, podemos implementar as funções em src/app/services/travel.service.ts , para evitar injetar repetidamente as mesmas funções do AngularFire em todas as páginas.

Comece injetando Auth , semelhante à etapa anterior, bem como Firestore em nosso serviço. Definir um objeto user$ observável que ouça o status de autenticação atual também é útil.

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

Adicionando uma postagem de viagem

As postagens de viagem existirão como documentos armazenados no Firestore e, como os documentos devem existir nas coleções, a coleção que contém todas as postagens de viagem será chamada de travels . Assim, o caminho de qualquer posto de viagem será travels/

Usando a função addDoc do AngularFire, um objeto pode ser inserido em uma coleção:

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;

	})
}

Atualizando e excluindo dados

Dado o uid de qualquer postagem de viagem, pode-se deduzir o caminho do documento armazenado no Firestore, que pode então ser lido, atualizado ou excluído usando as funções updateFoc e deleteDoc do 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)
}

Lendo dados como um observável

Como as postagens de viagem e as paradas ao longo do caminho podem ser modificadas após a criação, seria mais útil obter objetos de documento como observáveis, para assinar quaisquer alterações feitas. Esta funcionalidade é oferecida pelas funções docData e collectionData de @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[]>
}

Adicionando paradas a uma postagem de viagem

Agora que as operações do posto de viagem estão configuradas, é hora de considerar as paradas, que existirão em uma subcoleção de um posto de viagem como: travels/ /stops/ travels/ /stops/

Isso é quase idêntico à criação de um post de viagem, então desafie-se a implementá-lo sozinho ou confira a implementação abaixo:

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

Legal! As funções do Firestore foram implementadas no serviço Travel, agora você pode vê-las em ação.

Usando funções do Firestore no aplicativo

Navegue para src/app/pages/my-travels/my-travels.component.ts e injete TravelService para usar suas funções.

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

TravelService é chamado no construtor para obter uma matriz observável de todas as viagens.

No caso em que são necessárias apenas as viagens do usuário atual, utilize a função query .

Outros métodos para garantir a segurança incluem a implementação de regras de segurança ou o uso do Cloud Functions com Firestore, conforme explorado nas etapas opcionais abaixo.

Depois, basta chamar as funções implementadas no TravelService .

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

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

Agora a página Minhas Viagens deve estar funcional! Confira o que acontece no emulador do Firestore quando você cria uma nova postagem de viagem.

Em seguida, repita para as funções de atualização em /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. Configurando armazenamento

Agora você implementará o Storage para armazenar imagens e outros tipos de mídia.

O Cloud Firestore é melhor usado para armazenar dados estruturados, como objetos JSON. O Cloud Storage foi projetado para armazenar arquivos ou blobs. Neste aplicativo, você o usará para permitir que os usuários compartilhem suas fotos de viagens.

Da mesma forma que no Firestore, armazenar e atualizar arquivos com o Storage requer um identificador exclusivo para cada arquivo.

Vamos implementar as funções em TraveService :

Fazendo upload de um arquivo

Navegue para src/app/services/travel.service.ts e injete armazenamento do AngularFire:

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

E implemente a função de upload:

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

A principal diferença entre acessar documentos do Firestore e arquivos do Cloud Storage é que, embora ambos sigam caminhos estruturados de pastas, o URL base e a combinação do caminho são obtidos por meio do getDownloadURL , que pode então ser armazenado e usado em um arquivo.

Usando a função no aplicativo

Navegue para src/app/components/edit-stop/edit-stop.component.ts e chame a função de upload usando:

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

Quando a imagem for carregada, o próprio arquivo de mídia será carregado para armazenamento e o URL será armazenado de acordo no documento no Firestore.

9. Implantando o aplicativo

Agora estamos prontos para implantar o aplicativo!

Copie as configurações firebase de src/environments/environment.ts para src/environments/environment.prod.ts e execute:

firebase deploy

Você deverá ver algo assim:

✔ 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. Parabéns!

Agora seu aplicativo deve estar completo e implantado no Firebase Hosting! Todos os dados e análises agora estarão acessíveis em seu Firebase Console.

Para mais recursos sobre AngularFire, Funções, regras de segurança, não se esqueça de conferir as etapas opcionais abaixo, bem como outros Codelabs do Firebase !

11. Opcional: protetores de autenticação AngularFire

Junto com o Firebase Authentication, o AngularFire também oferece proteções baseadas em autenticação nas rotas, para que usuários com acesso insuficiente possam ser redirecionados. Isso ajuda a proteger o aplicativo contra usuários que acessam dados protegidos.

Em src/app/app-routing.module.ts , importe

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

Você pode então definir funções sobre quando e para onde os usuários devem ser redirecionados em determinadas páginas:

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

Em seguida, basta adicioná-los às suas rotas:

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. Opcional: regras de segurança

Tanto o Firestore quanto o Cloud Storage usam regras de segurança ( firestore.rules e security.rules respectivamente) para reforçar a segurança e validar dados.

No momento, os dados do Firestore e do Storage têm acesso aberto para leituras e gravações, mas você não quer que as pessoas mudem as postagens dos outros! Você pode usar regras de segurança para restringir o acesso às suas coleções e documentos.

Regras do Firestore

Para permitir que apenas usuários autenticados visualizem postagens de viagens, acesse o arquivo firestore.rules e adicione:

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

As regras de segurança também podem ser usadas para validar dados:

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

Regras de armazenamento

Da mesma forma, podemos usar regras de segurança para impor acesso a bancos de dados de armazenamento em storage.rules . Observe que também podemos usar funções para verificações mais complexas:

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