2023-06-26 20:38:38 +02:00
const KEY _MAPPING = {
2023-08-04 23:56:27 +02:00
LLMProvider : {
envKey : "LLM_PROVIDER" ,
checks : [ isNotEmpty , supportedLLM ] ,
2024-01-17 21:59:25 +01:00
postUpdate : [ wipeWorkspaceModelPreference ] ,
2023-08-04 23:56:27 +02:00
} ,
// OpenAI Settings
2023-06-26 20:38:38 +02:00
OpenAiKey : {
envKey : "OPEN_AI_KEY" ,
checks : [ isNotEmpty , validOpenAIKey ] ,
} ,
OpenAiModelPref : {
envKey : "OPEN_MODEL_PREF" ,
2023-10-31 19:38:28 +01:00
checks : [ isNotEmpty ] ,
2023-06-26 20:38:38 +02:00
} ,
2023-08-04 23:56:27 +02:00
// Azure OpenAI Settings
AzureOpenAiEndpoint : {
envKey : "AZURE_OPENAI_ENDPOINT" ,
checks : [ isNotEmpty , validAzureURL ] ,
} ,
2023-11-06 22:13:53 +01:00
AzureOpenAiTokenLimit : {
envKey : "AZURE_OPENAI_TOKEN_LIMIT" ,
checks : [ validOpenAiTokenLimit ] ,
} ,
2023-08-04 23:56:27 +02:00
AzureOpenAiKey : {
envKey : "AZURE_OPENAI_KEY" ,
checks : [ isNotEmpty ] ,
} ,
AzureOpenAiModelPref : {
envKey : "OPEN_MODEL_PREF" ,
checks : [ isNotEmpty ] ,
} ,
AzureOpenAiEmbeddingModelPref : {
envKey : "EMBEDDING_MODEL_PREF" ,
checks : [ isNotEmpty ] ,
} ,
2023-10-30 23:44:03 +01:00
// Anthropic Settings
AnthropicApiKey : {
envKey : "ANTHROPIC_API_KEY" ,
checks : [ isNotEmpty , validAnthropicApiKey ] ,
} ,
AnthropicModelPref : {
envKey : "ANTHROPIC_MODEL_PREF" ,
checks : [ isNotEmpty , validAnthropicModel ] ,
} ,
2023-12-28 02:08:03 +01:00
GeminiLLMApiKey : {
envKey : "GEMINI_API_KEY" ,
checks : [ isNotEmpty ] ,
} ,
GeminiLLMModelPref : {
envKey : "GEMINI_LLM_MODEL_PREF" ,
checks : [ isNotEmpty , validGeminiModel ] ,
} ,
2023-11-09 21:33:21 +01:00
// LMStudio Settings
LMStudioBasePath : {
envKey : "LMSTUDIO_BASE_PATH" ,
2023-12-28 19:47:02 +01:00
checks : [ isNotEmpty , validLLMExternalBasePath , validDockerizedUrl ] ,
2023-11-09 21:33:21 +01:00
} ,
2024-03-22 22:39:30 +01:00
LMStudioModelPref : {
envKey : "LMSTUDIO_MODEL_PREF" ,
checks : [ ] ,
} ,
2023-11-09 21:33:21 +01:00
LMStudioTokenLimit : {
envKey : "LMSTUDIO_MODEL_TOKEN_LIMIT" ,
checks : [ nonZero ] ,
} ,
2023-11-14 21:31:44 +01:00
// LocalAI Settings
LocalAiBasePath : {
envKey : "LOCAL_AI_BASE_PATH" ,
2023-12-28 19:47:02 +01:00
checks : [ isNotEmpty , validLLMExternalBasePath , validDockerizedUrl ] ,
2023-11-14 21:31:44 +01:00
} ,
LocalAiModelPref : {
envKey : "LOCAL_AI_MODEL_PREF" ,
checks : [ ] ,
} ,
LocalAiTokenLimit : {
envKey : "LOCAL_AI_MODEL_TOKEN_LIMIT" ,
checks : [ nonZero ] ,
} ,
2023-12-04 17:38:15 +01:00
LocalAiApiKey : {
envKey : "LOCAL_AI_API_KEY" ,
checks : [ ] ,
} ,
2023-11-14 21:31:44 +01:00
2023-12-28 02:21:47 +01:00
OllamaLLMBasePath : {
envKey : "OLLAMA_BASE_PATH" ,
2023-12-28 19:47:02 +01:00
checks : [ isNotEmpty , validOllamaLLMBasePath , validDockerizedUrl ] ,
2023-12-28 02:21:47 +01:00
} ,
OllamaLLMModelPref : {
envKey : "OLLAMA_MODEL_PREF" ,
checks : [ ] ,
} ,
OllamaLLMTokenLimit : {
envKey : "OLLAMA_MODEL_TOKEN_LIMIT" ,
checks : [ nonZero ] ,
} ,
2024-02-06 18:17:51 +01:00
// Mistral AI API Settings
2024-01-17 23:42:05 +01:00
MistralApiKey : {
envKey : "MISTRAL_API_KEY" ,
checks : [ isNotEmpty ] ,
} ,
MistralModelPref : {
envKey : "MISTRAL_MODEL_PREF" ,
checks : [ isNotEmpty ] ,
} ,
2023-12-07 23:48:27 +01:00
// Native LLM Settings
NativeLLMModelPref : {
envKey : "NATIVE_LLM_MODEL_PREF" ,
checks : [ isDownloadedModel ] ,
} ,
2024-01-18 01:25:30 +01:00
NativeLLMTokenLimit : {
envKey : "NATIVE_LLM_MODEL_TOKEN_LIMIT" ,
checks : [ nonZero ] ,
} ,
2024-02-06 18:17:51 +01:00
// Hugging Face LLM Inference Settings
HuggingFaceLLMEndpoint : {
envKey : "HUGGING_FACE_LLM_ENDPOINT" ,
checks : [ isNotEmpty , isValidURL , validHuggingFaceEndpoint ] ,
} ,
HuggingFaceLLMAccessToken : {
envKey : "HUGGING_FACE_LLM_API_KEY" ,
checks : [ isNotEmpty ] ,
} ,
HuggingFaceLLMTokenLimit : {
envKey : "HUGGING_FACE_LLM_TOKEN_LIMIT" ,
checks : [ nonZero ] ,
} ,
2023-10-30 23:44:03 +01:00
EmbeddingEngine : {
envKey : "EMBEDDING_ENGINE" ,
checks : [ supportedEmbeddingModel ] ,
} ,
2023-11-14 22:49:31 +01:00
EmbeddingBasePath : {
envKey : "EMBEDDING_BASE_PATH" ,
2024-02-27 01:12:20 +01:00
checks : [ isNotEmpty , validDockerizedUrl ] ,
2023-11-14 22:49:31 +01:00
} ,
EmbeddingModelPref : {
envKey : "EMBEDDING_MODEL_PREF" ,
checks : [ isNotEmpty ] ,
} ,
2023-12-08 01:27:36 +01:00
EmbeddingModelMaxChunkLength : {
envKey : "EMBEDDING_MODEL_MAX_CHUNK_LENGTH" ,
checks : [ nonZero ] ,
} ,
2023-10-30 23:44:03 +01:00
2023-08-04 23:56:27 +02:00
// Vector Database Selection Settings
2023-06-26 20:38:38 +02:00
VectorDB : {
envKey : "VECTOR_DB" ,
checks : [ isNotEmpty , supportedVectorDB ] ,
} ,
2023-09-29 22:20:06 +02:00
// Chroma Options
2023-06-26 20:38:38 +02:00
ChromaEndpoint : {
envKey : "CHROMA_ENDPOINT" ,
2023-12-28 19:47:02 +01:00
checks : [ isValidURL , validChromaURL , validDockerizedUrl ] ,
2023-06-26 20:38:38 +02:00
} ,
2023-09-29 22:20:06 +02:00
ChromaApiHeader : {
envKey : "CHROMA_API_HEADER" ,
checks : [ ] ,
} ,
ChromaApiKey : {
envKey : "CHROMA_API_KEY" ,
checks : [ ] ,
} ,
// Weaviate Options
2023-08-09 03:02:30 +02:00
WeaviateEndpoint : {
envKey : "WEAVIATE_ENDPOINT" ,
2023-12-28 19:47:02 +01:00
checks : [ isValidURL , validDockerizedUrl ] ,
2023-08-09 03:02:30 +02:00
} ,
WeaviateApiKey : {
envKey : "WEAVIATE_API_KEY" ,
checks : [ ] ,
} ,
2023-09-29 22:20:06 +02:00
// QDrant Options
2023-08-16 00:26:44 +02:00
QdrantEndpoint : {
envKey : "QDRANT_ENDPOINT" ,
2023-12-28 19:47:02 +01:00
checks : [ isValidURL , validDockerizedUrl ] ,
2023-08-16 00:26:44 +02:00
} ,
QdrantApiKey : {
envKey : "QDRANT_API_KEY" ,
checks : [ ] ,
} ,
2023-06-26 20:38:38 +02:00
PineConeKey : {
envKey : "PINECONE_API_KEY" ,
checks : [ ] ,
} ,
PineConeIndex : {
envKey : "PINECONE_INDEX" ,
checks : [ ] ,
} ,
2023-08-04 23:56:27 +02:00
2024-01-12 22:23:57 +01:00
// Milvus Options
MilvusAddress : {
envKey : "MILVUS_ADDRESS" ,
checks : [ isValidURL , validDockerizedUrl ] ,
} ,
MilvusUsername : {
envKey : "MILVUS_USERNAME" ,
checks : [ isNotEmpty ] ,
} ,
MilvusPassword : {
envKey : "MILVUS_PASSWORD" ,
checks : [ isNotEmpty ] ,
} ,
2024-01-18 03:00:54 +01:00
// Zilliz Cloud Options
ZillizEndpoint : {
envKey : "ZILLIZ_ENDPOINT" ,
checks : [ isValidURL ] ,
} ,
ZillizApiToken : {
envKey : "ZILLIZ_API_TOKEN" ,
checks : [ isNotEmpty ] ,
} ,
2024-01-26 22:07:53 +01:00
// Astra DB Options
AstraDBApplicationToken : {
envKey : "ASTRA_DB_APPLICATION_TOKEN" ,
checks : [ isNotEmpty ] ,
} ,
AstraDBEndpoint : {
envKey : "ASTRA_DB_ENDPOINT" ,
checks : [ isNotEmpty ] ,
} ,
2024-01-10 21:35:30 +01:00
// Together Ai Options
TogetherAiApiKey : {
envKey : "TOGETHER_AI_API_KEY" ,
checks : [ isNotEmpty ] ,
} ,
TogetherAiModelPref : {
envKey : "TOGETHER_AI_MODEL_PREF" ,
checks : [ isNotEmpty ] ,
} ,
2024-02-22 21:48:57 +01:00
// Perplexity Options
PerplexityApiKey : {
envKey : "PERPLEXITY_API_KEY" ,
checks : [ isNotEmpty ] ,
} ,
PerplexityModelPref : {
envKey : "PERPLEXITY_MODEL_PREF" ,
checks : [ isNotEmpty ] ,
} ,
2024-02-24 02:18:58 +01:00
// OpenRouter Options
OpenRouterApiKey : {
envKey : "OPENROUTER_API_KEY" ,
checks : [ isNotEmpty ] ,
} ,
OpenRouterModelPref : {
envKey : "OPENROUTER_MODEL_PREF" ,
checks : [ isNotEmpty ] ,
} ,
2024-03-06 23:48:38 +01:00
// Groq Options
GroqApiKey : {
envKey : "GROQ_API_KEY" ,
checks : [ isNotEmpty ] ,
} ,
GroqModelPref : {
envKey : "GROQ_MODEL_PREF" ,
checks : [ isNotEmpty ] ,
} ,
2024-03-14 23:43:26 +01:00
// Whisper (transcription) providers
WhisperProvider : {
envKey : "WHISPER_PROVIDER" ,
checks : [ isNotEmpty , supportedTranscriptionProvider ] ,
postUpdate : [ ] ,
} ,
2023-08-04 23:56:27 +02:00
// System Settings
2023-07-21 00:25:47 +02:00
AuthToken : {
envKey : "AUTH_TOKEN" ,
2023-09-29 19:44:40 +02:00
checks : [ requiresForceMode ] ,
2023-07-21 00:25:47 +02:00
} ,
JWTSecret : {
envKey : "JWT_SECRET" ,
2023-09-29 19:44:40 +02:00
checks : [ requiresForceMode ] ,
2023-07-21 00:25:47 +02:00
} ,
2024-03-15 00:56:15 +01:00
DisableTelemetry : {
envKey : "DISABLE_TELEMETRY" ,
checks : [ ] ,
} ,
2023-06-26 20:38:38 +02:00
} ;
function isNotEmpty ( input = "" ) {
return ! input || input . length === 0 ? "Value cannot be empty" : null ;
}
2023-11-09 21:33:21 +01:00
function nonZero ( input = "" ) {
if ( isNaN ( Number ( input ) ) ) return "Value must be a number" ;
return Number ( input ) <= 0 ? "Value must be greater than zero" : null ;
}
2023-06-26 20:38:38 +02:00
function isValidURL ( input = "" ) {
try {
new URL ( input ) ;
return null ;
} catch ( e ) {
return "URL is not a valid URL." ;
}
}
function validOpenAIKey ( input = "" ) {
return input . startsWith ( "sk-" ) ? null : "OpenAI Key must start with sk-" ;
}
2023-10-30 23:44:03 +01:00
function validAnthropicApiKey ( input = "" ) {
return input . startsWith ( "sk-ant-" )
? null
: "Anthropic Key must start with sk-ant-" ;
}
2023-11-14 21:31:44 +01:00
function validLLMExternalBasePath ( input = "" ) {
2023-11-09 21:33:21 +01:00
try {
new URL ( input ) ;
if ( ! input . includes ( "v1" ) ) return "URL must include /v1" ;
if ( input . split ( "" ) . slice ( - 1 ) ? . [ 0 ] === "/" )
return "URL cannot end with a slash" ;
return null ;
} catch {
return "Not a valid URL" ;
}
}
2023-12-28 02:21:47 +01:00
function validOllamaLLMBasePath ( input = "" ) {
try {
new URL ( input ) ;
if ( input . split ( "" ) . slice ( - 1 ) ? . [ 0 ] === "/" )
return "URL cannot end with a slash" ;
return null ;
} catch {
return "Not a valid URL" ;
}
}
2023-08-04 23:56:27 +02:00
function supportedLLM ( input = "" ) {
2024-01-10 21:35:30 +01:00
const validSelection = [
2023-12-07 23:48:27 +01:00
"openai" ,
"azure" ,
"anthropic" ,
2023-12-28 02:08:03 +01:00
"gemini" ,
2023-12-07 23:48:27 +01:00
"lmstudio" ,
"localai" ,
2023-12-28 02:21:47 +01:00
"ollama" ,
2023-12-07 23:48:27 +01:00
"native" ,
2024-01-10 21:35:30 +01:00
"togetherai" ,
2024-01-17 23:42:05 +01:00
"mistral" ,
2024-02-06 18:17:51 +01:00
"huggingface" ,
2024-02-22 21:48:57 +01:00
"perplexity" ,
2024-02-24 02:18:58 +01:00
"openrouter" ,
2024-03-06 23:48:38 +01:00
"groq" ,
2023-12-07 23:48:27 +01:00
] . includes ( input ) ;
2024-01-10 21:35:30 +01:00
return validSelection ? null : ` ${ input } is not a valid LLM provider. ` ;
2023-08-04 23:56:27 +02:00
}
2024-03-14 23:43:26 +01:00
function supportedTranscriptionProvider ( input = "" ) {
const validSelection = [ "openai" , "local" ] . includes ( input ) ;
return validSelection
? null
: ` ${ input } is not a valid transcription model provider. ` ;
}
2023-12-28 02:08:03 +01:00
function validGeminiModel ( input = "" ) {
const validModels = [ "gemini-pro" ] ;
return validModels . includes ( input )
? null
: ` Invalid Model type. Must be one of ${ validModels . join ( ", " ) } . ` ;
}
2023-10-30 23:44:03 +01:00
function validAnthropicModel ( input = "" ) {
2024-03-06 23:57:47 +01:00
const validModels = [
"claude-instant-1.2" ,
"claude-2.0" ,
"claude-2.1" ,
"claude-3-opus-20240229" ,
"claude-3-sonnet-20240229" ,
2024-03-14 01:32:02 +01:00
"claude-3-haiku-20240307" ,
2024-03-06 23:57:47 +01:00
] ;
2023-10-30 23:44:03 +01:00
return validModels . includes ( input )
? null
: ` Invalid Model type. Must be one of ${ validModels . join ( ", " ) } . ` ;
}
function supportedEmbeddingModel ( input = "" ) {
2024-02-27 01:12:20 +01:00
const supported = [ "openai" , "azure" , "localai" , "native" , "ollama" ] ;
2023-10-30 23:44:03 +01:00
return supported . includes ( input )
? null
: ` Invalid Embedding model type. Must be one of ${ supported . join ( ", " ) } . ` ;
}
2023-06-26 20:38:38 +02:00
function supportedVectorDB ( input = "" ) {
2024-01-12 22:23:57 +01:00
const supported = [
"chroma" ,
"pinecone" ,
"lancedb" ,
"weaviate" ,
"qdrant" ,
"milvus" ,
2024-01-18 03:00:54 +01:00
"zilliz" ,
2024-01-26 22:07:53 +01:00
"astra" ,
2024-01-12 22:23:57 +01:00
] ;
2023-06-26 20:38:38 +02:00
return supported . includes ( input )
? null
: ` Invalid VectorDB type. Must be one of ${ supported . join ( ", " ) } . ` ;
}
function validChromaURL ( input = "" ) {
return input . slice ( - 1 ) === "/"
? ` Chroma Instance URL should not end in a trailing slash. `
: null ;
}
2023-08-04 23:56:27 +02:00
function validAzureURL ( input = "" ) {
try {
new URL ( input ) ;
if ( ! input . includes ( "openai.azure.com" ) )
return "URL must include openai.azure.com" ;
return null ;
} catch {
return "Not a valid URL" ;
}
}
2023-11-06 22:13:53 +01:00
function validOpenAiTokenLimit ( input = "" ) {
const tokenLimit = Number ( input ) ;
if ( isNaN ( tokenLimit ) ) return "Token limit is not a number" ;
2024-01-02 21:49:48 +01:00
if ( ! [ 4_096 , 16_384 , 8_192 , 32_768 , 128_000 ] . includes ( tokenLimit ) )
2023-11-06 22:13:53 +01:00
return "Invalid OpenAI token limit." ;
return null ;
}
2023-09-29 19:44:40 +02:00
function requiresForceMode ( _ , forceModeEnabled = false ) {
return forceModeEnabled === true ? null : "Cannot set this setting." ;
}
2023-12-07 23:48:27 +01:00
function isDownloadedModel ( input = "" ) {
const fs = require ( "fs" ) ;
const path = require ( "path" ) ;
const storageDir = path . resolve (
process . env . STORAGE _DIR
? path . resolve ( process . env . STORAGE _DIR , "models" , "downloaded" )
: path . resolve ( _ _dirname , ` ../../storage/models/downloaded ` )
) ;
if ( ! fs . existsSync ( storageDir ) ) return false ;
const files = fs
. readdirSync ( storageDir )
. filter ( ( file ) => file . includes ( ".gguf" ) ) ;
return files . includes ( input ) ;
}
2024-04-02 19:34:50 +02:00
async function validDockerizedUrl ( input = "" ) {
2023-12-28 19:47:02 +01:00
if ( process . env . ANYTHING _LLM _RUNTIME !== "docker" ) return null ;
2024-04-02 19:34:50 +02:00
2023-12-28 19:47:02 +01:00
try {
2024-04-02 19:34:50 +02:00
const { isPortInUse , getLocalHosts } = require ( "./portAvailabilityChecker" ) ;
const localInterfaces = getLocalHosts ( ) ;
const url = new URL ( input ) ;
const hostname = url . hostname . toLowerCase ( ) ;
const port = parseInt ( url . port , 10 ) ;
// If not a loopback, skip this check.
if ( ! localInterfaces . includes ( hostname ) ) return null ;
if ( isNaN ( port ) ) return "Invalid URL: Port is not specified or invalid" ;
const isPortAvailableFromDocker = await isPortInUse ( port , hostname ) ;
if ( isPortAvailableFromDocker )
return "Port is not running a reachable service on loopback address from inside the AnythingLLM container. Please use host.docker.internal (for linux use 172.17.0.1), a real machine ip, or domain to connect to your service." ;
} catch ( error ) {
console . error ( error . message ) ;
return "An error occurred while validating the URL" ;
}
2023-12-28 19:47:02 +01:00
return null ;
}
2024-02-06 18:17:51 +01:00
function validHuggingFaceEndpoint ( input = "" ) {
return input . slice ( - 6 ) !== ".cloud"
? ` Your HF Endpoint should end in ".cloud" `
: null ;
}
2024-01-17 21:59:25 +01:00
// If the LLMProvider has changed we need to reset all workspace model preferences to
// null since the provider<>model name combination will be invalid for whatever the new
// provider is.
async function wipeWorkspaceModelPreference ( key , prev , next ) {
if ( prev === next ) return ;
const { Workspace } = require ( "../../models/workspace" ) ;
await Workspace . resetWorkspaceChatModels ( ) ;
}
2023-06-26 20:38:38 +02:00
// This will force update .env variables which for any which reason were not able to be parsed or
// read from an ENV file as this seems to be a complicating step for many so allowing people to write
// to the process will at least alleviate that issue. It does not perform comprehensive validity checks or sanity checks
// and is simply for debugging when the .env not found issue many come across.
[FEAT] Automated audit logging (#667)
* WIP event logging - new table for events and new settings view for viewing
* WIP add logging
* UI for log rows
* rename files to Logging to prevent getting gitignore
* add metadata for all logging events and colored badges in logs page
* remove unneeded comment
* cleanup namespace for logging
* clean up backend calls
* update logging to show to => from settings changes
* add logging for invitations, created, deleted, and accepted
* add logging for user created, updated, suspended, or removed
* add logging for workspace deleted
* add logging for chat logs exported
* add logging for API keys, LLM, embedder, vector db, embed chat, and reset button
* modify event logs
* update to event log types
* simplify rendering of event badges
---------
Co-authored-by: timothycarambat <rambat1010@gmail.com>
2024-02-07 00:21:40 +01:00
async function updateENV ( newENVs = { } , force = false , userId = null ) {
2023-06-26 20:38:38 +02:00
let error = "" ;
const validKeys = Object . keys ( KEY _MAPPING ) ;
2023-08-04 00:59:51 +02:00
const ENV _KEYS = Object . keys ( newENVs ) . filter (
( key ) => validKeys . includes ( key ) && ! newENVs [ key ] . includes ( "******" ) // strip out answers where the value is all asterisks
2023-06-26 20:38:38 +02:00
) ;
const newValues = { } ;
2024-01-17 21:59:25 +01:00
for ( const key of ENV _KEYS ) {
const { envKey , checks , postUpdate = [ ] } = KEY _MAPPING [ key ] ;
const prevValue = process . env [ envKey ] ;
const nextValue = newENVs [ key ] ;
2023-06-26 20:38:38 +02:00
2024-04-02 19:34:50 +02:00
const errors = await executeValidationChecks ( checks , nextValue , force ) ;
2023-06-26 20:38:38 +02:00
if ( errors . length > 0 ) {
error += errors . join ( "\n" ) ;
2024-01-17 21:59:25 +01:00
break ;
2023-06-26 20:38:38 +02:00
}
2024-01-17 21:59:25 +01:00
newValues [ key ] = nextValue ;
process . env [ envKey ] = nextValue ;
for ( const postUpdateFunc of postUpdate )
await postUpdateFunc ( key , prevValue , nextValue ) ;
}
2023-06-26 20:38:38 +02:00
[FEAT] Automated audit logging (#667)
* WIP event logging - new table for events and new settings view for viewing
* WIP add logging
* UI for log rows
* rename files to Logging to prevent getting gitignore
* add metadata for all logging events and colored badges in logs page
* remove unneeded comment
* cleanup namespace for logging
* clean up backend calls
* update logging to show to => from settings changes
* add logging for invitations, created, deleted, and accepted
* add logging for user created, updated, suspended, or removed
* add logging for workspace deleted
* add logging for chat logs exported
* add logging for API keys, LLM, embedder, vector db, embed chat, and reset button
* modify event logs
* update to event log types
* simplify rendering of event badges
---------
Co-authored-by: timothycarambat <rambat1010@gmail.com>
2024-02-07 00:21:40 +01:00
await logChangesToEventLog ( newValues , userId ) ;
2023-06-26 20:38:38 +02:00
return { newValues , error : error ? . length > 0 ? error : false } ;
}
2024-04-02 19:34:50 +02:00
async function executeValidationChecks ( checks , value , force ) {
const results = await Promise . all (
checks . map ( ( validator ) => validator ( value , force ) )
) ;
return results . filter ( ( err ) => typeof err === "string" ) ;
}
[FEAT] Automated audit logging (#667)
* WIP event logging - new table for events and new settings view for viewing
* WIP add logging
* UI for log rows
* rename files to Logging to prevent getting gitignore
* add metadata for all logging events and colored badges in logs page
* remove unneeded comment
* cleanup namespace for logging
* clean up backend calls
* update logging to show to => from settings changes
* add logging for invitations, created, deleted, and accepted
* add logging for user created, updated, suspended, or removed
* add logging for workspace deleted
* add logging for chat logs exported
* add logging for API keys, LLM, embedder, vector db, embed chat, and reset button
* modify event logs
* update to event log types
* simplify rendering of event badges
---------
Co-authored-by: timothycarambat <rambat1010@gmail.com>
2024-02-07 00:21:40 +01:00
async function logChangesToEventLog ( newValues = { } , userId = null ) {
const { EventLogs } = require ( "../../models/eventLogs" ) ;
const eventMapping = {
LLMProvider : "update_llm_provider" ,
EmbeddingEngine : "update_embedding_engine" ,
VectorDB : "update_vector_db" ,
} ;
for ( const [ key , eventName ] of Object . entries ( eventMapping ) ) {
if ( ! newValues . hasOwnProperty ( key ) ) continue ;
await EventLogs . logEvent ( eventName , { } , userId ) ;
}
return ;
}
2023-08-16 01:00:27 +02:00
async function dumpENV ( ) {
const fs = require ( "fs" ) ;
const path = require ( "path" ) ;
const frozenEnvs = { } ;
const protectedKeys = [
... Object . values ( KEY _MAPPING ) . map ( ( values ) => values . envKey ) ,
"STORAGE_DIR" ,
"SERVER_PORT" ,
2023-12-05 18:13:06 +01:00
// Password Schema Keys if present.
"PASSWORDMINCHAR" ,
"PASSWORDMAXCHAR" ,
"PASSWORDLOWERCASE" ,
"PASSWORDUPPERCASE" ,
"PASSWORDNUMERIC" ,
"PASSWORDSYMBOL" ,
"PASSWORDREQUIREMENTS" ,
2024-01-19 03:13:24 +01:00
// HTTPS SETUP KEYS
"ENABLE_HTTPS" ,
"HTTPS_CERT_PATH" ,
"HTTPS_KEY_PATH" ,
// DISABLED TELEMETRY
"DISABLE_TELEMETRY" ,
2023-08-16 01:00:27 +02:00
] ;
2024-03-29 21:03:05 +01:00
// Simple sanitization of each value to prevent ENV injection via newline or quote escaping.
function sanitizeValue ( value ) {
const offendingChars =
/[\n\r\t\v\f\u0085\u00a0\u1680\u180e\u2000-\u200a\u2028\u2029\u202f\u205f\u3000"'`#]/ ;
const firstOffendingCharIndex = value . search ( offendingChars ) ;
if ( firstOffendingCharIndex === - 1 ) return value ;
return value . substring ( 0 , firstOffendingCharIndex ) ;
}
2023-08-16 01:00:27 +02:00
for ( const key of protectedKeys ) {
const envValue = process . env ? . [ key ] || null ;
if ( ! envValue ) continue ;
frozenEnvs [ key ] = process . env ? . [ key ] || null ;
}
var envResult = ` # Auto-dump ENV from system call on ${ new Date ( ) . toTimeString ( ) } \n ` ;
envResult += Object . entries ( frozenEnvs )
2024-03-29 21:03:05 +01:00
. map ( ( [ key , value ] ) => ` ${ key } =' ${ sanitizeValue ( value ) } ' ` )
2023-08-16 01:00:27 +02:00
. join ( "\n" ) ;
const envPath = path . join ( _ _dirname , "../../.env" ) ;
fs . writeFileSync ( envPath , envResult , { encoding : "utf8" , flag : "w" } ) ;
return true ;
}
2023-06-26 20:38:38 +02:00
module . exports = {
2023-08-16 01:00:27 +02:00
dumpENV ,
2023-06-26 20:38:38 +02:00
updateENV ,
} ;