react-sprucebot
Version:
React components for your Sprucebot Skill 💪🏼
225 lines (199 loc) • 5.6 kB
JavaScript
import React, { Component } from 'react'
import * as actions from '../store/actions'
import ServerCookies from 'cookies'
import ClientCookies from 'js-cookies'
import skill from '../index'
import DevControls from '../../components/DevControls/DevControls'
import Loader from '../../components/Loader/Loader'
import qs from 'qs'
import lang from '../helpers/lang'
const debug = require('debug')('react-sprucebot')
const setCookie = (named, value, req, res) => {
if (req && req.headers) {
const cookies = new ServerCookies(req, res, { secure: true })
return cookies.set(named, value)
} else {
return ClientCookies.setItem(named, value)
}
}
const getCookie = (named, req, res) => {
if (req && req.headers) {
const cookies = new ServerCookies(req, res, { secure: true })
return cookies.get(named)
} else {
return ClientCookies.getItem(named)
}
}
const Page = Wrapped => {
// const ConnectedWrapped = connect(mapStateToProps, mapDispatchToProps)(Wrapped)
const ConnectedWrapped = Wrapped
return class extends Component {
constructor(props) {
super(props)
this.state = {
attemptingReAuth: !!props.attemptingReAuth,
isIframed: true
}
this.messageHandler = this.messageHandler.bind(this)
}
// Everything here is run server side
static async getInitialProps({
pathname,
query,
asPath,
store,
res,
req,
isServer
}) {
let props = { pathname, query, asPath, skill }
const jwt = query.jwt || getCookie('jwt', req, res)
if (jwt) {
try {
await store.dispatch(actions.auth.go(jwt))
await store.dispatch(actions.onboarding.didOnboarding())
// only save cookie if a new one has been passed
if (query.jwt) {
setCookie('jwt', query.jwt, req, res)
}
} catch (err) {
debug(err)
debug('Error fetching user from jwt')
}
} else {
debug(
'This looks pretty bad. You are missing a jwt and will probably be unauthorized'
)
}
const state = store.getState()
if (state.auth && !state.auth.error) {
state.auth.role =
(state.config.DEV_MODE && getCookie('devRole', req, res)) ||
state.auth.role
}
if (ConnectedWrapped.getInitialProps) {
const args = Array.from(arguments)
args[0] = { ...args[0], ...state }
props = {
...props,
...(await ConnectedWrapped.getInitialProps.apply(this, args))
}
}
let redirect = props.redirect || false
if (
query.back &&
query.jwt &&
(query.back.search('sprucebot.com') > 0 ||
query.back.search('bshop.io') > 0)
) {
// if there is a jwt, we are being authed
redirect = query.back
} else if (
!redirect &&
!props.public &&
(!state.auth || !state.auth.role || state.auth.error)
) {
// no redirect is set, we're not public, but auth failed
redirect = '/unauthorized'
debug('AUTH FAILED', state)
} else if (!redirect && !props.public) {
// all things look good, lets just make sure we're in the right area (owner, teammate, or guest)
const role = state.auth.role
const firstPart = props.pathname.split('/')[1]
const { jwt, ...rest } = query
const queryString = qs.stringify(rest)
// we are at '/' then redirect to the corresponding role's path
if (props.pathname === '/') {
redirect = `/${role}?${queryString}`
} else if (role !== firstPart) {
redirect = `/unauthorized`
}
}
if (redirect && res) {
res.writeHead(302, {
Location: redirect
})
res.end()
res.finished = true
return
} else if (redirect) {
window.location.href = redirect
}
// if we are /unauthorized, don't have a cookie, but have NOT done cookie check
if (
props.pathname === '/unauthorized' &&
(!state.auth || !state.auth.role)
) {
props.attemptingReAuth = true
}
// We can only return a plain object here because it is passed to the browser
// No circular dependencies
return props
}
messageHandler(e) {
if (e.data === 'Skill:NotReAuthing') {
this.setState({
attemptingReAuth: false
})
}
}
async componentDidMount() {
window.addEventListener('message', this.messageHandler)
if (window.self === window.top || window.__SBTEAMMATE__) {
// make sure we are being loaded inside sb
console.error('NOT LOADED FROM SPRUCEBOT!! BAIL BAIL BAIL')
this.setState({
attemptingReAuth: false,
isIframed: !!window.__SBTEAMMATE__
})
} else if (this.props.attemptingReAuth) {
skill.forceAuth()
}
// NOTE: Need to do this require here so that we can be sure the global window is defined
const WebFont = require('webfontloader') //eslint-disable-line
WebFont.load({
google: {
families: ['Material Icons']
}
})
}
componentWillUnmount() {
window.removeEventListener('message', this.messageHandler)
}
render() {
if (this.state.attemptingReAuth) {
return <Loader />
}
if (this.props.config.DEV_MODE) {
return (
<div>
{this.state.isIframed ? (
<style jsx global>{`
html,
body {
overflow: hidden;
}
`}</style>
) : null}
<DevControls auth={this.props.auth} />
<ConnectedWrapped {...this.props} skill={skill} lang={lang} />
</div>
)
}
return (
<div>
{this.state.isIframed ? (
<style jsx global>{`
html,
body {
overflow: hidden;
}
`}</style>
) : null}
<ConnectedWrapped {...this.props} skill={skill} lang={lang} />
</div>
)
}
}
}
export default Wrapped => Page(Wrapped)