diff --git a/.eslintrc b/.eslintrc
index 87e8323..43b7592 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -192,7 +192,6 @@
],
"react/destructuring-assignment": "off",
"react/forbid-component-props": "off",
- "react/forbid-prop-types": "off",
"react/jsx-handler-names": "off",
"react/jsx-indent-props": "off",
"react/jsx-max-depth": "off",
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ee8ce35..9fbf0e8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,19 @@
# Changelog
+# [v7.0.0](https://github.com/neonexus/sails-react-bootstrap-webpack/compare/v6.1.0...v7.0.0) (2025-01-23)
+### Features
+
+* Updated to React 19.
+ * Rewrote most (nearly all) custom React elements to work with v19 (due to removal of `PropTypes`).
+* Fixed (read: silenced) SASS deprecation warnings in Webpack config.
+* Fixed security audits.
+* Updated dependencies.
+* Set minimum Node version to v22.13.
+
+### Breaking Changes
+
+* Because of the upgrade to React 19, there are fairly major changes to the custom React elements.
+
# [v6.1.0](https://github.com/neonexus/sails-react-bootstrap-webpack/compare/v6.0.1...v6.1.0) (2024-11-18)
### Features
@@ -17,10 +31,13 @@
# [v6.0.0](https://github.com/neonexus/sails-react-bootstrap-webpack/compare/v5.3.4...v6.0.0) (2024-11-06)
### Features
-* Removed unneeded index.jsx.
* Made self-update a little smarter.
* Updated dependencies.
+### Breaking Changes
+
+* Removed unneeded index.jsx.
+
# [v5.3.4](https://github.com/neonexus/sails-react-bootstrap-webpack/compare/v5.3.3...v5.3.4) (2024-05-03)
### Features
diff --git a/README.md b/README.md
index 8d2cfb6..d706256 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,9 @@
# sails-react-bootstrap-webpack
-[](https://sailsjs.com)
-[](https://react.dev)
-[](https://getbootstrap.com)
-[](https://webpack.js.org)
+[](https://sailsjs.com)
+[](https://react.dev)
+[](https://getbootstrap.com)
+[](https://webpack.js.org)
Latest version only:
[](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!
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.
-[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
-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
+
+ For a free / quick option, check out...
+
+[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
+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
extra when you start a trial (trial is NOT needed for the free servers).
+
```shell
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
## Current Dependencies
* [Sails](https://sailsjs.com) **v1**
-* [React](https://react.dev) **v18**
-* [React Router](https://reactrouter.com) **v6**
+* [React](https://react.dev) **v19**
+* [React Router](https://reactrouter.com) **v7**
* [Bootstrap](https://getbootstrap.com) **v5**
* [React-Bootstrap](https://react-bootstrap.github.io) **v2**
* [Webpack](https://webpack.js.org) **v5**
@@ -697,7 +701,16 @@ Start said service:
sudo systemctl start crond.service
```
-Edit the `crontab` to run the script at `@reboot`:
+Edit the `crontab` to run the script at `@reboot` (you don't have to use `nano`, I just find it easier):
+
+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.
+For better security, it should be run via a user with limited permissions.
+
+```shell
+EDITOR=nano crontab -e
+```
+
+Add this:
```shell
@reboot cd myapp; ./tmux.sh
diff --git a/api/controllers/README.md b/api/controllers/README.md
index 8d70c67..659203f 100644
--- a/api/controllers/README.md
+++ b/api/controllers/README.md
@@ -2,4 +2,4 @@
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.
-See: https://sailsjs.com/documentation/concepts/actions-and-controllers
+See: https://sailsjs.com/documentation/concepts/actions-and-controllers#?actions-2
diff --git a/api/controllers/admin/create-user.js b/api/controllers/admin/create-user.js
index 5a73cf0..065737d 100644
--- a/api/controllers/admin/create-user.js
+++ b/api/controllers/admin/create-user.js
@@ -40,7 +40,7 @@ module.exports = {
generatePassword: {
type: 'boolean',
defaultsTo: false,
- description: 'Used to auto-generate a password for the user'
+ description: 'Used to auto-generate a password for the user. Negates the `password` input.'
}
},
diff --git a/assets/src/Admin/AdminRouter.jsx b/assets/src/Admin/AdminRouter.jsx
index 2799681..31ff4e0 100644
--- a/assets/src/Admin/AdminRouter.jsx
+++ b/assets/src/Admin/AdminRouter.jsx
@@ -1,5 +1,4 @@
import {Component, StrictMode, Suspense, lazy} from 'react';
-import PropTypes from 'prop-types';
import '../../styles/admin/admin.scss';
import {
Routes,
@@ -37,11 +36,6 @@ function RenderOrLogin(props) {
return null; // not ready yet
}
-RenderOrLogin.propTypes = {
- api: PropTypes.object.isRequired,
- userContext: PropTypes.object.isRequired
-};
-
const theApi = new api();
class AdminRouter extends Component {
diff --git a/assets/src/Admin/Login.jsx b/assets/src/Admin/Login.jsx
index 67a4c29..5228c43 100644
--- a/assets/src/Admin/Login.jsx
+++ b/assets/src/Admin/Login.jsx
@@ -1,5 +1,4 @@
import { Component } from 'react';
-import PropTypes from 'prop-types';
import {UserConsumer} from '../data/UserContext';
import defaultAPIErrorHandler from '../data/defaultAPIErrorHandler';
@@ -10,6 +9,10 @@ class Login extends Component {
constructor(props) {
super(props);
+ if (!props.api) {
+ throw new Error('No API passed to Login.jsx.');
+ }
+
this.state = {
email: localStorage.getItem('email') || '',
password: '',
@@ -155,8 +158,4 @@ class Login extends Component {
}
}
-Login.propTypes = {
- api: PropTypes.object.isRequired
-};
-
export default Login;
diff --git a/assets/src/Admin/NavBar.jsx b/assets/src/Admin/NavBar.jsx
index 2bd09c3..a0238b7 100644
--- a/assets/src/Admin/NavBar.jsx
+++ b/assets/src/Admin/NavBar.jsx
@@ -1,6 +1,4 @@
import {useState, useContext, useEffect} from 'react';
-import PropTypes from 'prop-types';
-
import {Button, Nav, Navbar, NavDropdown} from 'react-bootstrap';
import {NavLink as ReactNavLink} from 'react-router-dom';
@@ -23,7 +21,13 @@ function scrollListener() {
}
}
-function NavBar(props) {
+function NavBar({handleLogout = null}) {
+ if (typeof handleLogout !== 'function') {
+ console.error('`handleLogout` is a required function for NavBar.');
+
+ return 'ERROR!';
+ }
+
const [isExpanded, setIsExpanded] = useState(false);
const user = useContext(UserContext);
@@ -110,7 +114,7 @@ function NavBar(props) {
user.isLoggedIn ?
<>
Welcome, {user.info.firstName}
-
+
>
: null
}
@@ -125,8 +129,4 @@ function NavBar(props) {
);
}
-NavBar.propTypes = {
- handleLogout: PropTypes.func.isRequired
-};
-
export default NavBar;
diff --git a/assets/src/Admin/Settings/ChangePasswordModal.jsx b/assets/src/Admin/Settings/ChangePasswordModal.jsx
index 953d86a..a2cd410 100644
--- a/assets/src/Admin/Settings/ChangePasswordModal.jsx
+++ b/assets/src/Admin/Settings/ChangePasswordModal.jsx
@@ -1,16 +1,38 @@
-import { useState } from 'react';
-import PropTypes from 'prop-types';
-
+import {useState} from 'react';
import {Button, Form, Modal, Spinner} from 'react-bootstrap';
-function ChangePasswordModal(props) {
+/**
+ * Change Password Modal
+ *
+ * @param {object} api
+ * @param {function} onCancel
+ * @param {function} onUpdate
+ * @param {boolean} show
+ *
+ * @returns {JSX.Element}
+ */
+function ChangePasswordModal({api, onCancel, onUpdate, show}) {
+ if (api === undefined || onCancel === undefined || onUpdate === undefined || show === undefined) {
+ throw new Error('`api`, `onCancel`, `onUpdate`, and `show` are required parameters for ChangePasswordModal.');
+ }
+
+ if (typeof onCancel !== 'function') {
+ throw new Error('`onCancel` must be a function in ChangePasswordModal.');
+ }
+
+ if (typeof onUpdate !== 'function') {
+ throw new Error('`onUpdate` must be a function in ChangePasswordModal.');
+ }
+
+ if (typeof show !== 'boolean') {
+ throw new Error('`show` must be a boolean in ChangePasswordModal.');
+ }
+
const [isLoading, setIsLoading] = useState(false);
const [current, setCurrent] = useState('');
const [newPass, setNewPass] = useState('');
const [confPass, setConfPass] = useState('');
-
-
function resetState() {
setCurrent('');
setNewPass('');
@@ -18,16 +40,16 @@ function ChangePasswordModal(props) {
setIsLoading(false);
}
- function onCancel() {
+ function onCancelHandler() {
resetState();
- props.onCancel();
+ onCancel();
}
- function onUpdate() {
+ function onUpdateHandler() {
setIsLoading(true);
- props.api.post({
+ api.post({
url: '/password',
body: {
currentPassword: current,
@@ -37,7 +59,7 @@ function ChangePasswordModal(props) {
}, (body) => {
setIsLoading(false);
alert('Password updated successfully.');
- props.onUpdate();
+ onUpdate();
resetState();
}, (err, body) => {
console.error(err);
@@ -47,8 +69,8 @@ function ChangePasswordModal(props) {
}
return (
-
-