Skip to content

Commit

Permalink
Merge pull request #21 from anihalaney/part-12
Browse files Browse the repository at this point in the history
Part 12
  • Loading branch information
anihalaney authored Jun 12, 2017
2 parents 8d3e20b + 1e6f2d8 commit de0400a
Show file tree
Hide file tree
Showing 24 changed files with 327 additions and 143 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,4 @@ testem.log
.DS_Store
Thumbs.db
functions/node_modules/
functions/server/
14 changes: 12 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ See demo (development server) - https://rwa-trivia.firebaseapp.com

[Part 11: Gameplay with Angular](https://blog.realworldfullstack.io/real-world-app-part-11-gameplay-with-angular-2a660fad52c2)

[Part 12: Cloud Functions for Firebase](https://blog.realworldfullstack.io/real-world-app-part-12-cloud-functions-for-firebase-8359787e26f3)

## Quick Installation Instructions

* Install cli globally (version should match the one in package.json)
Expand Down Expand Up @@ -90,9 +92,17 @@ Set: users/\<user id\>/roles/admin: true

`firebase serve`

* Deploy to firebase
* Deploy Site to firebase

`firebase deploy --only hosting`

* Setup firebase functions - creates functions folder and installs dependencies

`firebase init`

* Deploy functions to firebase

`firebase deploy`
`npm run deploy-functions`

## Testing
* Test the application using
Expand Down
131 changes: 131 additions & 0 deletions functions/app.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import { Game, Question } from '../src/app/model';

const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);

const express = require('express');
const cookieParser = require('cookie-parser')();
const cors = require('cors')({origin: true});
const app = express();

// Take the text parameter passed to this HTTP endpoint and insert it into the
// Realtime Database under the path /messages/:pushId/original
exports.addMessage = functions.https.onRequest((req, res) => {
// Grab the text parameter.

const original = req.query.text;
// Push it into the Realtime Database then send a response
admin.database().ref('/messages').push({original: original}).then(snapshot => {
// Redirect with 303 SEE OTHER to the URL of the pushed object in the Firebase console.
res.redirect(303, snapshot.ref);
});
});

const validateFirebaseIdToken = (req, res, next) => {
console.log('Check if request is authorized with Firebase ID token');

if ((!req.headers.authorization || !req.headers.authorization.startsWith('Bearer ')) &&
!req.cookies.__session) {
console.error('No Firebase ID token was passed as a Bearer token in the Authorization header.',
'Make sure you authorize your request by providing the following HTTP header:',
'Authorization: Bearer <Firebase ID Token>',
'or by passing a "__session" cookie.');
res.status(403).send('Unauthorized');
return;
}

let idToken;
if (req.headers.authorization && req.headers.authorization.startsWith('Bearer ')) {
console.log('Found "Authorization" header');
// Read the ID Token from the Authorization header.
idToken = req.headers.authorization.split('Bearer ')[1];
} else {
console.log('Found "__session" cookie');
// Read the ID Token from cookie.
idToken = req.cookies.__session;
}
admin.auth().verifyIdToken(idToken).then(decodedIdToken => {
console.log('ID Token correctly decoded', decodedIdToken);
req.user = decodedIdToken;
next();
}).catch(error => {
console.error('Error while verifying Firebase ID token:', error);
res.status(403).send('Unauthorized');
});
};

app.use(cors);
app.use(cookieParser);
//app.use(validateFirebaseIdToken);
app.get('/hello', validateFirebaseIdToken, (req, res) => {
res.send(`Hello ${req.user.email}`);
});

app.get('/getNextQuestion/:gameId', validateFirebaseIdToken, (req, res, next) => {

console.log(req.user.uid);
console.log(req.params.gameId);

let userId = req.user.uid;
let gameId = req.params.gameId;
let resp = `Hello ${req.user.email} - ${req.params.gameId}`;

//admin.database().enableLogging(true);

let game: Game;
admin.database().ref("/games/" + gameId).once("value").then(g => {
if (!g.exists()) {
//game not found
res.status(404).send('Game not found');
return;
}
game = Game.getViewModel(g.val());
console.log(game);
resp += " - Game Found !!!"

if (game.playerIds.indexOf(userId) < 0) {
//user not part of this game
res.status(403).send('User not part of this game');
return;
}

if (game.gameOver) {
//gameOver
res.status(403).send('Game over. No more Questions');
return;
}

if (game.gameOptions.gameMode !== 0) {
//Multiplayer mode - check whose turn it is. Not yet implemented
res.status(501).send('Wait for your turn. Not yet implemented.');
return;
}

admin.database().ref("/questions/published").orderByKey().limitToLast(1).once("value").then(qs => {
console.log(qs.key);
console.log(qs.val());
qs.forEach(q => {
console.log(q.key);
console.log(q.val());

let question: Question = q.val();
question.id = q.key;
res.send(question);
return;
})
return;
})
.catch(error => {
res.status(500).send('Failed to get Q');
return;
});
})
.catch(error => {
res.status(500).send('Uncaught Error');
return;
});

});

exports.app = functions.https.onRequest(app);
1 change: 1 addition & 0 deletions functions/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
exports.app = require('./server/functions/app').app;
15 changes: 15 additions & 0 deletions functions/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"name": "functions",
"description": "Cloud Functions for Firebase",
"dependencies": {
"firebase-admin": "5.0.0",
"firebase-functions": "0.5.7",
"express":"4.15.3",
"cookie-parser":"1.4.3",
"cors":"2.8.3"
},
"private": true,
"devDependencies": {
"@types/node": "^7.0.21"
}
}
21 changes: 21 additions & 0 deletions functions/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"compilerOptions": {
"sourceMap": true,
"declaration": false,
"moduleResolution": "node",
"target": "es6",
"outDir": "./server",
"lib": [
"es2016"
],
"typeRoots": [
"node_modules/@types"
],
"module": "commonjs",
"types": ["node"]
},
"exclude": [
"node_modules",
"**/*.spec.ts"
]
}
12 changes: 7 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
"lint": "tslint \"src/**/*.ts\" --project src/tsconfig.json --type-check && tslint \"e2e/**/*.ts\" --project e2e/tsconfig.json --type-check",
"test": "ng test",
"pree2e": "webdriver-manager update --standalone false --gecko false",
"e2e": "protractor"
"e2e": "protractor",
"compile-functions": "rmdir /s/q functions\\server & tsc --project functions",
"deploy-functions": "rmdir /s/q functions\\server & tsc --project functions && firebase deploy --only functions"
},
"private": true,
"dependencies": {
Expand All @@ -25,16 +27,16 @@
"@angular/platform-browser": "4.0.1",
"@angular/platform-browser-dynamic": "4.0.1",
"@angular/router": "4.0.1",
"angularfire2": "2.0.0-beta.8",
"angularfire2": "4.0.0-rc.1",
"@ngrx/core": "~1.2.0",
"@ngrx/effects": "~2.0.2",
"@ngrx/store": "~2.2.1",
"@ngrx/store-devtools": "~3.2.4",
"firebase": "~3.7.5",
"firebase": "4.1.2",
"core-js": "^2.4.1",
"hammerjs": "^2.0.8",
"rxjs": "^5.3.0",
"zone.js": "^0.8.5"
"rxjs": "^5.4.0",
"zone.js": "^0.8.11"
},
"devDependencies": {
"@angular/cli": "1.0.0",
Expand Down
2 changes: 1 addition & 1 deletion src/app/components/app/app.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<span md-line>My Questions</span>
<span md-line class="secondary">My Questions</span>
</a>
<a md-list-item *ngIf="user?.roles.admin" (click)="sidenav.close()" [routerLink]="['/admin']">
<a md-list-item *ngIf="user?.roles?.admin" (click)="sidenav.close()" [routerLink]="['/admin']">
<md-icon md-list-icon>settings</md-icon>
<span md-line>Admin</span>
<span md-line class="secondary">Admin</span>
Expand Down
25 changes: 7 additions & 18 deletions src/app/core/components/login/login.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import { Component, Input } from '@angular/core';
import { MdDialogRef, MdDialog } from '@angular/material';
import { Observable } from 'rxjs/Observable';
import { Store } from '@ngrx/store';
import { AngularFire, AuthProviders, AuthMethods } from 'angularfire2';
import { AngularFireAuth } from 'angularfire2/auth';
import * as firebase from 'firebase/app';

import { AppStore } from '../../store/app-store';
import { PasswordAuthComponent } from './password-auth.component';
Expand All @@ -14,38 +15,26 @@ import { PasswordAuthComponent } from './password-auth.component';
})
export class LoginComponent {

constructor(private af: AngularFire,
constructor(private afAuth: AngularFireAuth,
private dialog: MdDialog,
private passwordAuthDialogRef: MdDialogRef<PasswordAuthComponent>) {

}

googleLogin() {
this.af.auth.login({
provider: AuthProviders.Google,
method: AuthMethods.Popup
});
this.afAuth.auth.signInWithPopup(new firebase.auth.GoogleAuthProvider());
}

fbLogin() {
this.af.auth.login({
provider: AuthProviders.Facebook,
method: AuthMethods.Popup
});
this.afAuth.auth.signInWithPopup(new firebase.auth.FacebookAuthProvider());
}

twitterLogin() {
this.af.auth.login({
provider: AuthProviders.Twitter,
method: AuthMethods.Popup
});
this.afAuth.auth.signInWithPopup(new firebase.auth.TwitterAuthProvider());
}

githubLogin() {
this.af.auth.login({
provider: AuthProviders.Github,
method: AuthMethods.Popup
});
this.afAuth.auth.signInWithPopup(new firebase.auth.GithubAuthProvider());
}

passwordLogin() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { ReactiveFormsModule } from '@angular/forms';
import { FormBuilder, FormControl } from '@angular/forms';
import { SharedMaterialModule } from '../../../shared/shared-material.module';
import { MdDialogRef, MdDialog } from '@angular/material';
import * as firebase from 'firebase';
import * as firebase from 'firebase/app';
import { AngularFire, AuthProviders, AuthMethods, FirebaseAuthConfig, FirebaseAuthState } from 'angularfire2';

import { TEST_DATA } from '../../../testing';
Expand Down
24 changes: 11 additions & 13 deletions src/app/core/components/login/password-auth.component.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Component, Input, OnInit, OnDestroy } from '@angular/core';
import { AbstractControl, FormBuilder, FormGroup, Validators, FormArray, FormControl, ValidatorFn } from '@angular/forms';
import { MdDialogRef } from '@angular/material';
import * as firebase from 'firebase';
import { AngularFire, AuthMethods, FirebaseAuthState } from 'angularfire2';
import * as firebase from 'firebase/app';
import { AngularFireAuth } from 'angularfire2/auth';
import { Observable } from 'rxjs/Observable';

const EMAIL_REGEXP = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
Expand All @@ -21,7 +21,7 @@ export class PasswordAuthComponent implements OnInit {
forgotPasswordForm: FormGroup;

constructor(private fb: FormBuilder,
private af: AngularFire,
private afAuth: AngularFireAuth,
public dialogRef: MdDialogRef<PasswordAuthComponent>) {
this.mode = SignInMode.signIn;
}
Expand Down Expand Up @@ -49,12 +49,10 @@ export class PasswordAuthComponent implements OnInit {

//signin
onSigninSubmit() {
this.af.auth.login({
email: this.signinForm.get('email').value,
password: this.signinForm.get('password').value
}, {
method: AuthMethods.Password
}).then((user: FirebaseAuthState) => {
this.afAuth.auth.signInWithEmailAndPassword(
this.signinForm.get('email').value,
this.signinForm.get('password').value
).then((user: any) => {
//success
this.dialogRef.close();
}, (error: Error) => {
Expand All @@ -66,10 +64,10 @@ export class PasswordAuthComponent implements OnInit {

//register
onSignupSubmit() {
this.af.auth.createUser({
email: this.signupForm.get('email').value,
password: this.signupForm.get('password').value
}).then((user: FirebaseAuthState) => {
this.afAuth.auth.createUserWithEmailAndPassword(
this.signupForm.get('email').value,
this.signupForm.get('password').value
).then((user: any) => {
//success
this.dialogRef.close();
}, (error: Error) => {
Expand Down
4 changes: 4 additions & 0 deletions src/app/core/core.module.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { NgModule, ModuleWithProviders } from '@angular/core';

import { AngularFireModule, FirebaseAppConfig } from 'angularfire2';
import { AngularFireDatabaseModule } from 'angularfire2/database';
import { AngularFireAuthModule } from 'angularfire2/auth';

import { StoreModule } from '@ngrx/store';
import { EffectsModule } from '@ngrx/effects';
Expand Down Expand Up @@ -33,6 +35,8 @@ export const firebaseConfig: FirebaseAppConfig = CONFIG.firebaseConfig;
imports: [
//firebase
AngularFireModule.initializeApp(firebaseConfig),
AngularFireDatabaseModule,
AngularFireAuthModule,

//store
StoreModule.provideStore(reducer),
Expand Down
Loading

0 comments on commit de0400a

Please sign in to comment.