fix: credit tracking, voice clone TTL, avatar upload ui, asset serving fallback, OAuth encryption, free plan video renders, backlink outreach sprint

This commit is contained in:
ajaysi
2026-05-25 17:07:35 +05:30
parent 090d69761f
commit 9b3bec698b
99 changed files with 15892 additions and 1278 deletions

View File

@@ -1,4 +1,5 @@
import { aiApiClient, getAuthTokenGetter } from '../api/client';
import { getApiBaseUrl } from '../utils/apiUrl';
export interface ChartGenerateRequest {
chart_data?: Record<string, any>;
@@ -23,11 +24,7 @@ class ChartApiService {
private baseUrl: string;
constructor() {
const url = process.env.REACT_APP_API_URL;
if (process.env.NODE_ENV === 'production' && !url) {
throw new Error('REACT_APP_API_URL environment variable is required for production');
}
this.baseUrl = url || 'http://localhost:8000';
this.baseUrl = getApiBaseUrl();
}
async generateChartExplicit(params: {

View File

@@ -3,6 +3,7 @@
*/
import { longRunningApiClient } from '../api/client';
import { getApiBaseUrl } from '../utils/apiUrl';
export interface SourceDocument {
title: string;
@@ -79,13 +80,6 @@ class HallucinationDetectorService {
private baseUrl: string;
constructor() {
const getApiBaseUrl = () => {
const url = process.env.REACT_APP_API_URL;
if (process.env.NODE_ENV === 'production' && !url) {
throw new Error('REACT_APP_API_URL environment variable is required for production');
}
return url || 'http://localhost:8000';
};
this.baseUrl = getApiBaseUrl();
}

View File

@@ -1,4 +1,5 @@
import { aiApiClient } from '../api/client';
import { getApiBaseUrl } from '../utils/apiUrl';
export interface LinkSearchRequest {
query: string;
@@ -37,11 +38,7 @@ class LinkApiService {
private baseUrl: string;
constructor() {
const url = process.env.REACT_APP_API_URL;
if (process.env.NODE_ENV === 'production' && !url) {
throw new Error('REACT_APP_API_URL environment variable is required for production');
}
this.baseUrl = url || 'http://localhost:8000';
this.baseUrl = getApiBaseUrl();
}
async searchLinks(params: LinkSearchRequest): Promise<LinkSearchResponse> {

View File

@@ -39,14 +39,14 @@ const DEFAULT_KNOBS: Knobs = {
};
const VOICE_CLONE_STORAGE_KEY = "alwrity_voice_clone_info";
const VOICE_CLONE_CACHE_TTL = 30 * 60 * 1000; // 30 minutes
const VOICE_CLONE_CACHE_TTL = 2 * 60 * 60 * 1000; // 2 hours (WaveSpeed IDs last longer than documented 30 min)
function _readVoiceCloneCache() {
try {
const raw = localStorage.getItem(VOICE_CLONE_STORAGE_KEY);
if (!raw) return null;
const parsed = JSON.parse(raw);
if (parsed && typeof parsed.timestamp === "number" && Date.now() - parsed.timestamp < VOICE_CLONE_CACHE_TTL) {
if (parsed && typeof parsed.timestamp === "number") {
return parsed;
}
} catch {
@@ -78,10 +78,14 @@ function _clearVoiceCloneCache() {
/**
* Get cached voice clone info from localStorage (survives page refresh).
* Returns null if expired (>30 min) or not set.
* Returns null if not set. Includes `stale` flag if older than 2 hours
* so consumers can proactively re-clone before the API rejects the ID.
*/
export function getCachedVoiceCloneInfo() {
return _readVoiceCloneCache();
export function getCachedVoiceCloneInfo(): (ReturnType<typeof _readVoiceCloneCache> & { stale?: boolean }) | null {
const cached = _readVoiceCloneCache();
if (!cached) return null;
const stale = typeof cached.timestamp === "number" && Date.now() - cached.timestamp > VOICE_CLONE_CACHE_TTL;
return { ...cached, stale };
}
/**

View File

@@ -11,15 +11,7 @@ import {
CopilotActionResponse,
CopilotSuggestion
} from '../types/seoCopilotTypes';
// API URL - require REACT_APP_API_URL in production
const getApiBaseUrl = () => {
const url = process.env.REACT_APP_API_URL;
if (process.env.NODE_ENV === 'production' && !url) {
throw new Error('REACT_APP_API_URL environment variable is required for production');
}
return url || 'http://localhost:8000';
};
import { getApiBaseUrl } from '../utils/apiUrl';
const API_BASE_URL = getApiBaseUrl();

View File

@@ -1,3 +1,5 @@
import { getApiBaseUrl } from '../utils/apiUrl';
export interface WASource {
title: string;
url: string;
@@ -22,13 +24,6 @@ class WritingAssistantService {
private baseUrl: string;
private authTokenGetter: (() => Promise<string | null>) | null = null;
constructor() {
const getApiBaseUrl = () => {
const url = process.env.REACT_APP_API_URL;
if (process.env.NODE_ENV === 'production' && !url) {
throw new Error('REACT_APP_API_URL environment variable is required for production');
}
return url || 'http://localhost:8000';
};
this.baseUrl = getApiBaseUrl();
}