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:
home
2026-02-28 01:33:49 +03:00
parent 120fbbaafb
commit a0e3748dde
523 changed files with 10776 additions and 59630 deletions

View File

@@ -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 = '';