diff --git a/api/controllers/AuthController.js b/api/controllers/AuthController.js
index 502afca..5460626 100644
--- a/api/controllers/AuthController.js
+++ b/api/controllers/AuthController.js
@@ -115,6 +115,8 @@ module.exports = {
}
passportHelper.callback(req, res, function (err, user, info, status) {
+ // console.log(err)
+ // console.log(user)
if (err || !user) {
sails.log.warn(user, err, info, status)
if (!err && info) {
@@ -126,6 +128,7 @@ module.exports = {
req.login(user, function (err) {
if (err) {
sails.log.warn(err)
+ // console.log(err)
return negotiateError(err)
}
@@ -135,7 +138,7 @@ module.exports = {
if (req.query.next) {
res.status(302).set('Location', req.query.next)
} else if (req.query.code) { // if came from oauth callback
- res.status(302).set('Location', '/app')
+ res.status(302).set('Location', '/targets')
}
sails.log.info('user', user, 'authenticated successfully')
diff --git a/api/controllers/TargetController.js b/api/controllers/TargetController.js
index 057027e..6497265 100644
--- a/api/controllers/TargetController.js
+++ b/api/controllers/TargetController.js
@@ -3,7 +3,7 @@ const HttpError = require('../errors/HttpError')
module.exports = {
show: function (req, res) {
- res.view('pages/targets', {
+ res.view('pages/app', {
email: req.user.email
})
},
diff --git a/api/controllers/UserController.js b/api/controllers/UserController.js
index 03f8604..768051f 100644
--- a/api/controllers/UserController.js
+++ b/api/controllers/UserController.js
@@ -17,12 +17,11 @@ module.exports = {
error: err.toString()
})
}
-
res.json(user)
})
},
- update: async function (req, res, next) {
+ edit: async function (req, res, next) {
const passportHelper = await sails.helpers.passport()
passportHelper.protocols.local.update(req.body, function (err, user) {
if (err) {
@@ -30,7 +29,6 @@ module.exports = {
error: err.toString()
})
}
-
res.json(user)
})
},
diff --git a/assets/js/actions/index.js b/assets/js/actions/index.js
index ddaffdc..8f1f566 100644
--- a/assets/js/actions/index.js
+++ b/assets/js/actions/index.js
@@ -126,3 +126,31 @@ export const setUrl = (value) => async (dispatch, getState) => {
dispatch(setWorking(false))
}
}
+
+export const editUser = (user) => async (dispatch, getState) => {
+ dispatch(setWorking(true))
+
+ try {
+ if (!user.currentPassword) throw new Error('Please enter your current password.')
+ await Ajax.patch({
+ url: '/api/me',
+ data: {
+ id: user.id,
+ email: user.email,
+ password: user.password,
+ currentPassword: user.currentPassword
+ }
+ })
+ dispatch({
+ type: ACTIONS.error,
+ data: null
+ })
+ } catch (e) {
+ dispatch({
+ type: ACTIONS.error,
+ data: e
+ })
+ } finally {
+ dispatch(setWorking(false))
+ }
+}
diff --git a/assets/js/actions/login.js b/assets/js/actions/login.js
index 1976253..26347a5 100644
--- a/assets/js/actions/login.js
+++ b/assets/js/actions/login.js
@@ -47,7 +47,7 @@ export const clearError = () => ({
export const setLoggedIn = (data) => (dispatch, getState) => {
window.localStorage.setItem('roe-token', JSON.stringify(data))
- window.location.href = '/app'
+ window.location.href = '/targets'
}
export const checkEmail = email => async (dispatch, getState) => {
diff --git a/assets/js/index.js b/assets/js/index.js
index 6afbcc9..37a484e 100644
--- a/assets/js/index.js
+++ b/assets/js/index.js
@@ -2,10 +2,12 @@
import React from 'react'
import ReactDOM from 'react-dom'
+import { BrowserRouter as Router, Route, NavLink, Switch, Redirect } from 'react-router-dom'
import Progress from './components/Progress'
import UriListItem from './containers/UriListItem'
import reducer from './reducers'
-import { fetchData, createNewUrl, setEditing } from './actions'
+import { fetchData, createNewUrl, setEditing, editUser } from './actions'
+import UnderlineInput from './components/UnderlineInput'
import '../styles/index.scss'
class App extends React.Component {
@@ -14,8 +16,10 @@ class App extends React.Component {
this.state = {
error: '',
user: {
+ id: '',
email: '',
- password: ''
+ password: '',
+ currentPassword: ''
},
urls: [],
editingUrl: null,
@@ -24,6 +28,8 @@ class App extends React.Component {
this.dispatch = this.dispatch.bind(this)
this.getRegisteredUris = this.getRegisteredUris.bind(this)
+ this.setUserValue = this.setUserValue.bind(this)
+ this.saveUser = this.saveUser.bind(this)
}
dispatch (action) {
if (!action) throw new Error('dispatch: missing action')
@@ -49,33 +55,97 @@ class App extends React.Component {
editing={this.state.editingUrl === item.id} />)
})
}
+ setUserValue (which, e) {
+ this.setState({
+ user: {
+ ...this.state.user,
+ [which]: e.target.value
+ }
+ })
+ }
+ saveUser () {
+ this.dispatch(editUser(this.state.user))
+ this.setState({
+ user: {
+ ...this.state.user,
+ email: this.state.user.email,
+ password: '',
+ currentPassword: ''
+ }
+ })
+ }
render () {
return (
-
this.dispatch(setEditing(null))}>
-
-
-
- {this.state.error && {this.state.error}
}
-
-
- {this.getRegisteredUris()}
-
-
-
+
+ this.dispatch(setEditing(null))}>
+
+
+
+ {this.state.error && {this.state.error}
}
+
+ (
+
+
+
+ {this.getRegisteredUris()}
+
+
+ )} />
+
+ (
+
+
+
+ this.setUserValue('email', e)} />
+ this.setUserValue('password', e)} />
+ this.setUserValue('currentPassword', e)} />
+
+
+
+
+
+ )} />
+
+ } />
+
+
+
+
)
}
}
diff --git a/assets/js/login.js b/assets/js/login.js
index 790ffdf..940e983 100644
--- a/assets/js/login.js
+++ b/assets/js/login.js
@@ -108,8 +108,8 @@ class App extends React.Component {
error={this.state.passwordError}
button='Sign in'
onButtonClick={() => this.dispatch(checkPassword(this.state.user.email, this.state.user.password))}
- smallButton='Forgot password?'
- onSmallButtonClick={() => this.dispatch(setCarousel(3))} />
+ comment={null/*smallButton='Forgot password?'
+ onSmallButtonClick={() => this.dispatch(setCarousel(3))}*/} />
{
}
case Actions.error:
return {
- error: data.message
+ error: (data || {}).message || ''
}
default: return {}
}
diff --git a/assets/styles/index.scss b/assets/styles/index.scss
index e55ba57..42e3935 100644
--- a/assets/styles/index.scss
+++ b/assets/styles/index.scss
@@ -13,9 +13,10 @@
padding: 0 14px;
margin: -14px 0 8px 0;
}
- & > header {
- padding: 0 14px;
-
+ & > div {
+ & > header {
+ padding: 0 14px;
+ }
h1 {
text-shadow: 1px 1px 2px $black-3;
}
@@ -35,6 +36,14 @@
list-style: none;
// overflow: hidden;
}
+ .inputs {
+ padding: 20px 14px;
+
+ .buttons {
+ margin-top: 14px;
+ text-align: right;
+ }
+ }
&.working {
& > .progress {
top: 0;
diff --git a/assets/styles/shared/twopanels.scss b/assets/styles/shared/twopanels.scss
index 8c72e21..b53c1a1 100644
--- a/assets/styles/shared/twopanels.scss
+++ b/assets/styles/shared/twopanels.scss
@@ -6,7 +6,6 @@
box-shadow: $shadow-1;
header {
- height: 50px;
line-height: 50px;
padding: 0 14px;
diff --git a/config/protocols.js b/config/protocols.js
index 0a6183e..aed2dbe 100644
--- a/config/protocols.js
+++ b/config/protocols.js
@@ -66,7 +66,48 @@ module.exports.protocols = {
}
},
update: async function (user, next) {
- throw new Error('not implemented')
+ try {
+ const dbUser = await User.findOne({
+ id: user.id
+ })
+ if (!dbUser) throw new Error('an account with that id was not found')
+
+ const passport = await Passport.findOne({
+ protocol: 'local',
+ user: user.id
+ })
+ if (!user.currentPassword && passport) throw new Error('Missing current password')
+ if (passport) {
+ const res = await Passport.validatePassword(user.currentPassword, passport)
+ if (!res) throw new Error('incorrect password')
+
+ await User.update({ id: user.id }, {
+ email: user.email
+ })
+ if (user.password && user.password.length) {
+ await Passport.update({ id: passport.id }, {
+ password: user.password
+ })
+ }
+ } else { // no password yet, add one
+ await User.update({ id: user.id }, {
+ email: user.email
+ })
+ if (user.password && user.password.length) {
+ const token = generateToken()
+ await Passport.create({
+ protocol: 'local',
+ password: user.password,
+ user: dbUser.id,
+ accesstoken: token
+ })
+ }
+ }
+ delete dbUser.password
+ next(null, dbUser)
+ } catch (e) {
+ return next(e)
+ }
},
connect: async function (req, res, next) {
try {
diff --git a/config/routes.js b/config/routes.js
index dfe64c9..e85fc02 100644
--- a/config/routes.js
+++ b/config/routes.js
@@ -31,7 +31,9 @@ module.exports.routes = {
'GET /register': {
view: 'pages/login'
},
- 'GET /app': 'TargetController.show',
+ // figure out why proper clientside routing breaks the backend session
+ 'GET /account': 'TargetController.show',
+ 'GET /targets': 'TargetController.show',
/***************************************************************************
* *
@@ -55,6 +57,8 @@ module.exports.routes = {
'POST /auth/email_available': 'AuthController.emailAvailable',
// 'POST /auth/local': 'AuthController.callback',
// 'POST /auth/local/:action': 'AuthController.callback',
+ 'GET /api/me': 'UserController.me',
+ 'PATCH /api/me': 'UserController.edit',
'POST /auth/:provider': 'AuthController.callback',
'POST /auth/:provider/:action': 'AuthController.callback',
@@ -64,9 +68,7 @@ module.exports.routes = {
'GET /auth/:provider/:action': 'AuthController.callback',
'POST /api/publish': 'BooksController.publish',
-
'GET /api/books': 'BooksController.list',
- 'GET /api/me': 'UserController.me',
'POST /api/targets': 'TargetController.create',
'PATCH /api/targets/:id': 'TargetController.edit',
diff --git a/package.json b/package.json
index aba769e..5fff937 100644
--- a/package.json
+++ b/package.json
@@ -62,6 +62,7 @@
"mocha": "^5.2.0",
"node-sass": "^4.9.4",
"npm-run-all": "^4.1.3",
+ "react-router-dom": "^4.3.1",
"rimraf": "^2.6.2",
"sass-loader": "^7.1.0",
"standard": "^12.0.1",
diff --git a/shrinkwrap.yaml b/shrinkwrap.yaml
index 5dcf30d..282f6e6 100644
--- a/shrinkwrap.yaml
+++ b/shrinkwrap.yaml
@@ -1,5 +1,7 @@
dependencies:
request: 2.88.0
+devDependencies:
+ react-router-dom: 4.3.1
packages:
/ajv/6.8.1:
dependencies:
@@ -136,6 +138,20 @@ packages:
node: '>=6'
resolution:
integrity: sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==
+ /history/4.7.2:
+ dependencies:
+ invariant: 2.2.4
+ loose-envify: 1.4.0
+ resolve-pathname: 2.2.0
+ value-equal: 0.4.0
+ warning: 3.0.0
+ dev: true
+ resolution:
+ integrity: sha512-1zkBRWW6XweO0NBcjiphtVJVsIQ+SXF29z9DVkceeaSLVMFXHool+fdCZD4spDCfZJCILPILc3bm7Bc+HRi0nA==
+ /hoist-non-react-statics/2.5.5:
+ dev: true
+ resolution:
+ integrity: sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw==
/http-signature/1.2.0:
dependencies:
assert-plus: 1.0.0
@@ -147,14 +163,28 @@ packages:
npm: '>=1.3.7'
resolution:
integrity: sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=
+ /invariant/2.2.4:
+ dependencies:
+ loose-envify: 1.4.0
+ dev: true
+ resolution:
+ integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==
/is-typedarray/1.0.0:
dev: false
resolution:
integrity: sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=
+ /isarray/0.0.1:
+ dev: true
+ resolution:
+ integrity: sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=
/isstream/0.1.2:
dev: false
resolution:
integrity: sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=
+ /js-tokens/4.0.0:
+ dev: true
+ resolution:
+ integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
/jsbn/0.1.1:
dev: false
resolution:
@@ -182,6 +212,13 @@ packages:
'0': node >=0.6.0
resolution:
integrity: sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=
+ /loose-envify/1.4.0:
+ dependencies:
+ js-tokens: 4.0.0
+ dev: true
+ hasBin: true
+ resolution:
+ integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
/mime-db/1.37.0:
dev: false
engines:
@@ -200,10 +237,29 @@ packages:
dev: false
resolution:
integrity: sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==
+ /object-assign/4.1.1:
+ dev: true
+ engines:
+ node: '>=0.10.0'
+ resolution:
+ integrity: sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
+ /path-to-regexp/1.7.0:
+ dependencies:
+ isarray: 0.0.1
+ dev: true
+ resolution:
+ integrity: sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=
/performance-now/2.1.0:
dev: false
resolution:
integrity: sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=
+ /prop-types/15.6.2:
+ dependencies:
+ loose-envify: 1.4.0
+ object-assign: 4.1.1
+ dev: true
+ resolution:
+ integrity: sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==
/psl/1.1.31:
dev: false
resolution:
@@ -224,6 +280,33 @@ packages:
node: '>=0.6'
resolution:
integrity: sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==
+ /react-router-dom/4.3.1:
+ dependencies:
+ history: 4.7.2
+ invariant: 2.2.4
+ loose-envify: 1.4.0
+ prop-types: 15.6.2
+ react-router: 4.3.1
+ warning: 4.0.2
+ dev: true
+ peerDependencies:
+ react: '>=15'
+ resolution:
+ integrity: sha512-c/MlywfxDdCp7EnB7YfPMOfMD3tOtIjrQlj/CKfNMBxdmpJP8xcz5P/UAFn3JbnQCNUxsHyVVqllF9LhgVyFCA==
+ /react-router/4.3.1:
+ dependencies:
+ history: 4.7.2
+ hoist-non-react-statics: 2.5.5
+ invariant: 2.2.4
+ loose-envify: 1.4.0
+ path-to-regexp: 1.7.0
+ prop-types: 15.6.2
+ warning: 4.0.2
+ dev: true
+ peerDependencies:
+ react: '>=15'
+ resolution:
+ integrity: sha512-yrvL8AogDh2X42Dt9iknk4wF4V8bWREPirFfS9gLU1huk6qK41sg7Z/1S81jjTrGHxa3B8R3J6xIkDAA6CVarg==
/request/2.88.0:
dependencies:
aws-sign2: 0.7.0
@@ -251,6 +334,10 @@ packages:
node: '>= 4'
resolution:
integrity: sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==
+ /resolve-pathname/2.2.0:
+ dev: true
+ resolution:
+ integrity: sha512-bAFz9ld18RzJfddgrO2e/0S2O81710++chRMUxHjXOYKF6jTAMrUNZrEZ1PvV0zlhfjidm08iRPdTLPno1FuRg==
/safe-buffer/5.1.2:
dev: false
resolution:
@@ -306,6 +393,10 @@ packages:
hasBin: true
resolution:
integrity: sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==
+ /value-equal/0.4.0:
+ dev: true
+ resolution:
+ integrity: sha512-x+cYdNnaA3CxvMaTX0INdTCN8m8aF2uY9BvEqmxuYp8bL09cs/kWVQPVGcA35fMktdOsP69IgU7wFj/61dJHEw==
/verror/1.10.0:
dependencies:
assert-plus: 1.0.0
@@ -316,8 +407,21 @@ packages:
'0': node >=0.6.0
resolution:
integrity: sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=
+ /warning/3.0.0:
+ dependencies:
+ loose-envify: 1.4.0
+ dev: true
+ resolution:
+ integrity: sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=
+ /warning/4.0.2:
+ dependencies:
+ loose-envify: 1.4.0
+ dev: true
+ resolution:
+ integrity: sha512-wbTp09q/9C+jJn4KKJfJfoS6VleK/Dti0yqWSm6KMvJ4MRCXFQNapHuJXutJIrWV0Cf4AhTdeIe4qdKHR1+Hug==
registry: 'https://registry.npmjs.org/'
shrinkwrapMinorVersion: 9
shrinkwrapVersion: 3
specifiers:
+ react-router-dom: ^4.3.1
request: ^2.88.0
diff --git a/views/pages/app.ejs b/views/pages/app.ejs
new file mode 100644
index 0000000..11f0cbb
--- /dev/null
+++ b/views/pages/app.ejs
@@ -0,0 +1 @@
+<%- partial('../../.tmp/public/index.html') %>
diff --git a/views/pages/targets.ejs b/views/pages/targets.ejs
deleted file mode 100644
index 9816e60..0000000
--- a/views/pages/targets.ejs
+++ /dev/null
@@ -1 +0,0 @@
-<%- partial('../../.tmp/public/targets.html') %>