Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .cursorrules
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Cursor AI Rules for Cheolsu Proxy Project
당신은 TypeScript, React19, Shadcn UI, Radix UI, Tailwind, Rust, Tauri, MITM(Man In the Middle attack) Proxy, FSD(Feature-Sliced Design), zustand 전문가입니다.
당신은 TypeScript, React19, Shadcn UI, Radix UI, Tailwind, Rust, Tauri, MITM(Man In The Middle attack) Proxy, FSD(Feature-Sliced Design), zustand 전문가입니다.

## 프로젝트 개요
- Cheolsu Proxy Rust 기반의 프록시 도구로, Tauri를 사용한 데스크톱 애플리케이션입니다.
Expand Down
2 changes: 1 addition & 1 deletion pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion tauri-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
"tailwind-merge": "^3.3.1",
"tailwindcss": "^4.1.12",
"tw-animate-css": "^1.3.8",
"zod": "^4.1.8",
"zod": "^4.1.12",
"zustand": "^5.0.8"
},
"devDependencies": {
Expand Down
10 changes: 5 additions & 5 deletions tauri-ui/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 0 additions & 3 deletions tauri-ui/src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,6 @@ edition = "2021"
name = "cheolsu_proxy"
crate-type = ["staticlib", "cdylib", "rlib"]

# 빌드 스크립트 추가
build = "build.rs"

[build-dependencies]
tauri-build = { version = "2", features = [] }

Expand Down
8 changes: 5 additions & 3 deletions tauri-ui/src/app/App.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useEffect } from 'react';
import { useThemeProvider, RouterProvider } from './providers';
import { useThemeProvider, RouterProvider, ProxyEventProvider } from './providers';
import { Toaster } from '@/shared/ui';
import { useProxyStore } from '@/shared/stores';

Expand All @@ -14,8 +14,10 @@ const App: React.FC = () => {

return (
<div className="App">
<RouterProvider />
<Toaster richColors />
<ProxyEventProvider>
<RouterProvider />
<Toaster richColors />
</ProxyEventProvider>
</div>
);
};
Expand Down
1 change: 1 addition & 0 deletions tauri-ui/src/app/providers/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './use-theme-provider';
export * from './router-provider';
export * from './proxy-event-provider';
34 changes: 34 additions & 0 deletions tauri-ui/src/app/providers/proxy-event-provider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { useEffect } from 'react';

import { listen } from '@tauri-apps/api/event';

import type { ProxyEventTuple, HttpTransaction } from '@/entities/proxy';
import { useTransactionStore } from '@/shared/stores';

/**
* 전역 Proxy 이벤트 리스너 Provider
* 앱 전체에서 proxy_event를 수신하여 transaction store에 저장합니다.
* 탭 이동과 무관하게 이벤트를 계속 수신할 수 있습니다.
*/
export const ProxyEventProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const { addTransaction, isPaused } = useTransactionStore();

useEffect(() => {
const unlisten = listen<ProxyEventTuple>('proxy_event', (event) => {
// isPaused 상태와 관계없이 이벤트는 수신하되, store에 추가할지만 결정
if (isPaused) return;

const [request, response] = event.payload;
const transaction: HttpTransaction = { request, response };

// zustand store에 transaction 추가
addTransaction(transaction);
});

return () => {
unlisten.then((f) => f());
};
}, [addTransaction, isPaused]);

return <>{children}</>;
};
1 change: 0 additions & 1 deletion tauri-ui/src/pages/network-dashboard/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
export * from './use-transaction-filters';
export * from './use-proxy-event-control';
export * from './use-transactions';

This file was deleted.

84 changes: 53 additions & 31 deletions tauri-ui/src/pages/network-dashboard/hooks/use-transactions.ts
Original file line number Diff line number Diff line change
@@ -1,48 +1,70 @@
import { useState, useCallback } from 'react';
import { useCallback, useEffect } from 'react';

import type { HttpTransaction } from '@/entities/proxy';
import { useTransactionStore } from '@/shared/stores';

export const useTransactions = () => {
const [transactions, setTransactions] = useState<HttpTransaction[]>([]);
const [selectedTransaction, setSelectedTransaction] = useState<HttpTransaction | null>(null);
interface UseTransactionsProps {
initialPaused?: boolean;
}

const addTransaction = useCallback((transaction: HttpTransaction) => {
setTransactions(prev => {
const existingTransaction = prev.find(t => t.request?.time === transaction.request?.time);

if (existingTransaction) {
return prev;
}

return [...prev, transaction];
});
}, []);

const clearTransactions = useCallback(() => {
setTransactions([]);
setSelectedTransaction(null)
}, []);

const deleteTransaction = useCallback((id: number) => {
setTransactions(prev => prev.filter((transaction) => transaction?.request?.time !== id));
}, []);
/**
* Transaction 관련 모든 기능을 관리하는 통합 Hook
* - Transaction 데이터 관리
* - Transaction 선택 관리
* - Proxy 이벤트 제어 (pause/resume)
*/
export const useTransactions = ({ initialPaused = false }: UseTransactionsProps = {}) => {
const {
transactions,
selectedTransaction,
isPaused,
addTransaction,
clearTransactions,
deleteTransaction,
setSelectedTransaction,
setPaused,
togglePause,
} = useTransactionStore();

// 초기 paused 상태 설정
useEffect(() => {
if (initialPaused !== undefined) {
setPaused(initialPaused);
}
}, [initialPaused, setPaused]);

const createTransactionSelectHandler = useCallback((transaction: HttpTransaction) => () => {
setSelectedTransaction(transaction);
}, []);
const createTransactionSelectHandler = useCallback(
(transaction: HttpTransaction) => () => {
setSelectedTransaction(transaction);
},
[setSelectedTransaction],
);

const clearSelectedTransaction = useCallback(() => {
setSelectedTransaction(null)
}, [])
setSelectedTransaction(null);
}, [setSelectedTransaction]);

const pause = useCallback(() => setPaused(true), [setPaused]);
const resume = useCallback(() => setPaused(false), [setPaused]);

return {
// Transaction 데이터
transactions,
selectedTransaction,
addTransaction,
clearTransactions,
deleteTransaction,
selectedTransaction,

// Transaction 선택 관리
createTransactionSelectHandler,
clearSelectedTransaction
clearSelectedTransaction,

// Proxy 이벤트 제어
isPaused,
paused: isPaused, // 하위 호환성을 위해 별칭 제공
setPaused,
togglePause,
pause,
resume,
};
};
13 changes: 6 additions & 7 deletions tauri-ui/src/pages/network-dashboard/ui/network-dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { NetworkTable } from '@/widgets/network-table';

import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from '@/shared/ui';

import { useProxyEventControl, useTransactionFilters, useTransactions } from '../hooks';
import { useTransactionFilters, useTransactions } from '../hooks';
import { useProxyStore } from '@/shared/stores';
import { HostPathTree } from '@/widgets/host-path-tree/ui/host-path-tree';

Expand All @@ -17,16 +17,15 @@ export const NetworkDashboard = () => {

const {
transactions,
addTransaction,
clearTransactions,
deleteTransaction,
selectedTransaction,
createTransactionSelectHandler,
clearSelectedTransaction,
paused,
togglePause,
} = useTransactions();

const { paused, togglePause } = useProxyEventControl({ onTransactionReceived: addTransaction });

const {
searchQuery,
setMethodFilter,
Expand All @@ -38,14 +37,14 @@ export const NetworkDashboard = () => {
} = useTransactionFilters({ transactions });

const createTransactionDeleteHandler = useCallback(
(id: number) => () => {
(id: string) => () => {
deleteTransaction(id);

if (selectedTransaction?.request?.time === id) {
if (selectedTransaction?.request?.id === id) {
clearSelectedTransaction();
}
},
[],
[deleteTransaction, selectedTransaction, clearSelectedTransaction],
);

return (
Expand Down
25 changes: 25 additions & 0 deletions tauri-ui/src/pages/sessions/context/session-form-context.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { createContext, useContext } from 'react';
import { useForm } from '@tanstack/react-form';
import { zodValidator } from '@tanstack/zod-form-adapter';

import type { SessionEditFormData } from '../lib/session-edit-schema';

export interface SessionFormInstance {
getFieldValue: (name: string) => any;
setFieldValue: (name: string, value: any) => void;
validate: () => Promise<boolean>;
reset: () => void;
getValues: () => SessionEditFormData;
}

const SessionFormContext = createContext<SessionFormInstance | null>(null);

export const useSessionForm = () => {
const context = useContext(SessionFormContext);
if (!context) {
throw new Error('useSessionForm must be used within a SessionFormProvider');
}
return context;
};

export { SessionFormContext };
1 change: 1 addition & 0 deletions tauri-ui/src/pages/sessions/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './use-session-edit';
Loading