diff --git a/.drone.yml b/.drone.yml index 6936437..2d09410 100644 --- a/.drone.yml +++ b/.drone.yml @@ -1,13 +1,27 @@ pipeline: test: - image: node:7.5.0 + image: viestinta/node:viestinta-test when: event: [push,pull_request] branches: [master, react-start] + environment: + DATABASE_URL: postgres://postgres:viestintacentos@127.0.0.1:5432 + NODE_ENV: test + COVERALLS_REPO_TOKEN: $$COVERALLS_TOKEN + COVERALLS_TOKEN: $$COVERALLS_TOKEN + CODECOV_TOKEN: $$CODECOV_TOKEN + commands: - - npm install --silent + - cp -a /drone/node_modules /root + - npm install --depth=0 --quiet - npm install standard -g + - npm install codecov -g - npm run test + - cat ./coverage/lcov.info | codecov + - cp -a /root/node_modules /drone + volumes: + - "/drone/node_modules" + deploy: image: plugins/ssh when: @@ -18,3 +32,20 @@ pipeline: commands: - bash /scripts/deploy-viestinta-prod.sh +services: + postgres: + image: postgres:9.6 + container_name: viestinta_postgres + ports: + - "5432:5432" + environment: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: viestintacentos + POSTGRES_DB: viestintadb_dev + +cache: + mount: + - /drone/node_modules + + + diff --git a/.drone.yml.sig b/.drone.yml.sig index e3510e9..eacc854 100644 --- a/.drone.yml.sig +++ b/.drone.yml.sig @@ -1 +1 @@ -eyJhbGciOiJIUzI1NiJ9.cGlwZWxpbmU6CiAgdGVzdDogCiAgICBpbWFnZTogbm9kZTo3LjUuMAogICAgd2hlbjoKICAgICAgZXZlbnQ6IFtwdXNoLHB1bGxfcmVxdWVzdF0KICAgICAgYnJhbmNoZXM6IFttYXN0ZXIsIHJlYWN0LXN0YXJ0XQogICAgY29tbWFuZHM6CiAgICAgIC0gbnBtIGluc3RhbGwgLS1zaWxlbnQKICAgICAgLSBucG0gaW5zdGFsbCBzdGFuZGFyZCAtZwogICAgICAtIG5wbSBydW4gdGVzdAogIGRlcGxveToKICAgIGltYWdlOiBwbHVnaW5zL3NzaAogICAgd2hlbjoKICAgICAgZXZlbnQ6IHB1c2gKICAgICAgYnJhbmNoOiBwcm9kdWN0aW9uCiAgICBob3N0OiBzdG9ra2Vycy5ubwogICAgcG9ydDogMjIwMTQKICAgIGNvbW1hbmRzOgogICAgICAtIGJhc2ggL3NjcmlwdHMvZGVwbG95LXZpZXN0aW50YS1wcm9kLnNoCgo.HUOmcN4jh1vwJ1PEpLPzqjFyuhaL9yEoN-PJfMz4M14 \ No newline at end of file +eyJhbGciOiJIUzI1NiJ9.cGlwZWxpbmU6CiAgdGVzdDogCiAgICBpbWFnZTogdmllc3RpbnRhL25vZGU6dmllc3RpbnRhLXRlc3QKICAgIHdoZW46CiAgICAgIGV2ZW50OiBbcHVzaCxwdWxsX3JlcXVlc3RdCiAgICAgIGJyYW5jaGVzOiBbbWFzdGVyLCByZWFjdC1zdGFydF0KICAgIGVudmlyb25tZW50OgogICAgICBEQVRBQkFTRV9VUkw6IHBvc3RncmVzOi8vcG9zdGdyZXM6dmllc3RpbnRhY2VudG9zQDEyNy4wLjAuMTo1NDMyCiAgICAgIE5PREVfRU5WOiB0ZXN0CiAgICAgIENPVkVSQUxMU19SRVBPX1RPS0VOOiAkJENPVkVSQUxMU19UT0tFTgogICAgICBDT1ZFUkFMTFNfVE9LRU46ICQkQ09WRVJBTExTX1RPS0VOCiAgICAgIENPREVDT1ZfVE9LRU46ICQkQ09ERUNPVl9UT0tFTgoKICAgIGNvbW1hbmRzOgogICAgICAtIGNwIC1hIC9kcm9uZS9ub2RlX21vZHVsZXMgL3Jvb3QKICAgICAgLSBucG0gaW5zdGFsbCAtLWRlcHRoPTAgLS1xdWlldAogICAgICAtIG5wbSBpbnN0YWxsIHN0YW5kYXJkIC1nCiAgICAgIC0gbnBtIGluc3RhbGwgY29kZWNvdiAtZwogICAgICAtIG5wbSBydW4gdGVzdAogICAgICAtIGNhdCAuL2NvdmVyYWdlL2xjb3YuaW5mbyB8IGNvZGVjb3YKICAgICAgLSBjcCAtYSAvcm9vdC9ub2RlX21vZHVsZXMgL2Ryb25lCiAgICB2b2x1bWVzOgogICAgICAtICIvZHJvbmUvbm9kZV9tb2R1bGVzIgoKICBkZXBsb3k6CiAgICBpbWFnZTogcGx1Z2lucy9zc2gKICAgIHdoZW46CiAgICAgIGV2ZW50OiBwdXNoCiAgICAgIGJyYW5jaDogcHJvZHVjdGlvbgogICAgaG9zdDogc3Rva2tlcnMubm8KICAgIHBvcnQ6IDIyMDE0CiAgICBjb21tYW5kczoKICAgICAgLSBiYXNoIC9zY3JpcHRzL2RlcGxveS12aWVzdGludGEtcHJvZC5zaAoKc2VydmljZXM6CiAgICBwb3N0Z3JlczoKICAgICAgaW1hZ2U6IHBvc3RncmVzOjkuNgogICAgICBjb250YWluZXJfbmFtZTogdmllc3RpbnRhX3Bvc3RncmVzCiAgICAgIHBvcnRzOgogICAgICAgIC0gIjU0MzI6NTQzMiIKICAgICAgZW52aXJvbm1lbnQ6CiAgICAgICAgUE9TVEdSRVNfVVNFUjogcG9zdGdyZXMKICAgICAgICBQT1NUR1JFU19QQVNTV09SRDogdmllc3RpbnRhY2VudG9zCiAgICAgICAgUE9TVEdSRVNfREI6IHZpZXN0aW50YWRiX2RldgoKY2FjaGU6CiAgbW91bnQ6CiAgICAtIC9kcm9uZS9ub2RlX21vZHVsZXMKCgoK.Rs-UC3geCk_5gYUXlbmTIsNXGdBYZIsgEVQdYj_TZn0 \ No newline at end of file diff --git a/.gitignore b/.gitignore index 52f2fc1..cdf950c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,3 @@ -src/server/etc/config.json -src/server/config.json -*bundle.js -npm-debug.log - - ### Linux ### *~ @@ -97,11 +91,14 @@ $RECYCLE.BIN/ *.lnk #Pycharm settingsfile -.idea/* +.idea/ workspace.xml # End of https://www.gitignore.io/api/linux,windows,node -.idea/ -src/server/etc/ +*config.json +*bundle.js +npm-debug.log src/server/database/migrations/ src/server/database/seeders/ + + diff --git a/.sequelizesrc b/.sequelizesrc index dcf4114..067b363 100644 --- a/.sequelizesrc +++ b/.sequelizesrc @@ -1,8 +1,8 @@ var path = require('path'); module.exports = { - 'config': path.resolve('./', 'config/config.js'), - 'migrations-path': path.resolve('./', 'server/migrations'), - 'seeders-path': path.resolve('./', 'server/seeders'), - 'models-path': path.resolve('./', 'server/models') -}; + 'config': path.resolve('./', 'src/server/database/config/config.js'), + 'migrations-path': path.resolve('./', 'src/server/database/migrations'), + 'seeders-path': path.resolve('./', 'src/server/database/seeders'), + 'models-path': path.resolve('./', 'src/server/database/models') +}; \ No newline at end of file diff --git a/docker-compose.test.yml b/docker-compose.test.yml new file mode 100644 index 0000000..39bc318 --- /dev/null +++ b/docker-compose.test.yml @@ -0,0 +1,34 @@ +version: '3' + +services: + postgres: + image: postgres:9.6 + container_name: viestinta_postgres + ports: + - "5432:5432" + environment: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: viestintacentos + POSTGRES_DB: viestintadb_dev + tty: true + stdin_open: true + + nodejs: + build: . + image: node:viestinta + container_name: viestinta_node + depends_on: + - postgres + command: ["./wait-for-postgres.sh", "--", "npm" , "test"] + links: + - postgres + ports: + - "8000:8000" + volumes: + - .:/srv/app/ + - /arv/app/node_modules + environment: + DATABASE_URL: postgres://postgres:viestintacentos@postgres:5432/viestintadb_dev + NODE_ENV: test + tty: true + stdin_open: true diff --git a/docker-compose.yml b/docker-compose.yml index 5fe61c8..4472af8 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -16,11 +16,11 @@ services: nodejs: build: . - image: nodetest:viestinta + image: node:viestinta container_name: viestinta_node depends_on: - postgres - command: ["./wait-for-postgres.sh", "--", "npm" , "start"] + command: bash -c "npm install && ./node_modules/.bin/webpack && sleep 5 && npm start" links: - postgres diff --git a/package.json b/package.json index 95482d1..720dc61 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "scripts": { "dev": "grunt && sequelize db:migrate && sequelize db:seed:all && node src/server/app.js", "test-client": "node src/server/app.js && mocha --compilers js:babel-core/register ./src/client/test", - "test-server": "node src/server/app.js && node src/tests/init.js", + "test": "node src/server/app.js && nyc --check-coverage --lines 60 mocha src/test/server/**.js && nyc report --reporter=text-lcov > ./coverage/lcov.info", "start": "node src/server/app.js", "lint": "standard ./server/* ./client/*", "webpack": "./node_modules/.bin/webpack" @@ -40,21 +40,30 @@ "babel-preset-es2015": "^6.22.0", "babel-preset-react": "^6.23.0", "body-parser": "^1.16.0", + "codecov": "^1.0.1", "cookie-parser": "^1.4.3", "express": "^4.14.1", "express-session": "^1.15.0", + "fs": "0.0.1-security", + "istanbul": "^0.4.5", "latest-version": "^3.0.0", "loadash": "0.0.1", + "mocha": "^3.2.0", "morgan": "^1.7.0", "nconf": "^0.8.4", + "nyc": "^10.1.2", "passport": "^0.3.2", "passport-dataporten": "^1.1.3", "passport-openid-connect": "^0.1.0", + "path": "^0.12.7", "pg": "^6.1.2", + "pg-hstore": "^2.3.2", "react": "^15.4.2", "react-dom": "^15.4.2", + "react-tap-event-plugin": "^2.0.1", "sequelize": "^3.30.2", "sequelize-cli": "^2.5.1", + "socket.io-client": "^1.7.3", "webpack": "^2.2.1", "webpack-dev-server": "^2.3.0" }, @@ -65,13 +74,19 @@ "babel-preset-react": "^6.23.0", "chai": "^3.5.0", "check-dependencies": "^1.0.1", + "codecov": "^1.0.1", + "coveralls": "^2.11.16", "enzyme": "^2.7.1", "eslint": "^3.15.0", "eslint-config-standard": "^6.2.1", "eslint-plugin-promise": "^3.4.0", "eslint-plugin-standard": "^2.0.1", "http": "0.0.0", + "material-ui": "^0.17.0", + "istanbul": "^0.4.5", + "istanbul-coveralls": "^1.0.3", "mocha": "^3.2.0", + "nyc": "^10.1.2", "react-addons-test-utils": "^15.4.2", "should": "^11.2.0", "socket.io": "^1.7.2", @@ -83,7 +98,14 @@ "presets": [ "es2015", "react" - ] + ], + "env": { + "test": { + "plugins": [ + "istanbul" + ] + } + } }, "standard": { "globals": [ diff --git a/src/client/client.js b/src/client/client.js index d5e14ed..e3752e3 100644 --- a/src/client/client.js +++ b/src/client/client.js @@ -3,7 +3,14 @@ import ReactDOM from 'react-dom' import ChatApp from './components/ChatApp' +import injectTapEventPlugin from 'react-tap-event-plugin'; + +// Needed for onTouchTap +injectTapEventPlugin(); + if (typeof window !== 'undefined') { ReactDOM.render( - , document.getElementById('app')) + , + document.getElementById('app') + ) } diff --git a/src/client/components/ChatApp.js b/src/client/components/ChatApp.js index 626cea8..f063249 100644 --- a/src/client/components/ChatApp.js +++ b/src/client/components/ChatApp.js @@ -1,58 +1,67 @@ import React, { Component } from 'react' +import socket from '../../server/socket' +// Theme +import {deepOrange500} from 'material-ui/styles/colors' +import getMuiTheme from 'material-ui/styles/getMuiTheme' +import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider' + +// Components import ChatBox from './ChatBox' -import ChatField from './ChatField' +import MessageList from './MessageList' import Header from './Header' import Login from './Login' +import FeedbackBox from './FeedbackBox' + +const styles = { + container: { + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + paddingTop: 0, + height: 'auto', + }, + element: { + display: 'flex', + } +}; + +const muiTheme = getMuiTheme({ + palette: { + primary1Color: '#ec7c2f', // Orange + accent1Color: '#2daae4', // Blue + } +}) export default class ChatApp extends Component { // At beginning there is no msg and the text-field is empty constructor (props) { super(props) - this.state = { - messages: [], - text: '', - socket: io.connect() - } - - this.sendMessage = this.sendMessage.bind(this) - this.join = this.join.bind(this) - this.receiveMessage = this.receiveMessage.bind(this) } componentDidMount () { - this.state.socket.on('join', this.join) - this.state.socket.on('send-message', this.sendMessage) - this.state.socket.on('receive-message', this.receiveMessage) + socket.on('join', this.join) } join () { console.log('join') - this.state.socket.emit('join', 'Hello world from client') - } - - receiveMessage (msg) { - console.log('receiveMessage: ', msg.text) - this.state.messages.push(msg) - this.setState({ messages: this.state.messages }) - } - - // When a message is submitted - sendMessage (msg) { - console.log('sendMessage: ', msg.text) - this.state.socket.emit('new-message', msg) + socket.emit('join', 'Hello world from client') } render () { return ( -
-
- - - -
+ +
+
+ + {/* List of messages */} + + {/* Inputfield for user */} + + {/* Sidebar with feedback-options */} + +
+
) } } diff --git a/src/client/components/ChatBox.js b/src/client/components/ChatBox.js index 6f850d0..5a86a88 100644 --- a/src/client/components/ChatBox.js +++ b/src/client/components/ChatBox.js @@ -1,5 +1,34 @@ -import React, { Component } from 'react' +import React, { Component } from 'react'; +import socket from '../../server/socket'; +import Paper from 'material-ui/Paper'; +import TextField from 'material-ui/TextField'; +import RaisedButton from 'material-ui/RaisedButton'; + +const styles = { + parent: { + display: 'flex', + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + + maxWidth: 500, + width: '100%', + height: 80, + + marginTop: 10, + padding: 10, + }, + textField: { + width: '100%', + marginRight: 5, + }, + btn: { + + }, +}; + +// Text input field export default class ChatBox extends Component { constructor (props) { @@ -8,19 +37,24 @@ export default class ChatBox extends Component { this.state = { text: '' } - + this.changeHandler = this.changeHandler.bind(this) - this.handleMessageSubmit = this.handleMessageSubmit.bind(this) + this.sendMessage = this.sendMessage.bind(this) this.cleanInput = this.cleanInput.bind(this) + + } + + componentDidMount () { + socket.on('send-message', this.sendMessage) } + cleanInput () { this.setState({text: ''}) } - handleMessageSubmit (e) { - e.preventDefault() - console.log('In handle send') + sendMessage () { + console.log('[ChatBox] sendMessage') // Setting msg.text to written input var msg = { text: this.state.text @@ -29,26 +63,34 @@ export default class ChatBox extends Component { // Emtpy input field this.setState({text: ''}) - console.log('Empty message field: ', this.state.text) - - this.props.sendMessage(msg) + socket.emit('new-message', msg) + console.log("[ChatBox] After sending message") } // Listen and update field dynamically when something is written changeHandler (e) { this.setState({ text: e.target.value }) - console.log('Changing state') + console.log('[ChatBox] changeHandler') } render () { return ( -
-

Ny melding

- - - -
+ + + + ) } } diff --git a/src/client/components/ChatField.js b/src/client/components/ChatField.js deleted file mode 100644 index 3286821..0000000 --- a/src/client/components/ChatField.js +++ /dev/null @@ -1,41 +0,0 @@ -import React, { Component } from 'react' -import Message from './Message' - -export default class ChatField extends Component { - - constructor (props) { - super(props) - // Starting with empty message-list - this.state = { - messages: props.messages - } - - this.updateField = this.updateField.bind(this) - } - - updateField (msg) { - this.setState({messages: msg}) - } - - render () { - console.log('In render in chatField') - console.log('messages: ', this.state.messages) - - // Loop trought the messages in the state and create a Message component - const messages = this.state.messages.map((message, i) => { - console.log('Looping trought messages') - - return ( - - ) - }) - - return ( -
- { messages } -
- ) - } -} diff --git a/src/client/components/FeedBackMenu.js b/src/client/components/FeedBackMenu.js deleted file mode 100644 index d1b6b7c..0000000 --- a/src/client/components/FeedBackMenu.js +++ /dev/null @@ -1,3 +0,0 @@ -/** - * Created by doraoline on 12.02.17. - */ diff --git a/src/client/components/FeedbackBox.js b/src/client/components/FeedbackBox.js new file mode 100644 index 0000000..ce3c527 --- /dev/null +++ b/src/client/components/FeedbackBox.js @@ -0,0 +1,56 @@ +import React, { Component } from 'react' +import socket from '../../server/socket' + +import FeedbackMenu from './FeedbackMenu' +import FeedbackWindow from './FeedbackWindow' + +export default class FeedbackBox extends Component { + + constructor (props) { + super(props) + this.state = { + // [slow, fast] + feedback: [0, 0] + } + + this.onClick = this.onClick.bind(this) + this.updateFeedback = this.updateFeedback.bind(this) + } + + // Receiving updated feedback values + updateFeedback (feedback) { + console.log('In updateFeedback:', feedback) + + this.state.feedback = feedback + } + + updateFeedbackInterval () { + console.log('In updateFeedbackInterval') + } + + onClick (feedback) { + console.log('In onClick') + if (feedback.type === 'slow') { + feedbackList[0] = feedbackList[0] + 1 + } else if (feedback.type === 'fast') { + feedbackList[1] = feedbackList[1] + 1 + } else { + console.log('No valid type for buttonClick') + } + this.setState({feedback: feedbackList}) + this.props.sendFeedback(feedback) + } + + render () { + return ( +
+ + +
+ ) + } +} diff --git a/src/client/components/FeedbackMenu.js b/src/client/components/FeedbackMenu.js new file mode 100644 index 0000000..46bad7a --- /dev/null +++ b/src/client/components/FeedbackMenu.js @@ -0,0 +1,57 @@ +import React, { Component } from 'react' +import socket from '../../server/socket' + +import RaisedButton from 'material-ui/RaisedButton' + +const style = { + margin: 12 +} + +export default class FeedbackMenu extends Component { + + constructor (props) { + super(props) + this.state = { + disabled: false + } + + this.slowClick = this.slowClick.bind(this) + this.fastClick = this.fastClick.bind(this) + this.activateButtons = this.activateButtons.bind(this) + } + + componentDidMount () { + // Activate button every x min + this.interval = setInterval(this.activateButtons, 5 * 6000) + } + + activateButtons () { + console.log('[FeedbackMenu] activateButtons') + this.setState({ + disabled: false + }) + } + + slowClick () { + socket.emit('new-feedback', -1) + this.setState({ + disabled: true + }) + } + + fastClick () { + socket.emit('new-feedback', 1) + this.setState({ + disabled: true + }) + } + + render () { + return ( +
+ + +
+ ) + } +} diff --git a/src/client/components/FeedbackWindow.js b/src/client/components/FeedbackWindow.js new file mode 100644 index 0000000..19a9daf --- /dev/null +++ b/src/client/components/FeedbackWindow.js @@ -0,0 +1,67 @@ +import React, { Component } from 'react' +import socket from '../../server/socket' + +export default class FeedbackWindow extends Component { + + constructor (props) { + super(props) + + this.state = { + // minsElapsed: 0, + feedback: [0, 0] + } + + this.tick = this.tick.bind(this) + this.receiveFeedback = this.receiveFeedback.bind(this) + this.updateFeedbackInterval = this.updateFeedbackInterval.bind(this) + } + + tick () { + console.log('[FeedbackWindow] In tick') + + var minsElapsed = this.state.minsElapsed + 1 + this.setState({ + minsElapsed: minsElapsed + }) + if (this.state.minsElapsed === 5) { + this.state.updateFeedbackInterval() + } + } + + componentDidMount () { + // Increase every min + this.interval = setInterval(this.tick, 60000) + socket.on('receive-feedback', this.receiveFeedback) + socket.on('update-feedback-interval', this.updateFeedbackInterval) + } + + receiveFeedback (feedback) { + console.log('Setting feedback') + var feedbackList = this.state.feedback + if (feedback === -1) { + feedbackList[0] = feedbackList[0] - 1 + } else { + feedbackList[1] = feedbackList[1] + 1 + } + this.setState({ + feedback: feedbackList + }) + } + + updateFeedbackInterval (feedbacks) { + console.log('[FeedbackWindow] updateFeedbackInterval: ', feedbacks) + + this.setState({ + feedback: feedbacks + }) + } + + render () { + return ( +
+

Antall som synes det går for tregt: {this.state.feedback[0]}

+

Antall som synes det går for fort: {this.state.feedback[1]}

+
+ ) + } +} diff --git a/src/client/components/Header.js b/src/client/components/Header.js index a4d6fca..4e68eb9 100644 --- a/src/client/components/Header.js +++ b/src/client/components/Header.js @@ -1,11 +1,29 @@ import React from 'react' +const styles = { + + parent: { + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + + maxWidth: 500, + width: 'auto', + height: 'auto', + }, + + img: { + width: '100%', + }, +}; + export default class Header extends React.Component { - render () { - return ( -
-

Velkommen til Viestinta!

-
- ) - } + render () { + return ( +
+ Viestintä Logo +
+ ) + } } diff --git a/src/client/components/Login.js b/src/client/components/Login.js index a865865..1feb442 100644 --- a/src/client/components/Login.js +++ b/src/client/components/Login.js @@ -1,4 +1,5 @@ -import React from 'react' +import React from 'react'; +import RaisedButton from 'material-ui/RaisedButton'; const Login = React.createClass({ getInitialState () { @@ -11,10 +12,14 @@ const Login = React.createClass({ render () { return ( -
-
- -
+
+ } + />
) } diff --git a/src/client/components/Message.js b/src/client/components/Message.js index 3847690..405a2b4 100644 --- a/src/client/components/Message.js +++ b/src/client/components/Message.js @@ -1,11 +1,37 @@ -import React from 'react' +import React from 'react'; +import Paper from 'material-ui/Paper'; +import FlatButton from 'material-ui/FlatButton'; +import ActionSchedule from 'material-ui/svg-icons/action/schedule'; + +const styles = { + child: { + minHeight: 'auto', + width: '100%', + + padding: 10, + margin: 5, + textAlign: 'left', + }, + time: { + fontSize: '13px', + lineHeight: '13px', + height: '18px', + textAlign: 'left', + }, +} export default class Message extends React.Component { render () { return ( -
- { this.props.text } -
+ +

{this.props.text}

+ {this.props.time}} + disabled={true} + icon={} + /> +
) } } diff --git a/src/client/components/MessageList.js b/src/client/components/MessageList.js new file mode 100644 index 0000000..2a7e57a --- /dev/null +++ b/src/client/components/MessageList.js @@ -0,0 +1,93 @@ +import React, { Component } from 'react' +import socket from '../../server/socket' +import Paper from 'material-ui/Paper'; + +import Message from './Message' + +const styles = { + + parent: { + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + + maxWidth: 500, + maxHeight: 400, + width: '100%', + height: '100%', + + marginTop: 10, + padding: 15, + + overflowY: 'auto', + minHeight: 0, + }, + + child: { + minHeight: 'auto', + width: '100%', + + padding: 10, + margin: 5, + textAlign: 'left', + }, +}; + +export default class MessageList extends Component { + + constructor (props) { + super(props) + this.state = { + messages: [] + } + + this.receiveMessage = this.receiveMessage.bind(this) + this.lastTenMessages = this.lastTenMessages.bind(this) + } + + componentDidMount () { + socket.on('receive-message', this.receiveMessage) + socket.on('last-ten-messages', this.lastTenMessages) + } + + receiveMessage (msg) { + console.log('receiveMessage: ', msg.text) + // Copies the list + var messages = this.state.messages.slice() + // Adds the message + messages.push(msg) + this.setState({ messages: messages }) + } + + lastTenMessages (msgList) { + console.log('lastTenMessages: ', msgList) + this.setState({ + messages: msgList + }) + } + + render () { + var list = this.state.messages.map((message, i) => { + console.log('Looping trought messages in messageList') + + var time = message.time + // console.log('Date: ', date) + // var time = date.format('dd.MM.yyyy HH:mm') + // console.log('Time: ', time) + return ( + + ) + }) + + return ( + + {list} + + ) + } + +} diff --git a/src/client/index.html b/src/client/index.html index edecb3d..2efec78 100644 --- a/src/client/index.html +++ b/src/client/index.html @@ -3,7 +3,14 @@ - Viestinta + Viestintä + + + + @@ -16,6 +23,21 @@
+ + + diff --git a/src/server/app.js b/src/server/app.js index 99b6f32..8b14c67 100644 --- a/src/server/app.js +++ b/src/server/app.js @@ -23,6 +23,7 @@ const PDStrategy = require('passport-openid-connect').Strategy // Initial Server Setup // /////////////////////////////////////////////////// + nconf.argv() .env('__') .file({ file: path.resolve(__dirname, './etc/config.json') }) @@ -65,7 +66,7 @@ app.use(passport.initialize()) app.use(passport.session()) // /////////////////////////////////////////////////// -// Main App +// Routing // /////////////////////////////////////////////////// // URL-specifications @@ -99,56 +100,110 @@ app.get('/connect', (req, res) => { app.get('/login', passport.authenticate('passport-openid-connect', {'successReturnToOrRedirect': '/'})) app.get('/callback', passport.authenticate('passport-openid-connect', {'callback': true, 'successReturnToOrRedirect': '/'})) -server.listen(app.get('port'), (err) => { - if (err) throw err - console.log('Node app is running on port', app.get('port')) +if (process.env.NODE_ENV !== "test"){ + server.listen(app.get('port'), (err) => { + if (err) throw err + console.log('Node app is running on port', app.get('port')) + }) +} + +// /////////////////////////////////////////////////// +// Setup for database +// /////////////////////////////////////////////////// + +// TODO: Flytt til annen fil, eller gjør som del av user login/creation. Må bare kjøres før user objektet skal brukes. + +const db = require('./database/models/index') + +const user = db['User'] +const messageObj = db['Message'] +const feedbackObj = db['Feedback'] + +const users = require('./database/controllers').users +const messagesController = require('./database/controllers').messages +const feedbacksController = require('./database/controllers').feedbacks + +// Create tables, and drop them if they allready exists (force: true) +user.sync({force: true}).then(function () { + return user.create({ + name: 'Pekka Foreleser' + }) +}) + +messageObj.sync().then(function () { +}) + +feedbackObj.sync().then(function () { + }) // Create a connection // var socket = io.connect('http://localhost::8000') var io = require('socket.io')(server) +// When a new user connects +io.sockets.on('connect', function (socket) { + console.log('[app] connect') + // Get feedback status for last x min + feedbacksController.getLastIntervalNeg().then(function (resultNeg) { + feedbacksController.getLastIntervalPos().then(function (resultPos) { + socket.emit('update-feedback-interval', [resultNeg, resultPos]) + }) + }) + // Get last 10 messages + messagesController.getLastTen().then(function (result) { + socket.emit('last-ten-messages', result.reverse()) + }) +}) + // Listen for connections io.sockets.on('connection', function (socket) { // Reports when it finds a connection - console.log('Client connected') + console.log('[app] connection') // Wait for a message from the client for 'join' socket.on('join', function (data) { - console.log('New client have joined') + console.log('[app] join') socket.emit('messages', 'Hello from server') }) // Wait for a message from the client for 'join' socket.on('leave', function (data) { - console.log('Client have left') + console.log('[app] left') socket.emit('messages', 'Goodbye from server') }) // When a new message is sendt from somebody socket.on('new-message', function (msg) { - console.log('Message in new-message in app.js: ' + msg.text) + console.log('[app] new-message: ' + msg) + messagesController.create(msg) + io.sockets.emit('receive-message', msg) }) - socket.on('test', function () { - console.log('Mounted') + // When somebody gives feedback + socket.on('new-feedback', function (feedback) { + console.log('[app] new-feedback: ' + feedback) + feedbacksController.create({ + value: feedback + }) + console.log('[app] new-feedback: after') + io.sockets.emit('receive-feedback', feedback) }) -}) - -app.use('/', express.static(path.resolve(__dirname, '../client/'))) -app.use(express.static(path.resolve(__dirname, '../static'))) -app.use('/components', express.static(path.resolve(__dirname, '../client/components'))) -app.use('/css', express.static(path.resolve(__dirname, '../static/css'))) -// SETUP FOR DATABASE -// TODO: Flytt til annen fil, eller gjør som del av user login/creation. Må bare kjøres før user objektet skal brukes. - -const db = require('./database/models/index') - -const user = db['User'] -user.sync({force: true}).then(function () { - return user.create({ - name: 'Pekka Foreleser' + // Called every x minuts + socket.on('update-feedback-interval', function () { + // Get feedback from database for past x minuts + feedbacksController.getLastIntervalNeg().then(function (resultNeg) { + feedbacksController.getLastIntervalPos().then(function (resultPos) { + io.sockets.emit('update-feedback-interval', [resultNeg, resultPos]) + }) + }) + io.sockets.emit('update-feedback-interval') }) }) + +// To get static files +app.use('/', express.static(path.join(__dirname, '../static'))) +app.use('/css', express.static(path.join(__dirname, '../static/css'))) +app.use('/icons', express.static(path.join(__dirname, '../static/icons'))) diff --git a/src/server/database.js b/src/server/database.js new file mode 100644 index 0000000..8143f84 --- /dev/null +++ b/src/server/database.js @@ -0,0 +1,3 @@ +// /////////////////////////////////////////////////// +// Setup for database +// /////////////////////////////////////////////////// diff --git a/src/server/database/config/config.js b/src/server/database/config/config.js deleted file mode 100644 index f0c378e..0000000 --- a/src/server/database/config/config.js +++ /dev/null @@ -1,25 +0,0 @@ - -module.exports = { - development: { - 'username': 'postgres', - 'password': 'viestintacentos', - 'database': 'viestintadb_dev', - 'host': '0.0.0.0', - 'dialect': 'postgres' - }, - test: { - 'username': 'postgres', - 'password': 'viestintacentos', - 'database': 'viestintadb_test', - 'host': '0.0.0.0', - 'dialect': 'postgres', - 'logging': false - }, - production: { - 'username': 'root', - 'password': null, - 'database': 'database_production', - 'host': '127.0.0.1', - 'dialect': 'postgres' - } -} diff --git a/src/server/database/controllers/feedbacks.js b/src/server/database/controllers/feedbacks.js new file mode 100644 index 0000000..86e732f --- /dev/null +++ b/src/server/database/controllers/feedbacks.js @@ -0,0 +1,86 @@ +const Feedback = require('../models/index').Feedback + +// Controller for Feedback model + +const MIN = 60000 + +module.exports = { + + // Create a new Feedback using model.create() + create (req) { + return Feedback.create({ + value: req.value + }) + .then(function (newFeedback) { + console.log('New feedback created with value', newFeedback.value) + }) + }, + + // Edit an existing Feedback details using model.update() + update (req) { + Feedback.update(req.body, { + where: { + id: req.params.id + } + }) + }, + + // For last 5 min + getLastIntervalNeg () { + return Feedback.count({ + where: { + // TODO: just use createdAt? + time: { + // Set to 5 * MIN + $between: [new Date(new Date() - 5 * MIN), new Date()] + }, + + value: -1 + } + }) + }, + + // For last 5 min + getLastIntervalPos () { + return Feedback.count({ + where: { + // TODO: just use createdAt? + time: { + // Set to 5 * MIN + $between: [new Date(new Date() - 5 * MIN), new Date()] + }, + + value: 1 + } + }) + }, + + getAll () { + return Feedback.findAll() + .then(function (result) { + console.log(result) + }) + }, + + getAllLecture (req) { + Feedback.findAll({ + where: { + // TODO: just use createdAt? + time: { + $between: [new Date(), new Date(new Date() - 120 * MIN)] + } + } + }).then(function (result) { + console.log(result.count) + }) + }, + + // Delete an existing Feedback by the unique ID using model.destroy() + delete (req) { + Feedback.destroy({ + where: { + id: req.params.id + } + }) + } +} diff --git a/src/server/database/controllers/index.js b/src/server/database/controllers/index.js new file mode 100644 index 0000000..857d56f --- /dev/null +++ b/src/server/database/controllers/index.js @@ -0,0 +1,9 @@ +const users = require('./users') +const feedbacks = require('./feedbacks') +const messages = require('./messages') + +module.exports = { + users, + feedbacks, + messages +} diff --git a/src/server/database/controllers/messages.js b/src/server/database/controllers/messages.js new file mode 100644 index 0000000..26ef5d4 --- /dev/null +++ b/src/server/database/controllers/messages.js @@ -0,0 +1,51 @@ +// Controller for Message model + +var Message = require('../models/index').Message + +module.exports = { + + // Create a new Message using model.create() + create (req) { + return Message.create({ + time: new Date(), + text: req.text + }) + }, + + // Edit an existing Message details using model.update() + update (req) { + return Message.update(req.body, { + where: { + id: req.params.id + } + }) + }, + + // Delete an existing Message by the unique ID using model.destroy() + delete (req) { + Message.destroy({ + where: { + id: req.params.id + } + }) + }, + + // Get last 10 + getLastTen (req) { + return Message.all({ + order: '"time" DESC', + limit: 10 + }) + }, + + // Retrive an existing Message by the unique ID + retrieve (req) { + return Message + .findById(req.params.messageId, { + include: [{ + model: Message, + as: 'message' + }] + }) + } +} diff --git a/src/server/database/models/feedback.js b/src/server/database/models/feedback.js new file mode 100644 index 0000000..105d0f0 --- /dev/null +++ b/src/server/database/models/feedback.js @@ -0,0 +1,54 @@ +'use strict' + +// Feedback model + +module.exports = function (sequelize, DataTypes) { + // Definition of Feedback attributes + + var Feedback = sequelize.define('Feedback', { + time: { + type: DataTypes.DATE, + defaultValue: new Date() + }, + value: { + type: DataTypes.INTEGER, + defaultValue: 0 + } + }, { + + // Definition of methods related to the feedback object + // class-wide methods + classMethods: { + + // Associations to other models + associate: function (models) { + // associations can be defined here + /*Feedback.belongsTo(models.User, { + onDelete: 'CASCADE', + foreignKey: { + allowNull: true + } + })*/ + }, + + }, + /* + getterMethods: { + getLastInterval: function() { + return Feedback.findAll({ + where: { + // TODO: just use createdAt? + time: { + // Set to 5 * 60000 + $between: [new Date(), new Date(newDate - 5 * 1000)] + } + } + }).then(function (result) { + console.log(result.count) + }) + } + } + */ + }) + return Feedback +} diff --git a/src/server/database/models/index.js b/src/server/database/models/index.js index f1d6c89..d420ead 100644 --- a/src/server/database/models/index.js +++ b/src/server/database/models/index.js @@ -1,12 +1,10 @@ 'use strict' -/* +// /////////////////////////////////////////////////// +// Setup for Sequalize connection +// /////////////////////////////////////////////////// -SETUP FOR SEQUELIZE CONNECTION - -exports db object with all relevant references to models - - */ +// exports db object with all relevant references to models const fs = require('fs') const path = require('path') @@ -14,17 +12,17 @@ const Sequelize = require('sequelize') const basename = path.basename(module.filename) const env = process.env.NODE_ENV || 'development' -const config = require(path.join(__dirname, '/../config/config.js'))[env] -let db = {} -var sequelize = new Sequelize(process.env['DATABASE_URL']) +let db = {} -/* if (config.use_env_variable) { - var sequelize = new Sequelize(process.env[config.use_env_variable]) -} else { - var sequelize = new Sequelize(config.database, config.username, config.password, config) +if (process.env['DATABASE_URL']) { + var options = {} + if (process.env.NODE_ENV == "test"){ + options = {logging: false} + } + var sequelize = new Sequelize(process.env['DATABASE_URL'], options) } -*/ + fs .readdirSync(__dirname) .filter(function (file) { @@ -52,12 +50,6 @@ sequelize .catch(function (err) { console.log('Unable to connect to the database:', err) }) -/* -function logExceptOnTest(string) { - if (process.env.NODE_ENV !== 'test') { - console.log(string); - } -} -*/ + module.exports = db diff --git a/src/server/database/models/message.js b/src/server/database/models/message.js new file mode 100644 index 0000000..197c42d --- /dev/null +++ b/src/server/database/models/message.js @@ -0,0 +1,70 @@ +'use strict' + +// Message model + +module.exports = function (sequelize, DataTypes) { + // Definition of Message attributes + var Message = sequelize.define('Message', { + time: { + type: DataTypes.DATE, + defaultValue: new Date(), + get: function () { + var date = new Date(this.getDataValue('time')) + // var string = date.getDay() + '.' + date.getMonth() + '.' + date.getYear() + + var hours = date.getHours() + if (date.getHours() < 10) { + hours = '0' + date.getHours() + }else{ + + } + var mins = date.getMinutes() + if (date.getMinutes() < 10) { + mins = '0' + date.getMinutes() + } + return hours + ":" + mins + } + + }, + text: { + type: DataTypes.STRING, + allowNull: false, + validate: { + min: { + args: [3], + msg: 'The message must be at least 3 charaters long' + } + } + }, + // user: { + // } + // lecture: {} + votesUp: { + type: DataTypes.INTEGER, + defaultValue: 0 + }, + votesDown: { + type: DataTypes.INTEGER, + defaultValue: 0 + } + }, { + classMethods: { + + /* + assosiate: function(models) { + Message.belongsTo(models.User, { + onDelete: 'CASCADE', + foreignKey: { + allowNull: true + } + }), + // Add lecture + } */ + }, + instanceMethods: { + + } + }) + + return Message +} diff --git a/src/server/etc/config.json b/src/server/etc/config.json new file mode 100644 index 0000000..9a87b52 --- /dev/null +++ b/src/server/etc/config.json @@ -0,0 +1,13 @@ + +{ + "http": { + "port": 8000 + }, + "dataporten": { + "issuerHost": "https://auth.dataporten.no/", + "client_id": "", + "client_secret": "", + "redirect_uri": "http://localhost:8000/callback", + "scope": "groups longterm openid userid email profile userid-feide" + } +} \ No newline at end of file diff --git a/src/server/routes/index.js b/src/server/routes/index.js new file mode 100644 index 0000000..c83d36e --- /dev/null +++ b/src/server/routes/index.js @@ -0,0 +1,43 @@ + // URL-specifications + +const path = require('path') +const passport = require('passport') + +const userController = require('./controllers').user +const messagesController = require('./controllers').messages +const feedbacskCOntroller = require('./controllers').feedbacks + +module.exports = (app) => { + // Go to index.html + app.get('/', (req, res) => { res.sendFile(path.resolve(__dirname, '../client/index.html')) }) + app.get('/user', (req, res) => { + if (req.user) { + res.json({user: req.user}) + } else { + res.status(404) + } + }) + + app.get('/connect', (req, res) => { + if (req.user) { + let userinfo = req.user.data + db['User'].findOrCreate({ + where: {name: userinfo.name, sub: userinfo.sub, email: userinfo.email, email_verified: userinfo.email_verified} + }) + .spread(function (user, created) { + console.log(user) + }) + .catch((err) => { + console.error(err) + }) + } else { + res.status(403) + } + }) + + app.get('/login', passport.authenticate('passport-openid-connect', {'successReturnToOrRedirect': '/'})) + app.get('/callback', passport.authenticate('passport-openid-connect', {'callback': true, 'successReturnToOrRedirect': '/'})) + + // Related to database + app.post('/', messages) +} diff --git a/src/server/socket.js b/src/server/socket.js new file mode 100644 index 0000000..d0d32a7 --- /dev/null +++ b/src/server/socket.js @@ -0,0 +1 @@ +export default io.connect() diff --git a/src/static/css/style.css b/src/static/css/style.css index 39cfb49..2d7fade 100644 --- a/src/static/css/style.css +++ b/src/static/css/style.css @@ -1,3 +1,24 @@ +html { + font-family: 'Roboto', sans-serif; + min-height: 100%; +} + +body { + font-size: 13px; + line-height: 20px; + + height: 100%; + + background-repeat: no-repeat; + /* Maximum browser support */ + background: -webkit-gradient(linear, center top, center bottom, from(#2DAAE4), to(#15729E)); + background: -webkit-linear-gradient(#2DAAE4, #15729E); + background: -moz-linear-gradient(#2DAAE4, #15729E); + background: -o-linear-gradient(#2DAAE4, #15729E); + background: -ms-linear-gradient(#2DAAE4, #15729E); + background: linear-gradient(#2DAAE4, #15729E); +} + #chat-app { position: absolute; margin: auto; diff --git a/src/static/images/feide_100px_white.png b/src/static/images/feide_100px_white.png new file mode 100644 index 0000000..e64a04f Binary files /dev/null and b/src/static/images/feide_100px_white.png differ diff --git a/src/static/images/feide_32px.png b/src/static/images/feide_32px.png new file mode 100644 index 0000000..b994932 Binary files /dev/null and b/src/static/images/feide_32px.png differ diff --git a/src/static/images/logo.png b/src/static/images/logo.png new file mode 100644 index 0000000..f0041e1 Binary files /dev/null and b/src/static/images/logo.png differ diff --git a/src/static/images/logo_shadow.png b/src/static/images/logo_shadow.png new file mode 100644 index 0000000..3b40d40 Binary files /dev/null and b/src/static/images/logo_shadow.png differ diff --git a/src/static/images/thumb.png b/src/static/images/thumb.png new file mode 100644 index 0000000..5f03869 Binary files /dev/null and b/src/static/images/thumb.png differ diff --git a/src/static/images/viestinta_illustration.png b/src/static/images/viestinta_illustration.png new file mode 100644 index 0000000..f4a7bfc Binary files /dev/null and b/src/static/images/viestinta_illustration.png differ diff --git a/src/test/client/test.js b/src/test/client/socket_test.js similarity index 100% rename from src/test/client/test.js rename to src/test/client/socket_test.js diff --git a/src/test/init.js b/src/test/init.js deleted file mode 100644 index 29c1881..0000000 --- a/src/test/init.js +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Created by jacob on 22.02.17. - */ - -console.log('Running tests...') -process.env.NODE_ENV = 'test' -// const app = require('../server/app') - -const Mocha = require('mocha') -const fs = require('fs') -const path = require('path') - -// Instantiate a Mocha instance. -const mocha = new Mocha() - -const testDir = path.resolve(__dirname, './server') - -// Add each .js file to the mocha instance -fs.readdirSync(testDir).filter(function (file) { - // Only keep the .js files - return file.substr(-3) === '.js' -}).forEach(function (file) { - mocha.addFile( - path.join(testDir, file) - ) -}) - -// Run the tests. -mocha.run(function (failures) { - process.on('exit', function () { - process.exit(failures) // exit with non-zero status if there were failures - }) -}) diff --git a/src/test/server/database_test.js b/src/test/server/database_test.js index aaacb77..8e756c9 100644 --- a/src/test/server/database_test.js +++ b/src/test/server/database_test.js @@ -1,91 +1,104 @@ -/** - * Created by jacob on 20.02.17. - */ -// Hack for pg issues -var pg = require('pg') -delete pg.native - -var User = require('./models/').User -var assert = require('assert') -var db = require('./models/index') +const path = require('path') +const User = require('../../server/database/models/').User +const assert = require('assert') +const db = require('../../server/database/models/index') // Extra test frameworks: // var should = require('should'); -// Defines alphabet to be used +// Defines alphabets to be used var s = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' +var n = '0123456789' // Defines the function to generate the names to be used for the test // Returns a random String with N length and based on the alphabet s - -var testNameGenerator = function (N) { +var testStringGenerator = function (N, s) { return Array(N).join().split(',').map(function () { return s.charAt(Math.floor(Math.random() * s.length)) }).join('') } -// Unique firstName and lastName for each test -var firstName1 = testNameGenerator(10) -var lastName1 = testNameGenerator(10) -var firstName2 = testNameGenerator(10) -var lastName2 = testNameGenerator(10) -var firstName3 = testNameGenerator(10) -var lastName3 = testNameGenerator(10) - -// Builds the User object with the corresponding name. Does not save the User objects to the database. -var testUser1 = User.build({firstName: firstName1, lastName: lastName1}) -var testUser2 = User.build({firstName: firstName2, lastName: lastName2}) -var testUser3 = User.build({firstName: firstName3, lastName: lastName3}) - -// Creates a tuple array for the test -var testArray = [ [testUser1, firstName1 + ' ' + lastName1], - [testUser2, firstName2 + ' ' + lastName2], - [testUser3, firstName3 + ' ' + lastName3] -] - -// Test for local user creation -describe('Test suite: User build', function () { - testArray.forEach(function (arrElement, callback) { - describe('Build test for name: ' + arrElement[0].firstName + ' ' + arrElement[0].lastName, function () { - it('Name to local User object is identical to name generated', function (done) { - assert.equal(arrElement[0].firstName + ' ' + arrElement[0].lastName, arrElement[1]) - done() - }) + +/**TEST 1 USER MODEL**/ + +// Unique firstName and lastName for the test +var name = testStringGenerator(10, s) + +describe('Test suite 1: User model', function () { + + /** + * @description Test for database user creation + */ + describe('Database creation for name: ' + name, function () { + it('Name to User object in database is identical to name generated', function (done) { + // Access database and create the user if it doesn't exist + db['User'] + .findOrCreate({ + where: {name:name}, + attributes: ['name'] + }) + + // Then compare that user's variables to the variables given + .spread(function (user, created) { + assert.equal(name, user.name) + // Delete the user from the database + user.destroy() + done() + }) }) }) }) -// Get the User object from the database -// Creates a tuple array for the test -var test2Array = [ - [firstName1, lastName1], - [firstName2, lastName2], - [firstName3, lastName3] -] - -// Test for database user creation -describe('Test suite: User create', function () { - // For each element in test2Array - test2Array.forEach(function (arrElement, callback) { - describe('Database test for name: ' + arrElement[0] + ' ' + arrElement[1], function () { - it('Name to User object in database is identical to name generated', function (done) { - // Access database and create the user if it doesn't exist - db['User'] - .findOrCreate({ - where: {firstName: arrElement[0], lastName: arrElement[1]}, - attributes: ['id', 'firstName', 'lastName'] - }) - - // Then compare that user's variables to the variables given - .spread(function (user, created) { - assert.equal( - arrElement[0] + ' ' + arrElement[1], - user.firstName + ' ' + user.lastName - ) - - // Delete the user from the database - user.destroy() - done() - }) + +/**TEST 2 MESSAGE MODEL**/ + +var testString = testStringGenerator(30, s) + +var testTime = "07:04" +var testDate = new Date() +testDate.setHours(7) +testDate.setMinutes(4) + +describe('Test suite 2: Message model', function () { + + /** + * @description Test for create message + */ + describe('Database creation for message: ' + testString, function () { + it('Text in Message object in database is identical to testString', function (done) { + // Access database and create the message if it doesn't exist + db['Message'] + .findOrCreate({ + where: {text: testString}, + attributes: ['text'] + }) + // Then compare that messages's variables to the variables given + + .spread(function (message, created) { + assert.equal(testString, message.text) + message.destroy() + done() + }) + }) + }) + /** + * @description Test for message get time + */ + describe('Get formatted time from message model: ' + testTime, function () { + it('Get function returns string identical to testTime', function (done) { + db['Message'] + .findOrCreate({ + where: { + text:testString, + time:testDate + }, + attributes: ['time'] }) + // Then compare that messages's variables to the variables given + + .spread(function (message, created) { + assert.equal(testTime, message.get('time')) + message.destroy() + done() + }) }) }) }) diff --git a/wait-for-postgres.sh b/wait-for-postgres.sh index 1fb9452..dd6aee1 100755 --- a/wait-for-postgres.sh +++ b/wait-for-postgres.sh @@ -6,6 +6,6 @@ set -e host="$1" shift cmd="$@" -sleep 7 +sleep 2 >&2 echo "Postgres is up - executing command" exec $cmd \ No newline at end of file diff --git a/webpack.config.js b/webpack.config.js index e962b88..65ed5f1 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -4,7 +4,7 @@ module.exports = { entry: './src/client/client.js', output: { - filename: './src/client/bundle.js' // path: path.resolve(__dirname, 'dist') + filename: './src/static/bundle.js' // path: path.resolve(__dirname, 'dist') }, devServer: {