feat: auth service + security audit fixes + cleanup legacy services
Major changes:
- Add auth-svc: JWT auth, register/login/refresh, password reset
- Add auth UI: modals, pages (/login, /register, /forgot-password)
- Add usage tracking (usage_metrics table, daily limits)
- Add tiered rate limiting (free/pro/business)
- Add LLM usage limits per tier
Security fixes:
- All repos now require userID for Update/Delete operations
- JWT middleware in chat-svc, llm-svc, agent-svc, discover-svc
- ErrNotFound/ErrForbidden errors for proper access control
Cleanup:
- Remove legacy TypeScript services/ directory
- Remove computer-svc (to be reimplemented)
- Remove old deploy/docker configs
New files:
- backend/cmd/auth-svc/main.go
- backend/internal/auth/{types,repository}.go
- backend/internal/usage/{types,repository}.go
- backend/pkg/middleware/{llm_limits,ratelimit_tiered}.go
- backend/webui/src/components/auth/*
- backend/webui/src/app/(auth)/*
Made-with: Cursor
This commit is contained in:
@@ -1,8 +1,19 @@
|
||||
'use client';
|
||||
|
||||
import { useState, useCallback, useRef } from 'react';
|
||||
import type { Message, Citation, Widget, StreamEvent } from '../types';
|
||||
import type { Message, Citation, Widget, StreamEvent, ChatAttachmentInfo } from '../types';
|
||||
import { streamChat, generateId } from '../api';
|
||||
import { getLanguageFromStorage } from '../contexts/LanguageContext';
|
||||
import type { SendOptions, ChatAttachment } from '@/components/ChatInput';
|
||||
|
||||
function fileToDataUrl(file: File): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
reader.onload = () => resolve(reader.result as string);
|
||||
reader.onerror = reject;
|
||||
reader.readAsDataURL(file);
|
||||
});
|
||||
}
|
||||
|
||||
interface UseChatOptions {
|
||||
onError?: (error: Error) => void;
|
||||
@@ -15,7 +26,11 @@ export function useChat(options: UseChatOptions = {}) {
|
||||
const [widgets, setWidgets] = useState<Widget[]>([]);
|
||||
const abortControllerRef = useRef<AbortController | null>(null);
|
||||
|
||||
const sendMessage = useCallback(async (content: string, mode: 'speed' | 'balanced' | 'quality' = 'balanced') => {
|
||||
const sendMessage = useCallback(async (content: string, sendOptions?: SendOptions | 'speed' | 'balanced' | 'quality') => {
|
||||
const mode = typeof sendOptions === 'string' ? sendOptions : sendOptions?.mode ?? 'balanced';
|
||||
const webSearch = typeof sendOptions === 'object' ? sendOptions.webSearch : false;
|
||||
const attachments = typeof sendOptions === 'object' ? sendOptions.attachments : [];
|
||||
const locale = getLanguageFromStorage();
|
||||
if (!content.trim() || isLoading) return;
|
||||
|
||||
const userMessage: Message = {
|
||||
@@ -49,6 +64,21 @@ export function useChat(options: UseChatOptions = {}) {
|
||||
return acc;
|
||||
}, [] as [string, string][]);
|
||||
|
||||
const attachmentInfos = await Promise.all(
|
||||
attachments.map(async (att) => {
|
||||
let dataUrl: string | undefined;
|
||||
if (att.type === 'image' || att.file.type.startsWith('image/')) {
|
||||
dataUrl = await fileToDataUrl(att.file);
|
||||
}
|
||||
return {
|
||||
name: att.file.name,
|
||||
type: att.file.type,
|
||||
size: att.file.size,
|
||||
dataUrl,
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
try {
|
||||
const stream = streamChat({
|
||||
message: {
|
||||
@@ -58,7 +88,9 @@ export function useChat(options: UseChatOptions = {}) {
|
||||
},
|
||||
optimizationMode: mode,
|
||||
history,
|
||||
locale: 'ru',
|
||||
locale,
|
||||
webSearch,
|
||||
attachments: attachmentInfos.length > 0 ? attachmentInfos : undefined,
|
||||
});
|
||||
|
||||
let fullContent = '';
|
||||
|
||||
Reference in New Issue
Block a user