Skip to content

Commit acaed24

Browse files
committed
More work on tests. Updated deps. Removed md5 / sha1 / uuid packages in favor of node built-ins. Added precision to timestamps of logs.
1 parent ff04394 commit acaed24

File tree

94 files changed

+2384
-1615
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

94 files changed

+2384
-1615
lines changed

.dockerignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,11 @@ config/local.js
77
.editorconfig
88
.eslintignore
99
.eslintrc
10+
.mocharc.yaml
11+
.nycrc
1012
.tmp
1113
.travis.yml
14+
test
1215

1316
README.md
1417
CHANGELOG.md

.github/FUNDING.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
github: neonexus

.idea/codeStyles/Project.xml

Lines changed: 0 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/dictionaries/neonexusdemortis.xml

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/runConfigurations/Run_Tests.xml

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/runConfigurations/test_startTests_js.xml

Lines changed: 17 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.mocharc.yaml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
bail: false #stop on first error
22
async-only: true # require use of "done" cb or an async function (a Promise)
33
require:
4-
- 'test/hooks.js' # starting point for tests
5-
spec: 'test/**/*.test.js'
4+
- 'test/startTests.js' # starting point for tests
5+
spec:
6+
- 'test/unit/**/*.test.js'
7+
- 'test/integration/**/*.test.js'
68
timeout: 10000 # Give Sails some breathing room... We are building schemas / data fixtures.
79
checkLeaks: true
810
global:
@@ -15,3 +17,4 @@ global:
1517
- 'RequestLog'
1618
- 'Log'
1719
- '__coverage__' # NYC coverage global
20+
- 'match'

CHANGELOG.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
# Changelog
22

3-
## [v3.2.2](https://github.com/neonexus/sails-react-bootstrap-webpack/compare/v3.2.1...v3.2.2) (2022-11-16)
3+
## [v4.0.0](https://github.com/neonexus/sails-react-bootstrap-webpack/compare/v3.2.1...v4.0.0) (2023-02-11)
44

55
### Features
66

7-
* Built out more automated tests for better coverage.
7+
* More work on automated tests and utilities.
8+
* Renamed tests entry point (hooks.js -> startTests.js).
89
* Updated dependencies.
10+
* Removed `md5` / `sha1` / `uuid` packages, in favor of Node built-ins.
911

1012
## [v3.2.1](https://github.com/neonexus/sails-react-bootstrap-webpack/compare/v3.2.0...v3.2.1) (2022-11-16)
1113

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ Need help? Want to hire me to build your next app or prototype? You can contact
1111
* Automatic (incoming) request logging (manual outgoing), via Sails models / hooks.
1212
* Setup for Webpack auto-reload dev server.
1313
* Setup so Sails will serve Webpack-built bundles as separate apps (so, a marketing site, and an admin site can live side-by-side).
14+
* Custom functions to make pagination simple.
1415
* Includes [react-bootstrap](https://www.npmjs.com/package/react-bootstrap) to make using Bootstrap styles / features with React easier.
1516
* Schema validation and enforcement for `PRODUCTION`. This repo is set up for `MySQL`. If you plan to use a different datastore, you will likely want to disable the schema validation and enforcement feature inside [`config/bootstrap.js`](config/bootstrap.js). See [schema validation and enforcement](#schema-validation-and-enforcement) for more info.
1617
* New passwords can be checked against the [PwnedPasswords API](https://haveibeenpwned.com/API/v3#PwnedPasswords). If there is a single hit for the password, an error will be given, and the user will be forced to choose another. See [PwnedPasswords integration](#pwnedpasswordscom-integration) for more info.
@@ -58,8 +59,8 @@ Sails, by default, has middleware (akin to [Express.js Middleware](https://expre
5859
| npm run build | Will run `npm run clean`, then will build production-ready files with Webpack in the `.tmp/public` folder. |
5960
| npm run build:dev | Same thing as `npm run build`, except that it will not optimize the files, retaining newlines and empty spaces. |
6061
| npm run clean | Will delete everything in the `.tmp` folder. |
61-
| npm run lines | Will count the lines of code in the project, minus `.gitignore`'d files, for funzies. There are currently about 7k custom lines in this repo (views, controllers, helpers, hooks, etc). |
62-
| npm run test | Run [Mocha](https://mochajs.org/) tests. Everything starts in the [`test/hooks.js`](test/hooks.js) file. |
62+
| npm run lines | Will count the lines of code in the project, minus `.gitignore`'d files, for funzies. There are currently about 8k custom lines in this repo (views, controllers, helpers, hooks, etc). |
63+
| npm run test | Run [Mocha](https://mochajs.org/) tests. Everything starts in the [`test/startTests.js`](test/startTests.js) file. |
6364
| npm run coverage | Runs [NYC](https://www.npmjs.com/package/nyc) coverage reporting of the Mocha tests, which generates HTML in `test/coverage`. |
6465

6566
### Environment Variables
@@ -97,7 +98,7 @@ If you want to build assets, but retain spaces / tabs for debugging, you can use
9798
The webpack configuration can be found in the [`webpack`](webpack) folder. The majority of the configuration can be found in [`common.config.js`](webpack/common.config.js). Then, the other 3 files, such as [`dev.config.js`](webpack/dev.config.js) extend the `common.config.js` file.
9899

99100
## Building with React
100-
React source files live in the `assets/src` folder. It is structured in such a way, where the `index.jsx` is really only used for local development (to help Webpack serve up the correct "app"). Then, there are the individual "apps", [main](assets/src/main.jsx) and [admin](assets/src/admin.jsx). These files are used as Webpack "[entry points](https://webpack.js.org/concepts/entry-points/)", to create 2 separate application bundles.
101+
React source files live in the [`assets/src`](assets/src) folder. It is structured in such a way, where the `index.jsx` is really only used for local development (to help Webpack serve up the correct "app"). Then, there are the individual "apps", [main](assets/src/main.jsx) and [admin](assets/src/admin.jsx). These files are used as Webpack "[entry points](https://webpack.js.org/concepts/entry-points/)", to create 2 separate application bundles.
101102

102103
In a remote environment, Sails will look at the first subdirectory requested, and use that to determine which `index.html` file it needs to actually return. So, in this case, the "main" application will get built in `.tmp/public/main`, where the CSS is `.tmp/public/main/bundle.css`, the JavaScript is `.tmp/public/main/bundle.js`, and the HTML is `.tmp/public/main/index.html`. To view the main application, one would just go to `http://mydomain/` which gets redirected to `/main` (because we need to know what application we are using, we need a subdirectory), and now Sails will serve the `main` application. Whereas, if one were to go to `http://mydomain/admin`, Sails would now serve the `admin` application bundle (aka `.tmp/public/admin/index.html`, `.tmp/public/admin/bundle.css`, etc...).
103104

api/controllers/admin/create-api-token.js

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,8 @@ module.exports = {
66
inputs: {},
77

88
exits: {
9-
ok: {
10-
responseType: 'ok'
11-
},
12-
badRequest: {
13-
responseType: 'badRequest'
14-
},
15-
serverError: {
16-
responseType: 'serverError'
9+
created: {
10+
responseType: 'created'
1711
}
1812
},
1913

@@ -23,7 +17,7 @@ module.exports = {
2317
user: env.req.session.user.id
2418
}).fetch();
2519

26-
return exits.ok({
20+
return exits.created({
2721
token: newToken.token,
2822
__skipCSRF: true // this tells our "ok" response to ignore the CSRF token update
2923
});

api/controllers/admin/create-user.js

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,10 @@ module.exports = {
3737
]
3838
},
3939

40-
setPassword: {
40+
generatePassword: {
4141
type: 'boolean',
42-
defaultsTo: true
42+
defaultsTo: false,
43+
description: 'Used to auto-generate a password for the user'
4344
}
4445
},
4546

@@ -59,14 +60,16 @@ module.exports = {
5960
let password = inputs.password;
6061
let isPasswordValid;
6162

62-
if (inputs.setPassword) {
63+
if (!inputs.generatePassword) {
6364
isPasswordValid = await sails.helpers.isPasswordValid.with({
6465
password: inputs.password,
6566
user: {firstName: inputs.firstName, lastName: inputs.lastName, email: inputs.email}
6667
});
6768
} else {
6869
isPasswordValid = true;
6970
password = sails.helpers.generateToken();
71+
72+
// should probably send password somehow; it will be scrubbed in the custom response
7073
}
7174

7275
if (isPasswordValid !== true) {
@@ -87,6 +90,7 @@ module.exports = {
8790
role: inputs.role,
8891
email: inputs.email
8992
}).meta({fetch: true}).exec((err, newUser) => {
93+
/* istanbul ignore if */
9094
if (err) {
9195
console.error(err);
9296

api/controllers/admin/delete-user.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,6 @@ module.exports = {
1919
},
2020
badRequest: {
2121
responseType: 'badRequest'
22-
},
23-
serverError: {
24-
responseType: 'serverError'
2522
}
2623
},
2724

@@ -32,6 +29,7 @@ module.exports = {
3229

3330
const foundUser = await sails.models.user.findOne({id: inputs.id, deletedAt: null});
3431

32+
/* istanbul ignore if */
3533
if (!foundUser) {
3634
return exits.badRequest('User does not exist');
3735
}

api/controllers/common/get-me.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ module.exports = {
2020
fn: async (inputs, exits, env) => {
2121
const foundUser = await sails.models.user.findOne({id: env.req.session.user.id}); // req.session.user is filled in by the isLoggedIn policy
2222

23+
/* istanbul ignore if */
2324
if (!foundUser) {
2425
// this should not happen
2526
return exits.serverError();

api/controllers/common/login.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,21 +32,28 @@ module.exports = {
3232

3333
fn: async (inputs, exits, env) => {
3434
if (env.req.signedCookies[sails.config.session.name]) {
35-
return exits.badRequest('Already logged in.');
35+
const foundSession = await sails.models.session.findOne({id: env.req.signedCookies[sails.config.session.name]});
36+
37+
if (foundSession) {
38+
return exits.badRequest('Already logged in.');
39+
}
3640
}
3741

3842
const badEmailPass = 'Bad email / password combination.';
3943

44+
/* istanbul ignore if */
4045
if (await sails.helpers.isPasswordValid.with({password: inputs.password, skipPwned: true}) !== true) {
4146
return exits.badRequest(badEmailPass);
4247
}
4348

4449
const foundUser = await sails.models.user.findOne({email: inputs.email, deletedAt: null});
4550

51+
/* istanbul ignore if */
4652
if (!foundUser) {
4753
return exits.badRequest(badEmailPass);
4854
}
4955

56+
/* istanbul ignore if */
5057
if (!await sails.models.user.doPasswordsMatch(inputs.password, foundUser.password)) {
5158
return exits.badRequest(badEmailPass);
5259
}

api/controllers/common/logout.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ module.exports = {
2020
fn: async (inputs, exits, env) => {
2121
const foundSession = await sails.models.session.findOne({id: env.req.session.id});
2222

23+
/* istanbul ignore if */
2324
if (!foundSession) {
25+
// should not happen
2426
return exits.ok();
2527
}
2628

api/helpers/finalize-request-log.js

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -70,16 +70,17 @@ module.exports = {
7070
responseTime: totalTime
7171
};
7272

73-
sails.models.requestlog.update(inputs.req.requestId, log, (err) => {
73+
sails.models.requestlog.update({id: inputs.req.requestId}).set(log).exec((err) => {
7474
/* istanbul ignore if */
7575
if (err) {
76-
console.log(err);
76+
console.error(err);
7777
}
78+
79+
return exits.success();
7880
});
81+
} else {
82+
return exits.success();
7983
}
80-
81-
// All done.
82-
return exits.success();
8384
}
8485
};
8586

api/helpers/is-password-valid.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
const sha1 = require('sha1');
21
const superagent = require('superagent');
2+
const crypto = require('crypto');
33

44
module.exports = {
55
friendlyName: 'Is password valid',
@@ -78,11 +78,11 @@ module.exports = {
7878

7979
if (!errors.length) {
8080
if (sails.config.security.checkPwnedPasswords && !inputs.skipPwned) {
81-
const sha1pass = sha1(inputs.password).toUpperCase();
81+
const sha1pass = crypto.createHash('sha1').update(inputs.password).digest('hex').toUpperCase();
8282
const passChunk1 = sha1pass.substring(0, 5);
8383
const passChunk2 = sha1pass.substring(5);
8484

85-
superagent.get('https://api.pwnedpasswords.com/range/' + passChunk1).end((err, res) => {
85+
superagent.get('https://api.pwnedpasswords.com/range/' + passChunk1).retry(3).end((err, res) => {
8686
/* istanbul ignore if */
8787
if (err) {
8888
console.error(err);

api/helpers/paginate-for-query.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ module.exports = {
3232
fn: (inputs, exits) => {
3333
let baseObj = {
3434
limit: inputs.limit,
35-
page: inputs.page,
35+
page: inputs.page, // this is used to pass to other pagination options; is ignored by Waterline / Sails
3636
skip: (inputs.page - 1) * inputs.limit,
3737
sort: inputs.sort,
3838
where: inputs.where

api/models/Log.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ module.exports = {
2727

2828
createdAt: {
2929
type: 'ref',
30-
columnType: 'datetime',
30+
columnType: 'datetime(3)',
3131
autoCreatedAt: true
3232
},
3333

api/models/RequestLog.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ module.exports = {
8383

8484
createdAt: {
8585
type: 'ref',
86-
columnType: 'datetime',
86+
columnType: 'datetime(3)',
8787
autoCreatedAt: true
8888
},
8989

api/models/User.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
const scrypt = require('scrypt-kdf');
2-
const md5 = require('md5'); // NEVER USE FOR PASSWORDS!
2+
const crypto = require('crypto');
33

44
async function hashPassword(pass) {
55
const hash = await scrypt.kdf(pass, {logN: 15});
@@ -18,7 +18,7 @@ async function updatePassword(password) {
1818
}
1919

2020
function getGravatarUrl(email) {
21-
return 'https://www.gravatar.com/avatar/' + md5(email);
21+
return 'https://www.gravatar.com/avatar/' + crypto.createHash('md5').update(email).digest('hex');
2222
}
2323

2424
function forceUppercaseOnFirst(name) {

0 commit comments

Comments
 (0)