Merge pull request #12 from gkastanis/fix/missing-frontend-lib-files

Add missing frontend lib files
This commit is contained in:
Bob Roberts 2025-12-02 14:44:55 -06:00 • committed by GitHub
commit a7b7c4e51c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 227 additions and 0 deletions

1
.gitignore vendored
View file

@ -12,6 +12,7 @@ eggs/
.eggs/ .eggs/
lib/ lib/
lib64/ lib64/
!frontend/src/lib/
parts/ parts/
sdist/ sdist/
var/ var/

88
frontend/src/lib/api.ts Normal file
View file

@ -0,0 +1,88 @@
const API_BASE = 'http://localhost:8051';
export interface ChatResponse {
response: string;
sources?: Array<{
filename: string;
content: string;
score: number;
}>;
thinking_time?: number;
}
export interface KBStats {
document_count: number;
chunk_count: number;
last_updated?: string;
total_size?: number;
}
export interface Document {
filename: string;
chunk_count: number;
created_at?: string;
}
export async function chat(
kbName: string,
query: string,
options?: { useHybrid?: boolean; useRerank?: boolean; useAgentic?: boolean }
): Promise<ChatResponse> {
const res = await fetch(`${API_BASE}/api/kb/${kbName}/chat`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
query,
use_hybrid: options?.useHybrid ?? false,
use_rerank: options?.useRerank ?? false,
use_agentic: options?.useAgentic ?? false,
}),
});
if (!res.ok) {
throw new Error(`Chat failed: ${res.statusText}`);
}
return res.json();
}
export async function getKBStats(kbName: string): Promise<KBStats> {
const res = await fetch(`${API_BASE}/api/kb/${kbName}/stats`);
if (!res.ok) {
throw new Error(`Failed to get KB stats: ${res.statusText}`);
}
return res.json();
}
export async function listDocuments(kbName: string): Promise<Document[]> {
const res = await fetch(`${API_BASE}/api/kb/${kbName}/documents`);
if (!res.ok) {
throw new Error(`Failed to list documents: ${res.statusText}`);
}
const data = await res.json();
return data.documents || [];
}
export async function uploadDocument(kbName: string, file: File): Promise<void> {
const formData = new FormData();
formData.append('file', file);
const res = await fetch(`${API_BASE}/api/kb/${kbName}/upload`, {
method: 'POST',
body: formData,
});
if (!res.ok) {
throw new Error(`Failed to upload document: ${res.statusText}`);
}
}
export async function deleteDocument(kbName: string, filename: string): Promise<void> {
const res = await fetch(`${API_BASE}/api/kb/${kbName}/documents/${encodeURIComponent(filename)}`, {
method: 'DELETE',
});
if (!res.ok) {
throw new Error(`Failed to delete document: ${res.statusText}`);
}
}

102
frontend/src/lib/auth.tsx Normal file
View file

@ -0,0 +1,102 @@
import { createContext, useContext, useState, useEffect, ReactNode } from 'react';
const API_BASE = 'http://localhost:8051';
interface AuthContextType {
isAuthenticated: boolean;
isAuthEnabled: boolean;
isLoading: boolean;
user: { email: string } | null;
login: (email: string, password: string) => Promise<void>;
logout: () => void;
}
const AuthContext = createContext<AuthContextType | undefined>(undefined);
export function AuthProvider({ children }: { children: ReactNode }) {
const [isAuthenticated, setIsAuthenticated] = useState(false);
const [isAuthEnabled, setIsAuthEnabled] = useState(true);
const [isLoading, setIsLoading] = useState(true);
const [user, setUser] = useState<{ email: string } | null>(null);
useEffect(() => {
checkAuthStatus();
}, []);
const checkAuthStatus = async () => {
try {
// Check if auth is enabled
const statusRes = await fetch(`${API_BASE}/api/auth/status`);
if (statusRes.ok) {
const statusData = await statusRes.json();
setIsAuthEnabled(statusData.auth_enabled);
if (!statusData.auth_enabled) {
setIsAuthenticated(true);
setIsLoading(false);
return;
}
}
// Check if user is already logged in
const token = localStorage.getItem('auth_token');
if (token) {
const meRes = await fetch(`${API_BASE}/api/auth/me`, {
headers: { Authorization: `Bearer ${token}` }
});
if (meRes.ok) {
const userData = await meRes.json();
setUser(userData);
setIsAuthenticated(true);
} else {
localStorage.removeItem('auth_token');
}
}
} catch (error) {
// If we can't reach the server, assume auth is disabled for local dev
console.warn('Could not check auth status:', error);
setIsAuthEnabled(false);
setIsAuthenticated(true);
} finally {
setIsLoading(false);
}
};
const login = async (email: string, password: string) => {
const res = await fetch(`${API_BASE}/api/auth/login`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password })
});
if (!res.ok) {
const error = await res.json();
throw new Error(error.detail || 'Login failed');
}
const data = await res.json();
localStorage.setItem('auth_token', data.access_token);
setUser({ email });
setIsAuthenticated(true);
};
const logout = () => {
localStorage.removeItem('auth_token');
setUser(null);
setIsAuthenticated(false);
};
return (
<AuthContext.Provider value={{ isAuthenticated, isAuthEnabled, isLoading, user, login, logout }}>
{children}
</AuthContext.Provider>
);
}
export function useAuth() {
const context = useContext(AuthContext);
if (context === undefined) {
throw new Error('useAuth must be used within an AuthProvider');
}
return context;
}

View file

@ -0,0 +1,36 @@
export interface CommonPath {
label: string;
path: string;
description?: string;
}
export function getCommonProjectPaths(): CommonPath[] {
return [
{ label: 'Home', path: '~', description: 'Your home directory' },
{ label: 'Documents', path: '~/Documents', description: 'Documents folder' },
{ label: 'Projects', path: '~/Projects', description: 'Common projects folder' },
{ label: 'Sites', path: '~/sites', description: 'Web projects' },
{ label: 'Code', path: '~/code', description: 'Code repositories' },
{ label: 'Desktop', path: '~/Desktop', description: 'Desktop folder' },
];
}
export function formatPathForDisplay(path: string): string {
if (!path) return '';
// Replace home directory with ~
const homeDir = '/home/' + (typeof window !== 'undefined' ? 'user' : 'user');
if (path.startsWith(homeDir)) {
return path.replace(homeDir, '~');
}
// Shorten long paths
if (path.length > 50) {
const parts = path.split('/');
if (parts.length > 4) {
return `${parts[0]}/${parts[1]}/.../${parts[parts.length - 2]}/${parts[parts.length - 1]}`;
}
}
return path;
}