mirror of
https://github.com/mountain-loop/yaak.git
synced 2025-12-25 07:28:53 -05:00
Compare commits
2 Commits
request-bo
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9c5479b206 | ||
|
|
5f8902e57b |
@@ -2,7 +2,7 @@ use crate::error::Error::GenericError;
|
||||
use crate::error::Result;
|
||||
use crate::render::render_http_request;
|
||||
use crate::response_err;
|
||||
use log::debug;
|
||||
use log::{debug, warn};
|
||||
use reqwest_cookie_store::{CookieStore, CookieStoreMutex};
|
||||
use std::sync::Arc;
|
||||
use std::time::{Duration, Instant};
|
||||
@@ -19,8 +19,8 @@ use yaak_http::sender::ReqwestSender;
|
||||
use yaak_http::transaction::HttpTransaction;
|
||||
use yaak_http::types::{SendableHttpRequest, SendableHttpRequestOptions, append_query_params};
|
||||
use yaak_models::models::{
|
||||
CookieJar, Environment, HttpRequest, HttpResponse, HttpResponseEvent, HttpResponseHeader,
|
||||
HttpResponseState, ProxySetting, ProxySettingAuth,
|
||||
Cookie, CookieJar, Environment, HttpRequest, HttpResponse, HttpResponseEvent,
|
||||
HttpResponseHeader, HttpResponseState, ProxySetting, ProxySettingAuth,
|
||||
};
|
||||
use yaak_models::query_manager::QueryManagerExt;
|
||||
use yaak_models::util::UpdateSource;
|
||||
@@ -207,6 +207,29 @@ async fn send_http_request_inner<R: Runtime>(
|
||||
)
|
||||
.await;
|
||||
|
||||
// Persist cookies back to the database after the request completes
|
||||
if let Some((cookie_store, mut cj)) = maybe_cookie_manager {
|
||||
match cookie_store.lock() {
|
||||
Ok(store) => {
|
||||
let cookies: Vec<Cookie> = store
|
||||
.iter_any()
|
||||
.filter_map(|c| {
|
||||
// Convert cookie_store::Cookie -> yaak_models::Cookie via serde
|
||||
let json_cookie = serde_json::to_value(c).ok()?;
|
||||
serde_json::from_value(json_cookie).ok()
|
||||
})
|
||||
.collect();
|
||||
cj.cookies = cookies;
|
||||
if let Err(e) = window.db().upsert_cookie_jar(&cj, &update_source) {
|
||||
warn!("Failed to persist cookies to database: {}", e);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
warn!("Failed to lock cookie store: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match final_resp {
|
||||
Ok(r) => Ok(r),
|
||||
Err(e) => match app_handle.db().get_http_response(&resp_id) {
|
||||
|
||||
@@ -24,7 +24,7 @@ import { TabContent, Tabs } from './core/Tabs/Tabs';
|
||||
import { EmptyStateText } from './EmptyStateText';
|
||||
import { ErrorBoundary } from './ErrorBoundary';
|
||||
import { RecentHttpResponsesDropdown } from './RecentHttpResponsesDropdown';
|
||||
import { ResponseEvents } from './ResponseEvents';
|
||||
import { ResponseTimeline } from './ResponseEvents';
|
||||
import { ResponseHeaders } from './ResponseHeaders';
|
||||
import { ResponseInfo } from './ResponseInfo';
|
||||
import { AudioViewer } from './responseViewers/AudioViewer';
|
||||
@@ -48,7 +48,7 @@ interface Props {
|
||||
const TAB_BODY = 'body';
|
||||
const TAB_HEADERS = 'headers';
|
||||
const TAB_INFO = 'info';
|
||||
const TAB_EVENTS = 'events';
|
||||
const TAB_TIMELINE = 'events';
|
||||
|
||||
export function HttpResponsePane({ style, className, activeRequestId }: Props) {
|
||||
const { activeResponse, setPinnedResponseId, responses } = usePinnedHttpResponse(activeRequestId);
|
||||
@@ -87,7 +87,7 @@ export function HttpResponsePane({ style, className, activeRequestId }: Props) {
|
||||
),
|
||||
},
|
||||
{
|
||||
value: TAB_EVENTS,
|
||||
value: TAB_TIMELINE,
|
||||
label: 'Timeline',
|
||||
rightSlot: <CountBadge count={responseEvents.data?.length ?? 0} />,
|
||||
},
|
||||
@@ -233,8 +233,8 @@ export function HttpResponsePane({ style, className, activeRequestId }: Props) {
|
||||
<TabContent value={TAB_INFO}>
|
||||
<ResponseInfo response={activeResponse} />
|
||||
</TabContent>
|
||||
<TabContent value={TAB_EVENTS}>
|
||||
<ResponseEvents response={activeResponse} />
|
||||
<TabContent value={TAB_TIMELINE}>
|
||||
<ResponseTimeline response={activeResponse} />
|
||||
</TabContent>
|
||||
</Tabs>
|
||||
</div>
|
||||
|
||||
@@ -20,15 +20,15 @@ interface Props {
|
||||
response: HttpResponse;
|
||||
}
|
||||
|
||||
export function ResponseEvents({ response }: Props) {
|
||||
export function ResponseTimeline({ response }: Props) {
|
||||
return (
|
||||
<Fragment key={response.id}>
|
||||
<ActualResponseEvents response={response} />
|
||||
<Inner response={response} />
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
function ActualResponseEvents({ response }: Props) {
|
||||
function Inner({ response }: Props) {
|
||||
const [activeEventIndex, setActiveEventIndex] = useState<number | null>(null);
|
||||
const { data: events, error, isLoading } = useHttpResponseEvents(response);
|
||||
|
||||
@@ -57,8 +57,8 @@ function ActualResponseEvents({ response }: Props) {
|
||||
<SplitLayout
|
||||
layout="vertical"
|
||||
name="http_response_events"
|
||||
defaultRatio={0.5}
|
||||
minHeightPx={20}
|
||||
defaultRatio={0.25}
|
||||
minHeightPx={10}
|
||||
firstSlot={() => (
|
||||
<AutoScroller
|
||||
data={events}
|
||||
@@ -112,14 +112,14 @@ function EventRow({
|
||||
onClick={onClick}
|
||||
className={classNames(
|
||||
'w-full grid grid-cols-[auto_minmax(0,1fr)_auto] gap-2 items-center text-left',
|
||||
'px-1.5 h-xs font-mono cursor-default group focus:outline-none focus:text-text rounded',
|
||||
'px-1.5 h-xs font-mono text-editor cursor-default group focus:outline-none focus:text-text rounded',
|
||||
isActive && '!bg-surface-active !text-text',
|
||||
'text-text-subtle hover:text',
|
||||
)}
|
||||
>
|
||||
<Icon color={color} icon={icon} size="sm" />
|
||||
<div className="w-full truncate text-xs">{summary}</div>
|
||||
<div className="text-xs opacity-50">{format(`${event.createdAt}Z`, 'HH:mm:ss.SSS')}</div>
|
||||
<div className="w-full truncate">{summary}</div>
|
||||
<div className="opacity-50">{format(`${event.createdAt}Z`, 'HH:mm:ss.SSS')}</div>
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
@@ -219,7 +219,7 @@ function EventDetails({ event }: { event: HttpResponseEvent }) {
|
||||
return (
|
||||
<div className="flex flex-col gap-2">
|
||||
<DetailHeader title={`Data ${direction}`} timestamp={timestamp} />
|
||||
<div className="font-mono text-sm">{formatBytes(e.bytes)}</div>
|
||||
<div className="font-mono text-editor">{formatBytes(e.bytes)}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -229,7 +229,7 @@ function EventDetails({ event }: { event: HttpResponseEvent }) {
|
||||
return (
|
||||
<div className="flex flex-col gap-1">
|
||||
<DetailHeader title={label} timestamp={timestamp} />
|
||||
<div className="font-mono text-sm">{summary}</div>
|
||||
<div className="font-mono text-editor">{summary}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ interface Props {
|
||||
export function KeyValueRows({ children }: Props) {
|
||||
children = Array.isArray(children) ? children : [children];
|
||||
return (
|
||||
<table className="text-xs font-mono min-w-0 w-full mb-auto">
|
||||
<table className="text-editor font-mono min-w-0 w-full mb-auto">
|
||||
<tbody className="divide-y divide-surface-highlight">
|
||||
{children.map((child, i) => (
|
||||
// biome-ignore lint/suspicious/noArrayIndexKey: none
|
||||
|
||||
Reference in New Issue
Block a user