diff --git a/frontend/src/components/Modals/Settings/PasswordProtection/index.jsx b/frontend/src/components/Modals/Settings/PasswordProtection/index.jsx
new file mode 100644
index 00000000..2b6444ed
--- /dev/null
+++ b/frontend/src/components/Modals/Settings/PasswordProtection/index.jsx
@@ -0,0 +1,141 @@
+import React, { useState, useEffect } from "react";
+import System from "../../../../models/system";
+
+const noop = () => false;
+export default function PasswordProtection({ hideModal = noop }) {
+ const [loading, setLoading] = useState(true);
+ const [saving, setSaving] = useState(false);
+ const [success, setSuccess] = useState(false);
+ const [error, setError] = useState(null);
+ const [usePassword, setUsePassword] = useState(false);
+
+ const handleSubmit = async (e) => {
+ e.preventDefault();
+ setSaving(true);
+ setSuccess(false);
+ setError(null);
+
+ const form = new FormData(e.target);
+ const data = {
+ usePassword,
+ newPassword: form.get("password"),
+ };
+
+ const { success, error } = await System.updateSystemPassword(data);
+ if (success) {
+ setSuccess(true);
+ setSaving(false);
+ setTimeout(() => {
+ window.localStorage.removeItem("anythingllm_authToken");
+ window.location.reload();
+ }, 2_000);
+ return;
+ }
+
+ setError(error);
+ setSaving(false);
+ };
+
+ useEffect(() => {
+ async function fetchKeys() {
+ const settings = await System.keys();
+ setUsePassword(settings?.RequiresAuth);
+ setLoading(false);
+ }
+ fetchKeys();
+ }, []);
+
+ return (
+
+
+
+
+ Protect your AnythingLLM instance with a password. If you forget
+ this there is no recovery method so ensure you save this password.
+
+
+ {(error || success) && (
+
+ {error && (
+
+ {error}
+
+ )}
+ {success && (
+
+ Your page will refresh in a few seconds.
+
+ )}
+
+ )}
+
+ {loading ? (
+
+
+ loading system settings
+
+
+ ) : (
+
+ )}
+
+
+
+
+
+
+ );
+}
diff --git a/frontend/src/components/Modals/Settings/index.jsx b/frontend/src/components/Modals/Settings/index.jsx
index baa46df4..e898a7af 100644
--- a/frontend/src/components/Modals/Settings/index.jsx
+++ b/frontend/src/components/Modals/Settings/index.jsx
@@ -1,11 +1,13 @@
import React, { useState } from "react";
-import { Archive, Cloud, Key, X } from "react-feather";
+import { Archive, Lock, Key, X } from "react-feather";
import SystemKeys from "./Keys";
import ExportOrImportData from "./ExportImport";
+import PasswordProtection from "./PasswordProtection";
const TABS = {
keys: SystemKeys,
exportimport: ExportOrImportData,
+ password: PasswordProtection,
};
const noop = () => false;
@@ -62,6 +64,13 @@ function SettingTabs({ selectedTab, changeTab }) {
icon={}
onClick={changeTab}
/>
+ }
+ onClick={changeTab}
+ />
);
diff --git a/frontend/src/models/system.js b/frontend/src/models/system.js
index 06ca2b92..7e5f61bf 100644
--- a/frontend/src/models/system.js
+++ b/frontend/src/models/system.js
@@ -86,6 +86,18 @@ const System = {
return { newValues: null, error: e.message };
});
},
+ updateSystemPassword: async (data) => {
+ return await fetch(`${API_BASE}/system/update-password`, {
+ method: "POST",
+ headers: baseHeaders(),
+ body: JSON.stringify(data),
+ })
+ .then((res) => res.json())
+ .catch((e) => {
+ console.error(e);
+ return { success: false, error: e.message };
+ });
+ },
deleteDocument: async (name, meta) => {
return await fetch(`${API_BASE}/system/remove-document`, {
method: "DELETE",
diff --git a/server/endpoints/system.js b/server/endpoints/system.js
index 5193539c..a39ef3a3 100644
--- a/server/endpoints/system.js
+++ b/server/endpoints/system.js
@@ -13,6 +13,7 @@ const { getVectorDbClass } = require("../utils/helpers");
const { updateENV } = require("../utils/helpers/updateENV");
const { reqBody, makeJWT } = require("../utils/http");
const { setupDataImports } = require("../utils/files/multer");
+const { v4 } = require("uuid");
const { handleImports } = setupDataImports();
function systemEndpoints(app) {
@@ -155,6 +156,20 @@ function systemEndpoints(app) {
}
});
+ app.post("/system/update-password", async (request, response) => {
+ try {
+ const { usePassword, newPassword } = reqBody(request);
+ const { error } = updateENV({
+ AuthToken: usePassword ? newPassword : "",
+ JWTSecret: usePassword ? v4() : "",
+ });
+ response.status(200).json({ success: !error, error });
+ } catch (e) {
+ console.log(e.message, e);
+ response.sendStatus(500).end();
+ }
+ });
+
app.get("/system/data-export", async (_, response) => {
try {
const { filename, error } = await exportData();
diff --git a/server/utils/helpers/updateENV.js b/server/utils/helpers/updateENV.js
index 4161aec1..54eec1e5 100644
--- a/server/utils/helpers/updateENV.js
+++ b/server/utils/helpers/updateENV.js
@@ -27,9 +27,15 @@ const KEY_MAPPING = {
envKey: "PINECONE_INDEX",
checks: [],
},
+ AuthToken: {
+ envKey: "AUTH_TOKEN",
+ checks: [],
+ },
+ JWTSecret: {
+ envKey: "JWT_SECRET",
+ checks: [],
+ },
// Not supported yet.
- // 'AuthToken': 'AUTH_TOKEN',
- // 'JWTSecret': 'JWT_SECRET',
// 'StorageDir': 'STORAGE_DIR',
};
diff --git a/server/utils/http/index.js b/server/utils/http/index.js
index af42f5de..9fd643b7 100644
--- a/server/utils/http/index.js
+++ b/server/utils/http/index.js
@@ -2,7 +2,6 @@ process.env.NODE_ENV === "development"
? require("dotenv").config({ path: `.env.${process.env.NODE_ENV}` })
: require("dotenv").config();
const JWT = require("jsonwebtoken");
-const SECRET = process.env.JWT_SECRET;
function reqBody(request) {
return typeof request.body === "string"
@@ -15,15 +14,16 @@ function queryParams(request) {
}
function makeJWT(info = {}, expiry = "30d") {
- if (!SECRET) throw new Error("Cannot create JWT as JWT_SECRET is unset.");
- return JWT.sign(info, SECRET, { expiresIn: expiry });
+ if (!process.env.JWT_SECRET)
+ throw new Error("Cannot create JWT as JWT_SECRET is unset.");
+ return JWT.sign(info, process.env.JWT_SECRET, { expiresIn: expiry });
}
function decodeJWT(jwtToken) {
try {
- return JWT.verify(jwtToken, SECRET);
+ return JWT.verify(jwtToken, process.env.JWT_SECRET);
} catch {}
- return null;
+ return { p: null };
}
module.exports = {