mirror of
https://github.com/zhom/donutbrowser.git
synced 2026-08-01 08:57:27 +02:00
Compare commits
6
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1b4fc96785 | ||
|
|
0240b18377 | ||
|
|
7b756be072 | ||
|
|
bcca0b9a3a | ||
|
|
f4d9ca02be | ||
|
|
57d91d9ab2 |
@@ -116,13 +116,14 @@ jobs:
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
RESPONSE_FILE: ${{ steps.validate.outputs.response-file }}
|
||||
RESPONSE: ${{ steps.validate.outputs.response }}
|
||||
run: |
|
||||
# Prefer reading from the response file to avoid output truncation
|
||||
# Read from the response file (RESPONSE env var gets truncated for large outputs)
|
||||
if [ -n "$RESPONSE_FILE" ] && [ -f "$RESPONSE_FILE" ]; then
|
||||
echo "Reading response from file: $RESPONSE_FILE"
|
||||
RAW_OUTPUT=$(cat "$RESPONSE_FILE")
|
||||
else
|
||||
RAW_OUTPUT="$RESPONSE"
|
||||
echo "::error::Response file not found: $RESPONSE_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Extract JSON if wrapped in markdown code fences; otherwise use raw
|
||||
@@ -131,6 +132,14 @@ jobs:
|
||||
JSON_RESULT="$RAW_OUTPUT"
|
||||
fi
|
||||
|
||||
# Validate JSON before parsing
|
||||
if ! echo "$JSON_RESULT" | jq empty 2>/dev/null; then
|
||||
echo "::error::Invalid JSON in AI response"
|
||||
echo "Raw output:"
|
||||
echo "$RAW_OUTPUT"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Parse JSON fields
|
||||
IS_VALID=$(echo "$JSON_RESULT" | jq -r '.is_valid // false')
|
||||
ISSUE_TYPE=$(echo "$JSON_RESULT" | jq -r '.issue_type // "other"')
|
||||
|
||||
@@ -92,6 +92,13 @@ Have questions or want to contribute? We'd love to hear from you!
|
||||
<br />
|
||||
<sub><b>zhom</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/JorySeverijnse">
|
||||
<img src="https://avatars.githubusercontent.com/u/117462355?v=4" width="100;" alt="JorySeverijnse"/>
|
||||
<br />
|
||||
<sub><b>Jory Severijnse</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tbody>
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
"name": "donutbrowser",
|
||||
"private": true,
|
||||
"license": "AGPL-3.0",
|
||||
"version": "0.13.8",
|
||||
"version": "0.13.9",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "next dev --turbopack",
|
||||
|
||||
Generated
+1
-1
@@ -1284,7 +1284,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "donutbrowser"
|
||||
version = "0.13.8"
|
||||
version = "0.13.9"
|
||||
dependencies = [
|
||||
"aes-gcm",
|
||||
"argon2",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "donutbrowser"
|
||||
version = "0.13.8"
|
||||
version = "0.13.9"
|
||||
description = "Simple Yet Powerful Anti-Detect Browser"
|
||||
authors = ["zhom@github"]
|
||||
edition = "2021"
|
||||
|
||||
@@ -818,7 +818,7 @@ impl ApiClient {
|
||||
Ok(filtered_releases)
|
||||
}
|
||||
|
||||
/// Check if a Brave release has compatible assets for the given platform and architecture
|
||||
/// Check if a Camoufox release has compatible assets for the given platform and architecture
|
||||
fn has_compatible_camoufox_asset(
|
||||
&self,
|
||||
assets: &[crate::browser::GithubAsset],
|
||||
@@ -835,9 +835,9 @@ impl ApiClient {
|
||||
_ => return false,
|
||||
};
|
||||
|
||||
// Look for assets matching the pattern: camoufox-{version}-{release}-{os}.{arch}.zip
|
||||
// Use ends_with for precise matching to avoid false positives
|
||||
let pattern = format!(".{os_name}.{arch_name}.zip");
|
||||
// Look for assets matching the pattern: camoufox-{version}-beta.{number}-{os}.{arch}.zip
|
||||
// The separator before OS is a dash, e.g., camoufox-135.0.1-beta.24-lin.x86_64.zip
|
||||
let pattern = format!("-{os_name}.{arch_name}.zip");
|
||||
assets.iter().any(|asset| {
|
||||
let name = asset.name.to_lowercase();
|
||||
name.starts_with("camoufox-") && name.ends_with(&pattern)
|
||||
|
||||
@@ -310,7 +310,8 @@ impl Downloader {
|
||||
os: &str,
|
||||
arch: &str,
|
||||
) -> Option<String> {
|
||||
// Camoufox asset naming pattern: camoufox-{version}-{release}-{os}.{arch}.zip
|
||||
// Camoufox asset naming pattern: camoufox-{version}-beta.{number}-{os}.{arch}.zip
|
||||
// Example: camoufox-135.0.1-beta.24-lin.x86_64.zip
|
||||
let (os_name, arch_name) = match (os, arch) {
|
||||
("windows", "x64") => ("win", "x86_64"),
|
||||
("windows", "arm64") => ("win", "arm64"),
|
||||
@@ -322,7 +323,8 @@ impl Downloader {
|
||||
};
|
||||
|
||||
// Use ends_with for precise matching to avoid false positives
|
||||
let pattern = format!(".{os_name}.{arch_name}.zip");
|
||||
// The separator before OS is a dash: -lin.x86_64.zip, -mac.arm64.zip, etc.
|
||||
let pattern = format!("-{os_name}.{arch_name}.zip");
|
||||
let asset = assets.iter().find(|asset| {
|
||||
let name = asset.name.to_lowercase();
|
||||
name.starts_with("camoufox-") && name.ends_with(&pattern)
|
||||
|
||||
@@ -306,7 +306,8 @@ fn acquire_file_lock(lock_path: &PathBuf) -> Result<FileLockGuard, Box<dyn std::
|
||||
use windows::Win32::Storage::FileSystem::LOCKFILE_FAIL_IMMEDIATELY;
|
||||
use windows::Win32::System::IO::OVERLAPPED;
|
||||
|
||||
let handle = HANDLE(file.as_raw_handle() as *mut core::ffi::c_void);
|
||||
let raw_handle = file.as_raw_handle();
|
||||
let handle = HANDLE(raw_handle);
|
||||
unsafe {
|
||||
let mut overlapped: OVERLAPPED = std::mem::zeroed();
|
||||
if LockFileEx(
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://schema.tauri.app/config/2",
|
||||
"productName": "Donut",
|
||||
"version": "0.13.8",
|
||||
"version": "0.13.9",
|
||||
"identifier": "com.donutbrowser",
|
||||
"build": {
|
||||
"beforeDevCommand": "pnpm copy-proxy-binary && pnpm dev",
|
||||
|
||||
@@ -142,6 +142,10 @@ export function CreateProfileDialog({
|
||||
const [showProxyForm, setShowProxyForm] = useState(false);
|
||||
const [isCreating, setIsCreating] = useState(false);
|
||||
const [releaseTypes, setReleaseTypes] = useState<BrowserReleaseTypes>();
|
||||
const [isLoadingReleaseTypes, setIsLoadingReleaseTypes] = useState(false);
|
||||
const [releaseTypesError, setReleaseTypesError] = useState<string | null>(
|
||||
null,
|
||||
);
|
||||
const loadingBrowserRef = useRef<string | null>(null);
|
||||
|
||||
// Use the browser download hook
|
||||
@@ -179,6 +183,8 @@ export function CreateProfileDialog({
|
||||
async (browser: string) => {
|
||||
// Set loading state
|
||||
loadingBrowserRef.current = browser;
|
||||
setIsLoadingReleaseTypes(true);
|
||||
setReleaseTypesError(null);
|
||||
|
||||
try {
|
||||
const rawReleaseTypes = await invoke<BrowserReleaseTypes>(
|
||||
@@ -207,6 +213,7 @@ export function CreateProfileDialog({
|
||||
filtered.stable = rawReleaseTypes.stable;
|
||||
setReleaseTypes(filtered);
|
||||
}
|
||||
setReleaseTypesError(null);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Failed to load release types for ${browser}:`, error);
|
||||
@@ -223,17 +230,29 @@ export function CreateProfileDialog({
|
||||
fallback.stable = latest;
|
||||
}
|
||||
setReleaseTypes(fallback);
|
||||
setReleaseTypesError(null);
|
||||
} else if (loadingBrowserRef.current === browser) {
|
||||
// No downloaded versions and API failed - show error
|
||||
setReleaseTypesError(
|
||||
"Failed to fetch browser versions. Please check your internet connection and try again.",
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(
|
||||
`Failed to load downloaded versions for ${browser}:`,
|
||||
e,
|
||||
);
|
||||
if (loadingBrowserRef.current === browser) {
|
||||
setReleaseTypesError(
|
||||
"Failed to fetch browser versions. Please check your internet connection and try again.",
|
||||
);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
// Clear loading state only if we're still loading this browser
|
||||
if (loadingBrowserRef.current === browser) {
|
||||
loadingBrowserRef.current = null;
|
||||
setIsLoadingReleaseTypes(false);
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -377,6 +396,8 @@ export function CreateProfileDialog({
|
||||
setSelectedBrowser(null);
|
||||
setSelectedProxyId(undefined);
|
||||
setReleaseTypes({});
|
||||
setIsLoadingReleaseTypes(false);
|
||||
setReleaseTypesError(null);
|
||||
setCamoufoxConfig({
|
||||
geoip: true, // Reset to automatic geoip
|
||||
os: getCurrentOS(), // Reset to current OS
|
||||
@@ -553,7 +574,34 @@ export function CreateProfileDialog({
|
||||
// Camoufox Configuration
|
||||
<div className="space-y-6">
|
||||
{/* Camoufox Download Status */}
|
||||
{!isBrowserCurrentlyDownloading("camoufox") &&
|
||||
{isLoadingReleaseTypes && (
|
||||
<div className="flex gap-3 items-center p-3 rounded-md border">
|
||||
<div className="w-4 h-4 rounded-full border-2 animate-spin border-muted/40 border-t-primary" />
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Fetching available versions...
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
{!isLoadingReleaseTypes && releaseTypesError && (
|
||||
<div className="flex gap-3 items-center p-3 rounded-md border border-destructive/50 bg-destructive/10">
|
||||
<p className="flex-1 text-sm text-destructive">
|
||||
{releaseTypesError}
|
||||
</p>
|
||||
<RippleButton
|
||||
onClick={() =>
|
||||
selectedBrowser &&
|
||||
loadReleaseTypes(selectedBrowser)
|
||||
}
|
||||
size="sm"
|
||||
variant="outline"
|
||||
>
|
||||
Retry
|
||||
</RippleButton>
|
||||
</div>
|
||||
)}
|
||||
{!isLoadingReleaseTypes &&
|
||||
!releaseTypesError &&
|
||||
!isBrowserCurrentlyDownloading("camoufox") &&
|
||||
!isBrowserVersionAvailable("camoufox") &&
|
||||
getBestAvailableVersion("camoufox") && (
|
||||
<div className="flex gap-3 items-center p-3 rounded-md border">
|
||||
@@ -580,7 +628,9 @@ export function CreateProfileDialog({
|
||||
</LoadingButton>
|
||||
</div>
|
||||
)}
|
||||
{!isBrowserCurrentlyDownloading("camoufox") &&
|
||||
{!isLoadingReleaseTypes &&
|
||||
!releaseTypesError &&
|
||||
!isBrowserCurrentlyDownloading("camoufox") &&
|
||||
isBrowserVersionAvailable("camoufox") && (
|
||||
<div className="p-3 text-sm rounded-md border text-muted-foreground">
|
||||
{(() => {
|
||||
@@ -607,13 +657,41 @@ export function CreateProfileDialog({
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
// Regular Browser Configuration
|
||||
// Regular Browser Configuration (should not happen in anti-detect tab)
|
||||
<div className="space-y-4">
|
||||
{selectedBrowser && (
|
||||
<div className="space-y-3">
|
||||
{!isBrowserCurrentlyDownloading(
|
||||
selectedBrowser,
|
||||
) &&
|
||||
{isLoadingReleaseTypes && (
|
||||
<div className="flex gap-3 items-center">
|
||||
<div className="w-4 h-4 rounded-full border-2 animate-spin border-muted/40 border-t-primary" />
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Fetching available versions...
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
{!isLoadingReleaseTypes &&
|
||||
releaseTypesError && (
|
||||
<div className="flex gap-3 items-center p-3 rounded-md border border-destructive/50 bg-destructive/10">
|
||||
<p className="flex-1 text-sm text-destructive">
|
||||
{releaseTypesError}
|
||||
</p>
|
||||
<RippleButton
|
||||
onClick={() =>
|
||||
selectedBrowser &&
|
||||
loadReleaseTypes(selectedBrowser)
|
||||
}
|
||||
size="sm"
|
||||
variant="outline"
|
||||
>
|
||||
Retry
|
||||
</RippleButton>
|
||||
</div>
|
||||
)}
|
||||
{!isLoadingReleaseTypes &&
|
||||
!releaseTypesError &&
|
||||
!isBrowserCurrentlyDownloading(
|
||||
selectedBrowser,
|
||||
) &&
|
||||
!isBrowserVersionAvailable(selectedBrowser) &&
|
||||
getBestAvailableVersion(selectedBrowser) && (
|
||||
<div className="flex gap-3 items-center">
|
||||
@@ -643,9 +721,11 @@ export function CreateProfileDialog({
|
||||
</LoadingButton>
|
||||
</div>
|
||||
)}
|
||||
{!isBrowserCurrentlyDownloading(
|
||||
selectedBrowser,
|
||||
) &&
|
||||
{!isLoadingReleaseTypes &&
|
||||
!releaseTypesError &&
|
||||
!isBrowserCurrentlyDownloading(
|
||||
selectedBrowser,
|
||||
) &&
|
||||
isBrowserVersionAvailable(
|
||||
selectedBrowser,
|
||||
) && (
|
||||
@@ -739,9 +819,36 @@ export function CreateProfileDialog({
|
||||
<div className="space-y-4">
|
||||
{selectedBrowser && (
|
||||
<div className="space-y-3">
|
||||
{!isBrowserCurrentlyDownloading(
|
||||
selectedBrowser,
|
||||
) &&
|
||||
{isLoadingReleaseTypes && (
|
||||
<div className="flex gap-3 items-center">
|
||||
<div className="w-4 h-4 rounded-full border-2 animate-spin border-muted/40 border-t-primary" />
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Fetching available versions...
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
{!isLoadingReleaseTypes && releaseTypesError && (
|
||||
<div className="flex gap-3 items-center p-3 rounded-md border border-destructive/50 bg-destructive/10">
|
||||
<p className="flex-1 text-sm text-destructive">
|
||||
{releaseTypesError}
|
||||
</p>
|
||||
<RippleButton
|
||||
onClick={() =>
|
||||
selectedBrowser &&
|
||||
loadReleaseTypes(selectedBrowser)
|
||||
}
|
||||
size="sm"
|
||||
variant="outline"
|
||||
>
|
||||
Retry
|
||||
</RippleButton>
|
||||
</div>
|
||||
)}
|
||||
{!isLoadingReleaseTypes &&
|
||||
!releaseTypesError &&
|
||||
!isBrowserCurrentlyDownloading(
|
||||
selectedBrowser,
|
||||
) &&
|
||||
!isBrowserVersionAvailable(selectedBrowser) &&
|
||||
getBestAvailableVersion(selectedBrowser) && (
|
||||
<div className="flex gap-3 items-center">
|
||||
@@ -771,9 +878,11 @@ export function CreateProfileDialog({
|
||||
</LoadingButton>
|
||||
</div>
|
||||
)}
|
||||
{!isBrowserCurrentlyDownloading(
|
||||
selectedBrowser,
|
||||
) &&
|
||||
{!isLoadingReleaseTypes &&
|
||||
!releaseTypesError &&
|
||||
!isBrowserCurrentlyDownloading(
|
||||
selectedBrowser,
|
||||
) &&
|
||||
isBrowserVersionAvailable(selectedBrowser) && (
|
||||
<div className="text-sm text-muted-foreground">
|
||||
{(() => {
|
||||
|
||||
Reference in New Issue
Block a user