summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/client/api/crud.fetch.js13
-rw-r--r--app/client/auth/auth.actions.js86
-rw-r--r--app/client/auth/auth.gate.js63
-rw-r--r--app/client/auth/auth.reducer.js92
-rw-r--r--app/client/auth/index.js11
-rw-r--r--app/client/auth/login.component.js89
-rw-r--r--app/client/auth/logout.component.js24
-rw-r--r--app/client/auth/signup.component.js104
-rw-r--r--app/client/common/augmentationGrid.component.js47
-rw-r--r--app/client/common/buttonGrid.component.js51
-rw-r--r--app/client/common/currentTask.component.js6
-rw-r--r--app/client/common/index.js6
-rw-r--r--app/client/common/taskList.component.js2
-rw-r--r--app/client/common/textInput.component.js3
-rw-r--r--app/client/index.jsx30
-rw-r--r--app/client/modules/pix2pixhd/pix2pixhd.actions.js26
-rw-r--r--app/client/modules/pix2pixhd/pix2pixhd.reducer.js10
-rw-r--r--app/client/modules/pix2pixhd/views/pix2pixhd.train.js106
-rw-r--r--app/client/socket/socket.actions.js1
-rw-r--r--app/client/store.js2
-rw-r--r--app/client/system/system.actions.js11
-rw-r--r--app/client/types.js13
-rw-r--r--app/relay/remote.js10
-rw-r--r--app/relay/runner.js8
-rw-r--r--app/server/db/model.js55
-rw-r--r--app/server/db/models.js4
-rw-r--r--app/server/site.js34
-rw-r--r--app/server/util/auth.js168
28 files changed, 952 insertions, 123 deletions
diff --git a/app/client/api/crud.fetch.js b/app/client/api/crud.fetch.js
index 421510b..716ab3e 100644
--- a/app/client/api/crud.fetch.js
+++ b/app/client/api/crud.fetch.js
@@ -10,7 +10,7 @@ export function crud_fetch(type, tag) {
},
show: id => {
- return fetch(uri + id)
+ return fetch(uri + id, _get_headers(), _get_headers())
.then(req => req.json())
.catch(error)
},
@@ -45,15 +45,17 @@ function _get_url(_url, data) {
function _get_headers() {
return {
method: 'GET',
+ credentials: 'same-origin',
headers: {
'Accept': 'application/json',
},
}
}
-function post(data) {
+export function post(data) {
return {
method: 'POST',
body: JSON.stringify(data),
+ credentials: 'same-origin',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
@@ -64,25 +66,28 @@ export function postBody(data) {
return {
method: 'POST',
body: data,
+ credentials: 'same-origin',
headers: {
'Accept': 'application/json',
},
}
}
-function put(data) {
+export function put(data) {
return {
method: 'PUT',
body: JSON.stringify(data),
+ credentials: 'same-origin',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
}
}
-function destroy(data) {
+export function destroy(data) {
return {
method: 'DELETE',
body: JSON.stringify(data),
+ credentials: 'same-origin',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
diff --git a/app/client/auth/auth.actions.js b/app/client/auth/auth.actions.js
new file mode 100644
index 0000000..c4d9b52
--- /dev/null
+++ b/app/client/auth/auth.actions.js
@@ -0,0 +1,86 @@
+import types from '../types'
+import { put } from '../api/crud.fetch'
+
+export const setToken = (data) => {
+ return { type: types.auth.set_token, data }
+}
+export const setReturnTo = (data) => {
+ return { type: types.auth.set_return_to, data }
+}
+export const setError = (data) => {
+ return { type: types.auth.set_error, data }
+}
+export const setCurrentUser = (data) => {
+ return { type: types.auth.set_current_user, data }
+}
+export function logout() {
+ return { type: types.auth.logout_user }
+}
+export function initialized() {
+ return { type: types.auth.initialized }
+}
+export function loading() {
+ return { type: types.auth.loading }
+}
+
+export function InvalidCredentialsException(message) {
+ this.message = message
+ this.name = 'InvalidCredentialsException'
+}
+
+const api = {
+ login: '/api/login',
+ logout: '/api/logout',
+ signup: '/api/signup',
+ checkin: '/api/checkin',
+}
+
+export function login(username, password) {
+ return (dispatch) => {
+ dispatch(loading())
+ fetch(api.login, put({
+ username,
+ password
+ }))
+ .then(req => req.json())
+ .then(data => {
+ console.log(data)
+ dispatch(setCurrentUser(data.user))
+ })
+ .catch(error => {
+ console.error(error)
+ dispatch(setError(true))
+ })
+ }
+}
+
+export function signup(data) {
+ return (dispatch) => {
+ dispatch(loading())
+ fetch(api.signup, put(data))
+ .then(req => req.json())
+ .then(data => {
+ console.log(data)
+ dispatch(setCurrentUser(data.user))
+ })
+ .catch(error => {
+ console.error(error)
+ dispatch(initialized())
+ })
+ }
+}
+
+export function checkin() {
+ return (dispatch) => {
+ dispatch(loading())
+ fetch(api.checkin, put({}))
+ .then(req => req.json())
+ .then(data => {
+ dispatch(setCurrentUser(data.user))
+ })
+ .catch(error => {
+ console.error(error)
+ dispatch(initialized())
+ })
+ }
+}
diff --git a/app/client/auth/auth.gate.js b/app/client/auth/auth.gate.js
new file mode 100644
index 0000000..087dfc6
--- /dev/null
+++ b/app/client/auth/auth.gate.js
@@ -0,0 +1,63 @@
+import { h, Component } from 'preact';
+// import PropTypes from 'prop-types';
+import { BrowserRouter, Route, Switch, Redirect, withRouter } from 'react-router-dom'
+import { bindActionCreators } from 'redux';
+import { connect } from 'react-redux';
+
+import * as authActions from './auth.actions';
+
+import Login from './login.component';
+import Logout from './logout.component';
+import Signup from './signup.component';
+
+import { randint } from '../util/math'
+
+class AuthRouter extends Component {
+ render(){
+ return (
+ <BrowserRouter>
+ <div>
+ <div className="diamond"></div>
+ <Switch>
+ <Route exact path='/' component={Login} />
+ <Route exact path='/login' component={Login} />
+ <Route exact path='/logout' component={Logout} />
+ <Route exact path='/signup' component={Signup} />
+ <Route component={props => {
+ this.props.actions.setReturnTo(props.location.pathname)
+ return (
+ <Redirect to="/login" />
+ )
+ }} />
+ </Switch>
+ </div>
+ </BrowserRouter>
+ )
+ }
+ componentDidMount(){
+ document.querySelector('.diamond').style.backgroundImage = 'linear-gradient(' + (randint(40)-5) + 'deg, #fde, #ffe)'
+ }
+}
+
+class AuthGate extends Component {
+ render(){
+ if (!this.props.auth.initialized) {
+ return <div className='loading'>Loading</div>
+ }
+ if (this.props.auth.isAuthenticated) return <div>{this.props.children}</div>
+ return <AuthRouter {...this.props} />
+ }
+ componentDidMount(){
+ this.props.actions.checkin()
+ }
+}
+
+const mapStateToProps = (state) => ({
+ auth: state.auth
+});
+
+const mapDispatchToProps = (dispatch) => ({
+ actions: bindActionCreators(authActions, dispatch)
+});
+
+export default connect(mapStateToProps, mapDispatchToProps)(AuthGate);
diff --git a/app/client/auth/auth.reducer.js b/app/client/auth/auth.reducer.js
new file mode 100644
index 0000000..80b1ec5
--- /dev/null
+++ b/app/client/auth/auth.reducer.js
@@ -0,0 +1,92 @@
+import types from '../types'
+
+const authInitialState = {
+ token: null,
+ user: {},
+ groups: {},
+ initialized: false,
+ loading: false,
+ isAuthenticated: false,
+ returnTo: null,
+}
+
+const auth = (state = authInitialState, action) => {
+ switch(action.type) {
+ case types.auth.set_token:
+ return {
+ ...state,
+ token: action.data,
+ isAuthenticated: !!action.data,
+ loading: false,
+ error: null,
+ }
+
+ case types.auth.initialized:
+ return {
+ ...state,
+ loading: false,
+ initialized: true,
+ error: null,
+ }
+
+ case types.auth.loading:
+ return {
+ ...state,
+ loading: true,
+ error: null,
+ }
+
+ case types.auth.set_current_user:
+ return {
+ ...state,
+ loading: false,
+ initialized: true,
+ isAuthenticated: true,
+ user: action.data,
+ error: null,
+ }
+
+ case types.auth.set_return_to:
+ return {
+ ...state,
+ returnTo: action.data,
+ }
+
+ case types.auth.logout_user:
+ return {
+ ...authInitialState
+ }
+
+ case types.auth.set_error:
+ return {
+ ...state,
+ loading: false,
+ error: action.data,
+ }
+
+ case types.auth.loading:
+ // const initial_state_el = document.querySelector('#initial_state')
+ // if (initial_state_el) {
+ // try {
+ // const initial_state = JSON.parse(initial_state_el.innerHTML)
+ // if (initial_state && initial_state.auth && initial_state.auth.user) {
+ // console.log(initial_state.auth.user)
+ // return {
+ // ...state,
+ // user: {
+ // ...initial_state.auth.user,
+ // }
+ // }
+ // }
+ // } catch (e) {
+ // console.error("error loading initial state")
+ // }
+ // }
+ return state
+
+ default:
+ return state
+ }
+}
+
+export default auth
diff --git a/app/client/auth/index.js b/app/client/auth/index.js
new file mode 100644
index 0000000..5e6b2b0
--- /dev/null
+++ b/app/client/auth/index.js
@@ -0,0 +1,11 @@
+import Gate from './auth.gate';
+import Login from './login.component';
+import Logout from './logout.component';
+import Signup from './signup.component';
+
+export default {
+ Gate,
+ Login,
+ Logout,
+ Signup,
+} \ No newline at end of file
diff --git a/app/client/auth/login.component.js b/app/client/auth/login.component.js
new file mode 100644
index 0000000..2ef01a6
--- /dev/null
+++ b/app/client/auth/login.component.js
@@ -0,0 +1,89 @@
+import { h, Component } from 'preact';
+// import PropTypes from 'prop-types';
+import { bindActionCreators } from 'redux';
+import { connect } from 'react-redux';
+import { Redirect } from 'react-router-dom';
+// import { Link } from 'react-router-dom';
+import * as authActions from './auth.actions';
+
+import { Group, Param, TextInput, Button } from '../common';
+
+class Login extends Component {
+ state = {
+ username: '',
+ password: '',
+ }
+ constructor() {
+ super()
+ this.handleChange = this.handleChange.bind(this)
+ this.handleSubmit = this.handleSubmit.bind(this)
+ }
+ handleChange(value, name) {
+ this.setState({
+ [name]: value,
+ error: null,
+ })
+ }
+ handleSubmit(e) {
+ e.preventDefault()
+ if (this.props.auth.loading) return
+ this.props.actions.login(this.state.username, this.state.password)
+ }
+ render(){
+ if (this.props.auth.isAuthenticated) {
+ let { returnTo } = this.props.auth
+ if (!returnTo || returnTo.match(/(login|logout|signup)/i)) {
+ returnTo = '/'
+ }
+ return <Redirect to={returnTo} />
+ }
+ return (
+ <form onSubmit={this.handleSubmit}>
+ <h1>Log in</h1><br />
+ <Group>
+ <TextInput
+ autofocus
+ autocapitalize="off"
+ autocomplete="off"
+ title="Username"
+ name="username"
+ type="text"
+ value={this.state.username}
+ onInput={this.handleChange}
+ />
+ <TextInput
+ title="Password"
+ name="password"
+ type="password"
+ value={this.state.password}
+ onInput={this.handleChange}
+ />
+ <Button
+ loading={this.props.auth.loading}
+ >
+ Login
+ </Button>
+ {this.renderAuthError()}
+ </Group>
+ </form>
+ )
+ }
+ renderAuthError(){
+ if (this.props.auth.error) {
+ return (
+ <div className='form-input-hint'>{"There was an error logging you in (bad password?)"}</div>
+ )
+ }
+ return null
+ }
+}
+
+const mapStateToProps = (state) => ({
+ auth: state.auth,
+});
+
+const mapDispatchToProps = (dispatch) => ({
+ actions: bindActionCreators(authActions, dispatch)
+});
+
+export default connect(mapStateToProps, mapDispatchToProps)(Login);
diff --git a/app/client/auth/logout.component.js b/app/client/auth/logout.component.js
new file mode 100644
index 0000000..bcc3bce
--- /dev/null
+++ b/app/client/auth/logout.component.js
@@ -0,0 +1,24 @@
+import { h, Component } from 'preact';
+// import PropTypes from 'prop-types';
+import { bindActionCreators } from 'redux';
+import { connect } from 'react-redux';
+import { Redirect } from 'react-router-dom';
+import * as authActions from './auth.actions';
+
+class Logout extends Component {
+ componentWillMount(props){
+ this.props.actions.logout()
+ }
+ render(){
+ return <Redirect to="/" />
+ }
+}
+
+const mapStateToProps = (state) => ({
+});
+
+const mapDispatchToProps = (dispatch) => ({
+ actions: bindActionCreators(authActions, dispatch)
+});
+
+export default connect(mapStateToProps, mapDispatchToProps)(Logout);
diff --git a/app/client/auth/signup.component.js b/app/client/auth/signup.component.js
new file mode 100644
index 0000000..4882681
--- /dev/null
+++ b/app/client/auth/signup.component.js
@@ -0,0 +1,104 @@
+import { h, Component } from 'preact';
+import { bindActionCreators } from 'redux';
+import { connect } from 'react-redux';
+import { Redirect } from 'react-router-dom';
+import * as actions from './auth.actions';
+
+import { Group, Param, TextInput, Button } from '../common';
+
+class Signup extends Component {
+ state = {
+ username: '',
+ password: '',
+ password2: '',
+ }
+ constructor() {
+ super()
+ this.handleChange = this.handleChange.bind(this)
+ this.handleSubmit = this.handleSubmit.bind(this)
+ }
+ handleChange(value, name) {
+ this.setState({
+ [name]: value,
+ })
+ }
+ validate(){
+ if (!this.state.password || this.state.password !== this.state.password2) {
+ return false
+ }
+ return true
+ }
+ handleSubmit(e) {
+ e.preventDefault()
+ if (this.props.auth.loading) return
+ if (!this.validate()) {
+ return this.props.actions.setError('bad password')
+ }
+ let { ...user } = this.state
+ this.props.actions.signup(user)
+ }
+ render(){
+ if (this.props.auth.isAuthenticated) {
+ let { returnTo } = this.props.auth
+ if (!returnTo || returnTo.match(/(api|login|logout|signup)/i)) {
+ returnTo = '/'
+ }
+ return <Redirect to={returnTo} />
+ }
+ return (
+ <form onSubmit={this.handleSubmit}>
+ <h1>New account</h1><br />
+ <Group>
+ <TextInput
+ autofocus
+ autocapitalize="off"
+ autocomplete="off"
+ title="Username"
+ name="username"
+ type="text"
+ value={this.state.username}
+ onInput={this.handleChange}
+ />
+ <TextInput
+ title="Password"
+ name="password"
+ type="password"
+ value={this.state.password}
+ onInput={this.handleChange}
+ />
+ <TextInput
+ title="Password again :)"
+ name="password2"
+ type="password"
+ value={this.state.password2}
+ onInput={this.handleChange}
+ />
+ <Button
+ loading={this.props.auth.loading}
+ >
+ Sign up
+ </Button>
+ {this.renderAuthError()}
+ </Group>
+ </form>
+ )
+ }
+ renderAuthError(){
+ if (this.props.auth.error) {
+ return (
+ <div className='form-input-hint'>{"Please doublecheck the form (o=_o~~)"}</div>
+ )
+ }
+ return <div className='form-input-hint'></div>
+ }
+}
+
+const mapStateToProps = (state) => ({
+ auth: state.auth,
+});
+
+const mapDispatchToProps = (dispatch) => ({
+ actions: bindActionCreators({ ...actions }, dispatch)
+});
+
+export default connect(mapStateToProps, mapDispatchToProps)(Signup);
diff --git a/app/client/common/augmentationGrid.component.js b/app/client/common/augmentationGrid.component.js
new file mode 100644
index 0000000..af77f6c
--- /dev/null
+++ b/app/client/common/augmentationGrid.component.js
@@ -0,0 +1,47 @@
+import { h, Component } from 'preact'
+
+import Group from './group.component'
+import Param from './param.component'
+import Button from './button.component'
+import ButtonGrid from './buttonGrid.component'
+
+export default class AugmentationGrid extends Component {
+ state = {
+ x: 0, y: 0, sum: 0,
+ }
+ render() {
+ let {
+ make, take, checkpoint,
+ onTrain, onAugment,
+ } = this.props
+ let {
+ x, y, sum
+ } = this.state
+ let rows = []
+ return (
+ <Group className='augmentationGrid'>
+ <ButtonGrid
+ x={make}
+ y={take}
+ max={5000}
+ onHover={(x, y) => this.setState({ x, y })}
+ onClick={(x, y) => {
+ this.setState({ sum: sum + x * y })
+ onAugment(y, x)
+ }}
+ />
+ <Param title='Name'>{checkpoint.name}</Param>
+ <Param title='Take'>{y}</Param>
+ <Param title='Make'>{x}</Param>
+ <Param title='Will add to dataset'>{x * y}</Param>
+ <Param title='Total added this epoch'>{sum}</Param>
+ <Param title='Sequence length'>{checkpoint.sequenceCount}</Param>
+ <Param title='Dataset size'>{checkpoint.datasetCount}</Param>
+ <Button onClick={() => {
+ this.setState({ sum: 0 })
+ onTrain()
+ }}>Train</Button>
+ </Group>
+ )
+ }
+}
diff --git a/app/client/common/buttonGrid.component.js b/app/client/common/buttonGrid.component.js
new file mode 100644
index 0000000..6c7c105
--- /dev/null
+++ b/app/client/common/buttonGrid.component.js
@@ -0,0 +1,51 @@
+import { h, Component } from 'preact'
+
+export default class ButtonGrid extends Component {
+ state = {
+ x: 0, y: 0,
+ }
+
+ render() {
+ const {
+ x: _x,
+ y: _y,
+ } = this.state
+ const {
+ x: X,
+ y: Y,
+ max = Infinity,
+ onClick,
+ onHover,
+ } = this.props
+ return (
+ <table className='buttonGrid'>
+ <tr className='row'>
+ <th>{" "}</th>
+ {X.map(x => (
+ <th className={x === _x && 'bold'}>{x}</th>
+ ))}
+ </tr>
+ {Y.map(y => (
+ <tr className='row'>
+ <th className={y === _y && 'bold'}>{y}</th>
+ {X.map(x => (
+ <td>
+ {x * y > max ? " " :
+ <button
+ onClick={() => onClick(x, y)}
+ onMouseEnter={() => {
+ this.setState({ x, y })
+ onHover(x, y)
+ }}
+ >
+ {" "}
+ </button>
+ }
+ </td>
+ ))}
+ </tr>
+ ))}
+ </table>
+ )
+ }
+}
diff --git a/app/client/common/currentTask.component.js b/app/client/common/currentTask.component.js
index a4d9750..3c71a88 100644
--- a/app/client/common/currentTask.component.js
+++ b/app/client/common/currentTask.component.js
@@ -22,10 +22,10 @@ function CurrentTask ({ cpu, gpu, processor }) {
const { last_message, pid, task } = p
const { activity, epoch, epochs, dataset, module } = task
return (
- <div>
+ <div className='currentTask'>
#{pid}: <b>{module} {activity}</b> <i>{dataset}</i>
- {epochs
- ? <span>{epoch} epoch{util.courtesy_s(epoch)}</span>
+ {!!epochs
+ ? <span>{epochs} epoch{util.courtesy_s(epochs)}</span>
: ""}
{epoch
? <span>(currently #{epoch})</span>
diff --git a/app/client/common/index.js b/app/client/common/index.js
index 13b3189..e6baafc 100644
--- a/app/client/common/index.js
+++ b/app/client/common/index.js
@@ -1,4 +1,7 @@
+import AudioPlayer from './audioPlayer/audioPlayer.component'
+import AugmentationGrid from './augmentationGrid.component'
import Button from './button.component'
+import ButtonGrid from './buttonGrid.component'
import Checkbox from './checkbox.component'
import CurrentTask from './currentTask.component'
import { FileList, FileRow } from './fileList.component'
@@ -22,11 +25,12 @@ import * as Views from './views'
export {
Views,
- Loading, Progress, Header,
+ Loading, Progress, Header, AudioPlayer,
FolderList, FileList, FileRow, FileUpload,
Gallery, Player,
Group, ParamGroup, Param,
TextInput, NumberInput,
Slider, Select, SelectGroup, Button, Checkbox,
CurrentTask, TaskList,
+ ButtonGrid, AugmentationGrid,
} \ No newline at end of file
diff --git a/app/client/common/taskList.component.js b/app/client/common/taskList.component.js
index 710753f..c1ed38a 100644
--- a/app/client/common/taskList.component.js
+++ b/app/client/common/taskList.component.js
@@ -27,7 +27,7 @@ class TaskList extends Component {
const task = pair[1]
const { dataset } = task
let dataset_link, label = dataset;
- console.log(task)
+ // console.log(task)
switch (task.activity) {
case 'train':
if (task.epoch === 0) {
diff --git a/app/client/common/textInput.component.js b/app/client/common/textInput.component.js
index d429944..d3b16ad 100644
--- a/app/client/common/textInput.component.js
+++ b/app/client/common/textInput.component.js
@@ -34,6 +34,9 @@ class TextInput extends Component {
value={this.state.changed ? this.state.value : this.props.value}
onInput={this.handleInput}
onKeydown={this.handleKeydown}
+ autofocus={this.props.autofocus}
+ autoComplete={this.props.autocomplete}
+ autoCapitalize={this.props.autocapitalize || 'off'}
placeholder={this.props.placeholder}
autofocus={this.props.autofocus}
className={this.props.className || ''}
diff --git a/app/client/index.jsx b/app/client/index.jsx
index fd4679c..9c18251 100644
--- a/app/client/index.jsx
+++ b/app/client/index.jsx
@@ -1,14 +1,14 @@
import { h, render } from 'preact'
import { Provider } from 'react-redux'
-import { BrowserRouter, Route } from 'react-router-dom'
+import { BrowserRouter, Switch, Route, Redirect } from 'react-router-dom'
// import client from './client'
import { store, history } from './store'
import * as socket from './socket'
import util from './util'
-import Header from './common/header.component'
-import AudioPlayer from './common/audioPlayer/audioPlayer.component'
+import Auth from './auth'
+import { Header, AudioPlayer } from './common'
import System from './system/system.component'
import Dashboard from './dashboard/dashboard.component'
import modules from './modules'
@@ -22,16 +22,20 @@ const module_list = Object.keys(modules).map(name => {
const app = (
<Provider store={store}>
- <BrowserRouter>
- <div>
- <Route exact path='/' component={Dashboard} />
- <Route path='/system/' component={System} />
- <Route path='/dashboard/' component={Dashboard} />
- {module_list}
- <Route path='/' component={Header} />
- <AudioPlayer />
- </div>
- </BrowserRouter>
+ <Auth.Gate>
+ <BrowserRouter>
+ <Switch>
+ <Route exact path='/' component={Dashboard} />
+ <Route exact path='/system/' component={System} />
+ <Route exact path='/dashboard/' component={Dashboard} />
+ <Route exact path='/logout/' component={Auth.Logout} />
+ {module_list}
+ <Route exact path='/' component={Header} />
+ <Route component={() => <Redirect to="/" />} />
+ </Switch>
+ </BrowserRouter>
+ <AudioPlayer />
+ </Auth.Gate>
</Provider>
)
diff --git a/app/client/modules/pix2pixhd/pix2pixhd.actions.js b/app/client/modules/pix2pixhd/pix2pixhd.actions.js
index c1cd2b1..a17eeab 100644
--- a/app/client/modules/pix2pixhd/pix2pixhd.actions.js
+++ b/app/client/modules/pix2pixhd/pix2pixhd.actions.js
@@ -200,4 +200,30 @@ export const list_epochs = (checkpoint_name) => (dispatch) => {
},
})
})
+}
+
+export const count_dataset = (checkpoint_name) => (dispatch) => {
+ const module = pix2pixhdModule.name
+ util.allProgress([
+ actions.socket.count_directory({ module, dir: 'sequences/' + checkpoint_name + '/' }),
+ actions.socket.count_directory({ module, dir: 'datasets/' + checkpoint_name + '/train_A/' }),
+ ], (percent, i, n) => {
+ console.log('pix2pixhd load progress', i, n)
+ dispatch({
+ type: types.app.load_progress,
+ progress: { i, n },
+ data: { module: 'pix2pixhd' },
+ })
+ }).then(res => {
+ const [sequenceCount, datasetCount] = res //, datasets, results, output, datasetUsage, lossReport] = res
+ console.log(sequenceCount, datasetCount)
+ dispatch({
+ type: types.pix2pixhd.load_dataset_count,
+ data: {
+ name: checkpoint_name,
+ sequenceCount,
+ datasetCount,
+ }
+ })
+ })
} \ No newline at end of file
diff --git a/app/client/modules/pix2pixhd/pix2pixhd.reducer.js b/app/client/modules/pix2pixhd/pix2pixhd.reducer.js
index c3d52a3..5a2afc0 100644
--- a/app/client/modules/pix2pixhd/pix2pixhd.reducer.js
+++ b/app/client/modules/pix2pixhd/pix2pixhd.reducer.js
@@ -8,6 +8,11 @@ const pix2pixhdInitialState = {
folder_id: 0,
data: null,
results: null,
+ checkpoint: {
+ name: '',
+ sequenceCount: 0,
+ datasetCount: 0,
+ }
}
const pix2pixhdReducer = (state = pix2pixhdInitialState, action) => {
@@ -21,6 +26,11 @@ const pix2pixhdReducer = (state = pix2pixhdInitialState, action) => {
...state,
results: action.results,
}
+ case types.pix2pixhd.load_dataset_count:
+ return {
+ ...state,
+ checkpoint: action.data,
+ }
case types.file.destroy:
console.log('file destroy', state.results)
return {
diff --git a/app/client/modules/pix2pixhd/views/pix2pixhd.train.js b/app/client/modules/pix2pixhd/views/pix2pixhd.train.js
index 9c8aacc..06caa5a 100644
--- a/app/client/modules/pix2pixhd/views/pix2pixhd.train.js
+++ b/app/client/modules/pix2pixhd/views/pix2pixhd.train.js
@@ -11,7 +11,8 @@ import {
FileList, FileRow,
Select, SelectGroup, Group, Param, Button,
TextInput, NumberInput,
- CurrentTask, TaskList
+ CurrentTask, TaskList,
+ AugmentationGrid,
} from '../../../common'
import DatasetForm from '../../../dataset/dataset.form'
import NewDatasetForm from '../../../dataset/dataset.new'
@@ -32,30 +33,6 @@ class Pix2PixHDTrain extends Component {
augment_take: 100,
augment_make: 20,
}
- this.short_presets = [
- { augment_take: 100, augment_make: 5 },
- { augment_take: 200, augment_make: 5 },
- { augment_take: 200, augment_make: 3 },
- { augment_take: 50, augment_make: 10 },
- { augment_take: 100, augment_make: 10 },
- { augment_take: 1000, augment_make: 1 },
- ]
- this.medium_presets = [
- { augment_take: 30, augment_make: 20 },
- { augment_take: 20, augment_make: 30 },
- { augment_take: 30, augment_make: 30 },
- { augment_take: 50, augment_make: 20 },
- { augment_take: 20, augment_make: 50 },
- { augment_take: 15, augment_make: 70 },
- ]
- this.long_presets = [
- { augment_take: 2, augment_make: 100 },
- { augment_take: 2, augment_make: 200 },
- { augment_take: 5, augment_make: 100 },
- { augment_take: 5, augment_make: 200 },
- { augment_take: 10, augment_make: 100 },
- { augment_take: 10, augment_make: 200 },
- ]
}
componentWillMount(){
const id = this.props.match.params.id || localStorage.getItem('pix2pixhd.last_id')
@@ -75,6 +52,7 @@ class Pix2PixHDTrain extends Component {
if (prevState.checkpoint_name !== this.state.checkpoint_name) {
this.setState({ epoch: 'latest' })
this.props.actions.list_epochs(this.state.checkpoint_name)
+ this.props.actions.count_dataset(this.state.checkpoint_name)
}
}
handleChange(name, value){
@@ -84,7 +62,6 @@ class Pix2PixHDTrain extends Component {
interrupt(){
this.props.actions.queue.stop_task('gpu')
}
-
render(){
if (this.props.pix2pixhd.loading) {
return <Loading progress={this.props.pix2pixhd.progress} />
@@ -164,52 +141,41 @@ class Pix2PixHDTrain extends Component {
<Button
title="Make a movie without augmenting"
value="Generate"
- onClick={() => this.props.remote.augment_task(this.state.checkpoint_name, { ...this.state, no_symlinks: true, mov: true, folder_id: this.props.pix2pixhd.data.resultsFolder.id })}
- />
- </Group>
- <Group title='Augmentation Presets'>
- <Param title="Short Recursion">
- <div>
- {this.short_presets.map(p => (
- <button onClick={() => this.props.remote.augment_task(this.state.checkpoint_name, p)}>
- {p.augment_take}{'x'}{p.augment_make}
- </button>
- ))}
- </div>
- </Param>
- <Param title="Medium Recursion">
- <div>
- {this.medium_presets.map(p => (
- <button onClick={() => this.props.remote.augment_task(this.state.checkpoint_name, p)}>
- {p.augment_take}{'x'}{p.augment_make}
- </button>
- ))}
- </div>
- </Param>
- <Param title="Long Recursion">
- <div>
- {this.long_presets.map(p => (
- <button onClick={() => this.props.remote.augment_task(this.state.checkpoint_name, p)}>
- {p.augment_take}{'x'}{p.augment_make}
- </button>
- ))}
- </div>
- </Param>
- </Group>
-
- <Group title='Train'>
- <Button
- title="Train one epoch"
- value="Train"
- onClick={() => this.props.remote.train_task(this.state.checkpoint_name, pix2pixhd.folder_id, 1)}
+ onClick={() => {
+ this.props.remote.augment_task(this.state.checkpoint_name, {
+ ...this.state,
+ no_symlinks: true,
+ mov: true,
+ folder_id: this.props.pix2pixhd.data.resultsFolder.id
+ })
+ }}
/>
</Group>
-
- <Group title='Clear'>
- <Button
- title="Delete recursive frames"
- value="Clear"
- onClick={() => this.props.remote.clear_recursive_task(this.state.checkpoint_name)}
+ <Group title='Augmentation Grid'>
+ <AugmentationGrid
+ checkpoint={this.props.pix2pixhd.checkpoint}
+ take={[1,2,3,4,5,10,15,20,25,50,75,100,200,300,400,500,1000]}
+ make={[1,2,3,4,5,10,15,20,25,50,75,100,200,]}
+ onAugment={(augment_take, augment_make) => {
+ this.props.remote.augment_task(this.state.checkpoint_name, {
+ ...this.state,
+ augment_take,
+ augment_make,
+ })
+ }}
+ onTrain={() => {
+ this.props.remote.train_task(this.state.checkpoint_name, pix2pixhd.folder_id, 1)
+ setTimeout(() => { // auto-generate epoch demo
+ this.props.remote.augment_task(this.state.checkpoint_name, {
+ ...this.state,
+ augment_take: 10,
+ augment_make: 150,
+ no_symlinks: true,
+ mov: true,
+ folder_id: this.props.pix2pixhd.data.resultsFolder.id
+ })
+ }, 250)
+ }}
/>
</Group>
diff --git a/app/client/socket/socket.actions.js b/app/client/socket/socket.actions.js
index 78b0517..b80a0fa 100644
--- a/app/client/socket/socket.actions.js
+++ b/app/client/socket/socket.actions.js
@@ -4,6 +4,7 @@ import { socket } from './socket.connection'
export const run_system_command = opt => syscall_async('run_system_command', opt)
export const disk_usage = opt => syscall_async('run_system_command', { cmd: 'du', ...opt })
export const list_directory = opt => syscall_async('list_directory', opt).then(res => res.files)
+export const count_directory = opt => syscall_async('count_directory', opt).then(res => res.count)
export const list_sequences = opt => syscall_async('list_sequences', opt).then(res => res.sequences)
export const run_script = opt => syscall_async('run_script', opt)
export const upload_file = opt => syscall_async('upload_file', opt)
diff --git a/app/client/store.js b/app/client/store.js
index 8ffab15..654b22d 100644
--- a/app/client/store.js
+++ b/app/client/store.js
@@ -6,6 +6,7 @@ import createHistory from 'history/createBrowserHistory'
import { routerReducer } from 'react-router-redux'
// import navReducer from './nav.reducer'
+import authReducer from './auth/auth.reducer'
import systemReducer from './system/system.reducer'
import dashboardReducer from './dashboard/dashboard.reducer'
import liveReducer from './live/live.reducer'
@@ -15,6 +16,7 @@ import audioPlayerReducer from './common/audioPlayer/audioPlayer.reducer'
import { moduleReducer } from './modules/module.reducer'
const appReducer = combineReducers({
+ auth: authReducer,
system: systemReducer,
dashboard: dashboardReducer,
live: liveReducer,
diff --git a/app/client/system/system.actions.js b/app/client/system/system.actions.js
index a34bd58..ccd9acd 100644
--- a/app/client/system/system.actions.js
+++ b/app/client/system/system.actions.js
@@ -25,6 +25,17 @@ export const listDirectory = (opt) => (dispatch) => {
})
}
+export const countDirectory = (opt) => (dispatch) => {
+ dispatch({ type: types.system.counting_directory, opt })
+ socket.actions.count_directory(opt)
+ .then(data => {
+ dispatch({
+ type: types.system.count_directory,
+ data: data,
+ })
+ })
+}
+
export const changeTool = (tool) => {
localStorage.setItem('system.last_tool', tool)
return { type: types.app.change_tool, tool }
diff --git a/app/client/types.js b/app/client/types.js
index 2df494b..cfb590a 100644
--- a/app/client/types.js
+++ b/app/client/types.js
@@ -11,6 +11,8 @@ export default {
rpc_disconnected: 'SYSTEM_RPC_DISCONNECTED',
list_directory: 'SYSTEM_LIST_DIRECTORY',
listing_directory: 'SYSTEM_LISTING_DIRECTORY',
+ count_directory: 'SYSTEM_COUNT_DIRECTORY',
+ counting_directory: 'SYSTEM_COUNTING_DIRECTORY',
stdout: 'SYSTEM_STDOUT',
stderr: 'SYSTEM_STDERR',
},
@@ -34,6 +36,15 @@ export default {
'progress',
'epoch',
]),
+ auth: crud_type('auth', [
+ 'set_token',
+ 'set_error',
+ 'set_current_user',
+ 'logout_user',
+ 'loading',
+ 'initialized',
+ 'set_return_to',
+ ]),
socket: {
connect: 'SOCKET_CONNECT',
connect_error: 'SOCKET_CONNECT_ERROR',
@@ -108,7 +119,7 @@ export default {
'init', 'set_folder'
]),
pix2pixhd: with_type('pix2pixhd', [
- 'init', 'set_folder', 'load_results'
+ 'init', 'set_folder', 'load_results', 'load_dataset_count'
]),
pix2wav: with_type('pix2wav', [
'init', 'set_folder'
diff --git a/app/relay/remote.js b/app/relay/remote.js
index 6911572..1c9875f 100644
--- a/app/relay/remote.js
+++ b/app/relay/remote.js
@@ -116,6 +116,16 @@ remote.on('system', (data) => {
})
})
break
+ case 'count_directory':
+ runner.count_directory(data.payload, count => {
+ remote.emit('system_res', {
+ type: 'count_directory',
+ dir: data.payload,
+ uuid: data.uuid,
+ count,
+ })
+ })
+ break
case 'list_sequences':
runner.list_sequences(data.payload, sequences => {
remote.emit('system_res', {
diff --git a/app/relay/runner.js b/app/relay/runner.js
index 2bd0c56..44f2554 100644
--- a/app/relay/runner.js
+++ b/app/relay/runner.js
@@ -195,6 +195,14 @@ export function list_directory(opt, cb) {
})
}
+export function count_directory(opt, cb) {
+ const dir = module_dir(opt, opt.dir)
+ if (!dir) return cb([])
+ fs.readdir(dir, (err, files) => {
+ err ? cb(err) : cb(files.length)
+ })
+}
+
// list the contents of a directory of sequences
export function list_sequences(opt, cb) {
list_directory(opt, (files, root_dir) => {
diff --git a/app/server/db/model.js b/app/server/db/model.js
index dd851bf..c829c85 100644
--- a/app/server/db/model.js
+++ b/app/server/db/model.js
@@ -17,9 +17,8 @@ module.exports = function modelScope(type, db_model, _props) {
crud: crud,
index: (query) => {
-
- return new Promise( (resolve, reject) => {
- crud.index(query).then( (data) => {
+ return new Promise((resolve, reject) => {
+ crud.index(query).then(data => {
if (! props.hasOne) {
resolve(data ? data.toJSON() : [])
@@ -27,13 +26,13 @@ module.exports = function modelScope(type, db_model, _props) {
else {
let recs = data.toJSON()
const loader = new Loader ()
- loader.onReady( () => {
+ loader.onReady(() => {
// console.log(type, 'ready')
resolve(recs)
})
// console.log('hasOne')
loader.register('hasOne')
- Object.keys(props.hasOne).forEach( (key,i) => {
+ Object.keys(props.hasOne).forEach((key, i) => {
loader.register(key)
// console.log('register', key)
const type = props.hasOne[key]
@@ -45,7 +44,7 @@ module.exports = function modelScope(type, db_model, _props) {
})
// console.log('\n\n%%%%%%%%%%%%%%%%%%%%%%%% index > hasOne ' + key + '\n\n\n')
// console.log(recs.length, Object.keys(id_lookup).length)
- db_crud(type).show_ids(Object.keys(id_lookup)).then( (sub_recs) => {
+ db_crud(type).show_ids(Object.keys(id_lookup)).then(sub_recs => {
// console.log(key, 'sub_recs', sub_recs)
const short_key = key.replace('_id','')
sub_recs.toJSON().forEach(rec => {
@@ -57,49 +56,51 @@ module.exports = function modelScope(type, db_model, _props) {
})
loader.ready('hasOne')
}
- }) // }).catch( () => res.sendStatus(500) )
+ })
})
},
- show: (id) => {
- return new Promise( (resolve, reject) => {
- crud.show(id).then( (data) => {
- if (! props.hasOne) {
+ show: (id, field = 'id') => {
+ return new Promise((resolve, reject) => {
+ crud.show(id, field).then(data => {
+ if (!data) {
+ resolve()
+ } else if (! props.hasOne) {
resolve(data.toJSON())
}
else {
let rec = data.toJSON()
const loader = new Loader ()
- loader.onReady( () => {
+ loader.onReady(() => {
resolve(rec)
})
loader.register('hasOne')
- Object.keys(props.hasOne).forEach( (key,i) => {
+ Object.keys(props.hasOne).forEach((key, i) => {
loader.register(key)
const type = props.hasOne[key]
- db_crud(type).show(rec[key + '_id']).then( (sub_rec) => {
+ db_crud(type).show(rec[key + '_id']).then((sub_rec) => {
rec[key] = sub_rec
loader.ready(key)
})
})
loader.ready('hasOne')
}
- }) // .catch( (err) => res.sendStatus(500) )
+ })
})
},
findOrCreate: (data) => {
- return new Promise( (resolve, reject) => {
+ return new Promise((resolve, reject) => {
let query = Object.assign({}, data)
query.limit = 1
- crud.index(query).then( (recs) => {
+ crud.index(query).then((recs) => {
if (recs && recs.length) {
const rec = recs.at(0)
// console.log('found rec', data.name)
return resolve(rec)
}
// console.log('creating rec', data.name)
- model.create(data).then( (rec) => {
+ model.create(data).then((rec) => {
resolve(rec)
})
})
@@ -107,12 +108,12 @@ module.exports = function modelScope(type, db_model, _props) {
},
create: (data) => {
- return new Promise( (resolve, reject) => {
+ return new Promise((resolve, reject) => {
const should_relay = data.should_relay === 'true'
- crud.create( model.sanitize(data) ).then( (rec) => {
+ crud.create( model.sanitize(data) ).then((rec) => {
resolve(rec.toJSON())
props.afterCreate && props.afterCreate(rec, should_relay)
- }).catch( (e) => {
+ }).catch(e => {
console.error('error creating', e)
reject()
})
@@ -121,10 +122,10 @@ module.exports = function modelScope(type, db_model, _props) {
update: (id, data) => {
// console.log('update', id)
- return new Promise( (resolve, reject) => {
- crud.update(id, model.sanitize(data)).then( (data) => {
+ return new Promise((resolve, reject) => {
+ crud.update(id, model.sanitize(data)).then(data => {
resolve(data.toJSON())
- }).catch( (e) => {
+ }).catch(e => {
console.error('error updating', e)
reject()
})
@@ -132,7 +133,7 @@ module.exports = function modelScope(type, db_model, _props) {
},
destroy: (id) => {
- return new Promise( (resolve, reject) => {
+ return new Promise((resolve, reject) => {
crud.show(id).then( data => {
if (! data) {
console.error('no record found', id)
@@ -141,9 +142,9 @@ module.exports = function modelScope(type, db_model, _props) {
if (type === 'file') {
upload.destroyFile(data)
}
- crud.destroy(id).then( (destroyData) => {
+ crud.destroy(id).then((destroyData) => {
resolve(data.toJSON())
- })// .catch( () => res.sendStatus(500) )
+ })
})
})
},
diff --git a/app/server/db/models.js b/app/server/db/models.js
index 24be774..8bf6d9a 100644
--- a/app/server/db/models.js
+++ b/app/server/db/models.js
@@ -21,7 +21,7 @@ let Task = bookshelf.Model.extend({
jsonColumns: ['opt'],
})
let User = bookshelf.Model.extend({
- tableName: 'user',
+ tableName: 'users',
hasTimestamps: true,
}, {
jsonColumns: ['profile'],
@@ -61,7 +61,7 @@ module.exports = {
// bridge.processTasks()
}
}),
- user: model('user', Task, {
+ user: model('user', User, {
fields: "username password realname level avatar lastseen profile created_at updated_at".split(" "),
afterCreate: (user) => {
console.log('created user')
diff --git a/app/server/site.js b/app/server/site.js
index 85c932f..717e42b 100644
--- a/app/server/site.js
+++ b/app/server/site.js
@@ -2,12 +2,18 @@ const express = require('express')
const http = require('http')
const path = require('path')
const multer = require('multer')()
-const upload = require('./util/upload')
+const sessionstore = require('sessionstore')
+const session = require('express-session')
const bodyParser = require('body-parser')
+const cookieParser = require('cookie-parser')
+const MongoStore = require('connect-mongo')(session);
const compression = require('compression')
// const multer = require('multer')
// const upload = multer({ dest: 'uploads/' })
+const upload = require('./util/upload')
+const auth = require('./util/auth')
+
export const app = new express()
export const server = http.createServer(app)
@@ -17,6 +23,32 @@ app.use(bodyParser.urlencoded({ extended: false, limit: '100mb', }))
app.use(express.query())
app.use(express.static(path.join(__dirname, '../../public')))
app.use(compression())
+app.use(cookieParser())
+var sessionSettings = {
+ secret: 'argonauts',
+ proxy: true,
+ key: 'cortex.sid',
+ cookie: {
+ secure: process.env.NODE_ENV === 'production',
+ domain: '.' + process.env.HOST_NAME,
+ maxAge: 43200000000,
+ },
+ resave: true,
+ saveUninitialized: false,
+}
+if (!process.env.SESSIONS_IN_MEMORY) {
+ sessionSettings.store = new MongoStore({
+ url: 'mongodb://127.0.0.1:28108/cortexSessionDb'
+ // type: 'mongodb',
+ // host: 'localhost',
+ // port: 27017,
+ // dbName: 'buckySessionDb',
+ // collectionName: 'sessions',
+ // timeout: 10000,
+ })
+}
+app.use(session(sessionSettings))
+auth.route(app, serve_index)
export const io = require('socket.io').listen(server)
diff --git a/app/server/util/auth.js b/app/server/util/auth.js
new file mode 100644
index 0000000..1515bb4
--- /dev/null
+++ b/app/server/util/auth.js
@@ -0,0 +1,168 @@
+import passport from 'passport'
+import { Strategy as LocalStrategy } from 'passport-local'
+import crypto from 'crypto'
+import db from '../db'
+
+const { user: userModel } = db.models
+
+export function route(app, serve_index){
+ app.use(passport.initialize())
+ app.use(passport.session())
+ passport.serializeUser(serializeUser)
+ passport.deserializeUser(deserializeUser)
+ passport.use(new LocalStrategy(verifyLocalUser))
+
+ app.get("/login", serve_index)
+ app.get("/signup", serve_index)
+ app.get("/logout", logout)
+
+ app.put("/api/signup",
+ checkIfUserExists,
+ createUser,
+ login)
+ app.put("/api/login",
+ passport.authenticate("local"),
+ login)
+ app.put("/api/checkin",
+ ensureAuthenticated,
+ checkin)
+}
+
+export function ensureAuthenticated(req, res, next) {
+ if (!req.isAuthenticated()) {
+ if (req.session) req.session.returnTo = req.path
+ return res.redirect('/login')
+ }
+ next()
+}
+
+export function getUserByUsername(username) {
+ return userModel.show(sanitizeName(username), 'username')
+}
+
+export function checkIfUserExists(req, res, next) {
+ getUserByUsername(req.body.username)
+ .then((user) => {
+ console.log('gotta user?', !!user);
+ user ? res.json({ error: "user exists" }) : next()
+ }).catch(err => {
+ console.error('error', err)
+ })
+}
+
+export function sanitizeName(s) { return (s || "").replace(new RegExp('[^-_a-zA-Z0-9]', 'g'), "") }
+export function sanitizeUser(req_user) {
+ // sanitize user object
+ let user = JSON.parse(JSON.stringify(req_user))
+ try {
+ user.profile = JSON.parse(user.profile)
+ } catch (e) {
+ console.error('error decoding profile')
+ user.profile = {}
+ }
+ delete user.password
+ return user
+}
+
+export function createUser(req, res, next) {
+ const { username, password, password2 } = req.body
+ if (password !== password2) {
+ return res.json({ error: "passwords don't match" })
+ }
+ let data = {
+ username: sanitizeName(username),
+ realname: sanitizeName(username),
+ password: makePassword(password),
+ lastseen: new Date(),
+ level: 0,
+ profile: {},
+ }
+ userModel.create(data)
+ .then(user => {
+ console.log('created userrrrr', user)
+ req.login(user, err => {
+ console.log(err)
+ err ? next(err) : next()
+ })
+ })
+ .catch(err => {
+ res.json({ error })
+ })
+}
+
+export function login(req, res) {
+ console.log(req.user)
+ if (req.isAuthenticated()) {
+ let returnTo = req.session.returnTo
+ delete req.session.returnTo
+ console.log(">> logged in", req.user.username)
+ return res.json({
+ status: "OK",
+ user: sanitizeUser(req.user),
+ returnTo: returnTo || "/",
+ })
+ }
+ res.json({
+ error: 'bad credentials',
+ })
+}
+
+export function serializeUser(user, done) {
+ done(null, user.id)
+}
+
+export function deserializeUser(id, done) {
+ userModel.show(id).then(user => {
+ done(!user, user)
+ }).catch(done)
+}
+
+export function makePassword(password) {
+ let shasum = crypto.createHash('sha1')
+ shasum.update(password)
+ return shasum.digest('hex')
+}
+
+export function validPassword(user, password) {
+ return user.password === makePassword(password)
+}
+
+export function changePassword(req, res, next) {
+ if (!req.body.oldpassword && !req.body.newpassword) return next()
+ if (req.body.newpassword !== req.body.newpassword2) {
+ return res.send({ error: 'Passwords don\'t match.' })
+ }
+ if (!validPassword(res.user, req.body.oldpassword)) {
+ return res.send({ error: 'Password is incorrect.' })
+ }
+ let username = req.user.username
+ let newPassword = makePassword(req.body.newpassword)
+ res.user.password = newPassword
+ res.user.save()
+ .then(next)
+ .catch(err => res.send({ error: err }))
+}
+
+export function verifyLocalUser(username, password, done) {
+ // handle passwords!!
+ getUserByUsername(username)
+ .then(user => {
+ console.log(user)
+ // if (err) { return done(err) }
+ if (! user) { return done("no user") }
+ if (! user || !validPassword(user, password)) {
+ return done(null, false, { error: { message: 'Bad username/password.' } })
+ }
+ return done(null, user)
+ })
+}
+
+export function checkin(req, res) {
+ console.log(req.user)
+ res.json({ user: sanitizeUser(req.user) })
+}
+
+export const logout = (req, res) => {
+ req.logout()
+ res.redirect('/')
+} \ No newline at end of file