Persistent session

This commit is contained in:
dbroqua 2018-09-09 23:11:27 +02:00
parent c4f0a9ea62
commit 84c5fe0a6c
12 changed files with 432 additions and 249 deletions

76
package-lock.json generated
View file

@ -37,6 +37,41 @@
} }
} }
}, },
"@types/cookie": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.3.1.tgz",
"integrity": "sha512-64Uv+8bTRVZHlbB8eXQgMP9HguxPgnOOIYrQpwHWrtLDrtcG/lILKhUl7bV65NSOIJ9dXGYD7skQFXzhL8tk1A=="
},
"@types/hoist-non-react-statics": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.0.1.tgz",
"integrity": "sha512-3wTz66vV+WatOAjMST+hKCmo01KYPFgnsu+QeLcn0FuwPCoymX6aj1a4RvFCdVsfh2m0hfTPhE/zTv4M28ho1Q==",
"requires": {
"@types/react": "*"
}
},
"@types/object-assign": {
"version": "4.0.30",
"resolved": "https://registry.npmjs.org/@types/object-assign/-/object-assign-4.0.30.tgz",
"integrity": "sha1-iUk3HVqZ9Dge4PHfCpt6GH4H5lI="
},
"@types/prop-types": {
"version": "15.5.5",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.5.5.tgz",
"integrity": "sha512-mOrlCEdwX3seT3n0AXNt4KNPAZZxcsABUHwBgFXOt+nvFUXkxCAO6UBJHPrDxWEa2KDMil86355fjo8jbZ+K0Q==",
"requires": {
"@types/react": "*"
}
},
"@types/react": {
"version": "16.4.13",
"resolved": "https://registry.npmjs.org/@types/react/-/react-16.4.13.tgz",
"integrity": "sha512-a2Z7UmwnAzZ23bTHV6on141S8vvSC7MEJGG85R5/VG80ybzkt5QJqNzlaJ0Y6OX1dncrXFW8B0vWPIx7QuOUqA==",
"requires": {
"@types/prop-types": "*",
"csstype": "^2.2.0"
}
},
"abab": { "abab": {
"version": "1.0.4", "version": "1.0.4",
"resolved": "https://registry.npmjs.org/abab/-/abab-1.0.4.tgz", "resolved": "https://registry.npmjs.org/abab/-/abab-1.0.4.tgz",
@ -2669,6 +2704,11 @@
"cssom": "0.3.x" "cssom": "0.3.x"
} }
}, },
"csstype": {
"version": "2.5.6",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-2.5.6.tgz",
"integrity": "sha512-tKPyhy0FmfYD2KQYXD5GzkvAYLYj96cMLXr648CKGd3wBe0QqoPipImjGiLze9c8leJK8J3n7ap90tpk3E6HGQ=="
},
"currently-unhandled": { "currently-unhandled": {
"version": "0.4.1", "version": "0.4.1",
"resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz",
@ -8639,6 +8679,26 @@
"schedule": "^0.3.0" "schedule": "^0.3.0"
} }
}, },
"react-cookie": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/react-cookie/-/react-cookie-3.0.4.tgz",
"integrity": "sha512-WNf1LifcjRQfg/QEDYQkey78XNJ46/k+lhoKrTK1Iv1jiqInl5jmjRBnEqDJ32HhgeL0iJAsJrEC+o+LkJ/O9Q==",
"requires": {
"@types/hoist-non-react-statics": "^3.0.1",
"hoist-non-react-statics": "^3.0.0",
"universal-cookie": "^3.0.4"
},
"dependencies": {
"hoist-non-react-statics": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.0.1.tgz",
"integrity": "sha512-1kXwPsOi0OGQIZNVMPvgWJ9tSnGMiMfJdihqEzrPEXlHOBh9AAHXX/QYmAJTXztnz/K+PQ8ryCb4eGaN6HlGbQ==",
"requires": {
"react-is": "^16.3.2"
}
}
}
},
"react-dev-utils": { "react-dev-utils": {
"version": "5.0.2", "version": "5.0.2",
"resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-5.0.2.tgz", "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-5.0.2.tgz",
@ -8685,6 +8745,11 @@
"resolved": "https://registry.npmjs.org/react-icons/-/react-icons-3.0.5.tgz", "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-3.0.5.tgz",
"integrity": "sha512-gNOTMhB3QCFsDnBkO0psdcz84BjprjT95pX6SSJ9pNARQozTsBqOKeVl+uw8zMIBGGDX9GpdY9TflnRjiX4z1A==" "integrity": "sha512-gNOTMhB3QCFsDnBkO0psdcz84BjprjT95pX6SSJ9pNARQozTsBqOKeVl+uw8zMIBGGDX9GpdY9TflnRjiX4z1A=="
}, },
"react-is": {
"version": "16.5.0",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.5.0.tgz",
"integrity": "sha512-kpkCGLsChXTEQJVmowQqHpCjHKJFwB4SIChYaaaiAkq8OtE2aBg5pQe8/xnFlGmz9KmMx1H4oQRUyxP7qC9v5A=="
},
"react-lifecycles-compat": { "react-lifecycles-compat": {
"version": "3.0.4", "version": "3.0.4",
"resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
@ -10599,6 +10664,17 @@
"crypto-random-string": "^1.0.0" "crypto-random-string": "^1.0.0"
} }
}, },
"universal-cookie": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/universal-cookie/-/universal-cookie-3.0.4.tgz",
"integrity": "sha512-3rhx6RAIuRmCWJttnbgzMrp2TbHhUmgQ2GrpY/US03Siv5T28iXr2qYw1m3YqmluBxEyrvZaloVemkLSId+Oyg==",
"requires": {
"@types/cookie": "^0.3.1",
"@types/object-assign": "^4.0.30",
"cookie": "^0.3.1",
"object-assign": "^4.1.0"
}
},
"universalify": { "universalify": {
"version": "0.1.2", "version": "0.1.2",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",

View file

@ -6,6 +6,7 @@
"axios": "^0.18.0", "axios": "^0.18.0",
"bootstrap": "^4.1.3", "bootstrap": "^4.1.3",
"react": "^16.5.0", "react": "^16.5.0",
"react-cookie": "^3.0.4",
"react-dom": "^16.5.0", "react-dom": "^16.5.0",
"react-icons": "^3.0.5", "react-icons": "^3.0.5",
"react-router-dom": "^4.3.1", "react-router-dom": "^4.3.1",
@ -14,7 +15,7 @@
}, },
"scripts": { "scripts": {
"start": "react-scripts start", "start": "react-scripts start",
"build": "react-scripts build", "build": "react-scripts build && ssh guarda 'rm -r www/darkou.fr/cfa/bo/static' && scp -r build/* guarda:www/darkou.fr/cfa/bo",
"test": "react-scripts test --env=jsdom", "test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject" "eject": "react-scripts eject"
}, },

View file

@ -1,9 +1,15 @@
import axios from 'axios'; import axios from 'axios';
axios.defaults.headers.common['Access-Control-Allow-Origin'] = '*';
export function setDefaults(params) { export function setDefaults(params) {
axios.defaults.headers.common.Authorization = params.Authorization; axios.defaults.headers.common.Authorization = params.Authorization;
} }
export function setAuthorization(Authorization) {
axios.defaults.headers.common.Authorization = Authorization;
}
export default axios.create({ export default axios.create({
baseURL: 'https://api.cfa.darkou.fr/api/', baseURL: 'https://api.cfa.darkou.fr/api/',
}); });

View file

@ -9,6 +9,7 @@ import Vegetable from './Vegetable';
import requireAuth from './requireAuth'; import requireAuth from './requireAuth';
import Login from './Login'; import Login from './Login';
import Logout from './Logout';
import './App.css'; import './App.css';
@ -19,6 +20,7 @@ class App extends Component {
<Router> <Router>
<Switch> <Switch>
<Route exact path="/" component={requireAuth(Login, false)} /> <Route exact path="/" component={requireAuth(Login, false)} />
<Route exact path="/LogOut" component={Logout} />
<Route exact path="/categories" component={requireAuth(Categories)} /> <Route exact path="/categories" component={requireAuth(Categories)} />
<Route exact path="/categories/:categoryId" component={requireAuth(Category)} /> <Route exact path="/categories/:categoryId" component={requireAuth(Category)} />
<Route exact path="/categories/:categoryId/vegetables/:vegetableId" component={requireAuth(Vegetable)} /> <Route exact path="/categories/:categoryId/vegetables/:vegetableId" component={requireAuth(Vegetable)} />

View file

@ -9,6 +9,7 @@ import {
} from 'reactstrap'; } from 'reactstrap';
import { FaTrashAlt, FaPlus } from 'react-icons/fa'; import { FaTrashAlt, FaPlus } from 'react-icons/fa';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import Navigation from './Navigation';
import Header from './Header'; import Header from './Header';
import API from './Api'; import API from './Api';
@ -81,8 +82,10 @@ class Categories extends Component {
render() { render() {
return ( return (
<Container> <div>
<Header /> <Header />
<Container>
<Navigation />
<Table> <Table>
<thead> <thead>
<tr> <tr>
@ -119,10 +122,11 @@ class Categories extends Component {
<Button color="primary"> <Button color="primary">
<FaPlus /> <FaPlus />
{' '} {' '}
Ajouter Ajouter
</Button> </Button>
</Form> </Form>
</Container> </Container>
</div>
); );
} }
} }

View file

@ -9,6 +9,7 @@ import {
} from 'reactstrap'; } from 'reactstrap';
import { FaTrashAlt, FaPlus, FaEdit } from 'react-icons/fa'; import { FaTrashAlt, FaPlus, FaEdit } from 'react-icons/fa';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import Navigation from './Navigation';
import Header from './Header'; import Header from './Header';
import API from './Api'; import API from './Api';
@ -111,8 +112,10 @@ class Category extends Component {
render() { render() {
return ( return (
<div>
<Header />
<Container> <Container>
<Header categoryId={this.state.categoryId} categoryName={this.state.Category.name} /> <Navigation categoryId={this.state.categoryId} categoryName={this.state.Category.name} />
<Form inline onSubmit={this.updateCategory} style={{ marginBottom: '16px' }}> <Form inline onSubmit={this.updateCategory} style={{ marginBottom: '16px' }}>
<FormGroup className="mb-8 mr-sm-8 mb-sm-0"> <FormGroup className="mb-8 mr-sm-8 mb-sm-0">
<Input <Input
@ -126,7 +129,7 @@ class Category extends Component {
<Button color="primary"> <Button color="primary">
<FaEdit /> <FaEdit />
{' '} {' '}
Mettre à jour Mettre à jour
</Button> </Button>
</Form> </Form>
<Table> <Table>
@ -162,10 +165,11 @@ Mettre à jour
<Button color="primary"> <Button color="primary">
<FaPlus /> <FaPlus />
{' '} {' '}
Ajouter Ajouter
</Button> </Button>
</Form> </Form>
</Container> </Container>
</div>
); );
} }
} }

View file

@ -1,77 +1,51 @@
import React from 'react'; import React from 'react';
import { Breadcrumb, BreadcrumbItem } from 'reactstrap';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import API from './Api'; import {
Collapse,
Navbar,
NavbarToggler,
NavbarBrand,
Nav,
NavItem,
NavLink,
} from 'reactstrap';
import { FaSignOutAlt } from 'react-icons/fa';
class Header extends React.Component { export default class Example extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
const Breadcrumb = []; this.toggle = this.toggle.bind(this);
if (API.defaults.headers.common.Authorization !== undefined) {
Breadcrumb.push({
name: 'Catégories',
path: '/categories',
active: !this.props.categoryId,
});
if (this.props.categoryId) {
Breadcrumb.push({
name: this.props.categoryId,
path: `/categories/${this.props.categoryId}`,
active: !this.props.vegetableId,
});
if (this.props.vegetableId) {
Breadcrumb.push({
name: this.props.vegetableId,
path: `/categories/${this.props.categoryId}/vegetal/${this.props.vegetableId}`,
active: true,
});
}
}
}
this.state = { this.state = {
Breadcrumb, isOpen: false,
}; };
} }
componentWillReceiveProps(props) { toggle() {
if (props.categoryName) { this.setState({
const Breadcrumb = this.state.Breadcrumb; isOpen: !this.state.isOpen,
Breadcrumb[1].name = props.categoryName; });
this.setState({ Breadcrumb });
}
if (props.vegetableName) {
const Breadcrumb = this.state.Breadcrumb;
Breadcrumb[2].name = props.vegetableName;
this.setState({ Breadcrumb });
}
} }
render() { render() {
return ( return (
<div> <div>
<Breadcrumb> <Navbar color="light" light expand="md">
{ this.state.Breadcrumb.map((item, key) => ( <NavbarBrand>RodiVert</NavbarBrand>
item.active === true <NavbarToggler onClick={this.toggle} />
? ( <Collapse isOpen={this.state.isOpen} navbar>
<BreadcrumbItem key={key} active> <Nav className="ml-auto" navbar>
{item.name} <NavItem>
</BreadcrumbItem> <NavLink tag={Link} to="/LogOut">
) <FaSignOutAlt />
: ( {' '}
<BreadcrumbItem key={key}> Déconnexion
<Link to={item.path}>{item.name}</Link> </NavLink>
</BreadcrumbItem> </NavItem>
) </Nav>
))} </Collapse>
</Breadcrumb> </Navbar>
</div> </div>
); );
} }
} }
export default Header;

View file

@ -10,7 +10,8 @@ import {
Alert, Alert,
} from 'reactstrap'; } from 'reactstrap';
import { FaSignInAlt } from 'react-icons/fa'; import { FaSignInAlt } from 'react-icons/fa';
import API, { setDefaults } from './Api'; import { Cookies } from 'react-cookie';
import API, { setAuthorization } from './Api';
class Login extends Component { class Login extends Component {
constructor(props) { constructor(props) {
@ -37,9 +38,11 @@ class Login extends Component {
}) })
.then((response) => { .then((response) => {
if (response.status === 200) { if (response.status === 200) {
setDefaults({ const cookies = new Cookies();
Authorization: `Basic ${new Buffer(`${this.state.email}:${this.state.password}`).toString('base64')}`, const Authorization = `Basic ${new Buffer(`${this.state.email}:${this.state.password}`).toString('base64')}`;
});
cookies.set('cfa_bo', Authorization);
setAuthorization(Authorization);
this.setState({ message: '' }); this.setState({ message: '' });
this.props.history.push('/categories'); this.props.history.push('/categories');
} else { } else {

23
src/Logout.js Normal file
View file

@ -0,0 +1,23 @@
import { Component } from 'react';
import { Cookies } from 'react-cookie';
import { setAuthorization } from './Api';
class Logout extends Component {
constructor(props) {
super(props);
const cookies = new Cookies();
setAuthorization(null);
cookies.remove('cfa_bo');
this.props.history.push('/');
}
render() {
return (
'Bye !'
);
}
}
export default Logout;

77
src/Navigation.js Normal file
View file

@ -0,0 +1,77 @@
import React from 'react';
import { Breadcrumb, BreadcrumbItem } from 'reactstrap';
import { Link } from 'react-router-dom';
import API from './Api';
class Navigation extends React.Component {
constructor(props) {
super(props);
const Breadcrumb = [];
if (API.defaults.headers.common.Authorization !== undefined) {
Breadcrumb.push({
name: 'Catégories',
path: '/categories',
active: !this.props.categoryId,
});
if (this.props.categoryId) {
Breadcrumb.push({
name: this.props.categoryId,
path: `/categories/${this.props.categoryId}`,
active: !this.props.vegetableId,
});
if (this.props.vegetableId) {
Breadcrumb.push({
name: this.props.vegetableId,
path: `/categories/${this.props.categoryId}/vegetal/${this.props.vegetableId}`,
active: true,
});
}
}
}
this.state = {
Breadcrumb,
};
}
componentWillReceiveProps(props) {
if (props.categoryName) {
const Breadcrumb = this.state.Breadcrumb;
Breadcrumb[1].name = props.categoryName;
this.setState({ Breadcrumb });
}
if (props.vegetableName) {
const Breadcrumb = this.state.Breadcrumb;
Breadcrumb[2].name = props.vegetableName;
this.setState({ Breadcrumb });
}
}
render() {
return (
<div>
<Breadcrumb>
{ this.state.Breadcrumb.map((item, key) => (
item.active === true
? (
<BreadcrumbItem key={key} active>
{item.name}
</BreadcrumbItem>
)
: (
<BreadcrumbItem key={key}>
<Link to={item.path}>{item.name}</Link>
</BreadcrumbItem>
)
))}
</Breadcrumb>
</div>
);
}
}
export default Navigation;

View file

@ -10,6 +10,7 @@ import {
Input, Input,
} from 'reactstrap'; } from 'reactstrap';
import { FaEdit, FaMapMarkerAlt } from 'react-icons/fa'; import { FaEdit, FaMapMarkerAlt } from 'react-icons/fa';
import Navigation from './Navigation';
import Header from './Header'; import Header from './Header';
import API from './Api'; import API from './Api';
@ -161,8 +162,10 @@ class Vegetable extends Component {
} }
return ( return (
<div>
<Header />
<Container className="Vegetable"> <Container className="Vegetable">
<Header categoryId={this.state.Vegetable.Type ? this.state.Vegetable.Type.id : 1} categoryName={this.state.Vegetable.Type ? this.state.Vegetable.Type.name : null} vegetableId={this.state.Vegetable.id || 1} vegetableName={this.state.Vegetable.name} /> <Navigation categoryId={this.state.Vegetable.Type ? this.state.Vegetable.Type.id : 1} categoryName={this.state.Vegetable.Type ? this.state.Vegetable.Type.name : null} vegetableId={this.state.Vegetable.id || 1} vegetableName={this.state.Vegetable.name} />
<Form onSubmit={this.updateVegetable}> <Form onSubmit={this.updateVegetable}>
<Row> <Row>
<Col xs={12} sm={4}> <Col xs={12} sm={4}>
@ -251,10 +254,11 @@ class Vegetable extends Component {
<Button color="primary" style={{ marginTop: '16px' }}> <Button color="primary" style={{ marginTop: '16px' }}>
<FaEdit /> <FaEdit />
{' '} {' '}
Mettre à jour Mettre à jour
</Button> </Button>
</Form> </Form>
</Container> </Container>
</div>
); );
} }
} }

View file

@ -1,10 +1,19 @@
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import { Cookies } from 'react-cookie';
import App from './App'; import App from './App';
import registerServiceWorker from './registerServiceWorker'; import registerServiceWorker from './registerServiceWorker';
import { setAuthorization } from './Api';
import './index.css'; import './index.css';
import 'bootstrap/dist/css/bootstrap.min.css'; import 'bootstrap/dist/css/bootstrap.min.css';
const cookies = new Cookies();
const auth = cookies.get('cfa_bo');
if (auth) {
setAuthorization(auth);
}
ReactDOM.render(<App />, document.getElementById('root')); ReactDOM.render(<App />, document.getElementById('root'));
registerServiceWorker(); registerServiceWorker();