mirror of
https://github.com/terribleplan/next.js.git
synced 2024-01-19 02:48:18 +00:00
820 firebase (#1756)
* connecting to firebase * login and logout with sessions * setting messages on the client side * should have messages served on init * set messages in state * updating credentials * updating readme * more cred * iron out eslint issues * highlight where to put firebase variables * fix problem of database listener not picking up changes on load * remove isomorphic from main package.json
This commit is contained in:
parent
bbb8ff75a5
commit
06baed2fe8
33
examples/with-firebase/README.md
Normal file
33
examples/with-firebase/README.md
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
|
||||||
|
# With Firebase example
|
||||||
|
|
||||||
|
## How to use
|
||||||
|
|
||||||
|
Download the example [or clone the repo](https://github.com/zeit/next.js):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl https://codeload.github.com/zeit/next.js/tar.gz/master | tar -xz --strip=2 next.js-master/examples/with-firebase
|
||||||
|
cd with-firebase
|
||||||
|
```
|
||||||
|
|
||||||
|
Set up firebase:
|
||||||
|
- create a project
|
||||||
|
- get your service account credentials and client credentials and set both in firebaseCredentials.js
|
||||||
|
- set your firebase database url in server.js
|
||||||
|
- on the firebase Authentication console, select Google as your provider
|
||||||
|
|
||||||
|
Install it and run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
Deploy it to the cloud with [now](https://zeit.co/now) ([download](https://zeit.co/download))
|
||||||
|
|
||||||
|
```bash
|
||||||
|
now
|
||||||
|
```
|
||||||
|
|
||||||
|
## The idea behind the example
|
||||||
|
The goal is to authenticate users with firebase and store their auth token in sessions. A logged in user will see their messages on page load and then be able to post new messages.
|
8
examples/with-firebase/firebaseCredentials.js
Normal file
8
examples/with-firebase/firebaseCredentials.js
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
module.exports = {
|
||||||
|
clientCredentials: {
|
||||||
|
// TODO firebase client config
|
||||||
|
},
|
||||||
|
serverCredentials: {
|
||||||
|
// TODO service account json here
|
||||||
|
}
|
||||||
|
}
|
19
examples/with-firebase/package.json
Normal file
19
examples/with-firebase/package.json
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
"scripts": {
|
||||||
|
"dev": "node server.js",
|
||||||
|
"build": "next build",
|
||||||
|
"start": "NODE_ENV=production node server.js"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"body-parser": "^1.17.1",
|
||||||
|
"express": "^4.14.0",
|
||||||
|
"express-session": "^1.15.2",
|
||||||
|
"firebase": "^3.7.5",
|
||||||
|
"firebase-admin": "^4.2.0",
|
||||||
|
"isomorphic-fetch": "2.2.1",
|
||||||
|
"next": "latest",
|
||||||
|
"react": "^15.4.2",
|
||||||
|
"react-dom": "^15.4.2",
|
||||||
|
"session-file-store": "^1.0.0"
|
||||||
|
}
|
||||||
|
}
|
115
examples/with-firebase/pages/index.js
Normal file
115
examples/with-firebase/pages/index.js
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
import React, { Component } from 'react'
|
||||||
|
import firebase from 'firebase'
|
||||||
|
import 'isomorphic-fetch'
|
||||||
|
import { clientCredentials } from '../firebaseCredentials'
|
||||||
|
|
||||||
|
export default class Index extends Component {
|
||||||
|
static async getInitialProps ({req, query}) {
|
||||||
|
const user = req && req.session ? req.session.decodedToken : null
|
||||||
|
const snap = await req.firebaseServer.database().ref('messages').once('value')
|
||||||
|
return { user, messages: snap.val() }
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor (props) {
|
||||||
|
super(props)
|
||||||
|
this.state = {
|
||||||
|
user: this.props.user,
|
||||||
|
value: '',
|
||||||
|
messages: this.props.messages
|
||||||
|
}
|
||||||
|
|
||||||
|
this.addDbListener = this.addDbListener.bind(this)
|
||||||
|
this.handleChange = this.handleChange.bind(this)
|
||||||
|
this.handleSubmit = this.handleSubmit.bind(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount () {
|
||||||
|
firebase.initializeApp(clientCredentials)
|
||||||
|
|
||||||
|
if (this.state.user) this.addDbListener()
|
||||||
|
|
||||||
|
firebase.auth().onAuthStateChanged(user => {
|
||||||
|
if (user) {
|
||||||
|
this.setState({ user: user })
|
||||||
|
return user.getToken()
|
||||||
|
.then((token) => {
|
||||||
|
// eslint-disable-next-line no-undef
|
||||||
|
return fetch('/api/login', {
|
||||||
|
method: 'POST',
|
||||||
|
// eslint-disable-next-line no-undef
|
||||||
|
headers: new Headers({ 'Content-Type': 'application/json' }),
|
||||||
|
credentials: 'same-origin',
|
||||||
|
body: JSON.stringify({ token })
|
||||||
|
})
|
||||||
|
}).then((res) => this.addDbListener())
|
||||||
|
} else {
|
||||||
|
this.setState({ user: null })
|
||||||
|
// eslint-disable-next-line no-undef
|
||||||
|
fetch('/api/logout', {
|
||||||
|
method: 'POST',
|
||||||
|
credentials: 'same-origin'
|
||||||
|
}).then(() => firebase.database().ref('messages').off())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
addDbListener () {
|
||||||
|
firebase.database().ref('messages').on('value', snap => {
|
||||||
|
const messages = snap.val()
|
||||||
|
if (messages) this.setState({ messages })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
handleChange (event) {
|
||||||
|
this.setState({ value: event.target.value })
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSubmit (event) {
|
||||||
|
event.preventDefault()
|
||||||
|
const date = new Date().getTime()
|
||||||
|
firebase.database().ref(`messages/${date}`).set({
|
||||||
|
id: date,
|
||||||
|
text: this.state.value
|
||||||
|
})
|
||||||
|
this.setState({ value: '' })
|
||||||
|
}
|
||||||
|
|
||||||
|
handleLogin () {
|
||||||
|
firebase.auth().signInWithPopup(new firebase.auth.GoogleAuthProvider())
|
||||||
|
}
|
||||||
|
|
||||||
|
handleLogout () {
|
||||||
|
firebase.auth().signOut()
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const { user, value, messages } = this.state
|
||||||
|
|
||||||
|
return <div>
|
||||||
|
{
|
||||||
|
user
|
||||||
|
? <button onClick={this.handleLogout}>Logout</button>
|
||||||
|
: <button onClick={this.handleLogin}>Login</button>
|
||||||
|
}
|
||||||
|
{
|
||||||
|
user &&
|
||||||
|
<div>
|
||||||
|
<form onSubmit={this.handleSubmit}>
|
||||||
|
<input
|
||||||
|
type={'text'}
|
||||||
|
onChange={this.handleChange}
|
||||||
|
placeholder={'add message'}
|
||||||
|
value={value}
|
||||||
|
/>
|
||||||
|
</form>
|
||||||
|
<ul>
|
||||||
|
{
|
||||||
|
messages &&
|
||||||
|
Object.keys(messages).map(key => <li key={key}>{messages[key].text}</li>)
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
63
examples/with-firebase/server.js
Normal file
63
examples/with-firebase/server.js
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
const express = require('express')
|
||||||
|
const bodyParser = require('body-parser')
|
||||||
|
const session = require('express-session')
|
||||||
|
const FileStore = require('session-file-store')(session)
|
||||||
|
const next = require('next')
|
||||||
|
const admin = require('firebase-admin')
|
||||||
|
|
||||||
|
const dev = process.env.NODE_ENV !== 'production'
|
||||||
|
const app = next({ dev })
|
||||||
|
const handle = app.getRequestHandler()
|
||||||
|
|
||||||
|
const firebase = admin.initializeApp({
|
||||||
|
credential: admin.credential.cert(require('./firebaseCredentials').serverCredentials),
|
||||||
|
databaseURL: '' // TODO database URL goes here
|
||||||
|
}, 'server')
|
||||||
|
|
||||||
|
app.prepare()
|
||||||
|
.then(() => {
|
||||||
|
const server = express()
|
||||||
|
|
||||||
|
server.use(bodyParser.json())
|
||||||
|
server.use(session({
|
||||||
|
secret: 'geheimnis',
|
||||||
|
saveUninitialized: true,
|
||||||
|
store: new FileStore({path: '/tmp/sessions', secret: 'geheimnis'}),
|
||||||
|
resave: false,
|
||||||
|
rolling: true,
|
||||||
|
httpOnly: true,
|
||||||
|
cookie: { maxAge: 604800000 } // week
|
||||||
|
}))
|
||||||
|
|
||||||
|
server.use((req, res, next) => {
|
||||||
|
req.firebaseServer = firebase
|
||||||
|
next()
|
||||||
|
})
|
||||||
|
|
||||||
|
server.post('/api/login', (req, res) => {
|
||||||
|
if (!req.body) return res.sendStatus(400)
|
||||||
|
|
||||||
|
const token = req.body.token
|
||||||
|
firebase.auth().verifyIdToken(token)
|
||||||
|
.then((decodedToken) => {
|
||||||
|
req.session.decodedToken = decodedToken
|
||||||
|
return decodedToken
|
||||||
|
})
|
||||||
|
.then((decodedToken) => res.json({ status: true, decodedToken }))
|
||||||
|
.catch((error) => res.json({ error }))
|
||||||
|
})
|
||||||
|
|
||||||
|
server.post('/api/logout', (req, res) => {
|
||||||
|
req.session.decodedToken = null
|
||||||
|
res.json({ status: true })
|
||||||
|
})
|
||||||
|
|
||||||
|
server.get('*', (req, res) => {
|
||||||
|
return handle(req, res)
|
||||||
|
})
|
||||||
|
|
||||||
|
server.listen(3000, (err) => {
|
||||||
|
if (err) throw err
|
||||||
|
console.log('> Ready on http://localhost:3000')
|
||||||
|
})
|
||||||
|
})
|
Loading…
Reference in a new issue