Compare commits

...

3 Commits

Author SHA1 Message Date
shatfield4 f7940c1fea rename files to Logging to prevent getting gitignore 2024-02-02 18:38:32 -08:00
shatfield4 dac7c355d4 UI for log rows 2024-02-02 18:32:20 -08:00
shatfield4 0b313e7078 WIP add logging 2024-02-02 16:49:04 -08:00
16 changed files with 329 additions and 43 deletions

View File

@ -20,7 +20,7 @@ const AdminUsers = lazy(() => import("@/pages/Admin/Users"));
const AdminInvites = lazy(() => import("@/pages/Admin/Invitations")); const AdminInvites = lazy(() => import("@/pages/Admin/Invitations"));
const AdminWorkspaces = lazy(() => import("@/pages/Admin/Workspaces")); const AdminWorkspaces = lazy(() => import("@/pages/Admin/Workspaces"));
const AdminSystem = lazy(() => import("@/pages/Admin/System")); const AdminSystem = lazy(() => import("@/pages/Admin/System"));
const AdminLogs = lazy(() => import("@/pages/Admin/Logs")); const AdminLogs = lazy(() => import("@/pages/Admin/Logging"));
const GeneralChats = lazy(() => import("@/pages/GeneralSettings/Chats")); const GeneralChats = lazy(() => import("@/pages/GeneralSettings/Chats"));
const GeneralAppearance = lazy( const GeneralAppearance = lazy(
() => import("@/pages/GeneralSettings/Appearance") () => import("@/pages/GeneralSettings/Appearance")

View File

@ -161,7 +161,7 @@ export default function SettingsSidebar() {
icon={<Notepad className="h-5 w-5 flex-shrink-0" />} icon={<Notepad className="h-5 w-5 flex-shrink-0" />}
user={user} user={user}
flex={true} flex={true}
allowedRole={["admin", "manager"]} allowedRole={["admin"]}
/> />
</div> </div>
</div> </div>

View File

@ -389,6 +389,18 @@ const System = {
return []; return [];
}); });
}, },
logs: async (offset = 0) => {
return await fetch(`${API_BASE}/system/logs`, {
method: "POST",
headers: baseHeaders(),
body: JSON.stringify({ offset }),
})
.then((res) => res.json())
.catch((e) => {
console.error(e);
return [];
});
},
deleteChat: async (chatId) => { deleteChat: async (chatId) => {
return await fetch(`${API_BASE}/system/workspace-chats/${chatId}`, { return await fetch(`${API_BASE}/system/workspace-chats/${chatId}`, {
method: "DELETE", method: "DELETE",

View File

@ -0,0 +1,62 @@
import { CaretDown } from "@phosphor-icons/react";
import { useState } from "react";
export default function LogRow({ log }) {
const [expanded, setExpanded] = useState(false);
const handleRowClick = () => {
if (log.metadata !== "{}") {
setExpanded(!expanded);
}
};
return (
<>
<tr
onClick={handleRowClick}
className="bg-transparent text-white text-opacity-80 text-sm font-medium cursor-pointer"
>
<td className="px-6 py-4 font-medium whitespace-nowrap text-white">
{log.id}
</td>
<td className="px-6 py-4 font-medium whitespace-nowrap text-white flex items-center">
<span className="rounded-full bg-sky-600/20 px-2 py-0.5 text-sm font-medium text-sky-400 shadow-sm">
{log.event}
</span>
</td>
<td className="px-6 py-4 border-transparent transform transition-transform duration-200">
{log.user.username}
</td>
<td className="px-6 py-4 border-transparent transform transition-transform duration-200">
{log.occurredAt}
</td>
{log.metadata !== "{}" && (
<td
className={`flex items-center justify-center transform transition-transform duration-200 hover:scale-105 ${
expanded ? "rotate-0" : "rotate-90"
}`}
>
<CaretDown weight="bold" size={20} />
</td>
)}
</tr>
{expanded && (
<tr className="bg-sidebar">
<td
colSpan="2"
className="px-6 py-4 font-medium text-white rounded-l-2xl"
>
Event Metadata
</td>
<td colSpan="4" className="px-6 py-4 rounded-r-2xl">
<div className="w-full rounded-lg bg-main-2 p-2 text-white shadow-sm border-white border bg-opacity-10">
<pre className="overflow-scroll">
{JSON.stringify(JSON.parse(log.metadata), null, 2)}
</pre>
</div>
</td>
</tr>
)}
</>
);
}

View File

@ -0,0 +1,119 @@
import Sidebar, { SidebarMobileHeader } from "@/components/SettingsSidebar";
import useQuery from "@/hooks/useQuery";
import System from "@/models/system";
import { useEffect, useState } from "react";
import { isMobile } from "react-device-detect";
import * as Skeleton from "react-loading-skeleton";
import LogRow from "./LogRow";
export default function AdminLogs() {
return (
<div className="w-screen h-screen overflow-hidden bg-sidebar flex">
{!isMobile && <Sidebar />}
<div
style={{ height: isMobile ? "100%" : "calc(100% - 32px)" }}
className="transition-all duration-500 relative md:ml-[2px] md:mr-[16px] md:my-[16px] md:rounded-[26px] bg-main-gradient w-full h-full overflow-y-scroll border-4 border-accent"
>
{isMobile && <SidebarMobileHeader />}
<div className="flex flex-col w-full px-1 md:px-20 md:py-12 py-16">
<div className="w-full flex flex-col gap-y-1 pb-6 border-white border-b-2 border-opacity-10">
<div className="items-center flex gap-x-4">
<p className="text-2xl font-semibold text-white">Logs</p>
</div>
<p className="text-sm font-base text-white text-opacity-60">
View all logs and events happening on this instance.
</p>
</div>
<ChatsContainer />
</div>
</div>
</div>
);
}
function ChatsContainer() {
const query = useQuery();
const [loading, setLoading] = useState(true);
// const [chats, setChats] = useState([]);
const [logs, setLogs] = useState([]);
const [offset, setOffset] = useState(Number(query.get("offset") || 0));
const [canNext, setCanNext] = useState(false);
const handlePrevious = () => {
setOffset(Math.max(offset - 1, 0));
};
const handleNext = () => {
setOffset(offset + 1);
};
useEffect(() => {
async function fetchLogs() {
const { logs: _logs, hasPages = false } = await System.logs(offset);
console.log(_logs);
setLogs(_logs);
setCanNext(hasPages);
setLoading(false);
}
fetchLogs();
}, [offset]);
if (loading) {
return (
<Skeleton.default
height="80vh"
width="100%"
highlightColor="#3D4147"
baseColor="#2C2F35"
count={1}
className="w-full p-4 rounded-b-2xl rounded-tr-2xl rounded-tl-sm mt-6"
containerClassName="flex w-full"
/>
);
}
return (
<>
<table className="md:w-3/4 w-full text-sm text-left rounded-lg mt-5">
<thead className="text-white text-opacity-80 text-sm font-bold uppercase border-white border-b border-opacity-60">
<tr>
<th scope="col" className="px-6 py-3 rounded-tl-lg">
Id
</th>
<th scope="col" className="px-6 py-3">
Event Type
</th>
<th scope="col" className="px-6 py-3">
User
</th>
<th scope="col" className="px-6 py-3">
Occurred At
</th>
<th scope="col" className="px-6 py-3 rounded-tr-lg">
{" "}
</th>
</tr>
</thead>
<tbody>
{!!logs && logs.map((log) => <LogRow key={log.id} log={log} />)}
</tbody>
</table>
<div className="flex w-full justify-between items-center">
<button
onClick={handlePrevious}
className="px-4 py-2 rounded-lg border border-slate-200 text-slate-200 text-sm items-center flex gap-x-2 hover:bg-slate-200 hover:text-slate-800 disabled:invisible"
disabled={offset === 0}
>
{" "}
Previous Page
</button>
<button
onClick={handleNext}
className="px-4 py-2 rounded-lg border border-slate-200 text-slate-200 text-sm items-center flex gap-x-2 hover:bg-slate-200 hover:text-slate-800 disabled:invisible"
disabled={!canNext}
>
Next Page
</button>
</div>
</>
);
}

View File

@ -12,6 +12,7 @@ const {
findDocumentInDocuments, findDocumentInDocuments,
} = require("../../../utils/files"); } = require("../../../utils/files");
const { reqBody } = require("../../../utils/http"); const { reqBody } = require("../../../utils/http");
const { EventLogs } = require("../../../models/eventLogs");
const { handleUploads } = setupMulter(); const { handleUploads } = setupMulter();
function apiDocumentEndpoints(app) { function apiDocumentEndpoints(app) {
@ -22,7 +23,7 @@ function apiDocumentEndpoints(app) {
[validApiKey], [validApiKey],
handleUploads.single("file"), handleUploads.single("file"),
async (request, response) => { async (request, response) => {
/* /*
#swagger.tags = ['Documents'] #swagger.tags = ['Documents']
#swagger.description = 'Upload a new file to AnythingLLM to be parsed and prepared for embedding.' #swagger.description = 'Upload a new file to AnythingLLM to be parsed and prepared for embedding.'
#swagger.requestBody = { #swagger.requestBody = {
@ -68,9 +69,9 @@ function apiDocumentEndpoints(app) {
] ]
} }
} }
} }
} }
} }
#swagger.responses[403] = { #swagger.responses[403] = {
schema: { schema: {
"$ref": "#/definitions/InvalidAPIKey" "$ref": "#/definitions/InvalidAPIKey"
@ -105,6 +106,7 @@ function apiDocumentEndpoints(app) {
`Document ${originalname} uploaded processed and successfully. It is now available in documents.` `Document ${originalname} uploaded processed and successfully. It is now available in documents.`
); );
await Telemetry.sendTelemetry("document_uploaded"); await Telemetry.sendTelemetry("document_uploaded");
await EventLogs.logEvent("document_uploaded");
response.status(200).json({ success: true, error: null, documents }); response.status(200).json({ success: true, error: null, documents });
} catch (e) { } catch (e) {
console.log(e.message, e); console.log(e.message, e);
@ -117,7 +119,7 @@ function apiDocumentEndpoints(app) {
"/v1/document/upload-link", "/v1/document/upload-link",
[validApiKey], [validApiKey],
async (request, response) => { async (request, response) => {
/* /*
#swagger.tags = ['Documents'] #swagger.tags = ['Documents']
#swagger.description = 'Upload a valid URL for AnythingLLM to scrape and prepare for embedding.' #swagger.description = 'Upload a valid URL for AnythingLLM to scrape and prepare for embedding.'
#swagger.requestBody = { #swagger.requestBody = {
@ -132,7 +134,7 @@ function apiDocumentEndpoints(app) {
"link": "https://useanything.com" "link": "https://useanything.com"
} }
} }
} }
} }
} }
#swagger.responses[200] = { #swagger.responses[200] = {
@ -161,9 +163,9 @@ function apiDocumentEndpoints(app) {
] ]
} }
} }
} }
} }
} }
#swagger.responses[403] = { #swagger.responses[403] = {
schema: { schema: {
"$ref": "#/definitions/InvalidAPIKey" "$ref": "#/definitions/InvalidAPIKey"
@ -197,6 +199,7 @@ function apiDocumentEndpoints(app) {
`Link ${link} uploaded processed and successfully. It is now available in documents.` `Link ${link} uploaded processed and successfully. It is now available in documents.`
); );
await Telemetry.sendTelemetry("document_uploaded"); await Telemetry.sendTelemetry("document_uploaded");
await EventLogs.logEvent("document_uploaded");
response.status(200).json({ success: true, error: null, documents }); response.status(200).json({ success: true, error: null, documents });
} catch (e) { } catch (e) {
console.log(e.message, e); console.log(e.message, e);
@ -206,7 +209,7 @@ function apiDocumentEndpoints(app) {
); );
app.get("/v1/documents", [validApiKey], async (_, response) => { app.get("/v1/documents", [validApiKey], async (_, response) => {
/* /*
#swagger.tags = ['Documents'] #swagger.tags = ['Documents']
#swagger.description = 'List of all locally-stored documents in instance' #swagger.description = 'List of all locally-stored documents in instance'
#swagger.responses[200] = { #swagger.responses[200] = {
@ -231,9 +234,9 @@ function apiDocumentEndpoints(app) {
} }
} }
} }
} }
} }
} }
#swagger.responses[403] = { #swagger.responses[403] = {
schema: { schema: {
"$ref": "#/definitions/InvalidAPIKey" "$ref": "#/definitions/InvalidAPIKey"
@ -250,7 +253,7 @@ function apiDocumentEndpoints(app) {
}); });
app.get("/v1/document/:docName", [validApiKey], async (request, response) => { app.get("/v1/document/:docName", [validApiKey], async (request, response) => {
/* /*
#swagger.tags = ['Documents'] #swagger.tags = ['Documents']
#swagger.description = 'Get a single document by its unique AnythingLLM document name' #swagger.description = 'Get a single document by its unique AnythingLLM document name'
#swagger.parameters['docName'] = { #swagger.parameters['docName'] = {
@ -281,9 +284,9 @@ function apiDocumentEndpoints(app) {
} }
} }
} }
} }
} }
} }
#swagger.responses[403] = { #swagger.responses[403] = {
schema: { schema: {
"$ref": "#/definitions/InvalidAPIKey" "$ref": "#/definitions/InvalidAPIKey"
@ -308,7 +311,7 @@ function apiDocumentEndpoints(app) {
"/v1/document/accepted-file-types", "/v1/document/accepted-file-types",
[validApiKey], [validApiKey],
async (_, response) => { async (_, response) => {
/* /*
#swagger.tags = ['Documents'] #swagger.tags = ['Documents']
#swagger.description = 'Check available filetypes and MIMEs that can be uploaded.' #swagger.description = 'Check available filetypes and MIMEs that can be uploaded.'
#swagger.responses[200] = { #swagger.responses[200] = {
@ -337,9 +340,9 @@ function apiDocumentEndpoints(app) {
} }
} }
} }
} }
} }
} }
#swagger.responses[403] = { #swagger.responses[403] = {
schema: { schema: {
"$ref": "#/definitions/InvalidAPIKey" "$ref": "#/definitions/InvalidAPIKey"

View File

@ -16,6 +16,7 @@ const {
writeResponseChunk, writeResponseChunk,
VALID_CHAT_MODE, VALID_CHAT_MODE,
} = require("../../../utils/chats/stream"); } = require("../../../utils/chats/stream");
const { EventLogs } = require("../../../models/eventLogs");
function apiWorkspaceEndpoints(app) { function apiWorkspaceEndpoints(app) {
if (!app) return; if (!app) return;
@ -73,6 +74,12 @@ function apiWorkspaceEndpoints(app) {
Embedder: process.env.EMBEDDING_ENGINE || "inherit", Embedder: process.env.EMBEDDING_ENGINE || "inherit",
VectorDbSelection: process.env.VECTOR_DB || "pinecone", VectorDbSelection: process.env.VECTOR_DB || "pinecone",
}); });
await EventLogs.logEvent("workspace_created", {
multiUserMode: multiUserMode(response),
LLMSelection: process.env.LLM_PROVIDER || "openai",
Embedder: process.env.EMBEDDING_ENGINE || "inherit",
VectorDbSelection: process.env.VECTOR_DB || "pinecone",
});
response.status(200).json({ workspace, message }); response.status(200).json({ workspace, message });
} catch (e) { } catch (e) {
console.log(e.message, e); console.log(e.message, e);
@ -519,6 +526,11 @@ function apiWorkspaceEndpoints(app) {
Embedder: process.env.EMBEDDING_ENGINE || "inherit", Embedder: process.env.EMBEDDING_ENGINE || "inherit",
VectorDbSelection: process.env.VECTOR_DB || "pinecone", VectorDbSelection: process.env.VECTOR_DB || "pinecone",
}); });
await EventLogs.logEvent("sent_chat", {
LLMSelection: process.env.LLM_PROVIDER || "openai",
Embedder: process.env.EMBEDDING_ENGINE || "inherit",
VectorDbSelection: process.env.VECTOR_DB || "pinecone",
});
response.status(200).json({ ...result }); response.status(200).json({ ...result });
} catch (e) { } catch (e) {
response.status(500).json({ response.status(500).json({
@ -637,6 +649,11 @@ function apiWorkspaceEndpoints(app) {
Embedder: process.env.EMBEDDING_ENGINE || "inherit", Embedder: process.env.EMBEDDING_ENGINE || "inherit",
VectorDbSelection: process.env.VECTOR_DB || "pinecone", VectorDbSelection: process.env.VECTOR_DB || "pinecone",
}); });
await EventLogs.logEvent("sent_chat", {
LLMSelection: process.env.LLM_PROVIDER || "openai",
Embedder: process.env.EMBEDDING_ENGINE || "inherit",
VectorDbSelection: process.env.VECTOR_DB || "pinecone",
});
response.end(); response.end();
} catch (e) { } catch (e) {
console.error(e); console.error(e);

View File

@ -99,16 +99,17 @@ function chatEndpoints(app) {
Embedder: process.env.EMBEDDING_ENGINE || "inherit", Embedder: process.env.EMBEDDING_ENGINE || "inherit",
VectorDbSelection: process.env.VECTOR_DB || "pinecone", VectorDbSelection: process.env.VECTOR_DB || "pinecone",
}); });
await EventLogs.logEvent({
event: "sent_chat", await EventLogs.logEvent(
userId: user?.id || null, "sent_chat",
metadata: { {
multiUserMode: multiUserMode(response), multiUserMode: multiUserMode(response),
LLMSelection: process.env.LLM_PROVIDER || "openai", LLMSelection: process.env.LLM_PROVIDER || "openai",
Embedder: process.env.EMBEDDING_ENGINE || "inherit", Embedder: process.env.EMBEDDING_ENGINE || "inherit",
VectorDbSelection: process.env.VECTOR_DB || "pinecone", VectorDbSelection: process.env.VECTOR_DB || "pinecone",
}, },
}); user?.id
);
response.end(); response.end();
} catch (e) { } catch (e) {
console.error(e); console.error(e);

View File

@ -1,3 +1,4 @@
const { EventLogs } = require("../../models/eventLogs");
const { Telemetry } = require("../../models/telemetry"); const { Telemetry } = require("../../models/telemetry");
const { const {
forwardExtensionRequest, forwardExtensionRequest,
@ -42,6 +43,9 @@ function extensionEndpoints(app) {
await Telemetry.sendTelemetry("extension_invoked", { await Telemetry.sendTelemetry("extension_invoked", {
type: "github_repo", type: "github_repo",
}); });
await EventLogs.logEvent("extension_invoked", {
type: "github_repo",
});
response.status(200).json(responseFromProcessor); response.status(200).json(responseFromProcessor);
} catch (e) { } catch (e) {
console.error(e); console.error(e);
@ -63,6 +67,9 @@ function extensionEndpoints(app) {
await Telemetry.sendTelemetry("extension_invoked", { await Telemetry.sendTelemetry("extension_invoked", {
type: "youtube_transcript", type: "youtube_transcript",
}); });
await EventLogs.logEvent("extension_invoked", {
type: "youtube_transcript",
});
response.status(200).json(responseFromProcessor); response.status(200).json(responseFromProcessor);
} catch (e) { } catch (e) {
console.error(e); console.error(e);

View File

@ -150,11 +150,12 @@ function systemEndpoints(app) {
existingUser?.id existingUser?.id
); );
await EventLogs.logEvent({ await EventLogs.logEvent(
event: "login_event", "login_event",
userId: existingUser?.id || null, { multiUserMode: false },
metadata: {}, existingUser?.id
}); );
response.status(200).json({ response.status(200).json({
valid: true, valid: true,
user: existingUser, user: existingUser,
@ -182,6 +183,7 @@ function systemEndpoints(app) {
} }
await Telemetry.sendTelemetry("login_event", { multiUserMode: false }); await Telemetry.sendTelemetry("login_event", { multiUserMode: false });
await EventLogs.logEvent("login_event", { multiUserMode: false });
response.status(200).json({ response.status(200).json({
valid: true, valid: true,
token: makeJWT({ p: password }, "30d"), token: makeJWT({ p: password }, "30d"),
@ -371,6 +373,9 @@ function systemEndpoints(app) {
await Telemetry.sendTelemetry("enabled_multi_user_mode", { await Telemetry.sendTelemetry("enabled_multi_user_mode", {
multiUserMode: true, multiUserMode: true,
}); });
await EventLogs.logEvent("enabled_multi_user_mode", {
multiUserMode: true,
});
response.status(200).json({ success: !!user, error }); response.status(200).json({ success: !!user, error });
} catch (e) { } catch (e) {
await User.delete({}); await User.delete({});
@ -751,6 +756,26 @@ function systemEndpoints(app) {
} }
); );
app.post(
"/system/logs",
[validatedRequest, flexUserRoleValid([ROLES.admin, ROLES.manager])],
async (request, response) => {
try {
const { offset = 0, limit = 20 } = reqBody(request);
const logs = await EventLogs.whereWithData({}, limit, offset * limit, {
id: "desc",
});
const totalLogs = await EventLogs.count();
const hasPages = totalLogs > (offset + 1) * limit;
response.status(200).json({ logs: logs, hasPages, totalLogs });
} catch (e) {
console.error(e);
response.sendStatus(500).end();
}
}
);
app.post( app.post(
"/system/workspace-chats", "/system/workspace-chats",
[validatedRequest, flexUserRoleValid([ROLES.admin, ROLES.manager])], [validatedRequest, flexUserRoleValid([ROLES.admin, ROLES.manager])],

View File

@ -17,6 +17,7 @@ const {
flexUserRoleValid, flexUserRoleValid,
ROLES, ROLES,
} = require("../utils/middleware/multiUserProtected"); } = require("../utils/middleware/multiUserProtected");
const { EventLogs } = require("../models/eventLogs");
const { handleUploads } = setupMulter(); const { handleUploads } = setupMulter();
function workspaceEndpoints(app) { function workspaceEndpoints(app) {
@ -40,6 +41,17 @@ function workspaceEndpoints(app) {
}, },
user?.id user?.id
); );
await EventLogs.logEvent(
"workspace_created",
{
multiUserMode: multiUserMode(response),
LLMSelection: process.env.LLM_PROVIDER || "openai",
Embedder: process.env.EMBEDDING_ENGINE || "inherit",
VectorDbSelection: process.env.VECTOR_DB || "pinecone",
},
user?.id
);
if (onboardingComplete === true) if (onboardingComplete === true)
await Telemetry.sendTelemetry("onboarding_complete"); await Telemetry.sendTelemetry("onboarding_complete");
@ -109,6 +121,7 @@ function workspaceEndpoints(app) {
`Document ${originalname} uploaded processed and successfully. It is now available in documents.` `Document ${originalname} uploaded processed and successfully. It is now available in documents.`
); );
await Telemetry.sendTelemetry("document_uploaded"); await Telemetry.sendTelemetry("document_uploaded");
await EventLogs.logEvent("document_uploaded");
response.status(200).json({ success: true, error: null }); response.status(200).json({ success: true, error: null });
} }
); );
@ -141,6 +154,7 @@ function workspaceEndpoints(app) {
`Link ${link} uploaded processed and successfully. It is now available in documents.` `Link ${link} uploaded processed and successfully. It is now available in documents.`
); );
await Telemetry.sendTelemetry("link_uploaded"); await Telemetry.sendTelemetry("link_uploaded");
await EventLogs.logEvent("link_uploaded");
response.status(200).json({ success: true, error: null }); response.status(200).json({ success: true, error: null });
} }
); );

View File

@ -1,5 +1,6 @@
const { Telemetry } = require("./telemetry"); const { Telemetry } = require("./telemetry");
const prisma = require("../utils/prisma"); const prisma = require("../utils/prisma");
const { EventLogs } = require("./eventLogs");
const ApiKey = { const ApiKey = {
tablename: "api_keys", tablename: "api_keys",
@ -20,6 +21,7 @@ const ApiKey = {
}); });
await Telemetry.sendTelemetry("api_key_created"); await Telemetry.sendTelemetry("api_key_created");
await EventLogs.logEvent("api_key_created");
return { apiKey, error: null }; return { apiKey, error: null };
} catch (error) { } catch (error) {
console.error("FAILED TO CREATE API KEY.", error.message); console.error("FAILED TO CREATE API KEY.", error.message);

View File

@ -3,6 +3,7 @@ const { v4: uuidv4 } = require("uuid");
const { getVectorDbClass } = require("../utils/helpers"); const { getVectorDbClass } = require("../utils/helpers");
const prisma = require("../utils/prisma"); const prisma = require("../utils/prisma");
const { Telemetry } = require("./telemetry"); const { Telemetry } = require("./telemetry");
const { EventLogs } = require("./eventLogs");
const Document = { const Document = {
forWorkspace: async function (workspaceId = null) { forWorkspace: async function (workspaceId = null) {
@ -84,6 +85,11 @@ const Document = {
Embedder: process.env.EMBEDDING_ENGINE || "inherit", Embedder: process.env.EMBEDDING_ENGINE || "inherit",
VectorDbSelection: process.env.VECTOR_DB || "pinecone", VectorDbSelection: process.env.VECTOR_DB || "pinecone",
}); });
await EventLogs.logEvent("documents_embedded_in_workspace", {
LLMSelection: process.env.LLM_PROVIDER || "openai",
Embedder: process.env.EMBEDDING_ENGINE || "inherit",
VectorDbSelection: process.env.VECTOR_DB || "pinecone",
});
return { failedToEmbed, errors: Array.from(errors), embedded }; return { failedToEmbed, errors: Array.from(errors), embedded };
}, },
@ -119,6 +125,11 @@ const Document = {
Embedder: process.env.EMBEDDING_ENGINE || "inherit", Embedder: process.env.EMBEDDING_ENGINE || "inherit",
VectorDbSelection: process.env.VECTOR_DB || "pinecone", VectorDbSelection: process.env.VECTOR_DB || "pinecone",
}); });
await EventLogs.logEvent("documents_removed_in_workspace", {
LLMSelection: process.env.LLM_PROVIDER || "openai",
Embedder: process.env.EMBEDDING_ENGINE || "inherit",
VectorDbSelection: process.env.VECTOR_DB || "pinecone",
});
return true; return true;
}, },

View File

@ -1,22 +1,14 @@
const prisma = require("../utils/prisma"); const prisma = require("../utils/prisma");
const EventLogs = { const EventLogs = {
logEvent: async function ({ logEvent: async function (event, metadata = {}, userId = null) {
event,
description = null,
metadata = null,
userId = null,
ipAddress = null,
}) {
try { try {
const eventLog = await prisma.event_logs.create({ const eventLog = await prisma.event_logs.create({
data: { data: {
event, event,
description,
metadata: metadata ? JSON.stringify(metadata) : null, metadata: metadata ? JSON.stringify(metadata) : null,
userId, userId: userId ? Number(userId) : null,
occurredAt: new Date(), occurredAt: new Date(),
ipAddress,
}, },
}); });
return { eventLog, message: null }; return { eventLog, message: null };
@ -80,6 +72,31 @@ const EventLogs = {
} }
}, },
whereWithData: async function (
clause = {},
limit = null,
offset = null,
orderBy = null
) {
const { User } = require("./user");
try {
const results = await this.where(clause, limit, orderBy, offset);
for (const res of results) {
const user = res.userId ? await User.get({ id: res.userId }) : null;
res.user = user
? { username: user.username }
: { username: "unknown user" };
}
return results;
} catch (error) {
console.error(error.message);
return [];
}
},
count: async function (clause = {}) { count: async function (clause = {}) {
try { try {
const count = await prisma.event_logs.count({ const count = await prisma.event_logs.count({

View File

@ -2,11 +2,9 @@
CREATE TABLE "event_logs" ( CREATE TABLE "event_logs" (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"event" TEXT NOT NULL, "event" TEXT NOT NULL,
"description" TEXT,
"metadata" TEXT, "metadata" TEXT,
"userId" INTEGER, "userId" INTEGER,
"occurredAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, "occurredAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
"ipAddress" TEXT
); );
-- CreateIndex -- CreateIndex

View File

@ -135,11 +135,9 @@ model cache_data {
model event_logs { model event_logs {
id Int @id @default(autoincrement()) id Int @id @default(autoincrement())
event String event String
description String?
metadata String? metadata String?
userId Int? userId Int?
occurredAt DateTime @default(now()) occurredAt DateTime @default(now())
ipAddress String?
@@index([userId]) @@index([userId])
@@index([event]) @@index([event])
} }