Skip to content

Commit 8d2d8cb

Browse files
committed
Fixed webpack-merge issue. Started experimenting with React contexts.
1 parent 910150b commit 8d2d8cb

File tree

11 files changed

+184
-58
lines changed

11 files changed

+184
-58
lines changed

api/controllers/admin/login.js

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
module.exports = {
2+
friendlyName: 'Admin Login',
3+
4+
description: 'Basic authentication for admin panel.',
5+
6+
inputs: {
7+
email: {
8+
type: 'string',
9+
required: true,
10+
isEmail: true,
11+
maxLength: 191 // max length of UTF8MB4 varchar
12+
},
13+
14+
password: {
15+
type: 'string',
16+
required: true,
17+
maxLength: 191
18+
}
19+
},
20+
21+
exits: {
22+
ok: {
23+
responseType: 'ok'
24+
},
25+
badRequest: {
26+
responseType: 'badRequest'
27+
},
28+
serverError: {
29+
responseType: 'serverError'
30+
}
31+
},
32+
33+
fn: (inputs, exits) => {
34+
35+
36+
return exits.ok();
37+
}
38+
};

api/models/User.js

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,14 @@ async function updatePassword(password) {
1717
return password;
1818
}
1919

20+
function getGravatarUrl(email) {
21+
return 'https://www.gravatar.com/avatar/' + md5(email);
22+
}
23+
24+
function forceUppercaseOnFirst(name) {
25+
return name.trim().charAt(0).toUpperCase() + name.slice(1).trim();
26+
}
27+
2028
module.exports = {
2129
primaryKey: 'id',
2230

@@ -133,9 +141,9 @@ module.exports = {
133141
const email = user.email.toLowerCase().trim();
134142

135143
user.email = email;
136-
user.avatar = 'https://www.gravatar.com/avatar/' + md5(email);
137-
user.firstName = user.firstName.trim().charAt(0).toUpperCase() + user.firstName.slice(1).trim();
138-
user.lastName = user.lastName.trim().charAt(0).toUpperCase() + user.lastName.slice(1).trim();
144+
user.avatar = getGravatarUrl(email);
145+
user.firstName = forceUppercaseOnFirst(user.firstName);
146+
user.lastName = forceUppercaseOnFirst(user.lastName);
139147
user.id = sails.helpers.generateUuid();
140148

141149
user.password = await updatePassword(user.password);
@@ -147,9 +155,9 @@ module.exports = {
147155
const email = user.email.toLowerCase().trim();
148156

149157
user.email = email;
150-
user.avatar = 'https://www.gravatar.com/avatar/' + md5(email);
151-
user.firstName = user.firstName.trim().charAt(0).toUpperCase() + user.firstName.slice(1).trim();
152-
user.lastName = user.lastName.trim().charAt(0).toUpperCase() + user.lastName.slice(1).trim();
158+
user.avatar = getGravatarUrl(email);
159+
user.firstName = forceUppercaseOnFirst(user.firstName);
160+
user.lastName = forceUppercaseOnFirst(user.lastName);
153161

154162
if (user.password) {
155163
user.password = await updatePassword(user.password);

assets/src/Admin/AdminRouter.jsx

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,18 @@ import {
77
Route,
88
Redirect
99
} from 'react-router-dom';
10-
import api from '../common/api';
1110

1211
import Login from '../Admin/Login';
1312
import SidebarNav from './SidebarNav';
1413
import Upgrade from './Upgrade';
1514

15+
import {APIProvider} from '../data/api';
16+
1617
class AdminRouter extends React.Component {
1718
constructor(props) {
1819
super(props);
1920

2021
this.state = {
21-
api: new api(),
2222
isAuthenticated: false,
2323
user: sessionStorage.getItem('user')
2424
};
@@ -53,28 +53,30 @@ class AdminRouter extends React.Component {
5353
// }}
5454
// />
5555
// );
56-
return (<Login location={props.location} api={that.state.api} userLogin={that.userLogin} />);
56+
return (<Login location={props.location} userLogin={that.userLogin} />);
5757
}
5858

5959
return (
6060
<Router>
6161
<Route
6262
render={({location}) => (
63-
<RenderOrLogin location={location}>
64-
<SidebarNav>
65-
<Switch>
66-
<Route path="/admin/dashboard">
67-
<Dashboard api={this.state.api} user={this.state.user} />
68-
</Route>
69-
<Route path="/admin/upgrade">
70-
<Upgrade />
71-
</Route>
72-
<Route>
73-
<Redirect to="/admin/dashboard" />
74-
</Route>
75-
</Switch>
76-
</SidebarNav>
77-
</RenderOrLogin>
63+
<APIProvider>
64+
<RenderOrLogin location={location}>
65+
<SidebarNav>
66+
<Switch>
67+
<Route path="/admin/dashboard">
68+
<Dashboard api={this.state.api} user={this.state.user} />
69+
</Route>
70+
<Route path="/admin/upgrade">
71+
<Upgrade />
72+
</Route>
73+
<Route>
74+
<Redirect to="/admin/dashboard" />
75+
</Route>
76+
</Switch>
77+
</SidebarNav>
78+
</RenderOrLogin>
79+
</APIProvider>
7880
)}
7981
/>
8082
</Router>

assets/src/Admin/Login.jsx

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import React from 'react';
22
import PropTypes from 'prop-types';
33

4+
import {APIConsumer} from '../data/api';
5+
46
class Login extends React.Component {
57
constructor(props) {
68
super(props);
@@ -25,11 +27,13 @@ class Login extends React.Component {
2527
// this.props.location.history.push(dest);
2628
}
2729

28-
handleLogin(e) {
30+
handleLogin(e, api) {
2931
e.preventDefault();
3032

3133
localStorage.setItem('email', this.state.email);
3234

35+
console.log(api);
36+
3337
// TODO: actually hit API to login, then call this function
3438
this.props.userLogin({email: this.state.email});
3539

@@ -44,8 +48,8 @@ class Login extends React.Component {
4448
this.setState({password: e.target.value});
4549
}
4650

47-
handleRememberMe(val) {
48-
this.setState({rememberMe: val});
51+
handleRememberMe() {
52+
this.setState((current) => ({rememberMe: !current.rememberMe}));
4953
}
5054

5155
render() {
@@ -59,22 +63,28 @@ class Login extends React.Component {
5963
{
6064
!this.state.isLoggedIn &&
6165
<div className="row justify-content-center">
62-
<form onSubmit={this.handleLogin} className="col-3">
63-
<h3 className="row">Login</h3>
64-
<div className="row pb-2">
65-
<input type="email" className="form-control" placeholder="Email" value={this.state.email} onChange={this.handleEmail} />
66-
</div>
67-
<div className="row pb-4">
68-
<input type="password" className="form-control" placeholder="Password" value={this.state.password} onChange={this.handlePassword} />
69-
</div>
70-
<div className="row pb-4 form-check">
71-
<input type="checkbox" className="form-check-input" id="rememberMe" value={this.state.rememberMe} onChange={() => this.handleRememberMe(!this.state.rememberMe)} />
72-
<label htmlFor="rememberMe">Remember Me</label>
73-
</div>
74-
<div className="row">
75-
<button type="submit" className="btn btn-primary">Login</button>
76-
</div>
77-
</form>
66+
<APIConsumer>
67+
{
68+
(api) => (
69+
<form onSubmit={(e) => this.handleLogin(e, api)} className="col-3">
70+
<h3 className="row">Login</h3>
71+
<div className="row pb-2">
72+
<input type="email" className="form-control" placeholder="Email" value={this.state.email} onChange={this.handleEmail} />
73+
</div>
74+
<div className="row pb-4">
75+
<input type="password" className="form-control" placeholder="Password" value={this.state.password} onChange={this.handlePassword} />
76+
</div>
77+
<div className="row pb-4 form-check">
78+
<input type="checkbox" className="form-check-input" id="rememberMe" value={this.state.rememberMe} onChange={this.handleRememberMe} />
79+
<label htmlFor="rememberMe">Remember Me</label>
80+
</div>
81+
<div className="row">
82+
<button type="submit" className="btn btn-primary">Login</button>
83+
</div>
84+
</form>
85+
)
86+
}
87+
</APIConsumer>
7888
</div>
7989
}
8090
</div>
@@ -83,7 +93,6 @@ class Login extends React.Component {
8393
}
8494

8595
Login.propTypes = {
86-
api: PropTypes.object.isRequired,
8796
location: PropTypes.object.isRequired,
8897
userLogin: PropTypes.func.isRequired
8998
};

assets/src/common/api.js

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,17 @@
11
import request from 'superagent';
22

3+
let requester = null;
4+
35
class api {
46
constructor(baseUrl) {
5-
this.baseUrl = baseUrl || process.env.baseUrl;
6-
this.request = request.agent(); // cookie handler
7-
this.queue = [];
7+
this.baseUrl = baseUrl || process.env.baseUrl; // process.env is coming from webpack
8+
9+
this.get = this.get.bind(this);
10+
this.post = this.post.bind(this);
11+
this.put = this.put.bind(this);
12+
this.del = this.del.bind(this);
13+
14+
requester = request.agent(); // cookie handler
815
}
916

1017
static __buildRequestObj(thisReq, req) {
@@ -43,7 +50,7 @@ class api {
4350
req = {url: req};
4451
}
4552

46-
const thisReq = api.__buildRequestObj(this.request.get(this.baseUrl + req.url), req);
53+
const thisReq = api.__buildRequestObj(requester.get(this.baseUrl + req.url), req);
4754

4855
thisReq.then((res) => api.__buildResponseWrapper(res, good), bad);
4956
}
@@ -53,7 +60,7 @@ class api {
5360
req = {url: req};
5461
}
5562

56-
const thisReq = api.__buildRequestObj(this.request.post(this.baseUrl + req.url), req);
63+
const thisReq = api.__buildRequestObj(requester.post(this.baseUrl + req.url), req);
5764

5865
thisReq.then((res) => api.__buildResponseWrapper(res, good), bad);
5966
}
@@ -63,7 +70,7 @@ class api {
6370
req = {url: req};
6471
}
6572

66-
const thisReq = api.__buildRequestObj(this.request.put(this.baseUrl + req.url), req);
73+
const thisReq = api.__buildRequestObj(requester.put(this.baseUrl + req.url), req);
6774

6875
thisReq.then((res) => api.__buildResponseWrapper(res, good), bad);
6976
}
@@ -73,7 +80,7 @@ class api {
7380
req = {url: req};
7481
}
7582

76-
const thisReq = api.__buildRequestObj(this.request.del(this.baseUrl + req.url), req);
83+
const thisReq = api.__buildRequestObj(requester.del(this.baseUrl + req.url), req);
7784

7885
thisReq.then((res) => api.__buildResponseWrapper(res, good), bad);
7986
}

assets/src/data/api.jsx

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import React from 'react';
2+
import api from '../common/api';
3+
4+
const apiContext = React.createContext();
5+
6+
export class APIProvider extends React.Component {
7+
constructor(props) {
8+
super(props);
9+
10+
this.state = {
11+
api: new api()
12+
};
13+
}
14+
15+
render() {
16+
return (
17+
<apiContext.Provider value={this.state.api}>
18+
{/* eslint-disable-next-line react/prop-types */}
19+
{this.props.children}
20+
</apiContext.Provider>
21+
);
22+
}
23+
}
24+
25+
export const APIConsumer = apiContext.Consumer;

assets/src/data/user.jsx

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import React from 'react';
2+
3+
const userContext = React.createContext();
4+
5+
export class UserProvider extends React.Component {
6+
constructor(props) {
7+
super(props);
8+
9+
this.state = {
10+
user: {},
11+
isLoggedIn: false,
12+
isRememberMeOn: JSON.parse(localStorage.getItem('user_remember_me')) || false
13+
};
14+
15+
if (this.state.isRememberMeOn) {
16+
this.state.user.email = localStorage.getItem('user_email') || '';
17+
} else {
18+
this.state.user.email = '';
19+
}
20+
}
21+
22+
render() {
23+
return (
24+
<userContext.Provider
25+
value={{
26+
user: this.state.user,
27+
isLoggedIn: this.state.isLoggedIn,
28+
isRememberMeOn: this.state.isRememberMeOn
29+
}}
30+
>
31+
{/* eslint-disable-next-line react/prop-types */}
32+
{this.props.children}
33+
</userContext.Provider>
34+
);
35+
}
36+
}
37+
38+
export const UserConsumer = userContext.Consumer;

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,6 @@
7575
"clean": "rimraf .tmp && mkdirp .tmp/public",
7676
"lift": "node app.js",
7777
"debug": "node --inspect app.js",
78-
"blah": "node app.js",
7978
"lines": "git ls-files --exclude-standard -- ':!:**/*.[pjs][npv]g' ':!:**/*.ai' ':!:.idea' ':!:test/coverage/*' ':!:config/*' ':!:.babelrc' ':!:.editorconfig' ':!:.eslintrc' ':!:.eslintignore' ':!:.gitignore' ':!:.mocharc.yaml' ':!:.npmrc' ':!:.nycrc' ':!:.sailsrc' ':!:LICENSE' ':!:package-lock.json' | xargs wc -l"
8079
},
8180
"main": "app.js",

webpack/common.config.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ try {
2626

2727
baseUrl = config.baseUrl;
2828
frontendUrl = config.frontendUrl;
29-
assetUrl = config.assetsUrl ? config.assetsUrl : '/';
29+
assetUrl = config.assetsUrl ? config.assetsUrl : '/'; // used for CDN rewrites on asset URLs
3030
} catch (err) {
3131
return console.error(err);
3232
}
@@ -68,7 +68,7 @@ let entry = {},
6868
for (let i = 0; i < entryPoints.length; ++i) {
6969
const template = (entryPoints[i].template) ? entryPoints[i].template : baseHtmlConfig.template;
7070

71-
if (!entryPoints[i].include || !_.isArray(entryPoints[i])) {
71+
if (!entryPoints[i].include) {
7272
entryPoints[i].include = [entryPoints[i].name];
7373
}
7474

webpack/dev.config.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
const mergeThem = require('webpack-merge');
1+
const {merge} = require('webpack-merge');
22
const baseConfig = require('./common.config.js');
33
const path = require('path');
44

5-
module.exports = mergeThem(baseConfig, {
5+
module.exports = merge(baseConfig, {
66
devServer: {
77
historyApiFallback: true,
88
contentBase: path.join(__dirname, '../../assets')

0 commit comments

Comments
 (0)