mirror of
https://github.com/zhom/donutbrowser.git
synced 2026-08-12 06:00:23 +02:00
feat: prevent launch with inconsistent geodata
This commit is contained in:
@@ -73,6 +73,7 @@ export type BackendErrorCode =
|
||||
| "REMOTE_RATE_LIMITED"
|
||||
| "REMOTE_NO_CAPACITY"
|
||||
| "REMOTE_NOT_ENTITLED"
|
||||
| "REMOTE_INTERACTIVE_NOT_ENTITLED"
|
||||
| "REMOTE_SESSION_REFUSED"
|
||||
| "REMOTE_SESSION_NOT_FOUND"
|
||||
| "REMOTE_SESSION_CONFLICT"
|
||||
@@ -105,6 +106,10 @@ export type BackendErrorCode =
|
||||
// rendered as the raw machine identifier.
|
||||
| "COOKIE_BOT_REQUIRES_PROXY"
|
||||
| "COOKIE_BOT_TOUCH_FINGERPRINT_UNSUPPORTED"
|
||||
| "FINGERPRINT_EXIT_MISMATCH"
|
||||
| "LAUNCH_CONSENT_EXPIRED"
|
||||
| "VPN_WORKER_START_FAILED"
|
||||
| "EXIT_PROBE_FAILED"
|
||||
| "INTERNAL_ERROR";
|
||||
|
||||
export interface BackendError {
|
||||
@@ -306,6 +311,12 @@ export function translateBackendError(t: TFunction, err: unknown): string {
|
||||
return t("backendErrors.remoteNoCapacity");
|
||||
case "REMOTE_NOT_ENTITLED":
|
||||
return t("backendErrors.remoteNotEntitled");
|
||||
// Distinct from the above: the plan HAS remote hours, it just may not spend
|
||||
// them by hand (solo funds a nightly Cookie Bot only). Telling such a user
|
||||
// "your plan does not include remote execution" while their bot visibly
|
||||
// runs every night is the confusing case this code exists to avoid.
|
||||
case "REMOTE_INTERACTIVE_NOT_ENTITLED":
|
||||
return t("backendErrors.remoteInteractiveNotEntitled");
|
||||
case "REMOTE_SESSION_REFUSED":
|
||||
return t("backendErrors.remoteSessionRefused");
|
||||
case "REMOTE_SESSION_NOT_FOUND":
|
||||
@@ -380,6 +391,19 @@ export function translateBackendError(t: TFunction, err: unknown): string {
|
||||
return t("backendErrors.cookieBotRequiresExitNode");
|
||||
case "COOKIE_BOT_TOUCH_FINGERPRINT_UNSUPPORTED":
|
||||
return t("backendErrors.cookieBotTouchFingerprintUnsupported");
|
||||
// The launch gate's block. The dialog renders the mismatch detail from
|
||||
// `params` itself; this string is the fallback for anywhere that only has
|
||||
// room for one sentence.
|
||||
case "FINGERPRINT_EXIT_MISMATCH":
|
||||
return t("backendErrors.fingerprintExitMismatch");
|
||||
case "LAUNCH_CONSENT_EXPIRED":
|
||||
return t("backendErrors.launchConsentExpired");
|
||||
case "VPN_WORKER_START_FAILED":
|
||||
return t("backendErrors.vpnWorkerStartFailed", {
|
||||
detail: parsed.params?.detail ?? "",
|
||||
});
|
||||
case "EXIT_PROBE_FAILED":
|
||||
return t("backendErrors.exitProbeFailed");
|
||||
case "INTERNAL_ERROR":
|
||||
return t("backendErrors.internal", {
|
||||
detail: parsed.params?.detail ?? "",
|
||||
|
||||
+28
-10
@@ -8,6 +8,7 @@ interface Capabilities {
|
||||
cloudBackup: boolean;
|
||||
teamCollaboration: boolean;
|
||||
cookieBot: boolean;
|
||||
remoteInteractive: boolean;
|
||||
}
|
||||
|
||||
const NONE: Entitlements = {
|
||||
@@ -17,6 +18,7 @@ const NONE: Entitlements = {
|
||||
cloudBackup: false,
|
||||
teamCollaboration: false,
|
||||
cookieBot: false,
|
||||
remoteInteractive: false,
|
||||
profileLimit: 0,
|
||||
requestsPerHour: 0,
|
||||
remoteBrowserHours: 0,
|
||||
@@ -25,12 +27,16 @@ const NONE: Entitlements = {
|
||||
// Mirror of PLAN_CAPABILITIES in apps/backend/src/plans/entitlements.ts. Keep in
|
||||
// sync — a new plan must be declared here too, or it falls back to DEFAULT_PAID.
|
||||
const PLAN_CAPABILITIES: Record<string, Capabilities> = {
|
||||
starter: {
|
||||
// The one row where cookieBot, browserAutomation and remoteInteractive all
|
||||
// disagree: solo pays for a nightly bot and nothing else that drives a
|
||||
// browser. No fingerprint editing either.
|
||||
solo: {
|
||||
browserAutomation: false,
|
||||
crossOsFingerprints: true,
|
||||
crossOsFingerprints: false,
|
||||
cloudBackup: true,
|
||||
teamCollaboration: false,
|
||||
cookieBot: false,
|
||||
cookieBot: true,
|
||||
remoteInteractive: false,
|
||||
},
|
||||
pro: {
|
||||
browserAutomation: true,
|
||||
@@ -38,6 +44,7 @@ const PLAN_CAPABILITIES: Record<string, Capabilities> = {
|
||||
cloudBackup: true,
|
||||
teamCollaboration: false,
|
||||
cookieBot: true,
|
||||
remoteInteractive: true,
|
||||
},
|
||||
team: {
|
||||
browserAutomation: true,
|
||||
@@ -45,6 +52,7 @@ const PLAN_CAPABILITIES: Record<string, Capabilities> = {
|
||||
cloudBackup: true,
|
||||
teamCollaboration: true,
|
||||
cookieBot: true,
|
||||
remoteInteractive: true,
|
||||
},
|
||||
enterprise: {
|
||||
browserAutomation: true,
|
||||
@@ -52,6 +60,7 @@ const PLAN_CAPABILITIES: Record<string, Capabilities> = {
|
||||
cloudBackup: true,
|
||||
teamCollaboration: true,
|
||||
cookieBot: true,
|
||||
remoteInteractive: true,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -62,6 +71,7 @@ const DEFAULT_PAID: Capabilities = {
|
||||
cloudBackup: true,
|
||||
teamCollaboration: false,
|
||||
cookieBot: true,
|
||||
remoteInteractive: true,
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -75,16 +85,23 @@ export function getEntitlements(
|
||||
): Entitlements {
|
||||
if (user?.entitlements) {
|
||||
const server = user.entitlements;
|
||||
// A backend (or a cached login) older than the cookie-bot release omits
|
||||
// these two keys. Reading them as `undefined` would hide a paid feature
|
||||
// from a paying customer with nothing logged anywhere, so resolve them
|
||||
// here — the one place every caller already goes through. Cookie Bot is
|
||||
// remote automation on leased hardware, so it tracks `browserAutomation`
|
||||
// exactly; `remoteBrowserHours` stays 0 because the spendable figure is
|
||||
// whatever `get_remote_hours_quota` reports, never a client guess.
|
||||
// A backend (or a cached login) older than the current release omits these
|
||||
// keys. Reading them as `undefined` would hide a paid feature from a paying
|
||||
// customer with nothing logged anywhere, so resolve them here — the one
|
||||
// place every caller already goes through.
|
||||
//
|
||||
// Both absent flags fall back to `browserAutomation`, which is what they
|
||||
// were derived from before solo existed: on every plan a pre-solo backend
|
||||
// knows about, automation implied both the bot and interactive remote
|
||||
// control. A solo user never hits this branch — the backend that can put
|
||||
// them on solo is by definition new enough to send both keys.
|
||||
//
|
||||
// `remoteBrowserHours` stays 0 because the spendable figure is whatever
|
||||
// `get_remote_hours_quota` reports, never a client guess.
|
||||
return {
|
||||
...server,
|
||||
cookieBot: server.cookieBot ?? server.browserAutomation,
|
||||
remoteInteractive: server.remoteInteractive ?? server.browserAutomation,
|
||||
remoteBrowserHours: server.remoteBrowserHours ?? 0,
|
||||
};
|
||||
}
|
||||
@@ -103,6 +120,7 @@ export function getEntitlements(
|
||||
cloudBackup: caps.cloudBackup,
|
||||
teamCollaboration: caps.teamCollaboration,
|
||||
cookieBot: caps.cookieBot,
|
||||
remoteInteractive: caps.remoteInteractive,
|
||||
profileLimit: user.profileLimit,
|
||||
requestsPerHour: caps.browserAutomation ? DEFAULT_REQUESTS_PER_HOUR : 0,
|
||||
remoteBrowserHours: 0,
|
||||
|
||||
Reference in New Issue
Block a user