Integrate login and auth server API
This commit is contained in:
parent
bd44438277
commit
75d4395964
|
@ -25,9 +25,6 @@ SECRET_KEY = 'h$p_vf^h$h@cebvgeynhda3-@i&+f-(3k9w-qsftn_+!-o_)$0'
|
||||||
# SECURITY WARNING: don't run with debug turned on in production!
|
# SECURITY WARNING: don't run with debug turned on in production!
|
||||||
DEBUG = True
|
DEBUG = True
|
||||||
|
|
||||||
ALLOWED_HOSTS = []
|
|
||||||
|
|
||||||
|
|
||||||
# Application definition
|
# Application definition
|
||||||
|
|
||||||
INSTALLED_APPS = [
|
INSTALLED_APPS = [
|
||||||
|
@ -39,6 +36,7 @@ INSTALLED_APPS = [
|
||||||
'django.contrib.staticfiles',
|
'django.contrib.staticfiles',
|
||||||
'rest_framework',
|
'rest_framework',
|
||||||
'rest_framework.authtoken',
|
'rest_framework.authtoken',
|
||||||
|
'corsheaders',
|
||||||
'authserver.api',
|
'authserver.api',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -49,8 +47,14 @@ REST_FRAMEWORK = {
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#CORS_ORIGIN_WHITELIST = (
|
||||||
|
# 'localhost:8000',
|
||||||
|
#)
|
||||||
|
|
||||||
|
CORS_ORIGIN_ALLOW_ALL = True
|
||||||
|
|
||||||
MIDDLEWARE = [
|
MIDDLEWARE = [
|
||||||
|
'corsheaders.middleware.CorsMiddleware',
|
||||||
'django.middleware.security.SecurityMiddleware',
|
'django.middleware.security.SecurityMiddleware',
|
||||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||||
'django.middleware.common.CommonMiddleware',
|
'django.middleware.common.CommonMiddleware',
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
certifi==2018.8.24
|
certifi==2018.8.24
|
||||||
chardet==3.0.4
|
chardet==3.0.4
|
||||||
Django==2.1.1
|
Django==2.1.1
|
||||||
|
django-cors-headers==2.4.0
|
||||||
djangorestframework==3.8.2
|
djangorestframework==3.8.2
|
||||||
idna==2.7
|
idna==2.7
|
||||||
Pillow==5.2.0
|
Pillow==5.2.0
|
||||||
|
|
|
@ -2,46 +2,71 @@ import React, { Component } from 'react';
|
||||||
import Categories from './Categories';
|
import Categories from './Categories';
|
||||||
import Category from './Category';
|
import Category from './Category';
|
||||||
import Tool from './Tool';
|
import Tool from './Tool';
|
||||||
import { Container, Dimmer, Dropdown, Header, Icon, Item, Loader, Menu, Segment, Input } from 'semantic-ui-react';
|
import { Button, Confirm, Container, Dimmer, Form, Header, Icon, Item, Label, Loader, Menu, Message, Segment, Input } from 'semantic-ui-react';
|
||||||
import { Link, Route } from 'react-router-dom';
|
import { Link, Route } from 'react-router-dom';
|
||||||
import io from 'socket.io-client';
|
import io from 'socket.io-client';
|
||||||
|
|
||||||
// Move to env var
|
// Move to env var
|
||||||
const SERVER_URL = 'http://localhost:8080';
|
const SOCKET_SERVER_URL = 'http://localhost:8080';
|
||||||
|
const AUTH_SERVER_URL = 'http://localhost:8000';
|
||||||
|
|
||||||
class App extends Component {
|
class App extends Component {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.socket = io(SERVER_URL);
|
this.socket = io(SOCKET_SERVER_URL);
|
||||||
|
this.storage = typeof localStorage !== 'undefined';
|
||||||
|
|
||||||
|
let token = this.storage ? localStorage.getItem('token') : null;
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
|
login: {token: token, error: null, username: '', password: '', confirmLogout: false},
|
||||||
user: null,
|
user: null,
|
||||||
toolData: null,
|
toolData: null,
|
||||||
toolStatus: null,
|
toolStatus: null,
|
||||||
connected: false,
|
connected: false,
|
||||||
|
network: true,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getUser = () => {
|
||||||
|
fetch(AUTH_SERVER_URL + '/user/', {
|
||||||
|
headers: {'Authorization': 'Token ' + this.state.login.token},
|
||||||
|
})
|
||||||
|
.then(response => {
|
||||||
|
if (response.ok) {
|
||||||
|
response.json().then(data => this.setState({ user: data[0] }));
|
||||||
|
} else {
|
||||||
|
this.handleLogout();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.log(error)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
fetch(SERVER_URL + '/api/tooldata')
|
fetch(AUTH_SERVER_URL + '/tooldata/')
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(data => this.setState({ toolData: data }));
|
.then(data => this.setState({ toolData: data }))
|
||||||
fetch(SERVER_URL + '/api/user')
|
.catch(error => {
|
||||||
.then(response => response.json())
|
console.log(error)
|
||||||
.then(data => this.setState({ user: data }));
|
this.setState({network: false});
|
||||||
|
});
|
||||||
|
|
||||||
|
if (this.state.login.token) this.getUser();
|
||||||
|
|
||||||
this.socket.on('toolStatus', toolStatus =>
|
this.socket.on('toolStatus', toolStatus =>
|
||||||
this.setState({ toolStatus: toolStatus })
|
this.setState({ toolStatus: toolStatus })
|
||||||
);
|
);
|
||||||
|
|
||||||
this.socket.on('connect', toolStatus =>
|
this.socket.on('connect', () =>
|
||||||
this.setState({ connected: true })
|
this.setState({ connected: true })
|
||||||
);
|
);
|
||||||
|
|
||||||
this.socket.on('disconnect', toolStatus =>
|
this.socket.on('disconnect', () => {
|
||||||
this.setState({ toolStatus: null, connected: false })
|
this.setState({ toolStatus: null, connected: false });
|
||||||
);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
|
@ -50,38 +75,103 @@ class App extends Component {
|
||||||
|
|
||||||
requestInterlock = change => {
|
requestInterlock = change => {
|
||||||
this.socket.emit('requestInterlock', {
|
this.socket.emit('requestInterlock', {
|
||||||
username: this.state.user.username,
|
token: this.state.login.token,
|
||||||
change: change,
|
change: change,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleChange = event => {
|
||||||
|
const name = event.target.name;
|
||||||
|
const value = event.target.value;
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
login: {...this.state.login, [name]: value}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSubmit = () => {
|
||||||
|
const login = this.state.login;
|
||||||
|
fetch(AUTH_SERVER_URL + '/login/', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {'Content-Type': 'application/json; charset=utf-8'},
|
||||||
|
body: JSON.stringify({'username': login.username, 'password': login.password})
|
||||||
|
})
|
||||||
|
.then(response => {
|
||||||
|
if (response.ok) {
|
||||||
|
response.json().then(data => {
|
||||||
|
this.setState({login: {...login, ...data, error: false}});
|
||||||
|
if (this.storage) localStorage.setItem('token', data.token);
|
||||||
|
this.getUser();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.setState({login: {...login, error: true}});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.log(error)
|
||||||
|
this.setState({network: false});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
confirmLogout = () => {
|
||||||
|
if (this.state.login.token) {
|
||||||
|
this.setState({
|
||||||
|
login: {...this.state.login, confirmLogout: true}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cancelLogout = () => {
|
||||||
|
this.setState({
|
||||||
|
login: {...this.state.login, confirmLogout: false}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
handleLogout = () => {
|
||||||
|
this.setState({
|
||||||
|
login: {...this.state.login, token: null, confirmLogout: false}
|
||||||
|
});
|
||||||
|
localStorage.removeItem('token');
|
||||||
|
}
|
||||||
|
|
||||||
|
handleRefresh = () => {
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const login = this.state.login;
|
||||||
|
const user = this.state.user;
|
||||||
const toolData = this.state.toolData;
|
const toolData = this.state.toolData;
|
||||||
const toolStatus = this.state.toolStatus;
|
const toolStatus = this.state.toolStatus;
|
||||||
const user = this.state.user;
|
|
||||||
const connected = this.state.connected;
|
const connected = this.state.connected;
|
||||||
|
const network = this.state.network;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
network ?
|
||||||
<div>
|
<div>
|
||||||
<Menu fixed='top' borderless inverted>
|
<Menu fixed='top' borderless inverted>
|
||||||
<Menu.Item>
|
<Menu.Item onClick={this.confirmLogout}>
|
||||||
<Icon name='bars' size='big' />
|
<Icon name='user close' size='big' />
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
<Menu.Item as={Link} to='/'>
|
<Menu.Item as={Link} to='/'>
|
||||||
<Icon name='home' size='big' />
|
<Icon name='home' size='big' />
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
<Menu.Item>
|
<Menu.Item>
|
||||||
<Icon name='circle' color={connected ? 'green' : 'red'} />
|
<Icon name='circle' color={connected && toolStatus ? 'green' : 'red'} />
|
||||||
|
{connected && toolStatus ? 'Connected' : 'Disconnected'}
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
<Menu.Menu position='right'>
|
|
||||||
<Menu.Item position='right'>
|
|
||||||
<Input transparent inverted placeholder='Search...' icon='search' />
|
|
||||||
</Menu.Item>
|
|
||||||
</Menu.Menu>
|
|
||||||
</Menu>
|
</Menu>
|
||||||
<div style={{height: '70px', display: 'inline-block'}} />
|
<div style={{height: '70px', display: 'inline-block'}} />
|
||||||
|
|
||||||
|
<Confirm
|
||||||
|
open={login.confirmLogout}
|
||||||
|
header='Logout'
|
||||||
|
onCancel={this.cancelLogout}
|
||||||
|
onConfirm={this.handleLogout}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{login.token ?
|
||||||
|
<div>
|
||||||
{toolData && user ?
|
{toolData && user ?
|
||||||
<div>
|
<div>
|
||||||
<Route exact path='/' render={props =>
|
<Route exact path='/' render={props =>
|
||||||
|
@ -92,7 +182,7 @@ class App extends Component {
|
||||||
<Category {...props} data={toolData} user={user} />
|
<Category {...props} data={toolData} user={user} />
|
||||||
} />
|
} />
|
||||||
|
|
||||||
<Route exact path='/:category/:id' render={props =>
|
<Route exact path='/:category/:slug' render={props =>
|
||||||
<Tool {...props}
|
<Tool {...props}
|
||||||
data={toolData}
|
data={toolData}
|
||||||
user={user}
|
user={user}
|
||||||
|
@ -107,6 +197,39 @@ class App extends Component {
|
||||||
</Dimmer>
|
</Dimmer>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
:
|
||||||
|
<Container text>
|
||||||
|
<Form size='large' onSubmit={this.handleSubmit}>
|
||||||
|
{login.error ?
|
||||||
|
<Message visible error header='Invalid username / password!' />
|
||||||
|
:
|
||||||
|
<Message visible warning header='Please log in to access the tools!' />
|
||||||
|
}
|
||||||
|
<Form.Field>
|
||||||
|
<label>Protospace Username</label>
|
||||||
|
<input name='username' placeholder='Username' value={login.username} onChange={this.handleChange} />
|
||||||
|
</Form.Field>
|
||||||
|
<Form.Field>
|
||||||
|
<label>Password</label>
|
||||||
|
<input name='password' type='password' value={login.password} onChange={this.handleChange} />
|
||||||
|
</Form.Field>
|
||||||
|
<Button type='submit'>Submit</Button>
|
||||||
|
</Form>
|
||||||
|
</Container>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
:
|
||||||
|
<Dimmer active>
|
||||||
|
<Header inverted icon>
|
||||||
|
<Icon color='red' name='wifi' />
|
||||||
|
Please connect to the "Protospace" wifi
|
||||||
|
</Header>
|
||||||
|
<br />
|
||||||
|
<Button size='big' inverted icon labelPosition='left' onClick={this.handleRefresh}>
|
||||||
|
<Icon name='refresh' />
|
||||||
|
Refresh
|
||||||
|
</Button>
|
||||||
|
</Dimmer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,12 +23,12 @@ class Category extends Component {
|
||||||
|
|
||||||
<Item.Group link unstackable divided>
|
<Item.Group link unstackable divided>
|
||||||
{category.tools.map((x, n) =>
|
{category.tools.map((x, n) =>
|
||||||
<Item as={Link} to={'/' + category.slug + '/' + x.id} key={n}>
|
<Item as={Link} to={'/' + category.slug + '/' + x.slug} key={n}>
|
||||||
<Item.Image size='tiny' src={x.photo} />
|
<Item.Image size='tiny' src={x.photo} />
|
||||||
<Item.Content>
|
<Item.Content>
|
||||||
<Item.Header>{x.name}</Item.Header>
|
<Item.Header>{x.name}</Item.Header>
|
||||||
<Item.Description>
|
<Item.Description>
|
||||||
{user.authorizedTools.includes(x.id) ?
|
{user.profile.authorized_tools.includes(x.slug) ?
|
||||||
<Label color='green'>
|
<Label color='green'>
|
||||||
<Icon name='check' /> Authorized
|
<Icon name='check' /> Authorized
|
||||||
</Label> : <Label color='red'>
|
</Label> : <Label color='red'>
|
||||||
|
|
|
@ -9,7 +9,7 @@ class Tool extends Component {
|
||||||
} else if (status.action == 'arm') {
|
} else if (status.action == 'arm') {
|
||||||
return { msg: 'Arming...', canArm: false, canDisarm: true, };
|
return { msg: 'Arming...', canArm: false, canDisarm: true, };
|
||||||
} else if (status.action == 'disarm') {
|
} else if (status.action == 'disarm') {
|
||||||
return { msg: 'Disarming...', canArm: true, canDisarm: false, };
|
return { msg: 'Disarming...', canArm: false, canDisarm: false, };
|
||||||
} else if (status.state == 'off') {
|
} else if (status.state == 'off') {
|
||||||
return { msg: 'Off.', canArm: true, canDisarm: false, };
|
return { msg: 'Off.', canArm: true, canDisarm: false, };
|
||||||
} else if (status.state == 'armed') {
|
} else if (status.state == 'armed') {
|
||||||
|
@ -33,16 +33,14 @@ class Tool extends Component {
|
||||||
);
|
);
|
||||||
|
|
||||||
const tool = category.tools.find(x =>
|
const tool = category.tools.find(x =>
|
||||||
x.id.toString() === match.params.id
|
x.slug === match.params.slug
|
||||||
);
|
);
|
||||||
|
|
||||||
const status = toolStatus.find(x =>
|
const status = toolStatus[match.params.slug] || null;
|
||||||
x.id.toString() === match.params.id
|
|
||||||
) || null;
|
|
||||||
const decodedStatus = this.decodeStatus(status);
|
const decodedStatus = this.decodeStatus(status);
|
||||||
console.log(decodedStatus);
|
console.log(decodedStatus);
|
||||||
|
|
||||||
const approved = user.authorizedTools.includes(tool.id);
|
const approved = user.profile.authorized_tools.includes(tool.slug);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
@ -58,24 +56,25 @@ class Tool extends Component {
|
||||||
<Segment>
|
<Segment>
|
||||||
<Image src={tool.photo} size='medium' centered rounded />
|
<Image src={tool.photo} size='medium' centered rounded />
|
||||||
<Segment textAlign='center' basic>
|
<Segment textAlign='center' basic>
|
||||||
<p> Status: {decodedStatus.msg}</p>
|
<p>Status: {decodedStatus.msg}</p>
|
||||||
<div>
|
<div>
|
||||||
<Button color='green'
|
<Button color='green'
|
||||||
disabled={!approved || !decodedStatus.canArm}
|
disabled={!approved || !decodedStatus.canArm}
|
||||||
onClick={() => requestInterlock({toolId: tool.id, action: 'arm',})}
|
onClick={() => requestInterlock({toolSlug: tool.slug, action: 'arm',})}
|
||||||
>
|
>
|
||||||
<Icon name='lightning' /> Arm
|
<Icon name='lightning' /> Arm
|
||||||
</Button>
|
</Button>
|
||||||
<Button color='red'
|
<Button color='red'
|
||||||
disabled={!approved || !decodedStatus.canDisarm}
|
disabled={!approved || !decodedStatus.canDisarm}
|
||||||
onClick={() => requestInterlock({toolId: tool.id, action: 'disarm',})}
|
onClick={() => requestInterlock({toolSlug: tool.slug, action: 'disarm',})}
|
||||||
>
|
>
|
||||||
<Icon name='stop' /> Disarm
|
<Icon name='stop' /> Disarm
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
<br />
|
||||||
{approved || <Label basic color='red' pointing>
|
{approved || <Label basic color='red' pointing>
|
||||||
Not authorized! Please take the xyz course.
|
Not authorized! Please take the xyz course.
|
||||||
</Label>}
|
</Label>}
|
||||||
|
</div>
|
||||||
</Segment>
|
</Segment>
|
||||||
|
|
||||||
<Table basic='very' unstackable>
|
<Table basic='very' unstackable>
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"axios": "^0.18.0",
|
||||||
"body-parser": "^1.18.2",
|
"body-parser": "^1.18.2",
|
||||||
"express": "^4.16.2",
|
"express": "^4.16.2",
|
||||||
"socket.io": "^2.0.4"
|
"socket.io": "^2.0.4"
|
||||||
|
|
|
@ -1,106 +1,20 @@
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
const bodyParser = require('body-parser');
|
const bodyParser = require('body-parser');
|
||||||
|
const axios = require('axios');
|
||||||
const app = express();
|
const app = express();
|
||||||
|
|
||||||
|
const AUTH_SERVER_URL = 'http://localhost:8000';
|
||||||
|
|
||||||
// Enums
|
// Enums
|
||||||
const lockStates = {
|
const lockStates = {
|
||||||
LOCK_OFF: 0,
|
LOCK_OFF: 0,
|
||||||
LOCK_ARMED: 1,
|
LOCK_PREARM: 1,
|
||||||
LOCK_ON_PRESSED: 2,
|
LOCK_ARMED: 2,
|
||||||
LOCK_ON: 3,
|
LOCK_ON_PRESSED: 3,
|
||||||
LOCK_OFF_PRESSED: 4,
|
LOCK_ON: 4,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Hardcoded data - can only be changed by admin
|
let toolStatus = null;
|
||||||
const toolData = {
|
|
||||||
categories: [
|
|
||||||
{
|
|
||||||
name: 'Woodshop',
|
|
||||||
slug: 'woodshop',
|
|
||||||
info: 'Some info about the woodshop.',
|
|
||||||
photo: 'https://i.imgur.com/RIm8aoG.jpg',
|
|
||||||
tools: [
|
|
||||||
{
|
|
||||||
id: 0,
|
|
||||||
name: 'White Bandsaw',
|
|
||||||
photo: 'http://wiki.protospace.ca/images/thumb/f/f8/105.jpg/146px-105.jpg',
|
|
||||||
notes: 'Requires training. Tighten tension lever prior to use / Loosen tension lever after use. Tension lever is on rear.',
|
|
||||||
wikiId: 105,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
name: 'Jointer',
|
|
||||||
photo: 'http://wiki.protospace.ca/images/thumb/6/6f/73.jpg/302px-73.jpg',
|
|
||||||
notes: 'Boards must be ABSOLUTELY FREE of nails, staples, screws, or other metal. Check boards thoroughly each and every time, make no presumptions. One mistake will chip the blade and render it useless (drum blades are very expensive).',
|
|
||||||
wikiId: 73,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Metalshop',
|
|
||||||
slug: 'metalshop',
|
|
||||||
info: 'Some info about the metalshop.',
|
|
||||||
photo: 'https://i.imgur.com/A2L2ACY.jpg',
|
|
||||||
tools: [
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
name: 'Tormach CNC',
|
|
||||||
photo: 'http://wiki.protospace.ca/images/thumb/c/cf/49.jpg/320px-49.jpg',
|
|
||||||
notes: 'Must complete Shop Safety, Lathe Training, 3D CAD Training, CAM Training, 1 on 1 Project.',
|
|
||||||
wikiId: 49,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 3,
|
|
||||||
name: 'Powerfist Bench Grinder',
|
|
||||||
photo: 'http://wiki.protospace.ca/images/thumb/c/cd/39.jpg/298px-39.jpg',
|
|
||||||
notes: 'A bench grinder is for grinding steels or deburring steel or aluminum (if fitted with a wire wheel).',
|
|
||||||
wikiId: 39,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
// Hardcoded data - can only be changed by admin
|
|
||||||
const users = [
|
|
||||||
{
|
|
||||||
username: "protospace",
|
|
||||||
name: "Protospace User",
|
|
||||||
authorizedTools: [1, 2],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
// Hardcoded data - can only be changed by admin
|
|
||||||
const lockoutData = [
|
|
||||||
{
|
|
||||||
id: 0,
|
|
||||||
mac: 'ABCDEF000000',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
mac: '2C3AE843A15F',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
mac: '2C3AE8439EAD',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 3,
|
|
||||||
mac: 'ABCDEF000003',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
// Derived data - changes through use of system
|
|
||||||
let toolStatus = lockoutData.map(x => (
|
|
||||||
{
|
|
||||||
id: x.id,
|
|
||||||
action: '',
|
|
||||||
state: 'off',
|
|
||||||
lastState: 'n/a',
|
|
||||||
}
|
|
||||||
));
|
|
||||||
|
|
||||||
console.log(toolStatus);
|
|
||||||
|
|
||||||
const server = app.listen(8080, () => {
|
const server = app.listen(8080, () => {
|
||||||
console.log('Example app listening on port 8080!');
|
console.log('Example app listening on port 8080!');
|
||||||
|
@ -118,41 +32,27 @@ app.use((req, res, next) => {
|
||||||
|
|
||||||
app.use(bodyParser.json());
|
app.use(bodyParser.json());
|
||||||
|
|
||||||
app.use('/', express.static('dist'));
|
|
||||||
|
|
||||||
app.get('/api/tooldata', (req, res) => {
|
|
||||||
console.log('Request for tool data');
|
|
||||||
|
|
||||||
res.setHeader('Content-Type', 'application/json');
|
|
||||||
res.send(toolData);
|
|
||||||
});
|
|
||||||
|
|
||||||
app.get('/api/user', (req, res) => {
|
|
||||||
console.log('Request for user data');
|
|
||||||
|
|
||||||
res.setHeader('Content-Type', 'application/json');
|
|
||||||
res.send(users[0]);
|
|
||||||
});
|
|
||||||
|
|
||||||
app.post('/api/lockout/:mac', (req, res) => {
|
app.post('/api/lockout/:mac', (req, res) => {
|
||||||
|
if (toolStatus) {
|
||||||
const mac = req.params.mac;
|
const mac = req.params.mac;
|
||||||
console.log('Request from MAC: ' + mac + ': ' + JSON.stringify(req.body));
|
console.log('Request from MAC:', mac, ': ', JSON.stringify(req.body));
|
||||||
|
|
||||||
const lockout = lockoutData.find(x => x.mac === mac);
|
const tmp = Object.entries(toolStatus).find(x => x[1].mac == mac)
|
||||||
if (!lockout) {
|
const toolSlug = tmp ? tmp[0] : null;
|
||||||
res.sendStatus(404);
|
|
||||||
}
|
|
||||||
|
|
||||||
const toolIndex = toolStatus.findIndex(x => x.id === lockout.id);
|
if (toolSlug) {
|
||||||
let tool = toolStatus[toolIndex];
|
let clearAction = false;
|
||||||
|
let tool = toolStatus[toolSlug];
|
||||||
tool.lastState = tool.state;
|
tool.lastState = tool.state;
|
||||||
|
|
||||||
switch (req.body.lockState) {
|
switch (req.body.lockState) {
|
||||||
case lockStates.LOCK_OFF:
|
case lockStates.LOCK_OFF:
|
||||||
tool.state = 'off';
|
tool.state = 'off';
|
||||||
|
if (tool.action == 'disarm') clearAction = true;
|
||||||
break;
|
break;
|
||||||
case lockStates.LOCK_ARMED:
|
case lockStates.LOCK_ARMED:
|
||||||
tool.state = 'armed';
|
tool.state = 'armed';
|
||||||
|
if (tool.action == 'arm') clearAction = true;
|
||||||
break;
|
break;
|
||||||
case lockStates.LOCK_ON:
|
case lockStates.LOCK_ON:
|
||||||
tool.state = 'on';
|
tool.state = 'on';
|
||||||
|
@ -161,21 +61,30 @@ app.post('/api/lockout/:mac', (req, res) => {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
res.setHeader('Content-Type', 'application/json');
|
// Track in case state is switched from elsewhere
|
||||||
res.send(JSON.stringify({ action: tool.action }));
|
if (tool.state != tool.lastState) clearAction = true;
|
||||||
|
|
||||||
if (tool.state != tool.lastState) {
|
if (clearAction) {
|
||||||
tool.action = '';
|
tool.action = '';
|
||||||
|
|
||||||
console.log(toolStatus);
|
console.log(tool);
|
||||||
io.sockets.emit('toolStatus', toolStatus);
|
io.sockets.emit('toolStatus', toolStatus);
|
||||||
}
|
}
|
||||||
|
|
||||||
toolStatus[toolIndex] = tool;
|
res.setHeader('Content-Type', 'application/json');
|
||||||
|
res.send(JSON.stringify({ action: tool.action }));
|
||||||
|
|
||||||
|
toolStatus[toolSlug] = tool;
|
||||||
|
} else {
|
||||||
|
res.sendStatus(404);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
res.sendStatus(503);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
app.get('*', (req, res) => {
|
app.get('*', (req, res) => {
|
||||||
res.sendFile(path.resolve(__dirname, 'dist', 'index.html'));
|
res.sendStatus(404);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Socket.io websocket stuff:
|
// Socket.io websocket stuff:
|
||||||
|
@ -187,19 +96,43 @@ io.on('connection', socket => {
|
||||||
socket.emit('toolStatus', toolStatus);
|
socket.emit('toolStatus', toolStatus);
|
||||||
|
|
||||||
socket.on('requestInterlock', data => {
|
socket.on('requestInterlock', data => {
|
||||||
console.log('Interlock change requested: ' + JSON.stringify(data));
|
console.log('Interlock change requested: ', JSON.stringify(data));
|
||||||
|
|
||||||
const user = users.find(x => x.username === data.username);
|
const token = data.token;
|
||||||
const toolId = data.change.toolId;
|
const toolSlug = data.change.toolSlug;
|
||||||
const action = data.change.action;
|
const action = data.change.action;
|
||||||
|
|
||||||
if (user && user.authorizedTools.includes(data.change.toolId)) {
|
axios.get(AUTH_SERVER_URL + '/user/', {
|
||||||
const toolIndex = toolStatus.findIndex(x => x.id === toolId);
|
headers: {'Authorization': 'Token ' + token},
|
||||||
|
})
|
||||||
|
.then(res => {
|
||||||
|
const profile = res.data[0].profile || null;
|
||||||
|
if (profile && profile.authorized_tools.includes(toolSlug)) {
|
||||||
|
toolStatus[toolSlug].action = action;
|
||||||
|
|
||||||
toolStatus[toolIndex].action = action;
|
console.log(profile.user, action, toolSlug);
|
||||||
|
|
||||||
console.log(toolStatus);
|
|
||||||
io.sockets.emit('toolStatus', toolStatus);
|
io.sockets.emit('toolStatus', toolStatus);
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
.catch(err =>
|
||||||
|
console.log(err.message)
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
setInterval(() => {
|
||||||
|
axios.get(AUTH_SERVER_URL + '/tooldata/')
|
||||||
|
.then(res => {
|
||||||
|
toolStatus = res.data.categories
|
||||||
|
.reduce((a, x) => a.concat(x.tools), [])
|
||||||
|
.reduce((a, x) => ({...a, [x.slug]: a[x.slug] ? {
|
||||||
|
...a[x.slug], mac: x.mac
|
||||||
|
} : {
|
||||||
|
mac: x.mac, action: '', state: 'off', lastState: 'n/a'
|
||||||
|
}}), toolStatus || {});
|
||||||
|
io.sockets.emit('toolStatus', toolStatus);
|
||||||
|
})
|
||||||
|
.catch(err =>
|
||||||
|
console.log(err.message)
|
||||||
|
);
|
||||||
|
}, 10000);
|
||||||
|
|
|
@ -25,6 +25,14 @@ async-limiter@~1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8"
|
resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8"
|
||||||
|
|
||||||
|
axios@^0.18.0:
|
||||||
|
version "0.18.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/axios/-/axios-0.18.0.tgz#32d53e4851efdc0a11993b6cd000789d70c05102"
|
||||||
|
integrity sha1-MtU+SFHv3AoRmTts0AB4nXDAUQI=
|
||||||
|
dependencies:
|
||||||
|
follow-redirects "^1.3.0"
|
||||||
|
is-buffer "^1.1.5"
|
||||||
|
|
||||||
backo2@1.0.2:
|
backo2@1.0.2:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/backo2/-/backo2-1.0.2.tgz#31ab1ac8b129363463e35b3ebb69f4dfcfba7947"
|
resolved "https://registry.yarnpkg.com/backo2/-/backo2-1.0.2.tgz#31ab1ac8b129363463e35b3ebb69f4dfcfba7947"
|
||||||
|
@ -119,7 +127,7 @@ debug@2.6.9:
|
||||||
dependencies:
|
dependencies:
|
||||||
ms "2.0.0"
|
ms "2.0.0"
|
||||||
|
|
||||||
debug@~3.1.0:
|
debug@=3.1.0, debug@~3.1.0:
|
||||||
version "3.1.0"
|
version "3.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
|
resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -237,6 +245,13 @@ finalhandler@1.1.1:
|
||||||
statuses "~1.4.0"
|
statuses "~1.4.0"
|
||||||
unpipe "~1.0.0"
|
unpipe "~1.0.0"
|
||||||
|
|
||||||
|
follow-redirects@^1.3.0:
|
||||||
|
version "1.5.9"
|
||||||
|
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.9.tgz#c9ed9d748b814a39535716e531b9196a845d89c6"
|
||||||
|
integrity sha512-Bh65EZI/RU8nx0wbYF9shkFZlqLP+6WT/5FnA3cE/djNSuKNHJEinGGZgu/cQEkeeb2GdFOgenAmn8qaqYke2w==
|
||||||
|
dependencies:
|
||||||
|
debug "=3.1.0"
|
||||||
|
|
||||||
forwarded@~0.1.2:
|
forwarded@~0.1.2:
|
||||||
version "0.1.2"
|
version "0.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84"
|
resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84"
|
||||||
|
@ -295,6 +310,11 @@ ipaddr.js@1.6.0:
|
||||||
version "1.6.0"
|
version "1.6.0"
|
||||||
resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.6.0.tgz#e3fa357b773da619f26e95f049d055c72796f86b"
|
resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.6.0.tgz#e3fa357b773da619f26e95f049d055c72796f86b"
|
||||||
|
|
||||||
|
is-buffer@^1.1.5:
|
||||||
|
version "1.1.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
|
||||||
|
integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==
|
||||||
|
|
||||||
isarray@2.0.1:
|
isarray@2.0.1:
|
||||||
version "2.0.1"
|
version "2.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.1.tgz#a37d94ed9cda2d59865c9f76fe596ee1f338741e"
|
resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.1.tgz#a37d94ed9cda2d59865c9f76fe596ee1f338741e"
|
||||||
|
|
Loading…
Reference in New Issue
Block a user