From 7fdc8620424fb4020fdd1969f4f27c5ac41ae11d Mon Sep 17 00:00:00 2001 From: Dan Orlando Date: Thu, 18 May 2023 11:09:31 -0700 Subject: [PATCH] Build/Refactor: lint pre-commit hook and reformat repo to spec (#314) * build/refactor: move lint/prettier packages to project root, install husky, add pre-commit hook * refactor: reformat files * build: put full eslintrc back with all rules --- .eslintrc.js | 113 + .husky/pre-commit | 5 + .prettierrc.js | 19 + api/.eslintrc.js | 39 - api/.prettierrc | 22 - api/app/clients/bingai.js | 3 +- api/app/clients/chatgpt-browser.js | 6 +- api/app/clients/chatgpt-client.js | 24 +- api/app/google/GoogleClient.js | 13 +- api/app/stream.js | 5 +- api/lib/parse/getCitations.js | 4 +- api/lib/utils/mergeSort.js | 10 +- api/middleware/requireLocalAuth.js | 2 +- api/models/Message.js | 14 +- api/models/Prompt.js | 33 +- api/models/User.js | 1 - api/models/plugins/mongoMeili.js | 11 +- api/models/schema/tokenSchema.js | 12 +- api/server/controllers/auth.controller.js | 52 +- api/server/index.js | 11 +- api/server/routes/ask/askBingAI.js | 21 +- api/server/routes/ask/askChatGPTBrowser.js | 8 +- api/server/routes/ask/askOpenAI.js | 13 +- api/server/routes/auth.js | 2 +- api/server/routes/endpoints.js | 13 +- api/server/routes/index.js | 2 +- api/server/routes/oauth.js | 2 +- api/server/routes/search.js | 18 +- api/server/services/auth.service.js | 9 +- api/strategies/facebookStrategy.js | 8 +- api/strategies/localStrategy.js | 1 - api/strategies/validators.js | 2 +- api/utils/LoggingSystem.js | 34 +- api/utils/genAzureEndpoints.js | 6 +- api/utils/migrateDataToFirstUser.js | 13 +- api/utils/sendEmail.js | 20 +- client/.eslintrc.js | 31 - client/.prettierrc | 22 - client/package.json | 8 - client/src/App.jsx | 9 +- client/src/components/Auth/Login.tsx | 110 +- client/src/components/Auth/Registration.tsx | 152 +- .../components/Auth/RequestPasswordReset.tsx | 55 +- client/src/components/Auth/ResetPassword.tsx | 249 +- client/src/components/Auth/index.ts | 2 +- .../components/Conversations/Conversation.jsx | 19 +- .../components/Conversations/DeleteButton.jsx | 14 +- client/src/components/Conversations/Pages.jsx | 4 +- .../components/Conversations/RenameButton.jsx | 2 +- client/src/components/Conversations/index.jsx | 8 +- .../components/Endpoints/BingAI/Settings.jsx | 31 +- .../components/Endpoints/EditPresetDialog.jsx | 32 +- .../Endpoints/EndpointOptionsDialog.jsx | 18 +- .../components/Endpoints/Google/Examples.jsx | 15 +- .../Endpoints/Google/OptionHover.jsx | 3 +- .../components/Endpoints/Google/Settings.jsx | 63 +- .../components/Endpoints/OpenAI/Settings.jsx | 62 +- .../Endpoints/SaveAsPresetDialog.jsx | 10 +- .../src/components/Input/AdjustToneButton.jsx | 6 +- .../components/Input/BingAIOptions/index.jsx | 7 +- .../components/Input/ChatGPTOptions/index.jsx | 2 +- client/src/components/Input/Footer.jsx | 8 +- .../components/Input/GoogleOptions/index.jsx | 2 +- .../NewConversationMenu/EndpointItem.jsx | 6 +- .../NewConversationMenu/EndpointItems.jsx | 7 +- .../Input/NewConversationMenu/FileUpload.jsx | 19 +- .../Input/NewConversationMenu/PresetItem.jsx | 12 +- .../Input/NewConversationMenu/index.jsx | 35 +- .../components/Input/OpenAIOptions/index.jsx | 13 +- client/src/components/Input/RowButton.jsx | 4 +- .../Input/SetTokenDialog/InputWithLabel.jsx | 5 +- .../components/Input/SetTokenDialog/index.jsx | 21 +- client/src/components/Input/SubmitButton.jsx | 9 +- client/src/components/Input/index.jsx | 10 +- .../src/components/MessageHandler/index.jsx | 19 +- .../components/Messages/Content/CodeBlock.jsx | 10 +- .../components/Messages/Content/Content.jsx | 15 +- .../components/Messages/Content/SubRow.jsx | 6 +- .../src/components/Messages/HoverButtons.jsx | 8 +- client/src/components/Messages/Message.jsx | 22 +- .../src/components/Messages/MessageHeader.jsx | 6 +- .../src/components/Messages/MultiMessage.jsx | 26 +- .../components/Messages/ScrollToBottom.jsx | 12 +- .../src/components/Messages/SiblingSwitch.jsx | 60 +- client/src/components/Messages/index.jsx | 7 +- client/src/components/Nav/ClearConvos.jsx | 4 +- .../Nav/ExportConversation/ExportModel.jsx | 70 +- .../Nav/ExportConversation/index.jsx | 7 +- client/src/components/Nav/Logout.jsx | 4 +- client/src/components/Nav/MobileNav.jsx | 43 +- client/src/components/Nav/NavLinks.jsx | 19 +- client/src/components/Nav/NewChat.jsx | 16 +- client/src/components/Nav/SearchBar.jsx | 5 +- client/src/components/Nav/index.jsx | 50 +- client/src/components/svg/BingChatIcon.jsx | 9 +- client/src/components/svg/BingIcon.jsx | 223 +- client/src/components/svg/CautionIcon.jsx | 14 +- client/src/components/svg/Clipboard.jsx | 9 +- client/src/components/svg/CrossIcon.jsx | 14 +- client/src/components/svg/DotsIcon.jsx | 18 +- client/src/components/svg/GPTIcon.jsx | 2 +- client/src/components/svg/LightModeIcon.jsx | 62 +- client/src/components/svg/LogOutIcon.jsx | 7 +- client/src/components/svg/Spinner.jsx | 56 +- .../src/components/svg/StopGeneratingIcon.jsx | 9 +- client/src/components/svg/SunIcon.jsx | 62 +- client/src/components/svg/TrashIcon.jsx | 14 +- client/src/components/svg/UserIcon.jsx | 6 +- client/src/components/ui/AlertDialog.tsx | 96 +- client/src/components/ui/Button.tsx | 52 +- client/src/components/ui/Checkbox.tsx | 22 +- client/src/components/ui/Dialog.tsx | 103 +- client/src/components/ui/DialogTemplate.jsx | 6 +- client/src/components/ui/Dropdown.jsx | 8 +- client/src/components/ui/DropdownMenu.tsx | 108 +- client/src/components/ui/HoverCard.tsx | 24 +- client/src/components/ui/Input.tsx | 37 +- client/src/components/ui/InputNumber.tsx | 35 +- client/src/components/ui/Label.tsx | 14 +- client/src/components/ui/Landing.jsx | 11 +- client/src/components/ui/ModelSelect.jsx | 11 +- client/src/components/ui/Prompt.jsx | 2 +- client/src/components/ui/SelectDropDown.jsx | 10 +- client/src/components/ui/Slider.tsx | 49 +- client/src/components/ui/Tabs.tsx | 33 +- client/src/components/ui/Templates.jsx | 30 +- client/src/components/ui/Textarea.tsx | 19 +- client/src/data-provider/api-endpoints.ts | 28 +- client/src/data-provider/data-service.ts | 43 +- client/src/data-provider/index.ts | 2 +- .../src/data-provider/react-query-service.ts | 370 ++- client/src/data-provider/request.ts | 27 +- client/src/data-provider/types.ts | 187 +- client/src/hooks/ApiErrorBoundaryContext.tsx | 8 +- client/src/hooks/AuthContext.tsx | 65 +- client/src/hooks/ThemeContext.jsx | 2 +- client/src/hooks/useDebounce.js | 21 +- client/src/main.jsx | 1 - client/src/routes/Chat.jsx | 4 +- client/src/routes/Root.jsx | 14 +- client/src/store/conversations.js | 17 +- client/src/store/endpoints.js | 2 +- client/src/store/submission.js | 9 +- client/src/store/token.js | 4 +- client/src/utils/buildTree.js | 2 +- client/src/utils/cleanupPreset.js | 4 +- client/src/utils/getDefaultConversation.js | 10 +- client/src/utils/getIcon.jsx | 27 +- client/src/utils/handleSubmit.js | 25 +- client/src/utils/screenshotContext.jsx | 4 +- client/vite.config.ts | 119 +- e2e/playwright.config.js | 16 +- e2e/specs/landing.spec.js | 7 +- eslintrc-stripped.js | 91 + lint-staged.config.js | 4 + package-lock.json | 2864 ++++++++++++++++- package.json | 22 +- 157 files changed, 4836 insertions(+), 2403 deletions(-) create mode 100644 .eslintrc.js create mode 100755 .husky/pre-commit create mode 100644 .prettierrc.js delete mode 100644 api/.eslintrc.js delete mode 100644 api/.prettierrc delete mode 100644 client/.eslintrc.js delete mode 100644 client/.prettierrc create mode 100644 eslintrc-stripped.js create mode 100644 lint-staged.config.js diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 000000000..7d452e7d5 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,113 @@ +module.exports = { + env: { + browser: true, + es2021: true, + node: true, + commonjs: true, + es6: true + }, + extends: [ + 'eslint:recommended', + 'plugin:react/recommended', + 'plugin:react-hooks/recommended', + 'prettier' + ], + parser: '@typescript-eslint/parser', + parserOptions: { + ecmaVersion: 'latest', + sourceType: 'module', + ecmaFeatures: { + jsx: true + } + }, + plugins: ['react', 'react-hooks', '@typescript-eslint'], + rules: { + '@typescript-eslint/ban-ts-comment': ['error', { 'ts-ignore': 'allow-with-description' }], + indent: ['error', 2, { SwitchCase: 1 }], + 'max-len': [ + 'error', + { + code: 150, + ignoreStrings: true, + ignoreTemplateLiterals: true, + ignoreComments: true + } + ], + 'linebreak-style': 0, + // "arrow-parens": [2, "as-needed", { requireForBlockBody: true }], + // 'no-plusplus': ['error', { allowForLoopAfterthoughts: true }], + 'no-console': 'off', + 'import/extensions': 'off', + 'no-use-before-define': [ + 'error', + { + functions: false + } + ], + 'no-promise-executor-return': 'off', + 'no-param-reassign': 'off', + 'no-continue': 'off', + 'no-restricted-syntax': 'off', + 'react/prop-types': ['off'], + 'react/display-name': ['off'] + }, + overrides: [ + { + files: ['**/*.ts', '**/*.tsx'], + rules: { + 'no-unused-vars': 'off', // off because it conflicts with '@typescript-eslint/no-unused-vars' + 'react/display-name': 'off', + '@typescript-eslint/no-unused-vars': 'warn' + } + }, + { + files: ['rollup.config.js', '.eslintrc.js', 'jest.config.js'], + env: { + node: true + } + }, + { + files: [ + '**/*.test.js', + '**/*.test.jsx', + '**/*.test.ts', + '**/*.test.tsx', + '**/*.spec.js', + '**/*.spec.jsx', + '**/*.spec.ts', + '**/*.spec.tsx', + 'setupTests.js' + ], + env: { + jest: true, + node: true + }, + rules: { + 'react/display-name': 'off', + 'react/prop-types': 'off', + 'react/no-unescaped-entities': 'off' + } + }, + { + files: '**/*.+(ts)', + parser: '@typescript-eslint/parser', + parserOptions: { + project: './client/tsconfig.json' + }, + plugins: ['@typescript-eslint/eslint-plugin'], + extends: [ + 'plugin:@typescript-eslint/eslint-recommended', + 'plugin:@typescript-eslint/recommended' + ] + } + ], + settings: { + react: { + createClass: 'createReactClass', // Regex for Component Factory to use, + // default to "createReactClass" + pragma: 'React', // Pragma to use, default to "React" + fragment: 'Fragment', // Fragment to use (may be a property of ), default to "Fragment" + version: 'detect' // React version. "detect" automatically picks the version you have installed. + } + } +}; diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100755 index 000000000..7d5c4c84d --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,5 @@ +#!/usr/bin/env sh +. "$(dirname -- "$0")/_/husky.sh" + +npx lint-staged + diff --git a/.prettierrc.js b/.prettierrc.js new file mode 100644 index 000000000..7b3ddb6da --- /dev/null +++ b/.prettierrc.js @@ -0,0 +1,19 @@ +module.exports = { + printWidth: 100, + useTabs: false, + tabWidth: 2, + semi: true, + singleQuote: true, + // bracketSpacing: false, + trailingComma: 'none', + arrowParens: 'always', + embeddedLanguageFormatting: 'auto', + insertPragma: false, + proseWrap: 'preserve', + quoteProps: 'as-needed', + requirePragma: false, + rangeStart: 0, + endOfLine: 'auto', + jsxBracketSameLine: false, + jsxSingleQuote: false, +}; diff --git a/api/.eslintrc.js b/api/.eslintrc.js deleted file mode 100644 index 6b1e808b4..000000000 --- a/api/.eslintrc.js +++ /dev/null @@ -1,39 +0,0 @@ -module.exports = { - env: { - es2021: true, - node: true - }, - extends: ['eslint:recommended'], - overrides: [], - parserOptions: { - ecmaVersion: 'latest', - sourceType: 'module' - }, - rules: { - indent: ['error', 2, { SwitchCase: 1 }], - 'max-len': [ - 'error', - { - code: 150, - ignoreStrings: true, - ignoreTemplateLiterals: true, - ignoreComments: true - } - ], - 'linebreak-style': 0, - 'arrow-parens': [2, 'as-needed', { requireForBlockBody: true }], - // 'no-plusplus': ['error', { allowForLoopAfterthoughts: true }], - 'no-console': 'off', - 'import/extensions': 'off', - 'no-use-before-define': [ - 'error', - { - functions: false - } - ], - 'no-promise-executor-return': 'off', - 'no-param-reassign': 'off', - 'no-continue': 'off', - 'no-restricted-syntax': 'off' - } -}; diff --git a/api/.prettierrc b/api/.prettierrc deleted file mode 100644 index 1c37ff0e5..000000000 --- a/api/.prettierrc +++ /dev/null @@ -1,22 +0,0 @@ -{ - "arrowParens": "always", - "bracketSpacing": true, - "endOfLine": "lf", - "htmlWhitespaceSensitivity": "css", - "insertPragma": false, - "singleAttributePerLine": true, - "bracketSameLine": false, - "jsxBracketSameLine": false, - "jsxSingleQuote": false, - "printWidth": 110, - "proseWrap": "preserve", - "quoteProps": "as-needed", - "requirePragma": false, - "semi": true, - "singleQuote": true, - "tabWidth": 2, - "trailingComma": "none", - "useTabs": false, - "vueIndentScriptAndStyle": false, - "parser": "babel" -} \ No newline at end of file diff --git a/api/app/clients/bingai.js b/api/app/clients/bingai.js index 982973a88..c4ad3b9e0 100644 --- a/api/app/clients/bingai.js +++ b/api/app/clients/bingai.js @@ -23,7 +23,8 @@ const askBing = async ({ const bingAIClient = new BingAIClient({ // "_U" cookie from bing.com - userToken: process.env.BINGAI_TOKEN == 'user_provided' ? token : process.env.BINGAI_TOKEN ?? null, + userToken: + process.env.BINGAI_TOKEN == 'user_provided' ? token : process.env.BINGAI_TOKEN ?? null, // If the above doesn't work, provide all your cookies as a string instead // cookies: '', debug: false, diff --git a/api/app/clients/chatgpt-browser.js b/api/app/clients/chatgpt-browser.js index 540086dcd..0e8cc25fd 100644 --- a/api/app/clients/chatgpt-browser.js +++ b/api/app/clients/chatgpt-browser.js @@ -18,9 +18,11 @@ const browserClient = async ({ const clientOptions = { // Warning: This will expose your access token to a third party. Consider the risks before using this. - reverseProxyUrl: process.env.CHATGPT_REVERSE_PROXY || 'https://ai.fakeopen.com/api/conversation', + reverseProxyUrl: + process.env.CHATGPT_REVERSE_PROXY || 'https://ai.fakeopen.com/api/conversation', // Access token from https://chat.openai.com/api/auth/session - accessToken: process.env.CHATGPT_TOKEN == 'user_provided' ? token : process.env.CHATGPT_TOKEN ?? null, + accessToken: + process.env.CHATGPT_TOKEN == 'user_provided' ? token : process.env.CHATGPT_TOKEN ?? null, model: model, debug: false, proxy: process.env.PROXY || null, diff --git a/api/app/clients/chatgpt-client.js b/api/app/clients/chatgpt-client.js index 9cdb630ac..2ce7367a8 100644 --- a/api/app/clients/chatgpt-client.js +++ b/api/app/clients/chatgpt-client.js @@ -1,7 +1,7 @@ require('dotenv').config(); const { KeyvFile } = require('keyv-file'); const { genAzureEndpoint } = require('../../utils/genAzureEndpoints'); -const tiktoken = require("@dqbd/tiktoken"); +const tiktoken = require('@dqbd/tiktoken'); const encoding_for_model = tiktoken.encoding_for_model; const askClient = async ({ @@ -27,7 +27,7 @@ const askClient = async ({ const azure = process.env.AZURE_OPENAI_API_KEY ? true : false; if (promptPrefix == null) { - promptText = "You are ChatGPT, a large language model trained by OpenAI."; + promptText = 'You are ChatGPT, a large language model trained by OpenAI.'; } else { promptText = promptPrefix; } @@ -45,7 +45,7 @@ const askClient = async ({ }, chatGptLabel, promptPrefix, - proxy: process.env.PROXY || null, + proxy: process.env.PROXY || null // debug: true }; @@ -77,16 +77,16 @@ const askClient = async ({ const res = await client.sendMessage(text, { ...options, userId }); // return res; // create a new response object that includes the token counts -const newRes = { - ...res, - usage: { - prompt_tokens: prompt_tokens.length, - completion_tokens: text_tokens.length, - total_tokens: prompt_tokens.length + text_tokens.length - } -}; + const newRes = { + ...res, + usage: { + prompt_tokens: prompt_tokens.length, + completion_tokens: text_tokens.length, + total_tokens: prompt_tokens.length + text_tokens.length + } + }; -return newRes; + return newRes; }; module.exports = { askClient }; diff --git a/api/app/google/GoogleClient.js b/api/app/google/GoogleClient.js index d6f0eff3a..a494b1a69 100644 --- a/api/app/google/GoogleClient.js +++ b/api/app/google/GoogleClient.js @@ -3,7 +3,10 @@ const TextStream = require('../stream'); const { google } = require('googleapis'); const { Agent, ProxyAgent } = require('undici'); const { getMessages, saveMessage, saveConvo } = require('../../models'); -const { encoding_for_model: encodingForModel, get_encoding: getEncoding } = require('@dqbd/tiktoken'); +const { + encoding_for_model: encodingForModel, + get_encoding: getEncoding +} = require('@dqbd/tiktoken'); const tokenizersCache = {}; @@ -65,7 +68,8 @@ class GoogleAgent { // The max prompt tokens is determined by the max context tokens minus the max response tokens. // Earlier messages will be dropped until the prompt is within the limit. this.maxResponseTokens = this.modelOptions.maxOutputTokens || 1024; - this.maxPromptTokens = this.options.maxPromptTokens || this.maxContextTokens - this.maxResponseTokens; + this.maxPromptTokens = + this.options.maxPromptTokens || this.maxContextTokens - this.maxResponseTokens; if (this.maxPromptTokens + this.maxResponseTokens > this.maxContextTokens) { throw new Error( @@ -291,7 +295,10 @@ class GoogleAgent { try { const result = await this.getCompletion(message, messages, opts.abortController); blocked = result?.predictions?.[0]?.safetyAttributes?.blocked; - reply = result?.predictions?.[0]?.candidates?.[0]?.content || result?.predictions?.[0]?.content || ''; + reply = + result?.predictions?.[0]?.candidates?.[0]?.content || + result?.predictions?.[0]?.content || + ''; if (blocked === true) { reply = `Google blocked a proper response to your message:\n${JSON.stringify( result.predictions[0].safetyAttributes diff --git a/api/app/stream.js b/api/app/stream.js index 17a3a61b9..ec18f1236 100644 --- a/api/app/stream.js +++ b/api/app/stream.js @@ -16,10 +16,7 @@ class TextStream extends Readable { if (this.currentIndex < this.text.length) { setTimeout(() => { const remainingChars = this.text.length - this.currentIndex; - const chunkSize = Math.min( - this.randomInt(minChunkSize, maxChunkSize + 1), - remainingChars - ); + const chunkSize = Math.min(this.randomInt(minChunkSize, maxChunkSize + 1), remainingChars); const chunk = this.text.slice(this.currentIndex, this.currentIndex + chunkSize); this.push(chunk); diff --git a/api/lib/parse/getCitations.js b/api/lib/parse/getCitations.js index 0e36de695..69e142e8b 100644 --- a/api/lib/parse/getCitations.js +++ b/api/lib/parse/getCitations.js @@ -7,8 +7,8 @@ const getCitations = (res) => { if (!textBlocks) return ''; let links = textBlocks[textBlocks.length - 1]?.text.match(regex); if (links?.length === 0 || !links) return ''; - links = links.map((link) => link.trim()); + links = links.map(link => link.trim()); return links.join('\n'); }; -module.exports = getCitations; \ No newline at end of file +module.exports = getCitations; diff --git a/api/lib/utils/mergeSort.js b/api/lib/utils/mergeSort.js index 660d44566..b93e3e990 100644 --- a/api/lib/utils/mergeSort.js +++ b/api/lib/utils/mergeSort.js @@ -2,11 +2,11 @@ function mergeSort(arr, compareFn) { if (arr.length <= 1) { return arr; } - + const mid = Math.floor(arr.length / 2); const leftArr = arr.slice(0, mid); const rightArr = arr.slice(mid); - + return merge(mergeSort(leftArr, compareFn), mergeSort(rightArr, compareFn), compareFn); } @@ -14,7 +14,7 @@ function merge(leftArr, rightArr, compareFn) { const result = []; let leftIndex = 0; let rightIndex = 0; - + while (leftIndex < leftArr.length && rightIndex < rightArr.length) { if (compareFn(leftArr[leftIndex], rightArr[rightIndex]) < 0) { result.push(leftArr[leftIndex++]); @@ -22,8 +22,8 @@ function merge(leftArr, rightArr, compareFn) { result.push(rightArr[rightIndex++]); } } - + return result.concat(leftArr.slice(leftIndex)).concat(rightArr.slice(rightIndex)); } -module.exports = mergeSort; \ No newline at end of file +module.exports = mergeSort; diff --git a/api/middleware/requireLocalAuth.js b/api/middleware/requireLocalAuth.js index 84b3a85f0..b6a9c575f 100644 --- a/api/middleware/requireLocalAuth.js +++ b/api/middleware/requireLocalAuth.js @@ -19,7 +19,7 @@ const requireLocalAuth = (req, res, next) => { } if (!user) { log({ - title: '(requireLocalAuth) Error: No user', + title: '(requireLocalAuth) Error: No user' }); return res.status(422).send(info); } diff --git a/api/models/Message.js b/api/models/Message.js index 5c07af497..9fbdbd612 100644 --- a/api/models/Message.js +++ b/api/models/Message.js @@ -2,7 +2,7 @@ const Message = require('./schema/messageSchema'); module.exports = { Message, - + async saveMessage({ messageId, newMessageId, @@ -32,7 +32,7 @@ module.exports = { }, { upsert: true, new: true } ); - + return { messageId, conversationId, @@ -41,13 +41,12 @@ module.exports = { text, isCreatedByUser }; - } catch (err) { console.error(`Error saving message: ${err}`); throw new Error('Failed to save message.'); } }, - + async deleteMessagesSince({ messageId, conversationId }) { try { const message = await Message.findOne({ messageId }).exec(); @@ -57,27 +56,24 @@ module.exports = { .deleteMany({ createdAt: { $gt: message.createdAt } }) .exec(); } - } catch (err) { console.error(`Error deleting messages: ${err}`); throw new Error('Failed to delete messages.'); } }, - + async getMessages(filter) { try { return await Message.find(filter).sort({ createdAt: 1 }).exec(); - } catch (err) { console.error(`Error getting messages: ${err}`); throw new Error('Failed to get messages.'); } }, - + async deleteMessages(filter) { try { return await Message.deleteMany(filter).exec(); - } catch (err) { console.error(`Error deleting messages: ${err}`); throw new Error('Failed to delete messages.'); diff --git a/api/models/Prompt.js b/api/models/Prompt.js index f122d5af4..deb40b531 100644 --- a/api/models/Prompt.js +++ b/api/models/Prompt.js @@ -1,18 +1,21 @@ const mongoose = require('mongoose'); -const promptSchema = mongoose.Schema({ - title: { - type: String, - required: true +const promptSchema = mongoose.Schema( + { + title: { + type: String, + required: true + }, + prompt: { + type: String, + required: true + }, + category: { + type: String + } }, - prompt: { - type: String, - required: true - }, - category: { - type: String, - }, -}, { timestamps: true }); + { timestamps: true } +); const Prompt = mongoose.models.Prompt || mongoose.model('Prompt', promptSchema); @@ -31,7 +34,7 @@ module.exports = { }, getPrompts: async (filter) => { try { - return await Prompt.find(filter).exec() + return await Prompt.find(filter).exec(); } catch (error) { console.error(error); return { prompt: 'Error getting prompts' }; @@ -39,10 +42,10 @@ module.exports = { }, deletePrompts: async (filter) => { try { - return await Prompt.deleteMany(filter).exec() + return await Prompt.deleteMany(filter).exec(); } catch (error) { console.error(error); return { prompt: 'Error deleting prompts' }; } } -} \ No newline at end of file +}; diff --git a/api/models/User.js b/api/models/User.js index 9333e507f..bade1d78d 100644 --- a/api/models/User.js +++ b/api/models/User.js @@ -142,7 +142,6 @@ userSchema.methods.comparePassword = function (candidatePassword, callback) { }; module.exports.hashPassword = async (password) => { - const hashedPassword = await new Promise((resolve, reject) => { bcrypt.hash(password, 10, function (err, hash) { if (err) reject(err); diff --git a/api/models/plugins/mongoMeili.js b/api/models/plugins/mongoMeili.js index b428a9cd8..ef65da7d3 100644 --- a/api/models/plugins/mongoMeili.js +++ b/api/models/plugins/mongoMeili.js @@ -19,10 +19,7 @@ const createMeiliMongooseModel = function ({ index, indexName, client, attribute static async clearMeiliIndex() { await index.delete(); // await index.deleteAllDocuments(); - await this.collection.updateMany( - { _meiliIndex: true }, - { $set: { _meiliIndex: false } } - ); + await this.collection.updateMany({ _meiliIndex: true }, { $set: { _meiliIndex: false } }); } static async resetIndex() { @@ -67,7 +64,7 @@ const createMeiliMongooseModel = function ({ index, indexName, client, attribute return { ...results, [key]: 1 }; }, { _id: 1 } - ), + ) ); // Add additional data from mongodb into Meili search hits @@ -198,8 +195,8 @@ module.exports = function mongoMeili(schema, options) { if (Object.prototype.hasOwnProperty.call(schema.obj, 'messages')) { console.log('Syncing convos...'); mongoose.model('Conversation').syncWithMeili(); - } - + } + if (Object.prototype.hasOwnProperty.call(schema.obj, 'messageId')) { console.log('Syncing messages...'); mongoose.model('Message').syncWithMeili(); diff --git a/api/models/schema/tokenSchema.js b/api/models/schema/tokenSchema.js index 2142348e3..67fdf3e52 100644 --- a/api/models/schema/tokenSchema.js +++ b/api/models/schema/tokenSchema.js @@ -1,22 +1,22 @@ -const mongoose = require("mongoose"); +const mongoose = require('mongoose'); const Schema = mongoose.Schema; const tokenSchema = new Schema({ userId: { type: Schema.Types.ObjectId, required: true, - ref: "user", + ref: 'user' }, token: { type: String, - required: true, + required: true }, createdAt: { type: Date, required: true, default: Date.now, - expires: 900, - }, + expires: 900 + } }); -module.exports = mongoose.model("Token", tokenSchema); \ No newline at end of file +module.exports = mongoose.model('Token', tokenSchema); diff --git a/api/server/controllers/auth.controller.js b/api/server/controllers/auth.controller.js index eef3d5276..f0099bb60 100644 --- a/api/server/controllers/auth.controller.js +++ b/api/server/controllers/auth.controller.js @@ -3,28 +3,26 @@ const { logoutUser, registerUser, requestPasswordReset, - resetPassword, -} = require("../services/auth.service"); + resetPassword +} = require('../services/auth.service'); const isProduction = process.env.NODE_ENV === 'production'; const loginController = async (req, res) => { try { const token = req.user.generateToken(); - const user = await loginUser(req.user) - if(user) { + const user = await loginUser(req.user); + if (user) { res.cookie('token', token, { expires: new Date(Date.now() + eval(process.env.SESSION_EXPIRY)), httpOnly: false, secure: isProduction }); res.status(200).send({ token, user }); - } - else { + } else { return res.status(400).json({ message: 'Invalid credentials' }); } - } - catch (err) { + } catch (err) { console.log(err); return res.status(500).json({ message: err.message }); } @@ -35,22 +33,20 @@ const logoutController = async (req, res) => { const { refreshToken } = signedCookies; try { const logout = await logoutUser(req.user, refreshToken); - console.log(logout) + console.log(logout); const { status, message } = logout; if (status === 200) { res.clearCookie('token'); res.clearCookie('refreshToken'); res.status(status).send({ message }); - } - else { + } else { res.status(status).send({ message }); } - } - catch (err) { + } catch (err) { console.log(err); return res.status(500).json({ message: err.message }); } -} +}; const registrationController = async (req, res) => { try { @@ -65,13 +61,11 @@ const registrationController = async (req, res) => { secure: isProduction }); res.status(status).send({ user }); - } - else { + } else { const { status, message } = response; res.status(status).send({ message }); } - } - catch (err) { + } catch (err) { console.log(err); return res.status(500).json({ message: err.message }); } @@ -83,17 +77,13 @@ const getUserController = async (req, res) => { const resetPasswordRequestController = async (req, res) => { try { - const resetService = await requestPasswordReset( - req.body.email - ); + const resetService = await requestPasswordReset(req.body.email); if (resetService.link) { return res.status(200).json(resetService); - } - else { + } else { return res.status(400).json(resetService); } - } - catch (e) { + } catch (e) { console.log(e); return res.status(400).json({ message: e.message }); } @@ -106,14 +96,12 @@ const resetPasswordController = async (req, res) => { req.body.token, req.body.password ); - if(resetPasswordService instanceof Error) { + if (resetPasswordService instanceof Error) { return res.status(400).json(resetPasswordService); - } - else { + } else { return res.status(200).json(resetPasswordService); } - } - catch (e) { + } catch (e) { console.log(e); return res.status(400).json({ message: e.message }); } @@ -176,5 +164,5 @@ module.exports = { refreshController, registrationController, resetPasswordRequestController, - resetPasswordController, -}; \ No newline at end of file + resetPasswordController +}; diff --git a/api/server/index.js b/api/server/index.js index 15b8e7507..043cd5502 100644 --- a/api/server/index.js +++ b/api/server/index.js @@ -30,13 +30,13 @@ const projectPath = path.join(__dirname, '..', '..', 'client'); app.use(passport.initialize()); require('../strategies/jwtStrategy'); require('../strategies/localStrategy'); - if(process.env.GOOGLE_CLIENT_ID && process.env.GOOGLE_CLIENT_SECRET) { + if (process.env.GOOGLE_CLIENT_ID && process.env.GOOGLE_CLIENT_SECRET) { require('../strategies/googleStrategy'); } - if(process.env.FACEBOOK_CLIENT_ID && process.env.FACEBOOK_CLIENT_SECRET) { + if (process.env.FACEBOOK_CLIENT_ID && process.env.FACEBOOK_CLIENT_SECRET) { require('../strategies/facebookStrategy'); } - app.use('/oauth', routes.oauth) + app.use('/oauth', routes.oauth); // api endpoint app.use('/api/auth', routes.auth); app.use('/api/search', routes.search); @@ -48,8 +48,6 @@ const projectPath = path.join(__dirname, '..', '..', 'client'); app.use('/api/tokenizer', routes.tokenizer); app.use('/api/endpoints', routes.endpoints); - - // static files app.get('/*', function (req, res) { res.sendFile(path.join(projectPath, 'dist', 'index.html')); @@ -60,7 +58,8 @@ const projectPath = path.join(__dirname, '..', '..', 'client'); console.log( `Server listening on all interface at port ${port}. Use http://localhost:${port} to access it` ); - else console.log(`Server listening at http://${host == '0.0.0.0' ? 'localhost' : host}:${port}`); + else + console.log(`Server listening at http://${host == '0.0.0.0' ? 'localhost' : host}:${port}`); }); })(); diff --git a/api/server/routes/ask/askBingAI.js b/api/server/routes/ask/askBingAI.js index 2d1f92418..ef319e7cd 100644 --- a/api/server/routes/ask/askBingAI.js +++ b/api/server/routes/ask/askBingAI.js @@ -147,11 +147,13 @@ const ask = async ({ const newConversationId = endpointOption?.jailbreak ? response.jailbreakConversationId : response.conversationId || conversationId; - const newUserMassageId = response.parentMessageId || response.details.requestId || userMessageId; + const newUserMassageId = + response.parentMessageId || response.details.requestId || userMessageId; const newResponseMessageId = response.messageId || response.details.messageId; // STEP1 generate response message - response.text = response.response || response.details.spokenText || '**Bing refused to answer.**'; + response.text = + response.response || response.details.spokenText || '**Bing refused to answer.**'; let responseMessage = { conversationId: newConversationId, @@ -161,7 +163,8 @@ const ask = async ({ sender: endpointOption?.jailbreak ? 'Sydney' : 'BingAI', text: await handleText(response, true), suggestions: - response.details.suggestedResponses && response.details.suggestedResponses.map((s) => s.text), + response.details.suggestedResponses && + response.details.suggestedResponses.map(s => s.text), unfinished: false, cancelled: false, error: false @@ -215,7 +218,11 @@ const ask = async ({ // If response has parentMessageId, the fake userMessage.messageId should be updated to the real one. if (!overrideParentMessageId) - await saveMessage({ ...userMessage, messageId: userMessageId, newMessageId: newUserMassageId }); + await saveMessage({ + ...userMessage, + messageId: userMessageId, + newMessageId: newUserMassageId + }); userMessageId = newUserMassageId; sendMessage(res, { @@ -228,7 +235,11 @@ const ask = async ({ res.end(); if (userParentMessageId == '00000000-0000-0000-0000-000000000000') { - const title = await titleConvo({ endpoint: endpointOption?.endpoint, text, response: responseMessage }); + const title = await titleConvo({ + endpoint: endpointOption?.endpoint, + text, + response: responseMessage + }); await saveConvo(req.user.id, { conversationId: conversationId, diff --git a/api/server/routes/ask/askChatGPTBrowser.js b/api/server/routes/ask/askChatGPTBrowser.js index 16c67005f..068e3f4db 100644 --- a/api/server/routes/ask/askChatGPTBrowser.js +++ b/api/server/routes/ask/askChatGPTBrowser.js @@ -39,7 +39,7 @@ router.post('/', requireJwtAuth, async (req, res) => { }; const availableModels = getChatGPTBrowserModels(); - if (availableModels.find((model) => model === endpointOption.model) === undefined) + if (availableModels.find(model => model === endpointOption.model) === undefined) return handleError(res, { text: 'Illegal request: model' }); console.log('ask log', { @@ -180,7 +180,11 @@ const ask = async ({ // If response has parentMessageId, the fake userMessage.messageId should be updated to the real one. if (!overrideParentMessageId) - await saveMessage({ ...userMessage, messageId: userMessageId, newMessageId: newUserMassageId }); + await saveMessage({ + ...userMessage, + messageId: userMessageId, + newMessageId: newUserMassageId + }); userMessageId = newUserMassageId; sendMessage(res, { diff --git a/api/server/routes/ask/askOpenAI.js b/api/server/routes/ask/askOpenAI.js index 38e700db1..6a7515417 100644 --- a/api/server/routes/ask/askOpenAI.js +++ b/api/server/routes/ask/askOpenAI.js @@ -238,7 +238,11 @@ const ask = async ({ // If response has parentMessageId, the fake userMessage.messageId should be updated to the real one. if (!overrideParentMessageId) - await saveMessage({ ...userMessage, messageId: userMessageId, newMessageId: newUserMassageId }); + await saveMessage({ + ...userMessage, + messageId: userMessageId, + newMessageId: newUserMassageId + }); userMessageId = newUserMassageId; sendMessage(res, { @@ -251,7 +255,12 @@ const ask = async ({ res.end(); if (userParentMessageId == '00000000-0000-0000-0000-000000000000') { - const title = await titleConvo({ endpoint: endpointOption?.endpoint, text, response: responseMessage, oaiApiKey }); + const title = await titleConvo({ + endpoint: endpointOption?.endpoint, + text, + response: responseMessage, + oaiApiKey + }); await saveConvo(req.user.id, { conversationId: conversationId, title diff --git a/api/server/routes/auth.js b/api/server/routes/auth.js index 588a9eb83..3d5657d69 100644 --- a/api/server/routes/auth.js +++ b/api/server/routes/auth.js @@ -6,7 +6,7 @@ const { loginController, logoutController, refreshController, - registrationController, + registrationController } = require('../controllers/auth.controller'); const requireJwtAuth = require('../../middleware/requireJwtAuth'); const requireLocalAuth = require('../../middleware/requireLocalAuth'); diff --git a/api/server/routes/endpoints.js b/api/server/routes/endpoints.js index d3ec9288d..b44257d6a 100644 --- a/api/server/routes/endpoints.js +++ b/api/server/routes/endpoints.js @@ -36,13 +36,14 @@ router.get('/', async function (req, res) { } const google = - key || palmUser ? { userProvide: palmUser, availableModels: ['chat-bison', 'text-bison'] } : false; + key || palmUser + ? { userProvide: palmUser, availableModels: ['chat-bison', 'text-bison'] } + : false; const azureOpenAI = !!process.env.AZURE_OPENAI_KEY; const apiKey = process.env.OPENAI_KEY || process.env.AZURE_OPENAI_API_KEY; - const openAI = - apiKey - ? { availableModels: getOpenAIModels(), userProvide: apiKey === 'user_provided' } - : false; + const openAI = apiKey + ? { availableModels: getOpenAIModels(), userProvide: apiKey === 'user_provided' } + : false; const bingAI = process.env.BINGAI_TOKEN ? { userProvide: process.env.BINGAI_TOKEN == 'user_provided' } : false; @@ -56,4 +57,4 @@ router.get('/', async function (req, res) { res.send(JSON.stringify({ azureOpenAI, openAI, google, bingAI, chatGPTBrowser })); }); -module.exports = { router, getOpenAIModels, getChatGPTBrowserModels }; \ No newline at end of file +module.exports = { router, getOpenAIModels, getChatGPTBrowserModels }; diff --git a/api/server/routes/index.js b/api/server/routes/index.js index f90f55e1a..46c62a207 100644 --- a/api/server/routes/index.js +++ b/api/server/routes/index.js @@ -19,5 +19,5 @@ module.exports = { auth, oauth, tokenizer, - endpoints, + endpoints }; diff --git a/api/server/routes/oauth.js b/api/server/routes/oauth.js index 59d6148d6..bfb7bc4e4 100644 --- a/api/server/routes/oauth.js +++ b/api/server/routes/oauth.js @@ -61,4 +61,4 @@ router.get( } ); -module.exports = router; \ No newline at end of file +module.exports = router; diff --git a/api/server/routes/search.js b/api/server/routes/search.js index 91ebb718b..50375d8c1 100644 --- a/api/server/routes/search.js +++ b/api/server/routes/search.js @@ -27,7 +27,9 @@ router.get('/', requireJwtAuth, async function (req, res) { console.log('cache hit', key); const cached = cache.get(key); const { pages, pageSize, messages } = cached; - res.status(200).send({ conversations: cached[pageNumber], pages, pageNumber, pageSize, messages }); + res + .status(200) + .send({ conversations: cached[pageNumber], pages, pageNumber, pageSize, messages }); return; } else { cache.clear(); @@ -44,7 +46,7 @@ router.get('/', requireJwtAuth, async function (req, res) { }, true ) - ).hits.map(message => { + ).hits.map((message) => { const { _formatted, ...rest } = message; return { ...rest, @@ -95,12 +97,12 @@ router.get('/clear', async function (req, res) { router.get('/test', async function (req, res) { const { q } = req.query; - const messages = (await Message.meiliSearch(q, { attributesToHighlight: ['text'] }, true)).hits.map( - message => { - const { _formatted, ...rest } = message; - return { ...rest, searchResult: true, text: _formatted.text }; - } - ); + const messages = ( + await Message.meiliSearch(q, { attributesToHighlight: ['text'] }, true) + ).hits.map((message) => { + const { _formatted, ...rest } = message; + return { ...rest, searchResult: true, text: _formatted.text }; + }); res.send(messages); }); diff --git a/api/server/services/auth.service.js b/api/server/services/auth.service.js index a82ba0fd6..c382231bd 100644 --- a/api/server/services/auth.service.js +++ b/api/server/services/auth.service.js @@ -78,7 +78,7 @@ const registerUser = async (user) => { } //determine if this is the first registered user (not counting anonymous_user) - const isFirstRegisteredUser = await User.countDocuments({}) === 0; + const isFirstRegisteredUser = (await User.countDocuments({})) === 0; try { const newUser = await new User({ @@ -88,7 +88,7 @@ const registerUser = async (user) => { username, name, avatar: null, - role: isFirstRegisteredUser ? 'ADMIN' : 'USER', + role: isFirstRegisteredUser ? 'ADMIN' : 'USER' }); // todo: implement refresh token @@ -104,7 +104,7 @@ const registerUser = async (user) => { newUser.save(); }); }); - console.log('newUser', newUser) + console.log('newUser', newUser); if (isFirstRegisteredUser) { migrateDataToFirstUser(newUser); // console.log(migrate); @@ -186,12 +186,11 @@ const resetPassword = async (userId, token, password) => { return { message: 'Password reset was successful' }; }; - module.exports = { // signup, registerUser, loginUser, logoutUser, requestPasswordReset, - resetPassword, + resetPassword }; diff --git a/api/strategies/facebookStrategy.js b/api/strategies/facebookStrategy.js index 010683c46..939940e2f 100644 --- a/api/strategies/facebookStrategy.js +++ b/api/strategies/facebookStrategy.js @@ -1,7 +1,7 @@ -const passport = require('passport'); -const FacebookStrategy = require('passport-facebook').Strategy; +const passport = require('passport'); +const FacebookStrategy = require('passport-facebook').Strategy; const User = require('../models/User'); - + const serverUrl = process.env.NODE_ENV === 'production' ? process.env.SERVER_URL_PROD : process.env.SERVER_URL_DEV; @@ -11,7 +11,7 @@ const facebookLogin = new FacebookStrategy( clientID: process.env.FACEBOOK_APP_ID, clientSecret: process.env.FACEBOOK_SECRET, callbackURL: `${serverUrl}${process.env.FACEBOOK_CALLBACK_URL}`, - proxy: true, + proxy: true // profileFields: [ // 'id', // 'email', diff --git a/api/strategies/localStrategy.js b/api/strategies/localStrategy.js index b073546b2..09aa63a6d 100644 --- a/api/strategies/localStrategy.js +++ b/api/strategies/localStrategy.js @@ -65,4 +65,3 @@ function log({ title, parameters }) { DebugControl.log.parameters(parameters); } } - diff --git a/api/strategies/validators.js b/api/strategies/validators.js index 272b27b0f..316be4a3c 100644 --- a/api/strategies/validators.js +++ b/api/strategies/validators.js @@ -21,4 +21,4 @@ const registerSchema = Joi.object().keys({ module.exports = { loginSchema, registerSchema -}; \ No newline at end of file +}; diff --git a/api/utils/LoggingSystem.js b/api/utils/LoggingSystem.js index af61d0437..679e93c1d 100644 --- a/api/utils/LoggingSystem.js +++ b/api/utils/LoggingSystem.js @@ -3,7 +3,8 @@ const pino = require('pino'); const logger = pino({ level: 'info', redact: { - paths: [ // List of Paths to redact from the logs (https://getpino.io/#/docs/redaction) + paths: [ + // List of Paths to redact from the logs (https://getpino.io/#/docs/redaction) 'env.OPENAI_KEY', 'env.BINGAI_TOKEN', 'env.CHATGPT_TOKEN', @@ -11,14 +12,16 @@ const logger = pino({ 'env.GOOGLE_CLIENT_SECRET', 'env.JWT_SECRET_DEV', 'env.JWT_SECRET_PROD', - 'newUser.password'], // See example to filter object class instances - censor: '***', // Redaction character - }, + 'newUser.password' + ], // See example to filter object class instances + censor: '***' // Redaction character + } }); // Sanitize outside the logger paths. This is useful for sanitizing variables directly with Regex and patterns. -const redactPatterns = [ // Array of regular expressions for redacting patterns - /api[-_]?key/i, +const redactPatterns = [ + // Array of regular expressions for redacting patterns + /api[-_]?key/i, /password/i, /token/i, /secret/i, @@ -30,7 +33,7 @@ const redactPatterns = [ // Array of regular expressions for redacting patterns /authorization[-_]?acr[-_]?values/i, /authorization[-_]?response[-_]?mode/i, /authorization[-_]?nonce/i -]; +]; /* // Example of redacting sensitive data from object class instances @@ -49,21 +52,19 @@ const redactPatterns = [ // Array of regular expressions for redacting patterns */ const levels = { - TRACE: 10, - DEBUG: 20, - INFO: 30, - WARN: 40, - ERROR: 50, - FATAL: 60 + TRACE: 10, + DEBUG: 20, + INFO: 30, + WARN: 40, + ERROR: 50, + FATAL: 60 }; - - let level = levels.INFO; module.exports = { levels, - setLevel: (l) => (level = l), + setLevel: l => (level = l), log: { trace: (msg) => { if (level <= levels.TRACE) return; @@ -122,4 +123,3 @@ module.exports = { } } }; - diff --git a/api/utils/genAzureEndpoints.js b/api/utils/genAzureEndpoints.js index 76b8d10d7..4c232b685 100644 --- a/api/utils/genAzureEndpoints.js +++ b/api/utils/genAzureEndpoints.js @@ -1,4 +1,8 @@ -function genAzureEndpoint({ azureOpenAIApiInstanceName, azureOpenAIApiDeploymentName, azureOpenAIApiVersion }) { +function genAzureEndpoint({ + azureOpenAIApiInstanceName, + azureOpenAIApiDeploymentName, + azureOpenAIApiVersion +}) { return `https://${azureOpenAIApiInstanceName}.openai.azure.com/openai/deployments/${azureOpenAIApiDeploymentName}/chat/completions?api-version=${azureOpenAIApiVersion}`; } diff --git a/api/utils/migrateDataToFirstUser.js b/api/utils/migrateDataToFirstUser.js index efa74a9ac..6f7b558b7 100644 --- a/api/utils/migrateDataToFirstUser.js +++ b/api/utils/migrateDataToFirstUser.js @@ -3,28 +3,27 @@ const Preset = require('../models/schema/presetSchema'); const migrateConversations = async (userId) => { try { - return await Conversation.updateMany({ user: null }, { $set: { user: userId }}).exec(); + return await Conversation.updateMany({ user: null }, { $set: { user: userId } }).exec(); } catch (error) { console.log(error); return { message: 'Error saving conversation' }; } -} +}; const migratePresets = async (userId) => { try { - return await Preset.updateMany({ user: null }, { $set: { user: userId }}).exec(); + return await Preset.updateMany({ user: null }, { $set: { user: userId } }).exec(); } catch (error) { console.log(error); return { message: 'Error saving conversation' }; } -} +}; const migrateDataToFirstUser = async (user) => { const conversations = await migrateConversations(user.id); console.log(conversations); const presets = await migratePresets(user.id); console.log(presets); -} +}; - -module.exports = migrateDataToFirstUser; \ No newline at end of file +module.exports = migrateDataToFirstUser; diff --git a/api/utils/sendEmail.js b/api/utils/sendEmail.js index ff0de97ae..00a376090 100644 --- a/api/utils/sendEmail.js +++ b/api/utils/sendEmail.js @@ -1,7 +1,7 @@ -const nodemailer = require("nodemailer"); -const handlebars = require("handlebars"); -const fs = require("fs"); -const path = require("path"); +const nodemailer = require('nodemailer'); +const handlebars = require('handlebars'); +const fs = require('fs'); +const path = require('path'); const sendEmail = async (email, subject, payload, template) => { try { @@ -11,18 +11,18 @@ const sendEmail = async (email, subject, payload, template) => { port: 465, auth: { user: process.env.EMAIL_USERNAME, - pass: process.env.EMAIL_PASSWORD, - }, + pass: process.env.EMAIL_PASSWORD + } }); - const source = fs.readFileSync(path.join(__dirname, template), "utf8"); + const source = fs.readFileSync(path.join(__dirname, template), 'utf8'); const compiledTemplate = handlebars.compile(source); const options = () => { return { from: process.env.FROM_EMAIL, to: email, subject: subject, - html: compiledTemplate(payload), + html: compiledTemplate(payload) }; }; @@ -32,7 +32,7 @@ const sendEmail = async (email, subject, payload, template) => { return error; } else { return res.status(200).json({ - success: true, + success: true }); } }); @@ -51,4 +51,4 @@ sendEmail( ); */ -module.exports = sendEmail; \ No newline at end of file +module.exports = sendEmail; diff --git a/client/.eslintrc.js b/client/.eslintrc.js deleted file mode 100644 index 7b8f7bd09..000000000 --- a/client/.eslintrc.js +++ /dev/null @@ -1,31 +0,0 @@ -module.exports = { - "env": { - "browser": true, - "es2021": true, - "node": true, - "commonjs": true, - "es6": true, - }, - "extends": [ - "eslint:recommended", - "plugin:react/recommended", - "plugin:react-hooks/recommended" - ], - "overrides": [ - ], - "parserOptions": { - "ecmaFeatures": { - "jsx": true - }, - "ecmaVersion": "latest", - "sourceType": "module" - }, - "plugins": [ - "react" - ], - "rules": { - 'react/prop-types': ['off'], - 'react/display-name': ['off'], - "no-debugger":"off", - } -} diff --git a/client/.prettierrc b/client/.prettierrc deleted file mode 100644 index 34e12e2f4..000000000 --- a/client/.prettierrc +++ /dev/null @@ -1,22 +0,0 @@ -{ - "arrowParens": "avoid", - "bracketSpacing": true, - "endOfLine": "lf", - "htmlWhitespaceSensitivity": "css", - "insertPragma": false, - "singleAttributePerLine": true, - "bracketSameLine": false, - "jsxBracketSameLine": false, - "jsxSingleQuote": false, - "printWidth": 110, - "proseWrap": "preserve", - "quoteProps": "as-needed", - "requirePragma": false, - "semi": true, - "singleQuote": true, - "tabWidth": 2, - "trailingComma": "none", - "useTabs": false, - "vueIndentScriptAndStyle": false, - "parser": "babel" -} \ No newline at end of file diff --git a/client/package.json b/client/package.json index 046872545..c81189f5f 100644 --- a/client/package.json +++ b/client/package.json @@ -98,18 +98,10 @@ "babel-plugin-root-import": "^6.6.0", "babel-preset-react": "^6.24.1", "css-loader": "^6.7.3", - "eslint": "^8.33.0", - "eslint-config-airbnb-base": "^15.0.0", - "eslint-config-prettier": "^8.6.0", - "eslint-plugin-jest": "^27.2.1", - "eslint-plugin-react": "^7.32.2", - "eslint-plugin-react-hooks": "^4.6.0", "path": "^0.12.7", "postcss": "^8.4.21", "postcss-loader": "^7.1.0", "postcss-preset-env": "^8.2.0", - "prettier": "^2.8.3", - "prettier-plugin-tailwindcss": "^0.2.2", "source-map-loader": "^1.1.3", "style-loader": "^3.3.1", "tailwindcss": "^3.2.6", diff --git a/client/src/App.jsx b/client/src/App.jsx index 87df5c025..b43eb470e 100644 --- a/client/src/App.jsx +++ b/client/src/App.jsx @@ -44,12 +44,7 @@ const router = createBrowserRouter([ children: [ { index: true, - element: ( - - ) + element: }, { path: 'chat/:conversationId?', @@ -70,7 +65,7 @@ const App = () => { const queryClient = new QueryClient({ queryCache: new QueryCache({ - onError: error => { + onError: (error) => { if (error?.response?.status === 401) { setError(error); } diff --git a/client/src/components/Auth/Login.tsx b/client/src/components/Auth/Login.tsx index e157725ad..45a28581a 100644 --- a/client/src/components/Auth/Login.tsx +++ b/client/src/components/Auth/Login.tsx @@ -1,50 +1,48 @@ -import { useEffect } from "react"; -import { useForm } from "react-hook-form"; -import { TLoginUser } from "~/data-provider"; -import { useAuthContext } from "~/hooks/AuthContext"; -import { useNavigate } from "react-router-dom"; +import { useEffect } from 'react'; +import { useForm } from 'react-hook-form'; +import { TLoginUser } from '~/data-provider'; +import { useAuthContext } from '~/hooks/AuthContext'; +import { useNavigate } from 'react-router-dom'; function Login() { const { login, error, isAuthenticated } = useAuthContext(); const { register, handleSubmit, - formState: { errors }, + formState: { errors } } = useForm(); const navigate = useNavigate(); useEffect(() => { if (isAuthenticated) { - navigate("/chat/new"); + navigate('/chat/new'); } - }, [isAuthenticated, navigate]) - + }, [isAuthenticated, navigate]); const SERVER_URL = import.meta.env.DEV ? import.meta.env.VITE_SERVER_URL_DEV : import.meta.env.VITE_SERVER_URL_PROD; - const showGoogleLogin = - import.meta.env.VITE_SHOW_GOOGLE_LOGIN_OPTION === "true"; + const showGoogleLogin = import.meta.env.VITE_SHOW_GOOGLE_LOGIN_OPTION === 'true'; return ( -
-
-

Welcome back

+
+
+

Welcome back

{error && (
- Unable to login with the information provided. Please check your - credentials and try again. + Unable to login with the information provided. Please check your credentials and try + again.
)}
login(data))} + onSubmit={handleSubmit(data => login(data))} >
@@ -53,28 +51,28 @@ function Login() { id="email" autoComplete="email" aria-label="Email" - {...register("email", { - required: "Email is required", + {...register('email', { + required: 'Email is required', minLength: { value: 3, - message: "Email must be at least 6 characters", + message: 'Email must be at least 6 characters' }, maxLength: { value: 120, - message: "Email should not be longer than 120 characters", + message: 'Email should not be longer than 120 characters' }, pattern: { value: /\S+@\S+\.\S+/, - message: "You must enter a valid email address", - }, + message: 'You must enter a valid email address' + } })} aria-invalid={!!errors.email} - className="block rounded-t-md px-2.5 pb-2.5 pt-5 w-full text-sm text-gray-900 bg-gray-50 border-0 border-b-2 border-gray-300 appearance-none focus:outline-none focus:ring-0 focus:border-green-500 peer" + className="peer block w-full appearance-none rounded-t-md border-0 border-b-2 border-gray-300 bg-gray-50 px-2.5 pb-2.5 pt-5 text-sm text-gray-900 focus:border-green-500 focus:outline-none focus:ring-0" placeholder=" " > @@ -93,24 +91,24 @@ function Login() { id="password" autoComplete="current-password" aria-label="Password" - {...register("password", { - required: "Password is required", + {...register('password', { + required: 'Password is required', minLength: { value: 8, - message: "Password must be at least 8 characters", + message: 'Password must be at least 8 characters' }, maxLength: { value: 40, - message: "Password must be less than 40 characters", - }, + message: 'Password must be less than 40 characters' + } })} aria-invalid={!!errors.password} - className="block rounded-t-md px-2.5 pb-2.5 pt-5 w-full text-sm text-gray-900 bg-gray-50 border-0 border-b-2 border-gray-300 appearance-none focus:outline-none focus:ring-0 focus:border-green-500 peer" + className="peer block w-full appearance-none rounded-t-md border-0 border-b-2 border-gray-300 bg-gray-50 px-2.5 pb-2.5 pt-5 text-sm text-gray-900 focus:border-green-500 focus:outline-none focus:ring-0" placeholder=" " > @@ -123,10 +121,7 @@ function Login() { )}
- + Forgot Password?
@@ -139,28 +134,47 @@ function Login() {
-

- {" "} - Don't have an account?{" "} - +

+ {' '} + Don't have an account?{' '} + Sign up

{showGoogleLogin && ( <> -
-
Or
+
+
Or
- + + + + + +

Login with Google

diff --git a/client/src/components/Auth/Registration.tsx b/client/src/components/Auth/Registration.tsx index 5209959fb..54ddea037 100644 --- a/client/src/components/Auth/Registration.tsx +++ b/client/src/components/Auth/Registration.tsx @@ -1,65 +1,61 @@ -import { useState } from "react"; -import { useNavigate } from "react-router-dom"; -import { useForm } from "react-hook-form"; -import { useRegisterUserMutation, TRegisterUser } from "~/data-provider"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { faFacebook } from "@fortawesome/free-brands-svg-icons"; -import { faGoogle } from "@fortawesome/free-brands-svg-icons"; +import { useState } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { useForm } from 'react-hook-form'; +import { useRegisterUserMutation, TRegisterUser } from '~/data-provider'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faFacebook } from '@fortawesome/free-brands-svg-icons'; +import { faGoogle } from '@fortawesome/free-brands-svg-icons'; function Registration() { const SERVER_URL = import.meta.env.DEV ? import.meta.env.VITE_SERVER_URL_DEV : import.meta.env.VITE_SERVER_URL_PROD; - const showGoogleLogin = - import.meta.env.VITE_SHOW_GOOGLE_LOGIN_OPTION === "true"; + const showGoogleLogin = import.meta.env.VITE_SHOW_GOOGLE_LOGIN_OPTION === 'true'; const navigate = useNavigate(); const { register, watch, handleSubmit, - formState: { errors }, - } = useForm({ mode: "onChange" }); + formState: { errors } + } = useForm({ mode: 'onChange' }); const [error, setError] = useState(false); - const [errorMessage, setErrorMessage] = useState(""); + const [errorMessage, setErrorMessage] = useState(''); const registerUser = useRegisterUserMutation(); - const password = watch("password"); + const password = watch('password'); const onRegisterUserFormSubmit = (data: TRegisterUser) => { registerUser.mutate(data, { onSuccess: () => { - navigate("/chat/new"); + navigate('/chat/new'); }, onError: (error) => { setError(true); if (error.response?.data?.message) { setErrorMessage(error.response?.data?.message); } - }, + } }); }; return ( -
-
-

- Create your account -

+
+
+

Create your account

{error && (
- There was an error attempting to register your account. Please try - again. {errorMessage} + There was an error attempting to register your account. Please try again. {errorMessage}
)}
onRegisterUserFormSubmit(data))} + onSubmit={handleSubmit(data => onRegisterUserFormSubmit(data))} >
@@ -73,29 +69,29 @@ function Registration() { e.preventDefault(); return false; }} - {...register("name", { - required: "Name is required", + {...register('name', { + required: 'Name is required', minLength: { value: 3, - message: "Name must be at least 3 characters", + message: 'Name must be at least 3 characters' }, maxLength: { value: 80, - message: "Name must be less than 80 characters", - }, + message: 'Name must be less than 80 characters' + } })} aria-invalid={!!errors.name} - className="block rounded-t-md px-2.5 pb-2.5 pt-5 w-full text-sm text-gray-900 bg-gray-50 border-0 border-b-2 border-gray-300 appearance-none focus:outline-none focus:ring-0 focus:border-green-500 peer" + className="peer block w-full appearance-none rounded-t-md border-0 border-b-2 border-gray-300 bg-gray-50 px-2.5 pb-2.5 pt-5 text-sm text-gray-900 focus:border-green-500 focus:outline-none focus:ring-0" placeholder=" " >
- + {errors.name && ( {/* @ts-ignore */} @@ -109,25 +105,25 @@ function Registration() { type="text" id="username" aria-label="Username" - {...register("username", { - required: "Username is required", + {...register('username', { + required: 'Username is required', minLength: { value: 3, - message: "Username must be at least 3 characters", + message: 'Username must be at least 3 characters' }, maxLength: { value: 20, - message: "Username must be less than 20 characters", - }, + message: 'Username must be less than 20 characters' + } })} aria-invalid={!!errors.username} - className="block rounded-t-md px-2.5 pb-2.5 pt-5 w-full text-sm text-gray-900 bg-gray-50 border-0 border-b-2 border-gray-300 appearance-none focus:outline-none focus:ring-0 focus:border-green-500 peer" + className="peer block w-full appearance-none rounded-t-md border-0 border-b-2 border-gray-300 bg-gray-50 px-2.5 pb-2.5 pt-5 text-sm text-gray-900 focus:border-green-500 focus:outline-none focus:ring-0" placeholder=" " autoComplete="off" > @@ -147,28 +143,28 @@ function Registration() { id="email" autoComplete="email" aria-label="Email" - {...register("email", { - required: "Email is required", + {...register('email', { + required: 'Email is required', minLength: { value: 3, - message: "Email must be at least 6 characters", + message: 'Email must be at least 6 characters' }, maxLength: { value: 120, - message: "Email should not be longer than 120 characters", + message: 'Email should not be longer than 120 characters' }, pattern: { value: /\S+@\S+\.\S+/, - message: "You must enter a valid email address", - }, + message: 'You must enter a valid email address' + } })} aria-invalid={!!errors.email} - className="block rounded-t-md px-2.5 pb-2.5 pt-5 w-full text-sm text-gray-900 bg-gray-50 border-0 border-b-2 border-gray-300 appearance-none focus:outline-none focus:ring-0 focus:border-green-500 peer" + className="peer block w-full appearance-none rounded-t-md border-0 border-b-2 border-gray-300 bg-gray-50 px-2.5 pb-2.5 pt-5 text-sm text-gray-900 focus:border-green-500 focus:outline-none focus:ring-0" placeholder=" " > @@ -187,24 +183,24 @@ function Registration() { id="password" autoComplete="current-password" aria-label="Password" - {...register("password", { - required: "Password is required", + {...register('password', { + required: 'Password is required', minLength: { value: 8, - message: "Password must be at least 8 characters", + message: 'Password must be at least 8 characters' }, maxLength: { value: 40, - message: "Password must be less than 40 characters", - }, + message: 'Password must be less than 40 characters' + } })} aria-invalid={!!errors.password} - className="block rounded-t-md px-2.5 pb-2.5 pt-5 w-full text-sm text-gray-900 bg-gray-50 border-0 border-b-2 border-gray-300 appearance-none focus:outline-none focus:ring-0 focus:border-green-500 peer" + className="peer block w-full appearance-none rounded-t-md border-0 border-b-2 border-gray-300 bg-gray-50 px-2.5 pb-2.5 pt-5 text-sm text-gray-900 focus:border-green-500 focus:outline-none focus:ring-0" placeholder=" " > @@ -228,17 +224,16 @@ function Registration() { e.preventDefault(); return false; }} - {...register("confirm_password", { - validate: (value) => - value === password || "Passwords do not match", + {...register('confirm_password', { + validate: value => value === password || 'Passwords do not match' })} aria-invalid={!!errors.confirm_password} - className="block rounded-t-md px-2.5 pb-2.5 pt-5 w-full text-sm text-gray-900 bg-gray-50 border-0 border-b-2 border-gray-300 appearance-none focus:outline-none focus:ring-0 focus:border-green-500 peer" + className="peer block w-full appearance-none rounded-t-md border-0 border-b-2 border-gray-300 bg-gray-50 px-2.5 pb-2.5 pt-5 text-sm text-gray-900 focus:border-green-500 focus:outline-none focus:ring-0" placeholder=" " > @@ -268,29 +263,48 @@ function Registration() {
-

- {" "} - Already have an account?{" "} - +

+ {' '} + Already have an account?{' '} + Login

{showGoogleLogin && ( <>
-
Or
+
Or
- + + + + + +

Login with Google

{/* @@ -112,4 +115,4 @@ function RequestPasswordReset() { ); } -export default RequestPasswordReset; \ No newline at end of file +export default RequestPasswordReset; diff --git a/client/src/components/Auth/ResetPassword.tsx b/client/src/components/Auth/ResetPassword.tsx index d983d383e..f765c0c12 100644 --- a/client/src/components/Auth/ResetPassword.tsx +++ b/client/src/components/Auth/ResetPassword.tsx @@ -1,20 +1,20 @@ -import { useState } from "react"; -import { useForm } from "react-hook-form"; -import {useResetPasswordMutation, TResetPassword} from "~/data-provider"; -import { useNavigate, useSearchParams } from "react-router-dom"; +import { useState } from 'react'; +import { useForm } from 'react-hook-form'; +import { useResetPasswordMutation, TResetPassword } from '~/data-provider'; +import { useNavigate, useSearchParams } from 'react-router-dom'; function ResetPassword() { const { register, handleSubmit, watch, - formState: { errors }, + formState: { errors } } = useForm(); const resetPassword = useResetPasswordMutation(); const [resetError, setResetError] = useState(false); const [params] = useSearchParams(); const navigate = useNavigate(); - const password = watch("password"); + const password = watch('password'); const onSubmit = (data: TResetPassword) => { resetPassword.mutate(data, { @@ -26,19 +26,17 @@ function ResetPassword() { if (resetPassword.isSuccess) { return ( -
-
-

- Password Reset Success -

+
+
+

Password Reset Success

You may now login with your new password.
- ) - } - else { + ); + } else { return ( -
-
-

- Reset your password -

+
+
+

Reset your password

{resetError && (
- This password reset token is no longer valid. Click here to try again. + This password reset token is no longer valid.{' '} + + Click here + {' '} + to try again.
)}
-
- - - - -
+
+ + + + +
- {errors.password && ( - - {/* @ts-ignore */} - {errors.password.message} - - )} -
-
-
- { - e.preventDefault(); - return false; - }} - {...register("confirm_password", { - validate: (value) => - value === password || "Passwords do not match", - })} - aria-invalid={!!errors.confirm_password} - className="block rounded-t-md px-2.5 pb-2.5 pt-5 w-full text-sm text-gray-900 bg-gray-50 border-0 border-b-2 border-gray-300 appearance-none focus:outline-none focus:ring-0 focus:border-green-500 peer" - placeholder=" " - > - + {errors.password && ( + + {/* @ts-ignore */} + {errors.password.message} + + )}
- {errors.confirm_password && ( - - {/* @ts-ignore */} - {errors.confirm_password.message} - - )} - {errors.token && ( - - {/* @ts-ignore */} - {errors.token.message} - - )} - {errors.userId && ( - - {/* @ts-ignore */} - {errors.userId.message} - - )} -
-
- -
- +
+
+ { + e.preventDefault(); + return false; + }} + {...register('confirm_password', { + validate: value => value === password || 'Passwords do not match' + })} + aria-invalid={!!errors.confirm_password} + className="peer block w-full appearance-none rounded-t-md border-0 border-b-2 border-gray-300 bg-gray-50 px-2.5 pb-2.5 pt-5 text-sm text-gray-900 focus:border-green-500 focus:outline-none focus:ring-0" + placeholder=" " + > + +
+ {errors.confirm_password && ( + + {/* @ts-ignore */} + {errors.confirm_password.message} + + )} + {errors.token && ( + + {/* @ts-ignore */} + {errors.token.message} + + )} + {errors.userId && ( + + {/* @ts-ignore */} + {errors.userId.message} + + )} +
+
+ +
+ +
-
- ) + ); } -}; +} -export default ResetPassword; \ No newline at end of file +export default ResetPassword; diff --git a/client/src/components/Auth/index.ts b/client/src/components/Auth/index.ts index 89b8899e8..9003653cf 100644 --- a/client/src/components/Auth/index.ts +++ b/client/src/components/Auth/index.ts @@ -1,4 +1,4 @@ export { default as Login } from './Login'; export { default as Registration } from './Registration'; export { default as RequestPasswordReset } from './RequestPasswordReset'; -export { default as ResetPassword } from './ResetPassword'; \ No newline at end of file +export { default as ResetPassword } from './ResetPassword'; diff --git a/client/src/components/Conversations/Conversation.jsx b/client/src/components/Conversations/Conversation.jsx index 18831361a..eda119ca5 100644 --- a/client/src/components/Conversations/Conversation.jsx +++ b/client/src/components/Conversations/Conversation.jsx @@ -1,4 +1,4 @@ -import { useState, useRef, useEffect} from 'react'; +import { useState, useRef, useEffect } from 'react'; import { useRecoilState, useSetRecoilState } from 'recoil'; import { useUpdateConversationMutation } from '~/data-provider'; import RenameButton from './RenameButton'; @@ -38,7 +38,7 @@ export default function Conversation({ conversation, retainView }) { switchToConversation(conversation); }; - const renameHandler = e => { + const renameHandler = (e) => { e.preventDefault(); setTitleInput(title); setRenaming(true); @@ -47,12 +47,12 @@ export default function Conversation({ conversation, retainView }) { }, 25); }; - const cancelHandler = e => { + const cancelHandler = (e) => { e.preventDefault(); setRenaming(false); }; - const onRename = e => { + const onRename = (e) => { e.preventDefault(); setRenaming(false); if (titleInput === title) { @@ -61,7 +61,7 @@ export default function Conversation({ conversation, retainView }) { updateConvoMutation.mutate({ conversationId, title: titleInput }); }; - useEffect(() => { + useEffect(() => { if (updateConvoMutation.isSuccess) { refreshConversations(); if (conversationId == currentConversation?.conversationId) { @@ -73,7 +73,7 @@ export default function Conversation({ conversation, retainView }) { } }, [updateConvoMutation.isSuccess]); - const handleKeyDown = e => { + const handleKeyDown = (e) => { if (e.key === 'Enter') { onRename(e); } @@ -90,10 +90,7 @@ export default function Conversation({ conversation, retainView }) { } return ( - clickHandler()} - {...aProps} - > + clickHandler()} {...aProps}>
{renaming === true ? ( @@ -126,7 +123,7 @@ export default function Conversation({ conversation, retainView }) { />
) : ( -
+
)} ); diff --git a/client/src/components/Conversations/DeleteButton.jsx b/client/src/components/Conversations/DeleteButton.jsx index 578989252..5cb3f1902 100644 --- a/client/src/components/Conversations/DeleteButton.jsx +++ b/client/src/components/Conversations/DeleteButton.jsx @@ -14,26 +14,22 @@ export default function DeleteButton({ conversationId, renaming, cancelHandler, const deleteConvoMutation = useDeleteConversationMutation(conversationId); useEffect(() => { - if(deleteConvoMutation.isSuccess) { - if (currentConversation?.conversationId == conversationId) newConversation(); - + if (deleteConvoMutation.isSuccess) { + if (currentConversation?.conversationId == conversationId) newConversation(); + refreshConversations(); retainView(); } }, [deleteConvoMutation.isSuccess]); - const clickHandler = () => { - deleteConvoMutation.mutate({conversationId, source: 'button' }); + deleteConvoMutation.mutate({ conversationId, source: 'button' }); }; const handler = renaming ? cancelHandler : clickHandler; return ( - ); diff --git a/client/src/components/Conversations/Pages.jsx b/client/src/components/Conversations/Pages.jsx index 91b93388b..80ae5ffdc 100644 --- a/client/src/components/Conversations/Pages.jsx +++ b/client/src/components/Conversations/Pages.jsx @@ -1,13 +1,13 @@ import React from 'react'; export default function Pages({ pageNumber, pages, nextPage, previousPage }) { - const clickHandler = func => async e => { + const clickHandler = func => async (e) => { e.preventDefault(); await func(); }; return pageNumber == 1 && pages == 1 ? null : ( -
+
diff --git a/client/src/components/Input/BingAIOptions/index.jsx b/client/src/components/Input/BingAIOptions/index.jsx index 4f152c535..56f6b89ec 100644 --- a/client/src/components/Input/BingAIOptions/index.jsx +++ b/client/src/components/Input/BingAIOptions/index.jsx @@ -31,7 +31,7 @@ function BingAIOptions({ show }) { setSaveAsDialogShow(true); }; - const setOption = param => newValue => { + const setOption = param => (newValue) => { let update = {}; update[param] = newValue; setConversation(prevState => ({ @@ -44,7 +44,10 @@ function BingAIOptions({ show }) { 'transition-colors shadow-md rounded-md min-w-[75px] font-normal bg-white border-black/10 hover:border-black/10 focus:border-black/10 dark:border-black/10 dark:hover:border-black/10 dark:focus:border-black/10 border dark:bg-gray-700 text-black dark:text-white'; const defaultClasses = 'p-2 rounded-md min-w-[75px] font-normal bg-white/[.60] dark:bg-gray-700 text-black text-xs'; - const defaultSelected = cn(defaultClasses, 'font-medium data-[state=active]:text-white text-xs text-white'); + const defaultSelected = cn( + defaultClasses, + 'font-medium data-[state=active]:text-white text-xs text-white' + ); const selectedClass = val => val + '-tab ' + defaultSelected; return ( diff --git a/client/src/components/Input/ChatGPTOptions/index.jsx b/client/src/components/Input/ChatGPTOptions/index.jsx index e0f89f15b..9c1c8a5d3 100644 --- a/client/src/components/Input/ChatGPTOptions/index.jsx +++ b/client/src/components/Input/ChatGPTOptions/index.jsx @@ -17,7 +17,7 @@ function ChatGPTOptions() { const models = endpointsConfig?.['chatGPTBrowser']?.['availableModels'] || []; - const setOption = param => newValue => { + const setOption = param => (newValue) => { let update = {}; update[param] = newValue; setConversation(prevState => ({ diff --git a/client/src/components/Input/Footer.jsx b/client/src/components/Input/Footer.jsx index 3a87c12d5..4c0f0d1dd 100644 --- a/client/src/components/Input/Footer.jsx +++ b/client/src/components/Input/Footer.jsx @@ -2,17 +2,17 @@ import React from 'react'; export default function Footer() { return ( -
+
- {import.meta.env.VITE_APP_TITLE || "ChatGPT Clone"} + {import.meta.env.VITE_APP_TITLE || 'ChatGPT Clone'} - . Serves and searches all conversations reliably. All AI convos under one house. Pay per call and not - per month (cents compared to dollars). + . Serves and searches all conversations reliably. All AI convos under one house. Pay per call + and not per month (cents compared to dollars).
); } diff --git a/client/src/components/Input/GoogleOptions/index.jsx b/client/src/components/Input/GoogleOptions/index.jsx index d5c2c1168..382c1e939 100644 --- a/client/src/components/Input/GoogleOptions/index.jsx +++ b/client/src/components/Input/GoogleOptions/index.jsx @@ -40,7 +40,7 @@ function GoogleOptions() { setSaveAsDialogShow(true); }; - const setOption = param => newValue => { + const setOption = param => (newValue) => { let update = {}; update[param] = newValue; setConversation(prevState => ({ diff --git a/client/src/components/Input/NewConversationMenu/EndpointItem.jsx b/client/src/components/Input/NewConversationMenu/EndpointItem.jsx index f4a9f3c5d..97852e087 100644 --- a/client/src/components/Input/NewConversationMenu/EndpointItem.jsx +++ b/client/src/components/Input/NewConversationMenu/EndpointItem.jsx @@ -12,8 +12,8 @@ const alternateName = { azureOpenAI: 'Azure OpenAI', bingAI: 'Bing', chatGPTBrowser: 'ChatGPT', - google: 'PaLM', -} + google: 'PaLM' +}; export default function ModelItem({ endpoint, value, onSelect }) { const [setTokenDialogOpen, setSetTokenDialogOpen] = useState(false); @@ -42,7 +42,7 @@ export default function ModelItem({ endpoint, value, onSelect }) { {isUserProvided ? ( ); -} \ No newline at end of file +} diff --git a/client/src/components/Input/SetTokenDialog/InputWithLabel.jsx b/client/src/components/Input/SetTokenDialog/InputWithLabel.jsx index 3069b9de5..fbe8e3e60 100644 --- a/client/src/components/Input/SetTokenDialog/InputWithLabel.jsx +++ b/client/src/components/Input/SetTokenDialog/InputWithLabel.jsx @@ -9,10 +9,7 @@ function InputWithLabel({ value, onChange, label, id }) { return ( <> -
} diff --git a/client/src/components/Input/SubmitButton.jsx b/client/src/components/Input/SubmitButton.jsx index cc688b8f1..955f9b181 100644 --- a/client/src/components/Input/SubmitButton.jsx +++ b/client/src/components/Input/SubmitButton.jsx @@ -17,7 +17,7 @@ export default function SubmitButton({ const isTokenProvided = endpointsConfig?.[endpoint]?.userProvide ? !!getToken() : true; - const clickHandler = e => { + const clickHandler = (e) => { e.preventDefault(); submitMessage(); }; @@ -101,12 +101,7 @@ export default function SubmitButton({ width="1em" xmlns="http://www.w3.org/2000/svg" > - +
diff --git a/client/src/components/Input/index.jsx b/client/src/components/Input/index.jsx index b0463b3b8..fd2bf206c 100644 --- a/client/src/components/Input/index.jsx +++ b/client/src/components/Input/index.jsx @@ -34,7 +34,7 @@ export default function TextChat({ isSearchView = false }) { // const bingStylesRef = useRef(null); const [showBingToneSetting, setShowBingToneSetting] = useState(false); - const isNotAppendable = (latestMessage?.unfinished & !isSubmitting) || latestMessage?.error; + const isNotAppendable = latestMessage?.unfinished & !isSubmitting || latestMessage?.error; // auto focus to input, when enter a conversation. useEffect(() => { @@ -64,12 +64,12 @@ export default function TextChat({ isSearchView = false }) { setText(''); }; - const handleStopGenerating = e => { + const handleStopGenerating = (e) => { e.preventDefault(); stopGenerating(); }; - const handleKeyDown = e => { + const handleKeyDown = (e) => { if (e.key === 'Enter' && isSubmitting) { return; } @@ -83,7 +83,7 @@ export default function TextChat({ isSearchView = false }) { } }; - const handleKeyUp = e => { + const handleKeyUp = (e) => { if (e.keyCode === 8 && e.target.value.trim() === '') { setText(e.target.value); } @@ -105,7 +105,7 @@ export default function TextChat({ isSearchView = false }) { isComposing.current = false; }; - const changeHandler = e => { + const changeHandler = (e) => { const { value } = e.target; setText(value); diff --git a/client/src/components/MessageHandler/index.jsx b/client/src/components/MessageHandler/index.jsx index 6bea4743c..4adf93a58 100644 --- a/client/src/components/MessageHandler/index.jsx +++ b/client/src/components/MessageHandler/index.jsx @@ -27,7 +27,7 @@ export default function MessageHandler() { text: data, parentMessageId: message?.overrideParentMessageId, messageId: message?.overrideParentMessageId + '_', - submitting: true, + submitting: true // unfinished: true } ]); @@ -40,7 +40,7 @@ export default function MessageHandler() { text: data, parentMessageId: message?.messageId, messageId: message?.messageId + '_', - submitting: true, + submitting: true // unfinished: true } ]); @@ -153,7 +153,7 @@ export default function MessageHandler() { return; }; - const abortConversation = conversationId => { + const abortConversation = (conversationId) => { console.log(submission); const { endpoint } = submission?.conversation || {}; @@ -161,18 +161,18 @@ export default function MessageHandler() { method: 'POST', headers: { 'Content-Type': 'application/json', - 'Authorization': `Bearer ${token}` + Authorization: `Bearer ${token}` }, body: JSON.stringify({ abortKey: conversationId }) }) .then(response => response.json()) - .then(data => { + .then((data) => { console.log('aborted', data); cancelHandler(data, submission); }) - .catch(error => { + .catch((error) => { console.error('Error aborting request'); console.error(error); // errorHandler({ text: 'Error aborting request' }, { ...submission, message }); @@ -190,10 +190,10 @@ export default function MessageHandler() { const events = new SSE(server, { payload: JSON.stringify(payload), - headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}`} + headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` } }); - events.onmessage = e => { + events.onmessage = (e) => { const data = JSON.parse(e.data); if (data.final) { @@ -219,7 +219,8 @@ export default function MessageHandler() { events.onopen = () => console.log('connection is opened'); - events.oncancel = () => abortConversation(message?.conversationId || submission?.conversationId); + events.oncancel = () => + abortConversation(message?.conversationId || submission?.conversationId); events.onerror = function (e) { console.log('error in opening conn.'); diff --git a/client/src/components/Messages/Content/CodeBlock.jsx b/client/src/components/Messages/Content/CodeBlock.jsx index 23117fe10..4bd8a7507 100644 --- a/client/src/components/Messages/Content/CodeBlock.jsx +++ b/client/src/components/Messages/Content/CodeBlock.jsx @@ -7,15 +7,9 @@ const CodeBlock = ({ lang, codeChildren }) => { return (
- +
- + {codeChildren}
diff --git a/client/src/components/Messages/Content/Content.jsx b/client/src/components/Messages/Content/Content.jsx index 3cb0e2592..356dae348 100644 --- a/client/src/components/Messages/Content/Content.jsx +++ b/client/src/components/Messages/Content/Content.jsx @@ -4,7 +4,7 @@ import rehypeKatex from 'rehype-katex'; import rehypeHighlight from 'rehype-highlight'; import remarkMath from 'remark-math'; import remarkGfm from 'remark-gfm'; -import rehypeRaw from 'rehype-raw' +import rehypeRaw from 'rehype-raw'; import CodeBlock from './CodeBlock'; import { langSubset } from '~/utils/languages.mjs'; @@ -19,7 +19,7 @@ const Content = React.memo(({ content }) => { subset: langSubset } ], - [rehypeRaw], + [rehypeRaw] ]; return ( @@ -29,7 +29,7 @@ const Content = React.memo(({ content }) => { linkTarget="_new" components={{ code, - p, + p // em, }} > @@ -46,17 +46,12 @@ const code = React.memo((props) => { if (inline) { return {children}; } else { - return ( - - ); + return ; } }); const p = React.memo((props) => { - return

{props?.children}

; + return

{props?.children}

; }); // const blinker = ({ node }) => { diff --git a/client/src/components/Messages/Content/SubRow.jsx b/client/src/components/Messages/Content/SubRow.jsx index 55e522125..be1e9d72e 100644 --- a/client/src/components/Messages/Content/SubRow.jsx +++ b/client/src/components/Messages/Content/SubRow.jsx @@ -3,7 +3,11 @@ import React from 'react'; export default function SubRow({ children, classes = '', subclasses = '', onClick }) { return (
-
{children}
+
+ {children} +
); } diff --git a/client/src/components/Messages/HoverButtons.jsx b/client/src/components/Messages/HoverButtons.jsx index f280287e7..465d51aa5 100644 --- a/client/src/components/Messages/HoverButtons.jsx +++ b/client/src/components/Messages/HoverButtons.jsx @@ -32,10 +32,14 @@ export default function HoverButtons({ // for now, once branching is supported, regerate will be enabled const regenerateEnabled = // !message?.error && - !message?.isCreatedByUser && !message?.searchResult && !isEditting && !isSubmitting && branchingSupported; + !message?.isCreatedByUser && + !message?.searchResult && + !isEditting && + !isSubmitting && + branchingSupported; return ( -
+
{editEnabled ? ( -
diff --git a/client/src/components/Messages/MessageHeader.jsx b/client/src/components/Messages/MessageHeader.jsx index 65fea04ad..b63b5f0d2 100644 --- a/client/src/components/Messages/MessageHeader.jsx +++ b/client/src/components/Messages/MessageHeader.jsx @@ -6,7 +6,7 @@ import { Button } from '../ui/Button.tsx'; import store from '~/store'; -const clipPromptPrefix = str => { +const clipPromptPrefix = (str) => { if (typeof str !== 'string') { return null; } else if (str.length > 10) { @@ -61,7 +61,9 @@ const MessageHeader = ({ isSearchView = false }) => { )} onClick={() => (endpoint === 'chatGPTBrowser' ? null : setSaveAsDialogShow(true))} > -
{getConversationTitle()}
+
+ {getConversationTitle()} +
{ + const setSiblingIdxRev = (value) => { setSiblingIdx(messagesTree?.length - value - 1); }; @@ -42,18 +42,18 @@ export default function MultiMessage({ <> {messagesTree ? messagesTree.map(message => ( - - )) + + )) : null} ); diff --git a/client/src/components/Messages/ScrollToBottom.jsx b/client/src/components/Messages/ScrollToBottom.jsx index c0d85367e..eebddac4f 100644 --- a/client/src/components/Messages/ScrollToBottom.jsx +++ b/client/src/components/Messages/ScrollToBottom.jsx @@ -1,11 +1,10 @@ import React from 'react'; -export default function ScrollToBottom({ scrollHandler}) { - +export default function ScrollToBottom({ scrollHandler }) { return ( diff --git a/client/src/components/Messages/SiblingSwitch.jsx b/client/src/components/Messages/SiblingSwitch.jsx index ebca64cdf..e04b6c31a 100644 --- a/client/src/components/Messages/SiblingSwitch.jsx +++ b/client/src/components/Messages/SiblingSwitch.jsx @@ -1,26 +1,58 @@ import React from 'react'; -export default function SiblingSwitch({ - siblingIdx, - siblingCount, - setSiblingIdx -}) { +export default function SiblingSwitch({ siblingIdx, siblingCount, setSiblingIdx }) { const previous = () => { setSiblingIdx(siblingIdx - 1); - } + }; const next = () => { setSiblingIdx(siblingIdx + 1); - } + }; return siblingCount > 1 ? ( <> - - {siblingIdx + 1}/{siblingCount} - - - ):null; + + ) : null; } diff --git a/client/src/components/Messages/index.jsx b/client/src/components/Messages/index.jsx index 9ef97065e..fc493c253 100644 --- a/client/src/components/Messages/index.jsx +++ b/client/src/components/Messages/index.jsx @@ -76,7 +76,7 @@ export default function Messages({ isSearchView = false }) { timeoutId = setTimeout(handleScroll, 100); }; - const scrollHandler = e => { + const scrollHandler = (e) => { e.preventDefault(); scrollToBottom(); }; @@ -87,10 +87,7 @@ export default function Messages({ isSearchView = false }) { ref={scrollableRef} onScroll={debouncedHandleScroll} > -
+
{_messagesTree === null ? ( diff --git a/client/src/components/Nav/ClearConvos.jsx b/client/src/components/Nav/ClearConvos.jsx index 663e3659c..7eac1e1ff 100644 --- a/client/src/components/Nav/ClearConvos.jsx +++ b/client/src/components/Nav/ClearConvos.jsx @@ -25,9 +25,7 @@ export default function ClearConvos() { return ( - diff --git a/client/src/components/Nav/ExportConversation/ExportModel.jsx b/client/src/components/Nav/ExportConversation/ExportModel.jsx index a8cea3801..41d545295 100644 --- a/client/src/components/Nav/ExportConversation/ExportModel.jsx +++ b/client/src/components/Nav/ExportConversation/ExportModel.jsx @@ -52,7 +52,7 @@ export default function ExportModel({ open, onOpenChange }) { setRecursive(true); }, [open]); - const _setType = newType => { + const _setType = (newType) => { const exportBranchesSupport = newType === 'json' || newType === 'csv' || newType === 'webpage'; const exportOptionsSupport = newType !== 'csv' && newType !== 'screenshot'; @@ -66,7 +66,13 @@ export default function ExportModel({ open, onOpenChange }) { // return an object or an array based on branches and recursive option // messageId is used to get siblindIdx from recoil snapshot - const buildMessageTree = async ({ messageId, message, messages, branches = false, recursive = false }) => { + const buildMessageTree = async ({ + messageId, + message, + messages, + branches = false, + recursive = false + }) => { let children = []; if (messages?.length) if (branches) @@ -137,21 +143,39 @@ export default function ExportModel({ open, onOpenChange }) { extension: 'csv', exportType: exportFromJSON.types.csv, beforeTableEncode: entries => [ - { fieldName: 'sender', fieldValues: entries.find(e => e.fieldName == 'sender').fieldValues }, + { + fieldName: 'sender', + fieldValues: entries.find(e => e.fieldName == 'sender').fieldValues + }, { fieldName: 'text', fieldValues: entries.find(e => e.fieldName == 'text').fieldValues }, { fieldName: 'isCreatedByUser', fieldValues: entries.find(e => e.fieldName == 'isCreatedByUser').fieldValues }, - { fieldName: 'error', fieldValues: entries.find(e => e.fieldName == 'error').fieldValues }, - { fieldName: 'unfinished', fieldValues: entries.find(e => e.fieldName == 'unfinished').fieldValues }, - { fieldName: 'cancelled', fieldValues: entries.find(e => e.fieldName == 'cancelled').fieldValues }, - { fieldName: 'messageId', fieldValues: entries.find(e => e.fieldName == 'messageId').fieldValues }, + { + fieldName: 'error', + fieldValues: entries.find(e => e.fieldName == 'error').fieldValues + }, + { + fieldName: 'unfinished', + fieldValues: entries.find(e => e.fieldName == 'unfinished').fieldValues + }, + { + fieldName: 'cancelled', + fieldValues: entries.find(e => e.fieldName == 'cancelled').fieldValues + }, + { + fieldName: 'messageId', + fieldValues: entries.find(e => e.fieldName == 'messageId').fieldValues + }, { fieldName: 'parentMessageId', fieldValues: entries.find(e => e.fieldName == 'parentMessageId').fieldValues }, - { fieldName: 'createdAt', fieldValues: entries.find(e => e.fieldName == 'createdAt').fieldValues } + { + fieldName: 'createdAt', + fieldValues: entries.find(e => e.fieldName == 'createdAt').fieldValues + } ] }); }; @@ -284,10 +308,7 @@ export default function ExportModel({ open, onOpenChange }) { 'rounded-md border border-gray-200 focus:border-slate-400 focus:bg-gray-50 bg-transparent text-sm shadow-[0_0_10px_rgba(0,0,0,0.05)] outline-none placeholder:text-gray-400 focus:outline-none focus:ring-gray-400 focus:ring-opacity-20 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 dark:border-gray-500 dark:bg-gray-700 focus:dark:bg-gray-600 dark:text-gray-50 dark:shadow-[0_0_15px_rgba(0,0,0,0.10)] dark:focus:border-gray-400 dark:focus:outline-none dark:focus:ring-0 dark:focus:ring-gray-400 dark:focus:ring-offset-0'; return ( - +
-
-