fix: BUG-009/010/011 — DataMasking, cancel button, SQL casts
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled

BUG-009 (P1): Add frontend DataMasking in saas-relay-client.ts
- Masks ID cards, phones, emails, money, company names before relay
- Unmasks tokens in AI response so user sees original data
- Mirrors Rust DataMasking middleware patterns

BUG-010 (P3): Send button transforms to Stop during streaming
- Shows square icon when isStreaming, calls cancelStream()
- Normal arrow icon when idle, calls handleSend()

BUG-011 (P2): Add ::timestamptz casts for old TEXT timestamp columns
- account/handlers.rs: dashboard stats query
- telemetry/service.rs: reported_at comparisons
- workers/aggregate_usage.rs: usage aggregation query
This commit is contained in:
iven
2026-04-09 23:45:19 +08:00
parent a304544233
commit ba586e5aa7
5 changed files with 88 additions and 22 deletions

View File

@@ -10,7 +10,7 @@ import { useConfigStore } from '../store/configStore';
import { useSaaSStore } from '../store/saasStore';
import { type UnlistenFn } from '@tauri-apps/api/event';
import { safeListenEvent } from '../lib/safe-tauri';
import { Paperclip, ArrowUp, MessageSquare, Download, X, FileText, Image as ImageIcon, Search, ClipboardList } from 'lucide-react';
import { Paperclip, ArrowUp, MessageSquare, Download, X, FileText, Image as ImageIcon, Search, ClipboardList, Square } from 'lucide-react';
import { Button, EmptyState, MessageListSkeleton, LoadingDots } from './ui';
import { ResizableChatLayout } from './ai/ResizableChatLayout';
import { ArtifactPanel } from './ai/ArtifactPanel';
@@ -56,6 +56,7 @@ export function ChatArea({ compact, onOpenDetail }: { compact?: boolean; onOpenD
sendMessage: sendToGateway, initStreamListener,
chatMode, setChatMode, suggestions,
totalInputTokens, totalOutputTokens,
cancelStream,
} = useChatStore();
const currentAgent = useConversationStore((s) => s.currentAgent);
const currentModel = useConversationStore((s) => s.currentModel);
@@ -571,16 +572,28 @@ export function ChatArea({ compact, onOpenDetail }: { compact?: boolean; onOpenD
disabled={isStreaming}
/>
)}
<Button
variant="primary"
size="sm"
onClick={handleSend}
disabled={isStreaming || (!input.trim() && pendingFiles.length === 0)}
className="w-8 h-8 rounded-full p-0 flex items-center justify-center bg-orange-500 hover:bg-orange-600 text-white disabled:opacity-50"
aria-label="发送消息"
>
<ArrowUp className="w-4 h-4 text-white" />
</Button>
{isStreaming ? (
<Button
variant="primary"
size="sm"
onClick={cancelStream}
className="w-8 h-8 rounded-full p-0 flex items-center justify-center bg-gray-500 hover:bg-gray-600 text-white"
aria-label="停止生成"
>
<Square className="w-3.5 h-3.5 text-white fill-white" />
</Button>
) : (
<Button
variant="primary"
size="sm"
onClick={handleSend}
disabled={!input.trim() && pendingFiles.length === 0}
className="w-8 h-8 rounded-full p-0 flex items-center justify-center bg-orange-500 hover:bg-orange-600 text-white disabled:opacity-50"
aria-label="发送消息"
>
<ArrowUp className="w-4 h-4 text-white" />
</Button>
)}
</div>
</div>
</div>