diff --git a/Client/src/Utils/NetworkService.js b/Client/src/Utils/NetworkService.js index cb43a3d93..e6ece1079 100644 --- a/Client/src/Utils/NetworkService.js +++ b/Client/src/Utils/NetworkService.js @@ -1,16 +1,22 @@ import axios from "axios"; const BASE_URL = import.meta.env.VITE_APP_API_BASE_URL; const FALLBACK_BASE_URL = "http://localhost:5000/api/v1"; +import { clearAuthState } from "../Features/Auth/authSlice"; +import { clearUptimeMonitorState } from "../Features/UptimeMonitors/uptimeMonitorsSlice"; import { logger } from "./Logger"; class NetworkService { - constructor(store) { + constructor(store, dispatch, navigate) { this.store = store; + this.dispatch = dispatch; + this.navigate = navigate; let baseURL = BASE_URL; this.axiosInstance = axios.create(); this.setBaseUrl(baseURL); this.unsubscribe = store.subscribe(() => { const state = store.getState(); - if (BASE_URL === undefined && state.settings.apiBaseUrl) { + if (BASE_URL !== undefined) { + baseURL = BASE_URL; + } else if (state?.settings?.apiBaseUrl ?? null) { baseURL = state.settings.apiBaseUrl; } else { baseURL = FALLBACK_BASE_URL; @@ -20,9 +26,10 @@ class NetworkService { this.axiosInstance.interceptors.response.use( (response) => response, (error) => { - logger.error(error); if (error.response && error.response.status === 401) { - logger.error("Invalid token received"); + dispatch(clearAuthState()); + dispatch(clearUptimeMonitorState()); + navigate("/login"); } return Promise.reject(error); } @@ -750,3 +757,10 @@ class NetworkService { } export default NetworkService; + +let networkService; + +export const setNetworkService = (service) => { + networkService = service; +}; +export { networkService }; diff --git a/Client/src/Utils/NetworkServiceProvider.jsx b/Client/src/Utils/NetworkServiceProvider.jsx new file mode 100644 index 000000000..4c66681e0 --- /dev/null +++ b/Client/src/Utils/NetworkServiceProvider.jsx @@ -0,0 +1,15 @@ +import { useDispatch } from "react-redux"; +import { useNavigate } from "react-router"; +import { setNetworkService } from "./NetworkService"; +import NetworkService from "./NetworkService"; +import { store } from "../store"; + +const NetworkServiceProvider = ({ children }) => { + const dispatch = useDispatch(); + const navigate = useNavigate(); + const networkService = new NetworkService(store, dispatch, navigate); + setNetworkService(networkService); + return children; +}; + +export default NetworkServiceProvider; diff --git a/Client/src/main.jsx b/Client/src/main.jsx index d592f9137..4c04a65eb 100644 --- a/Client/src/main.jsx +++ b/Client/src/main.jsx @@ -1,18 +1,21 @@ -import React from "react"; import ReactDOM from "react-dom/client"; import App from "./App.jsx"; import "./index.css"; -import { BrowserRouter as Router, HashRouter } from "react-router-dom"; +import { BrowserRouter as Router } from "react-router-dom"; import { Provider } from "react-redux"; import { persistor, store } from "./store"; import { PersistGate } from "redux-persist/integration/react"; -import NetworkService from "./Utils/NetworkService.js"; -export const networkService = new NetworkService(store); +import NetworkServiceProvider from "./Utils/NetworkServiceProvider.jsx"; +import { networkService } from "./Utils/NetworkService"; +export { networkService }; + ReactDOM.createRoot(document.getElementById("root")).render( - + + + diff --git a/README.md b/README.md index 9f5aa5a78..52a12b28e 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,15 @@ BlueWave Uptime is an open source server monitoring application used to track th See [BlueWave Uptime](https://uptime-demo.bluewavelabs.ca/) in action. The username is uptimedemo@demo.com and the password is Demouser1! -## Questions & Ideas +## User's guide + +Usage instructions can be found [here](https://bluewavelabs.gitbook.io/uptime-manager). + +## Installation + +See installation instructions in [Uptime Manager documentation portal](https://bluewavelabs.gitbook.io/uptime-manager/quickstart). + +## Questions & ideas We've just launched our [Discussions](https://github.com/bluewave-labs/bluewave-uptime/discussions) page! Feel free to ask questions or share your ideas—we'd love to hear from you! @@ -78,215 +86,3 @@ Also check other developer and contributor-friendly projects of BlueWave: - [BlueWave DataRoom](https://github.com/bluewave-labs/bluewave-dataroom) - [VerifyWise](https://github.com/bluewave-labs/verifywise) -## Getting Started - -- Clone this repository to your local machine - -1. [Quickstart for Users](#user-quickstart) -2. [Quickstart for Developers](#dev-quickstart) -3. [Manual Install](#manual-install) - - - [Install Client](#install-client) - - [Environment](#env-vars-client) - - [Start Client](#start-client) - - [Install Server](#install-server) - - [Environment](#env-vars-server) - - [Database](#databases) - - [(Optional) Dockerised Databases](#optional-docker-databases) - - [Start Server](#start-server) - -4. [API Documentation](#api-documentation) -5. [Error Handling](#error-handling) -6. [Contributors](#contributors) - ---- - -## Quickstart for Users - -1. Download our [Docker Compose File](/Docker/dist/docker-compose.yaml) -2. Run `docker compose up` to start the application -3. Application is running at `http://localhost` - -## Quickstart for Developers - -MAKE SURE YOU CD TO THE SPECIFIED DIRECTORIES AS PATHS IN COMMANDS ARE RELATIVE - -### Cloning and Initial Setup - -1. Clone this repository -2. Checkout the `develop` branch `git checkout develop` - -### Docker Images Setup - -3. CD to the `Docker/dev` directory -4. Run `docker run -d -p 6379:6379 -v $(pwd)/redis/data:/data --name uptime_redis uptime_redis` -5. Run `docker run -d -p 27017:27017 -v $(pwd)/mongo/data:/data/db --name uptime_database_mongo uptime_database_mongo` - -### Server Setup - -6. CD to `Server` directory, run `npm install` -7. While in `Server` directory, create a `.env` file with the [required environmental variables](#env-vars-server) -8. While in the `Server` directory, run `npm run dev` - -### Client Setup - -9. CD to `Client` directory `run npm install` -10. While in the `Client` directory, create a `.env` file with the [required environmental variables](#env-vars-client) -11. While in the `Client` cirectory run `npm run dev` - -### Access Application - -12. Client is running at `localhost:5173` -13. Server is running at `localhost:5000` - ---- - -## Manual Install - ---- - -### Client Installation - -1. Change directory to the `Client` directory -2. Install all dependencies by running `npm install` - ---- - -### Environmental Variables - -| ENV Variable Name | Required/Optional | Type | Description | Accepted Values | -| --------------------- | ----------------- | --------- | ------------------ | ---------------------------------- | -| VITE_APP_API_BASE_URL | Required | `string` | Base URL of server | {host}/api/v1 | -| VITE_APP_LOG_LEVEL | Optional | `string` | Log level | `"none"`\|`"error"` \| `"warn"` \| | -| VITE_APP_DEMO | Optional | `boolean` | Demo server or not | `true`\|`false` \| | - ---- - -### Starting Development Server - -1. Run `npm run dev` to start the development server. - ---- - -### Server Installation - -1. Change directory to the `Server` directory -2. Install all dependencies by running `npm install` - ---- - -### Environmental Variables - -Configure the server with the following environmental variables: - -| ENV Variable Name | Required/Optional | Type | Description | Accepted Values | -| --------------------- | ----------------- | --------- | ---------------------------------------- | ------------------------------------------------ | -| CLIENT_HOST | Required | `string` | Frontend Host | | -| JWT_SECRET | Required | `string` | JWT secret | | -| DB_TYPE | Optional | `string` | Specify DB to use | `MongoDB \| FakeDB` | -| DB_CONNECTION_STRING | Required | `string` | Specifies URL for MongoDB Database | | -| PORT | Optional | `integer` | Specifies Port for Server | | -| LOGIN_PAGE_URL | Required | `string` | Login url to be used in emailing service | | -| REDIS_HOST | Required | `string` | Host address for Redis database | | -| REDIS_PORT | Required | `integer` | Port for Redis database | | -| TOKEN_TTL | Optional | `string` | Time for token to live | In vercel/ms format https://github.com/vercel/ms | -| PAGESPEED_API_KEY | Optional | `string` | API Key for PageSpeed requests | | -| SYSTEM_EMAIL_HOST | Required | `string` | Host to send System Emails From | | -| SYSTEM_EMAIL_PORT | Required | `number` | Port for System Email Host | | -| SYSTEM_EMAIL_ADDRESS | Required | `string` | System Email Address | | -| SYSTEM_EMAIL_PASSWORD | Required | `string` | System Email Password | | - ---- - -### Databases - -This project requires two databases: - -1. **Main Application Database:** The project uses MongoDB for its primary database, with a MongoDB Docker image provided for easy setup. -2. **Redis for Queue Management:** A Redis database is used for the PingService’s queue system, and a Redis Docker image is included for deployment. - -You may use the included Dockerfiles to spin up databases quickly if you wish. - -#### (Optional) Dockerised Databases - -Dockerfiles for the server and databases are located in the `Docker` directory - -
-MongoDB Image - -Location: `Docker/mongoDB.Dockerfile` - -The `Docker/mongo/data` directory should be mounted to the MongoDB container in order to persist data. - -From the `Docker` directory run - -1. Build the image: `docker build -f mongoDB.Dockerfile -t uptime_database_mongo .` -2. Run the docker image: `docker run -d -p 27017:27017 -v $(pwd)/mongo/data:/data/db --name uptime_database_mongo uptime_database_mongo` - -
-
-Redis Image - -Location `Docker/redis.Dockerfile` - -the `Docker/redis/data` directory should be mounted to the Redis container in order to persist data. - -From the `Docker` directory run - -1. Build the image: `docker build -f redis.Dockerfile -t uptime_redis .` -2. Run the image: `docker run -d -p 6379:6379 -v $(pwd)/redis/data:/data --name uptime_redis uptime_redis` -
- ---- - -### Starting the Development Server - -- run `npm run dev` to start the development server - -OR - -- run `node index.js` to start server - ---- - -## API Documentation - -Our API is documented in accordance with the [OpenAPI spec](https://www.openapis.org/). - -You can see the documentation on your local development server at [http://localhost:{port}/api-docs](htt>>p://localhost:5000/api-docs) - -You can also view the documentation on our demo server at [https://uptime-demo.bluewavelabs.ca/api-docs](https://uptime-demo.bluewavelabs.ca/api-docs) - -## Error handling - -Errors are returned in a standard format: - -`{"success": false, "msg": "No token provided"}` - -Errors are handled by error handling middleware and should be thrown with the following parameters - -| Name | Type | Default | Notes | -| ------- | --------- | ---------------------- | ------------------------------------ | -| status | `integer` | 500 | Standard HTTP codes | -| message | `string` | "Something went wrong" | An error message | -| service | `string` | "Unknown Service" | Name of service that threw the error | - -Example: - -``` -const myRoute = async(req, res, next) => { - try{ - const result = myRiskyOperationHere(); - } - catch(error){ - error.status = 404 - error.message = "Resource not found" - error.service = service name - next(error) - return; - } -} - -``` - -Errors should not be handled at the controller level and should be left to the middleware to handle. diff --git a/Server/middleware/verifyJWT.js b/Server/middleware/verifyJWT.js index 8e2e88b6f..fa1fc3afa 100644 --- a/Server/middleware/verifyJWT.js +++ b/Server/middleware/verifyJWT.js @@ -38,6 +38,11 @@ const verifyJWT = (req, res, next) => { const { jwtSecret } = req.settingsService.getSettings(); jwt.verify(parsedToken, jwtSecret, (err, decoded) => { if (err) { + if (err.name === "TokenExpiredError") { + res + .status(401) + .json({ success: false, msg: errorMessages.EXPIRED_AUTH_TOKEN }); + } return res .status(401) .json({ success: false, msg: errorMessages.INVALID_AUTH_TOKEN }); diff --git a/Server/utils/messages.js b/Server/utils/messages.js index e05602552..47e3061b5 100644 --- a/Server/utils/messages.js +++ b/Server/utils/messages.js @@ -12,6 +12,7 @@ const errorMessages = { UNKNOWN_SERVICE: "Unknown service", NO_AUTH_TOKEN: "No auth token provided", INVALID_AUTH_TOKEN: "Invalid auth token", + EXPIRED_AUTH_TOKEN: "Token expired", //Ownership Middleware VERIFY_OWNER_NOT_FOUND: "Document not found",