diff --git a/src/backends/file-system/API.js b/src/backends/file-system/API.js index 5e6e1ef1b965..72d5c5ffb83a 100644 --- a/src/backends/file-system/API.js +++ b/src/backends/file-system/API.js @@ -6,6 +6,7 @@ import { APIError } from "../../valueObjects/errors"; export default class API { constructor(config) { this.api_root = config.api_root || "/api"; + this.token = config.token; } user() { @@ -17,6 +18,9 @@ export default class API { "Content-Type": "application/json", ...headers, }; + if (this.token) { + baseHeader.Authorization = `Bearer ${ this.token }`; + } return baseHeader; } diff --git a/src/backends/file-system/AuthenticationPage.css b/src/backends/file-system/AuthenticationPage.css index bcddf775ff8f..c848406865cf 100644 --- a/src/backends/file-system/AuthenticationPage.css +++ b/src/backends/file-system/AuthenticationPage.css @@ -1,31 +1,46 @@ -.fs-auth-page-root { +.nc-filesystemAuthenticationPage-root { display: flex; + flex-flow: column nowrap; align-items: center; justify-content: center; height: 100vh; } -.fs-auth-page-card { +.nc-filesystemAuthenticationPage-form { width: 350px; - padding: 10px; -} + margin-top: -80px; + z-index: 1; + & input { + background-color: #fff; + border-radius: var(--borderRadius); -.fs-auth-page-card img { - display: block; - margin: auto; - padding: 20px; -} + font-size: 14px; + padding: 10px 10px; + margin-bottom: 15px; + margin-top: 6px; + width: 100%; + position: relative; + z-index: 1; -.fs-auth-page-errorMsg { - color: #dd0000; + &:focus { + outline: none; + box-shadow: inset 0 0 0 2px var(--colorBlue); + } + } } -.fs-auth-page-message { - font-size: 1.1em; - margin: 20px 10px; +.nc-filesystemAuthenticationPage-button { + @apply(--button); + @apply(--dropShadowDeep); + @apply(--buttonDefault); + @apply(--buttonGray); + + padding: 0 30px; + display: block; + margin-top: 20px; + margin: auto; } -.fs-auth-page-button { - padding: .25em 1em; - height: auto; +.nc-filesystemAuthenticationPage-errorMsg { + color: var(--colorErrorText); } diff --git a/src/backends/file-system/AuthenticationPage.js b/src/backends/file-system/AuthenticationPage.js index e22e38da43f9..f5e1dacb3eb6 100644 --- a/src/backends/file-system/AuthenticationPage.js +++ b/src/backends/file-system/AuthenticationPage.js @@ -1,40 +1,108 @@ import PropTypes from 'prop-types'; import React from 'react'; +import { partial } from 'lodash'; import { Icon } from "UI"; -import logo from "./netlify_logo.svg"; -import styles from "./AuthenticationPage.css"; + +let component = null; + +const localHosts = { + localhost: true, + "127.0.0.1": true, + "0.0.0.0": true +}; export default class AuthenticationPage extends React.Component { + constructor(props) { + super(props); + component = this; + } + + componentDidMount() { + } + + componentWillUnmount() { + component = null; + } + static propTypes = { onLogin: PropTypes.func.isRequired, inProgress: PropTypes.bool, }; - state = { email: '' }; + state = { email: "", password: "", errors: {} }; + + handleChange = (name, e) => { + this.setState({ ...this.state, [name]: e.target.value }); + }; handleLogin = (e) => { e.preventDefault(); - this.props.onLogin(this.state); - }; - handleEmailChange = (value) => { - this.setState({ email: value }); + const { email, password } = this.state; + const errors = {}; + + if (localHosts[document.location.host.split(":").shift()]) { + this.props.onLogin(this.state); + } else { + if (!email) { + errors.email = 'Make sure to enter your email.'; + } + if (!password) { + errors.password = 'Please enter your password.'; + } + if (Object.keys(errors).length > 0) { + this.setState({ errors }); + return; + } + AuthenticationPage.authClient.login(this.state.email, this.state.password, true) + .then((user) => { + this.props.onLogin(user); + }) + .catch((error) => { + this.setState({ errors: { server: error.description || error.msg || error }, loggingIn: false }); + }); + } }; render() { - const { inProgress } = this.props; - - return (
-
- - + const { errors } = this.state; + const { error, inProgress } = this.props; + + return ( +
+ +
+ {!error &&

+ {error} +

} + {!errors.server &&

+ {errors.server} +

} +
{ errors.email || null }
+ +
{ errors.password || null }
+ + +
-
); + ); } } diff --git a/src/backends/file-system/implementation.js b/src/backends/file-system/implementation.js index 527ed21e768c..a8a75167dccc 100644 --- a/src/backends/file-system/implementation.js +++ b/src/backends/file-system/implementation.js @@ -1,3 +1,6 @@ +import GoTrue from "gotrue-js"; +import jwtDecode from 'jwt-decode'; +import {List} from 'immutable'; import trimStart from 'lodash/trimStart'; import AuthenticationPage from './AuthenticationPage'; import API from "./API"; @@ -23,11 +26,22 @@ function nameFromEmail(email) { .join(' '); } +const localHosts = { + localhost: true, + "127.0.0.1": true, + "0.0.0.0": true +}; + export default class fs { constructor(config) { this.config = config; - this.api_root = config.getIn(["backend", "api_root"], "http://localhost:8080/api"); + this.api_root = config.getIn(["backend", "api_root"], "/api"); + + const APIUrl = config.getIn(["backend", "identity_url"], "/.netlify/identity"); + this.authClient = window.netlifyIdentity ? window.netlifyIdentity.gotrue : new GoTrue({APIUrl}); + + AuthenticationPage.authClient = this.authClient; } authComponent() { @@ -35,20 +49,59 @@ export default class fs { } restoreUser(user) { + if (!localHosts[document.location.host.split(":").shift()]) { + user = this.authClient && this.authClient.currentUser(); + if (!user) return Promise.reject(); + } return this.authenticate(user); } - authenticate(state) { - this.api = new API({ api_root: this.api_root }); - return Promise.resolve({ email: state.email, name: nameFromEmail(state.email) }); + authenticate(user) { + if (localHosts[document.location.host.split(":").shift()]) { + const userData = { name: '', email: '' }; + if (user.email) { + userData.name = nameFromEmail(user.email); + userData.email = user.email; + } + this.api = new API({ + api_root: this.api_root, + token: null, + }); + return Promise.resolve(userData); + } else { + this.tokenPromise = user.jwt.bind(user); + return this.tokenPromise() + .then((token) => { + const userData = { + name: user.user_metadata.name || nameFromEmail(user.email), + email: user.email, + avatar_url: user.user_metadata.avatar_url, + metadata: user.user_metadata, + }; + this.api = new API({ + api_root: this.api_root, + token: token, + }); + return userData; + }); + } } logout() { - return null; + if (localHosts[document.location.host.split(":").shift()]) { + return null; + } else { + const user = this.authClient.currentUser(); + return user && user.logout(); + } } getToken() { - return Promise.resolve(''); + if (localHosts[document.location.host.split(":").shift()]) { + return Promise.resolve(''); + } else { + return this.tokenPromise(); + } } entriesByFolder(collection, extension) { diff --git a/src/backends/file-system/netlify_logo.svg b/src/backends/file-system/netlify_logo.svg deleted file mode 100644 index 7e42689990a3..000000000000 --- a/src/backends/file-system/netlify_logo.svg +++ /dev/null @@ -1,23 +0,0 @@ - - - Netlify - - - - - - - - - - - - - - - - - - - - diff --git a/src/index.css b/src/index.css index 3254a451358b..2946512743ba 100644 --- a/src/index.css +++ b/src/index.css @@ -19,3 +19,4 @@ */ @import "./backends/git-gateway/AuthenticationPage.css"; @import "./backends/github/AuthenticationPage.css"; +@import "./backends/file-system/AuthenticationPage.css";