Skip to content
Snippets Groups Projects
Commit 37b71fab authored by meskio's avatar meskio :tent:
Browse files

Add auth to the frontend

parent a9ae96b5
No related branches found
No related tags found
No related merge requests found
......@@ -6367,6 +6367,19 @@
"resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz",
"integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ=="
},
"history": {
"version": "4.10.1",
"resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz",
"integrity": "sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==",
"requires": {
"@babel/runtime": "^7.1.2",
"loose-envify": "^1.2.0",
"resolve-pathname": "^3.0.0",
"tiny-invariant": "^1.0.2",
"tiny-warning": "^1.0.0",
"value-equal": "^1.0.1"
}
},
"hmac-drbg": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
......@@ -6377,6 +6390,14 @@
"minimalistic-crypto-utils": "^1.0.1"
}
},
"hoist-non-react-statics": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
"integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
"requires": {
"react-is": "^16.7.0"
}
},
"hosted-git-info": {
"version": "2.8.8",
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz",
......@@ -8254,6 +8275,15 @@
"resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz",
"integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg=="
},
"mini-create-react-context": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/mini-create-react-context/-/mini-create-react-context-0.4.0.tgz",
"integrity": "sha512-b0TytUgFSbgFJGzJqXPKCFCBWigAjpjo+Fl7Vf7ZbKRDptszpppKxXH6DRXEABZ/gcEQczeb0iZ7JvL8e8jjCA==",
"requires": {
"@babel/runtime": "^7.5.5",
"tiny-warning": "^1.0.3"
}
},
"mini-css-extract-plugin": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.9.0.tgz",
......@@ -10681,6 +10711,52 @@
"warning": "^4.0.3"
}
},
"react-router": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-5.2.0.tgz",
"integrity": "sha512-smz1DUuFHRKdcJC0jobGo8cVbhO3x50tCL4icacOlcwDOEQPq4TMqwx3sY1TP+DvtTgz4nm3thuo7A+BK2U0Dw==",
"requires": {
"@babel/runtime": "^7.1.2",
"history": "^4.9.0",
"hoist-non-react-statics": "^3.1.0",
"loose-envify": "^1.3.1",
"mini-create-react-context": "^0.4.0",
"path-to-regexp": "^1.7.0",
"prop-types": "^15.6.2",
"react-is": "^16.6.0",
"tiny-invariant": "^1.0.2",
"tiny-warning": "^1.0.0"
},
"dependencies": {
"isarray": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
"integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
},
"path-to-regexp": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz",
"integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==",
"requires": {
"isarray": "0.0.1"
}
}
}
},
"react-router-dom": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.2.0.tgz",
"integrity": "sha512-gxAmfylo2QUjcwxI63RhQ5G85Qqt4voZpUXSEqCwykV0baaOTQDR1f0PmY8AELqIyVc0NEZUj0Gov5lNGcXgsA==",
"requires": {
"@babel/runtime": "^7.1.2",
"history": "^4.9.0",
"loose-envify": "^1.3.1",
"prop-types": "^15.6.2",
"react-router": "5.2.0",
"tiny-invariant": "^1.0.2",
"tiny-warning": "^1.0.0"
}
},
"react-scripts": {
"version": "3.4.3",
"resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-3.4.3.tgz",
......@@ -11054,6 +11130,11 @@
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
"integrity": "sha1-six699nWiBvItuZTM17rywoYh0g="
},
"resolve-pathname": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz",
"integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng=="
},
"resolve-url": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
......@@ -12513,6 +12594,16 @@
"resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz",
"integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q="
},
"tiny-invariant": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.1.0.tgz",
"integrity": "sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw=="
},
"tiny-warning": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz",
"integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA=="
},
"tmp": {
"version": "0.0.33",
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
......@@ -12895,6 +12986,11 @@
"spdx-expression-parse": "^3.0.0"
}
},
"value-equal": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz",
"integrity": "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw=="
},
"vary": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
......
......@@ -9,7 +9,7 @@
name="description"
content="App de gestión del garbanzo negro"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/mano.svg" />
<!--<link rel="apple-touch-icon" href="%PUBLIC_URL%/mano.svg" /> -->
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
......
import React from 'react';
import logo from './logo.svg';
import MemberList from './member.js';
import { Container, Row } from 'react-bootstrap';
//import { BrowserRouter, Switch, Route } from 'react-router-dom';
import MemberList from './member';
import AuthContext from './AuthContext';
import SignIn from './SignIn';
import Head from './Head';
function App() {
return (
<div className="App">
<Container>
<Row className="justify-content-md-center">
<img src={logo} className="Garbanzo Negro" alt="logo" />
</Row>
<Row className="justify-content-md-center">
function Panel(props) {
return (
<div>
<Head onLogout={props.onLogout} />
<MemberList />
</Row>
</Container>
</div>
);
</div>
);
/*<BrowserRouter>
<Switch>
<Route path="/sign-in" component={SignIn} />
<Route path="/" component={Head} />
</Switch>
</BrowserRouter>*/
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
isLoged: false,
num: null,
name: null,
role: null,
token: null
};
this.logout = this.logout.bind(this);
this.login = this.login.bind(this);
}
login(token, member) {
this.setState({
isLoged: true,
num: member.num,
name: member.name,
role: member.role,
token: token
});
}
logout() {
this.setState({
isLoged: false,
num: null,
name: null,
role: null,
token: null
});
}
render() {
const component = this.state.isLoged ?
<Panel onLogout={this.logout} /> :
<SignIn onLogin={this.login} />;
return (
<AuthContext.Provider value={this.state}>
{component}
</AuthContext.Provider>
);
}
}
export default App;
import { createContext } from 'react';
export const AuthContext = createContext({});
export default AuthContext;
import React from 'react';
import mano from './mano.svg';
import { Navbar, Nav, Button, Form } from 'react-bootstrap';
function Head(props) {
return (
<Navbar bg="light">
<Navbar.Brand href="/">
<img src={mano} width="30" height="30" className="d-inline-block align-top" alt="Garbanzo Negro" />
</Navbar.Brand>
<Navbar.Toggle aria-controls="basic-navbar-nav" />
<Navbar.Collapse id="basic-navbar-nav">
<Form inline>
<Button
variant="outline-success"
onClick={props.onLogout}
>
Salir
</Button>
</Form>
</Navbar.Collapse>
</Navbar>
)
}
export default Head;
import React from 'react';
import logo from './logo.svg';
import { Form, Button, Container, Row, Spinner, Alert } from 'react-bootstrap';
class SignIn extends React.Component {
constructor (props) {
super(props);
this.state = {
name: "",
password: "",
isLoading: false,
badAuth: false,
error: null
};
this.onFormSubmit = this.onFormSubmit.bind(this);
}
onFormSubmit(e) {
e.preventDefault();
this.setState({isLoading: true});
const body = JSON.stringify({
name: this.state.name, password: this.state.password});
fetch("/api/signin", {method: "POST", body})
.then(response => {
if (response.status === 400) {
var error = new Error("Bad auth");
error.name ="bad-auth";
throw error;
} else if (!response.ok) {
throw new Error(response.status.toString()+' '+response.statusText);
}
return response.json();
})
.then(data => {
this.setState({isLoading: false, error: null, badAuth: false});
this.props.onLogin(data.token, data.member);
})
.catch(error => {
if (error.name === "bad-auth") {
this.setState({isLoading: false, error: null, badAuth: true});
} else {
this.setState({isLoading: false, error: error})
}
});
}
render() {
let form = (
<Form onSubmit={this.onFormSubmit}>
<Form.Group>
<Form.Label>Nombre</Form.Label>
<Form.Control
placeholder="Nombre"
value={this.state.name}
onChange={e => this.setState({name: e.target.value})}
isInvalid={this.state.badAuth}
/>
</Form.Group>
<Form.Group>
<Form.Label>Contraseña</Form.Label>
<Form.Control
type="password"
placeholder="Contraseña"
value={this.state.password}
onChange={e => this.setState({password: e.target.value}) }
isInvalid={this.state.badAuth}
/>
<Form.Control.Feedback type="invalid">
Nombre o contraseña invalidos.
</Form.Control.Feedback>
</Form.Group>
<Button
variant="primary"
type="submit"
className="w-100 mt-3"
>
Entra
</Button>
</Form>
);
if (this.state.isLoading) {
form = <Spinner animation="border" />;
}
let alert;
if (this.state.error != null) {
alert = (
<Alert variant="danger">
Ha ocurrido un error iniciando sesión: {this.state.error}
</Alert>
);
}
return (
<Container>
<Row className="justify-content-md-center">
<img src={logo} alt="Garbanzo Negro" />
</Row>
{alert}
<Row className="justify-content-md-center">
{form}
</Row>
</Container>
);
}
}
export default SignIn;
File moved
import React from 'react';
import { Table, Spinner, Alert } from 'react-bootstrap';
import { Table, Spinner, Alert, Row } from 'react-bootstrap';
import AuthContext from './AuthContext';
class MemberList extends React.Component {
static contextType = AuthContext;
constructor(props) {
super(props);
this.state = {
......@@ -14,18 +17,28 @@ class MemberList extends React.Component {
componentDidMount() {
this.setState({ isLoading: true });
fetch('/api/member')
.then(response => response.json())
fetch('/api/member', { headers: { 'x-authentication': this.context.token }})
.then(response => {
if (!response.ok) {
throw new Error(response.status.toString()+' '+response.statusText);
}
return response.json();
})
.then(members => this.setState({ members, isLoading: false }))
.catch(error => this.setState({error, isLoading: false }));
.catch(e => this.setState({ error: e.message, isLoading: false }));
}
render() {
if (this.state.isLoading) {
return <Spinner animation="border" />;
return (
<Row className="justify-content-md-center">
<Spinner animation="border" />
</Row>
);
}
if (this.state.error != null) {
console.log(this.state.error);
return (
<Alert variant="danger">
Ha ocurrido un error cargando el listado de socias: {this.state.error}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment