diff --git a/Client/src/Features/Settings/settingsSlice.js b/Client/src/Features/Settings/settingsSlice.js
new file mode 100644
index 000000000..0a7cb52c1
--- /dev/null
+++ b/Client/src/Features/Settings/settingsSlice.js
@@ -0,0 +1,119 @@
+import { networkService } from "../../main";
+import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
+import { jwtDecode } from "jwt-decode";
+import axios from "axios";
+
+const initialState = {
+ isLoading: false,
+ apiBaseUrl: "http://localhost:5000/api/v1",
+ logLevel: "debug",
+ clientHost: "http://localhost:5173",
+ jwtSecret: "my_secret",
+ dbType: "MongoDB",
+ dbConnectionString: "mongodb://localhost:27017/uptime_db",
+ redisHost: "127.0.0.1",
+ redisPort: 6379,
+ jwtTTL: "99d",
+ pagespeedApiKey: "",
+ systemEmailHost: "smtp.gmail.com",
+ systemEmailPort: 465,
+ systemEmailAddress: "",
+ systemEmailPassword: "",
+};
+
+export const getAppSettings = createAsyncThunk(
+ "settings/getSettings",
+ async (data, thunkApi) => {
+ try {
+ const res = await networkService.getAppSettings({
+ authToken: data.authToken,
+ });
+ return res.data;
+ } catch (error) {
+ if (error.response.data) {
+ return thunkApi.rejectWithValue(error.response.data);
+ }
+ const payload = {
+ status: false,
+ msg: error.message ? error.message : "Unknown error",
+ };
+ return thunkApi.rejectWithValue(payload);
+ }
+ }
+);
+
+export const updateAppSettings = createAsyncThunk(
+ "settings/updateSettings",
+ async (form, thunkApi) => {
+ try {
+ const res = await networkService.loginUser(form);
+ return res.data;
+ } catch (error) {
+ if (error.response && error.response.data) {
+ return thunkApi.rejectWithValue(error.response.data);
+ }
+ const payload = {
+ status: false,
+ msg: error.message ? error.message : "Unknown error",
+ };
+ return thunkApi.rejectWithValue(payload);
+ }
+ }
+);
+
+const handleGetSettingsFulfilled = (state, action) => {
+ state.isLoading = false;
+ state.success = action.payload.success;
+ state.msg = action.payload.msg;
+};
+const handleGetSettingsRejected = (state, action) => {
+ state.isLoading = false;
+ state.success = false;
+ state.msg = action.payload ? action.payload.msg : "Failed to get settings.";
+};
+const handleUpdateSettingsFulfilled = (state, action) => {
+ state.isLoading = false;
+ state.success = action.payload.success;
+ state.msg = action.payload.msg;
+};
+const handleUpdateSettingsRejected = (state, action) => {
+ state.isLoading = false;
+ state.success = false;
+ state.msg = action.payload
+ ? action.payload.msg
+ : "Failed to update settings.";
+};
+
+const settingsSlice = createSlice({
+ name: "settings",
+ initialState,
+ reducers: {
+ clearAuthState: (state) => {
+ state.authToken = "";
+ state.user = "";
+ state.isLoading = false;
+ state.success = true;
+ state.msg = "Logged out successfully";
+ },
+ },
+ extraReducers: (builder) => {
+ // Register thunk
+ builder
+ .addCase(getAppSettings.pending, (state) => {
+ state.isLoading = true;
+ })
+ .addCase(getAppSettings.fulfilled, handleGetSettingsFulfilled)
+ .addCase(getAppSettings.rejected, handleGetSettingsRejected);
+
+ // Login thunk
+ builder
+ .addCase(updateAppSettings.pending, (state) => {
+ state.isLoading = true;
+ })
+ .addCase(updateAppSettings.fulfilled, handleUpdateSettingsFulfilled)
+ .addCase(updateAppSettings.rejected, handleUpdateSettingsRejected);
+ },
+});
+
+export default settingsSlice.reducer;
+export const { clearAuthState } = settingsSlice.actions;
diff --git a/Client/src/Pages/Settings/index.jsx b/Client/src/Pages/Settings/index.jsx
index 2e59c8187..4a7ed13a9 100644
--- a/Client/src/Pages/Settings/index.jsx
+++ b/Client/src/Pages/Settings/index.jsx
@@ -13,11 +13,12 @@ import {
deleteAllMonitors,
} from "../../Features/UptimeMonitors/uptimeMonitorsSlice";
import { update } from "../../Features/Auth/authSlice";
+import { getAppSettings } from "../../Features/Settings/settingsSlice";
import PropTypes from "prop-types";
import LoadingButton from "@mui/lab/LoadingButton";
import { setTimezone } from "../../Features/UI/uiSlice";
import timezones from "../../Utils/timezones.json";
-import { useState } from "react";
+import { useEffect, useState } from "react";
import { ConfigBox } from "./styled";
import { networkService } from "../../main";
import { settingsValidation } from "../../Validation/validation";
@@ -35,9 +36,25 @@ const Settings = ({ isAdmin }) => {
const [form, setForm] = useState({
ttl: checkTTL ? (checkTTL / SECONDS_PER_DAY).toString() : 0,
});
+ const [settings, setSettings] = useState({
+ apiBaseUrl: "",
+ });
const [errors, setErrors] = useState({});
const dispatch = useDispatch();
+ useEffect(() => {
+ const fetchSettings = async () => {
+ const action = await dispatch(getAppSettings({ authToken }));
+ if (getAppSettings.fulfilled.match(action)) {
+ const settings = action.payload.data;
+ setSettings(settings);
+ } else if (getAppSettings.rejected.match(action)) {
+ throw new Error(action.error.message);
+ }
+ };
+ fetchSettings();
+ }, [dispatch, authToken]);
+
const handleChange = (event) => {
const { value, id } = event.target;
const { error } = settingsValidation.validate(
@@ -249,6 +266,23 @@ const Settings = ({ isAdmin }) => {
)}
+
+
+ Client Settings
+
+ Here you can modify settings for the client.
+
+
+
+
+
+
About
diff --git a/Client/src/Utils/NetworkService.js b/Client/src/Utils/NetworkService.js
index 905385e70..9545fb247 100644
--- a/Client/src/Utils/NetworkService.js
+++ b/Client/src/Utils/NetworkService.js
@@ -619,6 +619,27 @@ class NetworkService {
}
);
}
+
+ /**
+ * ************************************
+ * Get app settings
+ * ************************************
+ *
+ * @async
+ * @param {Object} config - The configuration object.
+ * @param {string} config.authToken - The authorization token to be used in the request header.
+ * @returns {Promise} The response from the axios GET request.
+ *
+ */
+
+ async getAppSettings(config) {
+ return this.axiosInstance.get("/settings", {
+ headers: {
+ Authorization: `Bearer ${config.authToken}`,
+ "Content-Type": "application/json",
+ },
+ });
+ }
}
export default NetworkService;