Merge remote-tracking branch 'overcuriousity/master' into ollama-auth

This commit is contained in:
projectmoon 2024-07-25 21:24:47 +02:00
commit b1b1db184d
6 changed files with 106 additions and 13 deletions

View File

@ -17,3 +17,14 @@ export const chats = sqliteTable('chats', {
createdAt: text('createdAt').notNull(), createdAt: text('createdAt').notNull(),
focusMode: text('focusMode').notNull(), focusMode: text('focusMode').notNull(),
}); });
export const settings = sqliteTable('settings', {
id: integer('id').primaryKey(),
chatModelProvider: text('chatModelProvider'),
chatModel: text('chatModel'),
embeddingModelProvider: text('embeddingModelProvider'),
embeddingModel: text('embeddingModel'),
openAIApiKey: text('openAIApiKey'),
openAIBaseURL: text('openAIBaseURL'),
// TODO: add user auth
});

View File

@ -5,6 +5,7 @@ import configRouter from './config';
import modelsRouter from './models'; import modelsRouter from './models';
import suggestionsRouter from './suggestions'; import suggestionsRouter from './suggestions';
import chatsRouter from './chats'; import chatsRouter from './chats';
import settingsRouter from './settings';
const router = express.Router(); const router = express.Router();
@ -14,5 +15,6 @@ router.use('/config', configRouter);
router.use('/models', modelsRouter); router.use('/models', modelsRouter);
router.use('/suggestions', suggestionsRouter); router.use('/suggestions', suggestionsRouter);
router.use('/chats', chatsRouter); router.use('/chats', chatsRouter);
router.use('/settings', settingsRouter);
export default router; export default router;

43
src/routes/settings.ts Normal file
View File

@ -0,0 +1,43 @@
import express from 'express';
import db from '../db';
import { settings, eq } from '../db/schema';
const router = express.Router();
router.post('/', async (req, res) => {
try {
const { chatModelProvider, chatModel, embeddingModelProvider, embeddingModel, openAIApiKey, openAIBaseURL } = req.body;
// TODO: Add user authentication
await db.delete(settings).execute();
await db.insert(settings).values({
chatModelProvider,
chatModel,
embeddingModelProvider,
embeddingModel,
openAIApiKey,
openAIBaseURL,
}).execute();
res.status(200).json({ message: 'Settings saved successfully' });
} catch (err) {
console.error('Error saving settings:', err);
res.status(500).json({ message: 'An error occurred while saving settings' });
}
});
router.get('/', async (req, res) => {
try {
// TODO: Add user authentication
const userSettings = await db.query.settings.findFirst();
if (!userSettings) {
return res.status(404).json({ message: 'No settings found' });
}
res.status(200).json(userSettings);
} catch (err) {
console.error('Error fetching settings:', err);
res.status(500).json({ message: 'An error occurred while fetching settings' });
}
});
export default router;

View File

@ -21,6 +21,25 @@ export type Message = {
sources?: Document[]; sources?: Document[];
}; };
const fetchSettings = async () => {
try {
const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/settings`);
const settings = await res.json();
if (settings) {
localStorage.setItem('chatModelProvider', settings.chatModelProvider);
localStorage.setItem('chatModel', settings.chatModel);
localStorage.setItem('embeddingModelProvider', settings.embeddingModelProvider);
localStorage.setItem('embeddingModel', settings.embeddingModel);
localStorage.setItem('openAIApiKey', settings.openAIApiKey);
localStorage.setItem('openAIBaseURL', settings.openAIBaseURL);
}
} catch (err) {
console.error('Failed to fetch settings:', err);
}
};
const useSocket = ( const useSocket = (
url: string, url: string,
setIsWSReady: (ready: boolean) => void, setIsWSReady: (ready: boolean) => void,
@ -31,6 +50,8 @@ const useSocket = (
useEffect(() => { useEffect(() => {
if (!ws) { if (!ws) {
const connectWs = async () => { const connectWs = async () => {
await fetchSettings();
let chatModel = localStorage.getItem('chatModel'); let chatModel = localStorage.getItem('chatModel');
let chatModelProvider = localStorage.getItem('chatModelProvider'); let chatModelProvider = localStorage.getItem('chatModelProvider');
let embeddingModel = localStorage.getItem('embeddingModel'); let embeddingModel = localStorage.getItem('embeddingModel');
@ -324,6 +345,9 @@ const ChatWindow = ({ id }: { id?: string }) => {
const messagesRef = useRef<Message[]>([]); const messagesRef = useRef<Message[]>([]);
useEffect(() => {
fetchSettings();
}, []);
useEffect(() => { useEffect(() => {
messagesRef.current = messages; messagesRef.current = messages;
}, [messages]); }, [messages]);

View File

@ -146,31 +146,35 @@ const SettingsDialog = ({
const handleSubmit = async () => { const handleSubmit = async () => {
setIsUpdating(true); setIsUpdating(true);
try { try {
await fetch(`${process.env.NEXT_PUBLIC_API_URL}/config`, { await fetch(`${process.env.NEXT_PUBLIC_API_URL}/settings`, {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
}, },
body: JSON.stringify(config), body: JSON.stringify({
chatModelProvider: selectedChatModelProvider,
chatModel: selectedChatModel,
embeddingModelProvider: selectedEmbeddingModelProvider,
embeddingModel: selectedEmbeddingModel,
openAIApiKey: customOpenAIApiKey,
openAIBaseURL: customOpenAIBaseURL,
}),
}); });
// Still keep localStorage for quick access on the client-side
localStorage.setItem('chatModelProvider', selectedChatModelProvider!); localStorage.setItem('chatModelProvider', selectedChatModelProvider!);
localStorage.setItem('chatModel', selectedChatModel!); localStorage.setItem('chatModel', selectedChatModel!);
localStorage.setItem( localStorage.setItem('embeddingModelProvider', selectedEmbeddingModelProvider!);
'embeddingModelProvider',
selectedEmbeddingModelProvider!,
);
localStorage.setItem('embeddingModel', selectedEmbeddingModel!); localStorage.setItem('embeddingModel', selectedEmbeddingModel!);
localStorage.setItem('openAIApiKey', customOpenAIApiKey!); localStorage.setItem('openAIApiKey', customOpenAIApiKey!);
localStorage.setItem('openAIBaseURL', customOpenAIBaseURL!); localStorage.setItem('openAIBaseURL', customOpenAIBaseURL!);
} catch (err) { } catch (err) {
console.log(err); console.error(err);
} finally { } finally {
setIsUpdating(false); setIsUpdating(false);
setIsOpen(false); setIsOpen(false);
window.location.reload(); window.location.reload();
} }
}; };

View File

@ -1,5 +1,4 @@
'use client'; 'use client';
import { cn } from '@/lib/utils'; import { cn } from '@/lib/utils';
import { BookOpenText, Home, Search, SquarePen, Settings } from 'lucide-react'; import { BookOpenText, Home, Search, SquarePen, Settings } from 'lucide-react';
import Link from 'next/link'; import Link from 'next/link';
@ -79,13 +78,13 @@ const Sidebar = ({ children }: { children: React.ReactNode }) => {
</div> </div>
</div> </div>
<div className="fixed bottom-0 w-full z-50 flex flex-row items-center gap-x-6 bg-light-primary dark:bg-dark-primary px-4 py-4 shadow-sm lg:hidden"> <div className="fixed bottom-0 w-full z-50 flex flex-row items-center justify-between bg-light-primary dark:bg-dark-primary px-4 py-4 shadow-sm lg:hidden">
{navLinks.map((link, i) => ( {navLinks.map((link, i) => (
<Link <Link
href={link.href} href={link.href}
key={i} key={i}
className={cn( className={cn(
'relative flex flex-col items-center space-y-1 text-center w-full', 'relative flex flex-col items-center space-y-1 text-center',
link.active link.active
? 'text-black dark:text-white' ? 'text-black dark:text-white'
: 'text-black dark:text-white/70', : 'text-black dark:text-white/70',
@ -98,6 +97,16 @@ const Sidebar = ({ children }: { children: React.ReactNode }) => {
<p className="text-xs">{link.label}</p> <p className="text-xs">{link.label}</p>
</Link> </Link>
))} ))}
<button
onClick={() => setIsSettingsOpen(!isSettingsOpen)}
className={cn(
'relative flex flex-col items-center space-y-1 text-center',
'text-black dark:text-white/70',
)}
>
<Settings />
<p className="text-xs">Settings</p>
</button>
</div> </div>
<Layout>{children}</Layout> <Layout>{children}</Layout>