157 lines
3.5 KiB
TypeScript
157 lines
3.5 KiB
TypeScript
import { CareInfo, IdentificationResult, Language, PlantHealthCheck } from '../../types';
|
|
|
|
export type PlanId = 'free' | 'pro';
|
|
export type BillingProvider = 'mock' | 'revenuecat' | 'stripe';
|
|
export type PurchaseProductId = 'monthly_pro' | 'yearly_pro' | 'topup_small' | 'topup_medium' | 'topup_large';
|
|
export type SimulatedWebhookEvent =
|
|
| 'entitlement_granted'
|
|
| 'entitlement_revoked'
|
|
| 'topup_granted'
|
|
| 'credits_depleted';
|
|
|
|
export interface BackendDatabaseEntry extends IdentificationResult {
|
|
imageUri: string;
|
|
imageStatus?: 'ok' | 'missing' | 'invalid';
|
|
categories: string[];
|
|
}
|
|
|
|
export interface CreditState {
|
|
monthlyAllowance: number;
|
|
usedThisCycle: number;
|
|
topupBalance: number;
|
|
available: number;
|
|
cycleStartedAt: string;
|
|
cycleEndsAt: string;
|
|
}
|
|
|
|
export interface EntitlementState {
|
|
plan: PlanId;
|
|
provider: BillingProvider;
|
|
status: 'active' | 'inactive';
|
|
renewsAt: string | null;
|
|
}
|
|
|
|
export interface BillingSummary {
|
|
entitlement: EntitlementState;
|
|
credits: CreditState;
|
|
availableProducts: PurchaseProductId[];
|
|
}
|
|
|
|
export interface ScanPlantRequest {
|
|
userId: string;
|
|
idempotencyKey: string;
|
|
imageUri: string;
|
|
language: Language;
|
|
}
|
|
|
|
export interface ScanPlantResponse {
|
|
result: IdentificationResult;
|
|
creditsCharged: number;
|
|
modelPath: string[];
|
|
modelUsed?: string | null;
|
|
modelFallbackCount?: number;
|
|
billing: BillingSummary;
|
|
}
|
|
|
|
export interface SemanticSearchRequest {
|
|
userId: string;
|
|
idempotencyKey: string;
|
|
query: string;
|
|
language: Language;
|
|
}
|
|
|
|
export interface SemanticSearchResponse {
|
|
status: 'success' | 'no_results';
|
|
results: BackendDatabaseEntry[];
|
|
creditsCharged: number;
|
|
billing: BillingSummary;
|
|
}
|
|
|
|
export interface HealthCheckRequest {
|
|
userId: string;
|
|
idempotencyKey: string;
|
|
imageUri: string;
|
|
language: Language;
|
|
plantContext?: {
|
|
name: string;
|
|
botanicalName: string;
|
|
careInfo: CareInfo;
|
|
description?: string;
|
|
};
|
|
}
|
|
|
|
export interface HealthCheckResponse {
|
|
healthCheck: PlantHealthCheck;
|
|
creditsCharged: number;
|
|
modelUsed?: string | null;
|
|
modelFallbackCount?: number;
|
|
billing: BillingSummary;
|
|
}
|
|
|
|
export interface ServiceHealthResponse {
|
|
ok: boolean;
|
|
uptimeSec: number;
|
|
timestamp: string;
|
|
openAiConfigured: boolean;
|
|
dbReady?: boolean;
|
|
dbPath?: string;
|
|
stripeConfigured?: boolean;
|
|
scanModel?: string;
|
|
healthModel?: string;
|
|
}
|
|
|
|
export interface SimulatePurchaseRequest {
|
|
userId: string;
|
|
idempotencyKey: string;
|
|
productId: PurchaseProductId;
|
|
}
|
|
|
|
export interface SimulatePurchaseResponse {
|
|
appliedProduct: PurchaseProductId;
|
|
billing: BillingSummary;
|
|
}
|
|
|
|
export interface SimulateWebhookRequest {
|
|
userId: string;
|
|
idempotencyKey: string;
|
|
event: SimulatedWebhookEvent;
|
|
payload?: {
|
|
credits?: number;
|
|
};
|
|
}
|
|
|
|
export interface SimulateWebhookResponse {
|
|
event: SimulatedWebhookEvent;
|
|
billing: BillingSummary;
|
|
}
|
|
|
|
export type BackendErrorCode =
|
|
| 'INSUFFICIENT_CREDITS'
|
|
| 'UNAUTHORIZED'
|
|
| 'TIMEOUT'
|
|
| 'PROVIDER_ERROR'
|
|
| 'BAD_REQUEST';
|
|
|
|
export class BackendApiError extends Error {
|
|
public readonly code: BackendErrorCode;
|
|
public readonly status: number;
|
|
public readonly metadata?: Record<string, unknown>;
|
|
|
|
constructor(
|
|
code: BackendErrorCode,
|
|
message: string,
|
|
status = 500,
|
|
metadata?: Record<string, unknown>,
|
|
) {
|
|
super(message);
|
|
this.name = 'BackendApiError';
|
|
this.code = code;
|
|
this.status = status;
|
|
this.metadata = metadata;
|
|
}
|
|
}
|
|
|
|
export const isBackendApiError = (error: unknown): error is BackendApiError => {
|
|
return error instanceof BackendApiError;
|
|
};
|