Skip to content

Commit 95c09d0

Browse files
authored
v7.0.0
Rewrote everything for React 19. Dep updates.
2 parents 1747059 + cda0c67 commit 95c09d0

31 files changed

+2041
-1241
lines changed

.eslintrc

-1
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,6 @@
192192
],
193193
"react/destructuring-assignment": "off",
194194
"react/forbid-component-props": "off",
195-
"react/forbid-prop-types": "off",
196195
"react/jsx-handler-names": "off",
197196
"react/jsx-indent-props": "off",
198197
"react/jsx-max-depth": "off",

CHANGELOG.md

+18-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,19 @@
11
# Changelog
22

3+
# [v7.0.0](https://github.com/neonexus/sails-react-bootstrap-webpack/compare/v6.1.0...v7.0.0) (2025-01-23)
4+
### Features
5+
6+
* Updated to React 19.
7+
* Rewrote most (nearly all) custom React elements to work with v19 (due to removal of `PropTypes`).
8+
* Fixed (read: silenced) SASS deprecation warnings in Webpack config.
9+
* Fixed security audits.
10+
* Updated dependencies.
11+
* Set minimum Node version to v22.13.
12+
13+
### Breaking Changes
14+
15+
* Because of the upgrade to React 19, there are fairly major changes to the custom React elements.
16+
317
# [v6.1.0](https://github.com/neonexus/sails-react-bootstrap-webpack/compare/v6.0.1...v6.1.0) (2024-11-18)
418
### Features
519

@@ -17,10 +31,13 @@
1731
# [v6.0.0](https://github.com/neonexus/sails-react-bootstrap-webpack/compare/v5.3.4...v6.0.0) (2024-11-06)
1832
### Features
1933

20-
* Removed unneeded index.jsx.
2134
* Made self-update a little smarter.
2235
* Updated dependencies.
2336

37+
### Breaking Changes
38+
39+
* Removed unneeded index.jsx.
40+
2441
# [v5.3.4](https://github.com/neonexus/sails-react-bootstrap-webpack/compare/v5.3.3...v5.3.4) (2024-05-03)
2542
### Features
2643

README.md

+22-9
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
# sails-react-bootstrap-webpack
22

3-
[![Sails version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fneonexus%2Fsails-react-bootstrap-webpack%2Fv6.1.0%2Fpackage.json&query=%24.dependencies.sails&label=Sails&logo=sailsdotjs)](https://sailsjs.com)
4-
[![React version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fneonexus%2Fsails-react-bootstrap-webpack%2Fv6.1.0%2Fpackage.json&query=%24.devDependencies.react&label=React&logo=react)](https://react.dev)
5-
[![Bootstrap version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fneonexus%2Fsails-react-bootstrap-webpack%2Fv6.1.0%2Fpackage.json&query=%24.devDependencies.bootstrap&label=Bootstrap&logo=bootstrap&logoColor=white)](https://getbootstrap.com)
6-
[![Webpack version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fneonexus%2Fsails-react-bootstrap-webpack%2Fv6.1.0%2Fpackage.json&query=%24.devDependencies.webpack&label=Webpack&logo=webpack)](https://webpack.js.org)
3+
[![Sails version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fneonexus%2Fsails-react-bootstrap-webpack%2Fv7.0.0%2Fpackage.json&query=%24.dependencies.sails&label=Sails&logo=sailsdotjs)](https://sailsjs.com)
4+
[![React version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fneonexus%2Fsails-react-bootstrap-webpack%2Fv7.0.0%2Fpackage.json&query=%24.devDependencies.react&label=React&logo=react)](https://react.dev)
5+
[![Bootstrap version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fneonexus%2Fsails-react-bootstrap-webpack%2Fv7.0.0%2Fpackage.json&query=%24.devDependencies.bootstrap&label=Bootstrap&logo=bootstrap&logoColor=white)](https://getbootstrap.com)
6+
[![Webpack version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fneonexus%2Fsails-react-bootstrap-webpack%2Fv7.0.0%2Fpackage.json&query=%24.devDependencies.webpack&label=Webpack&logo=webpack)](https://webpack.js.org)
77

88
Latest version only:
99
[![FOSSA License Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fneonexus%2Fsails-react-bootstrap-webpack.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2Fneonexus%2Fsails-react-bootstrap-webpack?ref=badge_shield)
@@ -21,9 +21,13 @@ A virtual start-up in a box!
2121

2222
NOTE: You will need access to a MySQL / MariaDB database for the quick setup. If you want to use a different datastore, you'll need to configure it manually.
2323

24-
[Aiven.io](https://aiven.io) has FREE (no CC required) secure MySQL (5 GB), and Redis (1 GB). Both require use of SSL, and can be restricted to specified IPs. (If you are having trouble finding the
25-
FREE instances, you need to select Digital Ocean as the cloud provider.) Use my [referral link](https://console.aiven.io/signup?referral_code=mk36ekt3wo1dvij7joon) to signup, and you'll get $100
24+
<details>
25+
<summary>For a free / quick option, check out...</summary>
26+
27+
[Aiven.io](https://aiven.io) has a no credit card required, secure MySQL (5 GB), and Redis (1 GB). Both require use of SSL, and can be restricted to specified IPs. (If you are having trouble finding
28+
the FREE instances, you need to select Digital Ocean as the cloud provider.) Use my [referral link](https://console.aiven.io/signup?referral_code=mk36ekt3wo1dvij7joon) to signup, and you'll get $100
2629
extra when you start a trial (trial is NOT needed for the free servers).
30+
</details>
2731

2832
```shell
2933
npx drfg neonexus/sails-react-bootstrap-webpack my-new-site
@@ -100,8 +104,8 @@ The `master` branch is experimental, and the [release branch](https://github.com
100104
## Current Dependencies
101105

102106
* [Sails](https://sailsjs.com) **v1**
103-
* [React](https://react.dev) **v18**
104-
* [React Router](https://reactrouter.com) **v6**
107+
* [React](https://react.dev) **v19**
108+
* [React Router](https://reactrouter.com) **v7**
105109
* [Bootstrap](https://getbootstrap.com) **v5**
106110
* [React-Bootstrap](https://react-bootstrap.github.io) **v2**
107111
* [Webpack](https://webpack.js.org) **v5**
@@ -697,7 +701,16 @@ Start said service:
697701
sudo systemctl start crond.service
698702
```
699703

700-
Edit the `crontab` to run the script at `@reboot`:
704+
Edit the `crontab` to run the script at `@reboot` (you don't have to use `nano`, I just find it easier):
705+
706+
Also, notice to lack of `sudo` here. This is because we DON'T want our backend to have super-user powers! That just, would not be good... We want it running under a general user.
707+
For better security, it should be run via a user with limited permissions.
708+
709+
```shell
710+
EDITOR=nano crontab -e
711+
```
712+
713+
Add this:
701714

702715
```shell
703716
@reboot cd myapp; ./tmux.sh

api/controllers/README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22

33
Here is where all of our API actions live. A controller in this context is a folder, and an action of the controller is an individual file. Each action is using the new "actions2" style, as opposed to the classic.
44

5-
See: https://sailsjs.com/documentation/concepts/actions-and-controllers
5+
See: https://sailsjs.com/documentation/concepts/actions-and-controllers#?actions-2

api/controllers/admin/create-user.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ module.exports = {
4040
generatePassword: {
4141
type: 'boolean',
4242
defaultsTo: false,
43-
description: 'Used to auto-generate a password for the user'
43+
description: 'Used to auto-generate a password for the user. Negates the `password` input.'
4444
}
4545
},
4646

assets/src/Admin/AdminRouter.jsx

-6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import {Component, StrictMode, Suspense, lazy} from 'react';
2-
import PropTypes from 'prop-types';
32
import '../../styles/admin/admin.scss';
43
import {
54
Routes,
@@ -37,11 +36,6 @@ function RenderOrLogin(props) {
3736
return null; // not ready yet
3837
}
3938

40-
RenderOrLogin.propTypes = {
41-
api: PropTypes.object.isRequired,
42-
userContext: PropTypes.object.isRequired
43-
};
44-
4539
const theApi = new api();
4640

4741
class AdminRouter extends Component {

assets/src/Admin/Login.jsx

+4-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { Component } from 'react';
2-
import PropTypes from 'prop-types';
32

43
import {UserConsumer} from '../data/UserContext';
54
import defaultAPIErrorHandler from '../data/defaultAPIErrorHandler';
@@ -10,6 +9,10 @@ class Login extends Component {
109
constructor(props) {
1110
super(props);
1211

12+
if (!props.api) {
13+
throw new Error('No API passed to Login.jsx.');
14+
}
15+
1316
this.state = {
1417
email: localStorage.getItem('email') || '',
1518
password: '',
@@ -155,8 +158,4 @@ class Login extends Component {
155158
}
156159
}
157160

158-
Login.propTypes = {
159-
api: PropTypes.object.isRequired
160-
};
161-
162161
export default Login;

assets/src/Admin/NavBar.jsx

+8-8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
import {useState, useContext, useEffect} from 'react';
2-
import PropTypes from 'prop-types';
3-
42
import {Button, Nav, Navbar, NavDropdown} from 'react-bootstrap';
53
import {NavLink as ReactNavLink} from 'react-router-dom';
64

@@ -23,7 +21,13 @@ function scrollListener() {
2321
}
2422
}
2523

26-
function NavBar(props) {
24+
function NavBar({handleLogout = null}) {
25+
if (typeof handleLogout !== 'function') {
26+
console.error('`handleLogout` is a required function for NavBar.');
27+
28+
return 'ERROR!';
29+
}
30+
2731
const [isExpanded, setIsExpanded] = useState(false);
2832
const user = useContext(UserContext);
2933

@@ -110,7 +114,7 @@ function NavBar(props) {
110114
user.isLoggedIn ?
111115
<>
112116
<Navbar.Text>Welcome, {user.info.firstName} &nbsp; &nbsp; </Navbar.Text>
113-
<Button variant="danger" onClick={() => props.handleLogout()} size="sm" className="mt-2 mt-sm-0 mb-2 mb-sm-0">Logout</Button>
117+
<Button variant="danger" onClick={() => handleLogout()} size="sm" className="mt-2 mt-sm-0 mb-2 mb-sm-0">Logout</Button>
114118
</>
115119
: null
116120
}
@@ -125,8 +129,4 @@ function NavBar(props) {
125129
);
126130
}
127131

128-
NavBar.propTypes = {
129-
handleLogout: PropTypes.func.isRequired
130-
};
131-
132132
export default NavBar;

assets/src/Admin/Settings/ChangePasswordModal.jsx

+36-21
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,55 @@
1-
import { useState } from 'react';
2-
import PropTypes from 'prop-types';
3-
1+
import {useState} from 'react';
42
import {Button, Form, Modal, Spinner} from 'react-bootstrap';
53

6-
function ChangePasswordModal(props) {
4+
/**
5+
* Change Password Modal
6+
*
7+
* @param {object} api
8+
* @param {function} onCancel
9+
* @param {function} onUpdate
10+
* @param {boolean} show
11+
*
12+
* @returns {JSX.Element}
13+
*/
14+
function ChangePasswordModal({api, onCancel, onUpdate, show}) {
15+
if (api === undefined || onCancel === undefined || onUpdate === undefined || show === undefined) {
16+
throw new Error('`api`, `onCancel`, `onUpdate`, and `show` are required parameters for ChangePasswordModal.');
17+
}
18+
19+
if (typeof onCancel !== 'function') {
20+
throw new Error('`onCancel` must be a function in ChangePasswordModal.');
21+
}
22+
23+
if (typeof onUpdate !== 'function') {
24+
throw new Error('`onUpdate` must be a function in ChangePasswordModal.');
25+
}
26+
27+
if (typeof show !== 'boolean') {
28+
throw new Error('`show` must be a boolean in ChangePasswordModal.');
29+
}
30+
731
const [isLoading, setIsLoading] = useState(false);
832
const [current, setCurrent] = useState('');
933
const [newPass, setNewPass] = useState('');
1034
const [confPass, setConfPass] = useState('');
1135

12-
13-
1436
function resetState() {
1537
setCurrent('');
1638
setNewPass('');
1739
setConfPass('');
1840
setIsLoading(false);
1941
}
2042

21-
function onCancel() {
43+
function onCancelHandler() {
2244
resetState();
2345

24-
props.onCancel();
46+
onCancel();
2547
}
2648

27-
function onUpdate() {
49+
function onUpdateHandler() {
2850
setIsLoading(true);
2951

30-
props.api.post({
52+
api.post({
3153
url: '/password',
3254
body: {
3355
currentPassword: current,
@@ -37,7 +59,7 @@ function ChangePasswordModal(props) {
3759
}, (body) => {
3860
setIsLoading(false);
3961
alert('Password updated successfully.');
40-
props.onUpdate();
62+
onUpdate();
4163
resetState();
4264
}, (err, body) => {
4365
console.error(err);
@@ -47,8 +69,8 @@ function ChangePasswordModal(props) {
4769
}
4870

4971
return (
50-
<Modal show={props.show} backdrop="static" size="md">
51-
<Form onSubmit={(e) => {e.preventDefault(); onUpdate();}}>
72+
<Modal show={show} backdrop="static" size="md">
73+
<Form onSubmit={(e) => {e.preventDefault(); onUpdateHandler();}}>
5274
<Modal.Header>
5375
<Modal.Title>
5476
Change Password
@@ -68,7 +90,7 @@ function ChangePasswordModal(props) {
6890
</Modal.Body>
6991

7092
<Modal.Footer className="justify-content-between">
71-
<Button variant="secondary" onClick={onCancel} disabled={isLoading}>Cancel</Button>
93+
<Button variant="secondary" onClick={onCancelHandler} disabled={isLoading}>Cancel</Button>
7294
<Button variant="primary" type="submit" disabled={isLoading}>
7395
{
7496
(isLoading)
@@ -82,11 +104,4 @@ function ChangePasswordModal(props) {
82104
);
83105
}
84106

85-
ChangePasswordModal.propTypes = {
86-
api: PropTypes.object.isRequired,
87-
onCancel: PropTypes.func.isRequired,
88-
onUpdate: PropTypes.func.isRequired,
89-
show: PropTypes.bool.isRequired
90-
};
91-
92107
export default ChangePasswordModal;

assets/src/Admin/Settings/Disable2FAModal.jsx

+20-15
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,20 @@
11
import {Button, Form, Modal} from 'react-bootstrap';
2-
import PropTypes from 'prop-types';
32
import {useState} from 'react';
43

5-
function Disable2FAModal(props) {
4+
function Disable2FAModal({api, onCancel, onDisable, show}) {
5+
if (!api || typeof api.post !== 'function') {
6+
throw new Error("Invalid or missing 'api' prop. Expected an object with a 'post' method.");
7+
}
8+
if (!onCancel || typeof onCancel !== 'function') {
9+
throw new Error("Invalid or missing 'onCancel' prop. Expected a function.");
10+
}
11+
if (!onDisable || typeof onDisable !== 'function') {
12+
throw new Error("Invalid or missing 'onDisable' prop. Expected a function.");
13+
}
14+
if (typeof show !== 'boolean') {
15+
throw new Error("Invalid or missing 'show' prop. Expected a boolean.");
16+
}
17+
618
const [isLoading, setIsLoading] = useState(false);
719
const [otp, setOTP] = useState('');
820
const [password, setPassword] = useState('');
@@ -13,9 +25,9 @@ function Disable2FAModal(props) {
1325
setIsLoading(false);
1426
}
1527

16-
function onCancel() {
28+
function handleCancel() {
1729
resetState();
18-
props.onCancel();
30+
onCancel();
1931
}
2032

2133
function disable() {
@@ -25,7 +37,7 @@ function Disable2FAModal(props) {
2537

2638
setIsLoading(true);
2739

28-
props.api.post({
40+
api.post({
2941
url: '/2fa/disable',
3042
body: {
3143
password,
@@ -35,7 +47,7 @@ function Disable2FAModal(props) {
3547
if (body.success) {
3648
alert('2-factor authentication has been disabled.');
3749
resetState();
38-
props.onDisable();
50+
onDisable();
3951
} else {
4052
alert('Unknown error.');
4153
console.error(body);
@@ -48,7 +60,7 @@ function Disable2FAModal(props) {
4860
}
4961

5062
return (
51-
<Modal show={props.show} backdrop="static">
63+
<Modal show={show} backdrop="static">
5264
<Modal.Header>
5365
<Modal.Title>
5466
Disable 2-Factor Authentication
@@ -86,18 +98,11 @@ function Disable2FAModal(props) {
8698
</Modal.Body>
8799

88100
<Modal.Footer className="justify-content-between">
89-
<Button variant="secondary" onClick={onCancel} disabled={isLoading}>Cancel</Button>
101+
<Button variant="secondary" onClick={handleCancel} disabled={isLoading}>Cancel</Button>
90102
<Button variant="danger" onClick={disable} disabled={isLoading}>Disable</Button>
91103
</Modal.Footer>
92104
</Modal>
93105
);
94106
}
95107

96-
Disable2FAModal.propTypes = {
97-
api: PropTypes.object.isRequired,
98-
onCancel: PropTypes.func.isRequired,
99-
onDisable: PropTypes.func.isRequired,
100-
show: PropTypes.bool.isRequired
101-
};
102-
103108
export default Disable2FAModal;

0 commit comments

Comments
 (0)