mirror of
https://github.com/zhom/donutbrowser.git
synced 2026-08-04 10:18:42 +02:00
648 lines
16 KiB
TypeScript
648 lines
16 KiB
TypeScript
import type { CookieBotSchedule } from "@/lib/cookie-bot";
|
|
import type { RemoteSessionState } from "@/lib/remote-sessions";
|
|
|
|
export interface ProxySettings {
|
|
proxy_type: string;
|
|
host: string;
|
|
port: number;
|
|
username?: string;
|
|
password?: string;
|
|
vless_uri?: string;
|
|
}
|
|
|
|
export interface TableSortingSettings {
|
|
column: string; // "name", "note", "status"
|
|
direction: string; // "asc" or "desc"
|
|
}
|
|
|
|
export interface BrowserProfile {
|
|
id: string; // UUID of the profile
|
|
name: string;
|
|
browser: string;
|
|
version: string;
|
|
proxy_id?: string; // Reference to stored proxy
|
|
vpn_id?: string; // Reference to stored VPN config
|
|
launch_hook?: string;
|
|
process_id?: number;
|
|
last_launch?: number;
|
|
release_type: string;
|
|
wayfern_config?: WayfernConfig; // Wayfern configuration
|
|
group_id?: string; // Reference to profile group
|
|
tags?: string[];
|
|
note?: string; // User note
|
|
window_color?: string; // Per-profile window frame color "#RRGGBB"; auto-derived from the id when unset
|
|
sync_mode?: SyncMode;
|
|
encryption_salt?: string;
|
|
last_sync?: number; // Timestamp of last successful sync (epoch seconds)
|
|
host_os?: string; // OS where profile was created ("macos", "windows", "linux")
|
|
ephemeral?: boolean;
|
|
clear_on_close?: boolean;
|
|
extension_group_id?: string;
|
|
proxy_bypass_rules?: string[];
|
|
created_by_id?: string;
|
|
created_by_email?: string;
|
|
/** Profile creation timestamp (epoch seconds, UTC). Undefined for legacy
|
|
* profiles created before this field existed. */
|
|
created_at?: number;
|
|
dns_blocklist?: string;
|
|
password_protected?: boolean;
|
|
}
|
|
|
|
export interface Extension {
|
|
id: string;
|
|
name: string;
|
|
file_name: string;
|
|
file_type: string;
|
|
browser_compatibility: string[];
|
|
created_at: number;
|
|
updated_at: number;
|
|
sync_enabled?: boolean;
|
|
last_sync?: number;
|
|
version?: string;
|
|
description?: string;
|
|
author?: string;
|
|
homepage_url?: string;
|
|
}
|
|
|
|
export interface ExtensionGroup {
|
|
id: string;
|
|
name: string;
|
|
extension_ids: string[];
|
|
created_at: number;
|
|
updated_at: number;
|
|
sync_enabled?: boolean;
|
|
last_sync?: number;
|
|
}
|
|
|
|
export type SyncMode = "Disabled" | "Regular" | "Encrypted";
|
|
|
|
export type SyncStatus = "Disabled" | "Syncing" | "Synced" | "Error";
|
|
|
|
export interface SyncSettings {
|
|
sync_server_url?: string;
|
|
sync_token?: string;
|
|
}
|
|
|
|
/**
|
|
* Capability/limit set derived from the plan by the backend. Features are gated
|
|
* on these flags instead of a single "is paid?" check, so a plan like the future
|
|
* "starter" tier (cross-OS fingerprints + cloud backup, no automation) is just
|
|
* data. Mirrors `apps/backend/src/plans/entitlements.ts`. Resolve via
|
|
* `getEntitlements()` — the desktop populates it, but it stays optional for
|
|
* safety on older state.
|
|
*/
|
|
export interface Entitlements {
|
|
active: boolean;
|
|
browserAutomation: boolean;
|
|
crossOsFingerprints: boolean;
|
|
cloudBackup: boolean;
|
|
teamCollaboration: boolean;
|
|
/** Overnight profile warming on a leased remote host. */
|
|
cookieBot: boolean;
|
|
profileLimit: number;
|
|
requestsPerHour: number;
|
|
/**
|
|
* Per-seat monthly allowance for remote sessions. Reporting only: the
|
|
* spendable figure is whatever `get_remote_hours_quota` returns, because a
|
|
* team pools this across its seats and only the server knows the seat count.
|
|
*/
|
|
remoteBrowserHours: number;
|
|
}
|
|
|
|
/**
|
|
* What a backend older than the cookie-bot release actually sends. Read it
|
|
* through `getEntitlements()`, which fills the gap — never off `CloudUser`
|
|
* directly, or a paying customer's Cookie Bot silently reads `false`.
|
|
*/
|
|
export type ServerEntitlements = Omit<
|
|
Entitlements,
|
|
"cookieBot" | "remoteBrowserHours"
|
|
> &
|
|
Partial<Pick<Entitlements, "cookieBot" | "remoteBrowserHours">>;
|
|
|
|
export interface CloudUser {
|
|
id: string;
|
|
email: string;
|
|
plan: string;
|
|
planPeriod: string | null;
|
|
subscriptionStatus: string;
|
|
profileLimit: number;
|
|
cloudProfilesUsed: number;
|
|
proxyBandwidthLimitMb: number;
|
|
proxyBandwidthUsedMb: number;
|
|
proxyBandwidthExtraMb: number;
|
|
teamId?: string;
|
|
teamName?: string;
|
|
teamRole?: string;
|
|
// This device's position among the user's active devices (oldest = 1).
|
|
// Ordinal 1 / isPrimaryDevice === true is the only device that can run
|
|
// browser automation. Optional: older backends omit them.
|
|
deviceOrdinal?: number | null;
|
|
deviceCount?: number | null;
|
|
isPrimaryDevice?: boolean | null;
|
|
// Plan-derived capabilities. The desktop resolves this before handing CloudUser
|
|
// to the UI; optional to stay safe on older cached state.
|
|
entitlements?: ServerEntitlements;
|
|
}
|
|
|
|
/**
|
|
* Cookie Bot and remote-session wire types. Defined next to the transport that
|
|
* owns them (`src/lib/cookie-bot.ts`, `src/lib/remote-sessions.ts`) and
|
|
* re-exported here so a component reads one module. Type-only, so nothing is
|
|
* pulled into the bundle.
|
|
*/
|
|
export type {
|
|
CookieBotConflict,
|
|
CookieBotPreset,
|
|
CookieBotPresetList,
|
|
CookieBotRun,
|
|
CookieBotRunPage,
|
|
CookieBotRunStarted,
|
|
CookieBotSchedule,
|
|
CookieBotScheduleInput,
|
|
CookieBotScheduleList,
|
|
CookieBotScheduleSaved,
|
|
CookieBotScope,
|
|
CookieBotUsage,
|
|
CookieBotUsageMember,
|
|
CookieBotUsageProfile,
|
|
RemoteHoursMember,
|
|
RemoteHoursQuota,
|
|
} from "@/lib/cookie-bot";
|
|
export type {
|
|
RemoteSessionPhase,
|
|
RemoteSessionSnapshot,
|
|
RemoteSessionState,
|
|
} from "@/lib/remote-sessions";
|
|
|
|
/** Where a profile stands with the bot, as one row of the profile table reads it. */
|
|
export interface ProfileBotState {
|
|
/** The stored enrolment, or null when the profile is not enrolled. */
|
|
schedule: CookieBotSchedule | null;
|
|
/** A remote session for this profile that has not closed yet. */
|
|
liveSession: RemoteSessionState | null;
|
|
}
|
|
|
|
export interface ProfileLockInfo {
|
|
profileId: string;
|
|
lockedBy: string;
|
|
lockedByEmail: string;
|
|
lockedAt: string;
|
|
expiresAt?: string;
|
|
}
|
|
|
|
export interface CloudAuthState {
|
|
user: CloudUser;
|
|
logged_in_at: string;
|
|
}
|
|
|
|
export interface ProfileSyncStatusEvent {
|
|
profile_id: string;
|
|
status: "disabled" | "syncing" | "synced" | "error" | "pending";
|
|
}
|
|
|
|
export interface ProxyCheckResult {
|
|
ip: string;
|
|
city?: string;
|
|
country?: string;
|
|
country_code?: string;
|
|
timestamp: number;
|
|
is_valid: boolean;
|
|
}
|
|
|
|
export function isSyncEnabled(profile: BrowserProfile): boolean {
|
|
return profile.sync_mode != null && profile.sync_mode !== "Disabled";
|
|
}
|
|
|
|
export const CLOUD_PROXY_ID = "cloud-included-proxy";
|
|
|
|
export interface StoredProxy {
|
|
id: string;
|
|
name: string;
|
|
proxy_settings: ProxySettings;
|
|
sync_enabled?: boolean;
|
|
last_sync?: number;
|
|
is_cloud_managed?: boolean;
|
|
is_cloud_derived?: boolean;
|
|
geo_country?: string;
|
|
geo_state?: string;
|
|
geo_region?: string;
|
|
geo_city?: string;
|
|
geo_isp?: string;
|
|
}
|
|
|
|
export interface LocationItem {
|
|
code: string;
|
|
name: string;
|
|
}
|
|
|
|
export interface ProfileGroup {
|
|
id: string;
|
|
name: string;
|
|
sync_enabled?: boolean;
|
|
last_sync?: number;
|
|
}
|
|
|
|
export interface GroupWithCount {
|
|
id: string;
|
|
name: string;
|
|
count: number;
|
|
sync_enabled?: boolean;
|
|
last_sync?: number;
|
|
}
|
|
|
|
export interface DetectedProfile {
|
|
browser: string;
|
|
name: string;
|
|
path: string;
|
|
description: string;
|
|
mapped_browser: string;
|
|
}
|
|
|
|
export interface ImportProfileItem {
|
|
source_path: string;
|
|
browser_type?: string;
|
|
new_profile_name: string;
|
|
/** Mutually exclusive with `vpn_id`; the importer rejects setting both. */
|
|
proxy_id?: string | null;
|
|
vpn_id?: string | null;
|
|
}
|
|
|
|
export interface ProfileImportItemResult {
|
|
name: string;
|
|
source_path: string;
|
|
status: "imported" | "skipped" | "failed";
|
|
profile_id: string | null;
|
|
error: string | null;
|
|
}
|
|
|
|
export interface ProfileImportBatchResult {
|
|
imported_count: number;
|
|
skipped_count: number;
|
|
failed_count: number;
|
|
results: ProfileImportItemResult[];
|
|
}
|
|
|
|
export interface ArchiveScanResult {
|
|
extracted_dir: string;
|
|
profiles: DetectedProfile[];
|
|
}
|
|
|
|
export interface ProfileImportProgress {
|
|
total: number;
|
|
completed: number;
|
|
index: number;
|
|
name: string;
|
|
status: "importing" | "imported" | "skipped" | "failed";
|
|
}
|
|
|
|
export interface BrowserReleaseTypes {
|
|
stable?: string;
|
|
}
|
|
|
|
export interface AppUpdateInfo {
|
|
current_version: string;
|
|
new_version: string;
|
|
release_notes: string;
|
|
download_url: string;
|
|
is_nightly: boolean;
|
|
published_at: string;
|
|
manual_update_required: boolean;
|
|
release_page_url?: string;
|
|
repo_update: boolean;
|
|
/** URL of the release's SHA256SUMS.txt; downloads are verified against it. */
|
|
checksums_url?: string | null;
|
|
/** GitHub-computed digest of the chosen asset ("sha256:<hex>"). */
|
|
asset_digest?: string | null;
|
|
}
|
|
|
|
export interface AppUpdateProgress {
|
|
stage: string; // "downloading", "extracting", "installing", "completed"
|
|
percentage?: number;
|
|
speed?: string; // MB/s
|
|
eta?: string; // estimated time remaining
|
|
message: string;
|
|
}
|
|
|
|
export type WayfernOS = "windows" | "macos" | "linux" | "android" | "ios";
|
|
|
|
export interface WayfernConfig {
|
|
proxy?: string;
|
|
screen_max_width?: number;
|
|
screen_max_height?: number;
|
|
screen_min_width?: number;
|
|
screen_min_height?: number;
|
|
geoip?: string | boolean; // For compatibility with shared config form
|
|
block_images?: boolean; // For compatibility with shared config form
|
|
block_webrtc?: boolean;
|
|
block_webgl?: boolean;
|
|
executable_path?: string;
|
|
fingerprint?: string; // JSON string of the complete fingerprint config
|
|
randomize_fingerprint_on_launch?: boolean; // Generate new fingerprint on every launch
|
|
os?: WayfernOS; // Operating system for fingerprint generation
|
|
geo_proxy_signature?: string; // Internal: routing the fingerprint's location was computed for
|
|
}
|
|
|
|
// Wayfern fingerprint config - matches the C++ FingerprintData structure
|
|
export interface WayfernFingerprintConfig {
|
|
// User agent and platform
|
|
userAgent?: string;
|
|
platform?: string;
|
|
platformVersion?: string;
|
|
brand?: string;
|
|
brandVersion?: string;
|
|
|
|
// Hardware
|
|
hardwareConcurrency?: number;
|
|
maxTouchPoints?: number;
|
|
deviceMemory?: number;
|
|
|
|
// Screen
|
|
screenWidth?: number;
|
|
screenHeight?: number;
|
|
screenAvailWidth?: number;
|
|
screenAvailHeight?: number;
|
|
screenColorDepth?: number;
|
|
screenPixelDepth?: number;
|
|
devicePixelRatio?: number;
|
|
|
|
// Window
|
|
windowOuterWidth?: number;
|
|
windowOuterHeight?: number;
|
|
windowInnerWidth?: number;
|
|
windowInnerHeight?: number;
|
|
screenX?: number;
|
|
screenY?: number;
|
|
|
|
// Language
|
|
language?: string;
|
|
languages?: string[];
|
|
|
|
// Browser features
|
|
doNotTrack?: string;
|
|
cookieEnabled?: boolean;
|
|
webdriver?: boolean;
|
|
pdfViewerEnabled?: boolean;
|
|
|
|
// WebGL
|
|
webglVendor?: string;
|
|
webglRenderer?: string;
|
|
webglVersion?: string;
|
|
webglShadingLanguageVersion?: string;
|
|
webglParameters?: string; // JSON string
|
|
webgl2Parameters?: string; // JSON string
|
|
webglShaderPrecisionFormats?: string; // JSON string
|
|
webgl2ShaderPrecisionFormats?: string; // JSON string
|
|
|
|
// Timezone and geolocation
|
|
timezone?: string;
|
|
timezoneOffset?: number;
|
|
latitude?: number;
|
|
longitude?: number;
|
|
accuracy?: number;
|
|
|
|
// Media queries / preferences
|
|
prefersReducedMotion?: boolean;
|
|
prefersDarkMode?: boolean;
|
|
prefersContrast?: string;
|
|
prefersReducedData?: boolean;
|
|
|
|
// Color/HDR
|
|
colorGamutSrgb?: boolean;
|
|
colorGamutP3?: boolean;
|
|
colorGamutRec2020?: boolean;
|
|
hdrSupport?: boolean;
|
|
|
|
// Audio
|
|
audioSampleRate?: number;
|
|
audioMaxChannelCount?: number;
|
|
|
|
// Storage
|
|
localStorage?: boolean;
|
|
sessionStorage?: boolean;
|
|
indexedDb?: boolean;
|
|
|
|
// Canvas
|
|
canvasNoiseSeed?: string;
|
|
|
|
// Fonts, plugins, mime types (JSON strings)
|
|
fonts?: string; // JSON array string
|
|
plugins?: string; // JSON array string
|
|
mimeTypes?: string; // JSON array string
|
|
|
|
// Battery (optional)
|
|
batteryCharging?: boolean;
|
|
batteryChargingTime?: number;
|
|
batteryDischargingTime?: number;
|
|
batteryLevel?: number;
|
|
|
|
// Voices
|
|
voices?: string; // JSON array string
|
|
|
|
// Vendor info
|
|
vendor?: string;
|
|
vendorSub?: string;
|
|
productSub?: string;
|
|
|
|
// Network (optional)
|
|
connectionEffectiveType?: string;
|
|
connectionDownlink?: number;
|
|
connectionRtt?: number;
|
|
|
|
// Performance
|
|
performanceMemory?: number;
|
|
}
|
|
|
|
export interface WayfernLaunchResult {
|
|
id: string;
|
|
processId?: number;
|
|
profilePath?: string;
|
|
url?: string;
|
|
cdp_port?: number;
|
|
}
|
|
|
|
// Synchronizer types
|
|
export interface SyncFollowerState {
|
|
profile_id: string;
|
|
profile_name: string;
|
|
failed_at_url: string | null;
|
|
}
|
|
|
|
export interface SyncSessionInfo {
|
|
id: string;
|
|
leader_profile_id: string;
|
|
leader_profile_name: string;
|
|
followers: SyncFollowerState[];
|
|
}
|
|
|
|
// Traffic stats types
|
|
export interface BandwidthDataPoint {
|
|
timestamp: number;
|
|
bytes_sent: number;
|
|
bytes_received: number;
|
|
}
|
|
|
|
export interface DomainAccess {
|
|
domain: string;
|
|
request_count: number;
|
|
bytes_sent: number;
|
|
bytes_received: number;
|
|
first_access: number;
|
|
last_access: number;
|
|
}
|
|
|
|
export interface TrafficStats {
|
|
proxy_id: string;
|
|
profile_id?: string;
|
|
session_start: number;
|
|
last_update: number;
|
|
total_bytes_sent: number;
|
|
total_bytes_received: number;
|
|
total_requests: number;
|
|
bandwidth_history: BandwidthDataPoint[];
|
|
domains: Record<string, DomainAccess>;
|
|
unique_ips: string[];
|
|
}
|
|
|
|
export interface TrafficSnapshot {
|
|
profile_id?: string;
|
|
session_start: number;
|
|
last_update: number;
|
|
total_bytes_sent: number;
|
|
total_bytes_received: number;
|
|
total_requests: number;
|
|
current_bytes_sent: number;
|
|
current_bytes_received: number;
|
|
recent_bandwidth: BandwidthDataPoint[];
|
|
}
|
|
|
|
export interface FilteredTrafficStats {
|
|
profile_id?: string;
|
|
session_start: number;
|
|
last_update: number;
|
|
total_bytes_sent: number;
|
|
total_bytes_received: number;
|
|
total_requests: number;
|
|
bandwidth_history: BandwidthDataPoint[];
|
|
period_bytes_sent: number;
|
|
period_bytes_received: number;
|
|
period_requests: number;
|
|
domains: Record<string, DomainAccess>;
|
|
unique_ips: string[];
|
|
}
|
|
|
|
// Cookie copy types
|
|
export interface UnifiedCookie {
|
|
name: string;
|
|
value: string;
|
|
domain: string;
|
|
path: string;
|
|
expires: number;
|
|
is_secure: boolean;
|
|
is_http_only: boolean;
|
|
same_site: number;
|
|
creation_time: number;
|
|
last_accessed: number;
|
|
}
|
|
|
|
export interface DomainCookies {
|
|
domain: string;
|
|
cookies: UnifiedCookie[];
|
|
cookie_count: number;
|
|
}
|
|
|
|
export interface CookieReadResult {
|
|
profile_id: string;
|
|
browser_type: string;
|
|
domains: DomainCookies[];
|
|
total_count: number;
|
|
}
|
|
|
|
export interface SelectedCookie {
|
|
domain: string;
|
|
name: string;
|
|
}
|
|
|
|
export interface CookieCopyRequest {
|
|
source_profile_id: string;
|
|
target_profile_ids: string[];
|
|
selected_cookies: SelectedCookie[];
|
|
}
|
|
|
|
export interface CookieCopyResult {
|
|
target_profile_id: string;
|
|
cookies_copied: number;
|
|
cookies_replaced: number;
|
|
errors: string[];
|
|
}
|
|
|
|
// Proxy import/export types
|
|
export interface ProxyExportData {
|
|
version: string;
|
|
proxies: ExportedProxy[];
|
|
exported_at: string;
|
|
source: string;
|
|
}
|
|
|
|
export interface ExportedProxy {
|
|
name: string;
|
|
type: string;
|
|
host: string;
|
|
port: number;
|
|
username?: string;
|
|
password?: string;
|
|
}
|
|
|
|
export interface ProxyImportResult {
|
|
imported_count: number;
|
|
skipped_count: number;
|
|
errors: string[];
|
|
proxies: StoredProxy[];
|
|
}
|
|
|
|
export interface ParsedProxyLine {
|
|
proxy_type: string;
|
|
host: string;
|
|
port: number;
|
|
username?: string;
|
|
password?: string;
|
|
vless_uri?: string;
|
|
original_line: string;
|
|
}
|
|
|
|
export type ProxyParseResult =
|
|
| ({ status: "parsed" } & ParsedProxyLine)
|
|
| { status: "ambiguous"; line: string; possible_formats: string[] }
|
|
| { status: "invalid"; line: string; reason: string };
|
|
|
|
// VPN types
|
|
export type VpnType = "WireGuard";
|
|
|
|
export interface VpnConfig {
|
|
id: string;
|
|
name: string;
|
|
vpn_type: VpnType;
|
|
config_data: string; // Raw config content (may be empty in list view)
|
|
created_at: number;
|
|
last_used?: number;
|
|
sync_enabled?: boolean;
|
|
last_sync?: number;
|
|
}
|
|
|
|
export interface VpnImportResult {
|
|
success: boolean;
|
|
vpn_id?: string;
|
|
vpn_type?: VpnType;
|
|
name: string;
|
|
error?: string;
|
|
}
|
|
|
|
export interface VpnStatus {
|
|
connected: boolean;
|
|
vpn_id: string;
|
|
connected_at?: number;
|
|
bytes_sent?: number;
|
|
bytes_received?: number;
|
|
last_handshake?: number;
|
|
}
|