removing typesense
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import Button from '../ui/Button';
|
||||
import { storyApi, authorApi, databaseApi, configApi, searchAdminApi } from '../../lib/api';
|
||||
import { databaseApi, configApi, searchAdminApi } from '../../lib/api';
|
||||
|
||||
interface SystemSettingsProps {
|
||||
// No props needed - this component manages its own state
|
||||
@@ -11,16 +11,12 @@ interface SystemSettingsProps {
|
||||
export default function SystemSettings({}: SystemSettingsProps) {
|
||||
const [searchEngineStatus, setSearchEngineStatus] = useState<{
|
||||
currentEngine: string;
|
||||
dualWrite: boolean;
|
||||
typesenseAvailable: boolean;
|
||||
openSearchAvailable: boolean;
|
||||
loading: boolean;
|
||||
message: string;
|
||||
success?: boolean;
|
||||
}>({
|
||||
currentEngine: 'typesense',
|
||||
dualWrite: false,
|
||||
typesenseAvailable: false,
|
||||
currentEngine: 'opensearch',
|
||||
openSearchAvailable: false,
|
||||
loading: false,
|
||||
message: ''
|
||||
@@ -34,13 +30,6 @@ export default function SystemSettings({}: SystemSettingsProps) {
|
||||
recreate: { loading: false, message: '' }
|
||||
});
|
||||
|
||||
const [typesenseStatus, setTypesenseStatus] = useState<{
|
||||
reindex: { loading: boolean; message: string; success?: boolean };
|
||||
recreate: { loading: boolean; message: string; success?: boolean };
|
||||
}>({
|
||||
reindex: { loading: false, message: '' },
|
||||
recreate: { loading: false, message: '' }
|
||||
});
|
||||
const [databaseStatus, setDatabaseStatus] = useState<{
|
||||
completeBackup: { loading: boolean; message: string; success?: boolean };
|
||||
completeRestore: { loading: boolean; message: string; success?: boolean };
|
||||
@@ -58,135 +47,7 @@ export default function SystemSettings({}: SystemSettingsProps) {
|
||||
execute: { loading: false, message: '' }
|
||||
});
|
||||
|
||||
const handleFullReindex = async () => {
|
||||
setTypesenseStatus(prev => ({
|
||||
...prev,
|
||||
reindex: { loading: true, message: 'Reindexing all collections...', success: undefined }
|
||||
}));
|
||||
|
||||
try {
|
||||
// Run both story and author reindex in parallel
|
||||
const [storiesResult, authorsResult] = await Promise.all([
|
||||
storyApi.reindexTypesense(),
|
||||
authorApi.reindexTypesense()
|
||||
]);
|
||||
|
||||
const allSuccessful = storiesResult.success && authorsResult.success;
|
||||
const messages: string[] = [];
|
||||
|
||||
if (storiesResult.success) {
|
||||
messages.push(`Stories: ${storiesResult.message}`);
|
||||
} else {
|
||||
messages.push(`Stories failed: ${storiesResult.error || 'Unknown error'}`);
|
||||
}
|
||||
|
||||
if (authorsResult.success) {
|
||||
messages.push(`Authors: ${authorsResult.message}`);
|
||||
} else {
|
||||
messages.push(`Authors failed: ${authorsResult.error || 'Unknown error'}`);
|
||||
}
|
||||
|
||||
setTypesenseStatus(prev => ({
|
||||
...prev,
|
||||
reindex: {
|
||||
loading: false,
|
||||
message: allSuccessful
|
||||
? `Full reindex completed successfully. ${messages.join(', ')}`
|
||||
: `Reindex completed with errors. ${messages.join(', ')}`,
|
||||
success: allSuccessful
|
||||
}
|
||||
}));
|
||||
|
||||
// Clear message after 8 seconds (longer for combined operation)
|
||||
setTimeout(() => {
|
||||
setTypesenseStatus(prev => ({
|
||||
...prev,
|
||||
reindex: { loading: false, message: '', success: undefined }
|
||||
}));
|
||||
}, 8000);
|
||||
} catch (error) {
|
||||
setTypesenseStatus(prev => ({
|
||||
...prev,
|
||||
reindex: {
|
||||
loading: false,
|
||||
message: 'Network error occurred during reindex',
|
||||
success: false
|
||||
}
|
||||
}));
|
||||
|
||||
setTimeout(() => {
|
||||
setTypesenseStatus(prev => ({
|
||||
...prev,
|
||||
reindex: { loading: false, message: '', success: undefined }
|
||||
}));
|
||||
}, 8000);
|
||||
}
|
||||
};
|
||||
|
||||
const handleRecreateAllCollections = async () => {
|
||||
setTypesenseStatus(prev => ({
|
||||
...prev,
|
||||
recreate: { loading: true, message: 'Recreating all collections...', success: undefined }
|
||||
}));
|
||||
|
||||
try {
|
||||
// Run both story and author recreation in parallel
|
||||
const [storiesResult, authorsResult] = await Promise.all([
|
||||
storyApi.recreateTypesenseCollection(),
|
||||
authorApi.recreateTypesenseCollection()
|
||||
]);
|
||||
|
||||
const allSuccessful = storiesResult.success && authorsResult.success;
|
||||
const messages: string[] = [];
|
||||
|
||||
if (storiesResult.success) {
|
||||
messages.push(`Stories: ${storiesResult.message}`);
|
||||
} else {
|
||||
messages.push(`Stories failed: ${storiesResult.error || 'Unknown error'}`);
|
||||
}
|
||||
|
||||
if (authorsResult.success) {
|
||||
messages.push(`Authors: ${authorsResult.message}`);
|
||||
} else {
|
||||
messages.push(`Authors failed: ${authorsResult.error || 'Unknown error'}`);
|
||||
}
|
||||
|
||||
setTypesenseStatus(prev => ({
|
||||
...prev,
|
||||
recreate: {
|
||||
loading: false,
|
||||
message: allSuccessful
|
||||
? `All collections recreated successfully. ${messages.join(', ')}`
|
||||
: `Recreation completed with errors. ${messages.join(', ')}`,
|
||||
success: allSuccessful
|
||||
}
|
||||
}));
|
||||
|
||||
// Clear message after 8 seconds (longer for combined operation)
|
||||
setTimeout(() => {
|
||||
setTypesenseStatus(prev => ({
|
||||
...prev,
|
||||
recreate: { loading: false, message: '', success: undefined }
|
||||
}));
|
||||
}, 8000);
|
||||
} catch (error) {
|
||||
setTypesenseStatus(prev => ({
|
||||
...prev,
|
||||
recreate: {
|
||||
loading: false,
|
||||
message: 'Network error occurred during recreation',
|
||||
success: false
|
||||
}
|
||||
}));
|
||||
|
||||
setTimeout(() => {
|
||||
setTypesenseStatus(prev => ({
|
||||
...prev,
|
||||
recreate: { loading: false, message: '', success: undefined }
|
||||
}));
|
||||
}, 8000);
|
||||
}
|
||||
};
|
||||
|
||||
const handleCompleteBackup = async () => {
|
||||
setDatabaseStatus(prev => ({
|
||||
@@ -451,8 +312,6 @@ export default function SystemSettings({}: SystemSettingsProps) {
|
||||
setSearchEngineStatus(prev => ({
|
||||
...prev,
|
||||
currentEngine: status.primaryEngine,
|
||||
dualWrite: status.dualWrite,
|
||||
typesenseAvailable: status.typesenseAvailable,
|
||||
openSearchAvailable: status.openSearchAvailable,
|
||||
}));
|
||||
} catch (error: any) {
|
||||
@@ -460,76 +319,7 @@ export default function SystemSettings({}: SystemSettingsProps) {
|
||||
}
|
||||
};
|
||||
|
||||
const handleSwitchEngine = async (engine: string) => {
|
||||
setSearchEngineStatus(prev => ({ ...prev, loading: true, message: `Switching to ${engine}...` }));
|
||||
|
||||
try {
|
||||
const result = engine === 'opensearch'
|
||||
? await searchAdminApi.switchToOpenSearch()
|
||||
: await searchAdminApi.switchToTypesense();
|
||||
|
||||
setSearchEngineStatus(prev => ({
|
||||
...prev,
|
||||
loading: false,
|
||||
message: result.message,
|
||||
success: true,
|
||||
currentEngine: engine
|
||||
}));
|
||||
|
||||
setTimeout(() => {
|
||||
setSearchEngineStatus(prev => ({ ...prev, message: '', success: undefined }));
|
||||
}, 5000);
|
||||
} catch (error: any) {
|
||||
setSearchEngineStatus(prev => ({
|
||||
...prev,
|
||||
loading: false,
|
||||
message: error.message || 'Failed to switch engine',
|
||||
success: false
|
||||
}));
|
||||
|
||||
setTimeout(() => {
|
||||
setSearchEngineStatus(prev => ({ ...prev, message: '', success: undefined }));
|
||||
}, 5000);
|
||||
}
|
||||
};
|
||||
|
||||
const handleToggleDualWrite = async () => {
|
||||
const newDualWrite = !searchEngineStatus.dualWrite;
|
||||
setSearchEngineStatus(prev => ({
|
||||
...prev,
|
||||
loading: true,
|
||||
message: `${newDualWrite ? 'Enabling' : 'Disabling'} dual-write...`
|
||||
}));
|
||||
|
||||
try {
|
||||
const result = newDualWrite
|
||||
? await searchAdminApi.enableDualWrite()
|
||||
: await searchAdminApi.disableDualWrite();
|
||||
|
||||
setSearchEngineStatus(prev => ({
|
||||
...prev,
|
||||
loading: false,
|
||||
message: result.message,
|
||||
success: true,
|
||||
dualWrite: newDualWrite
|
||||
}));
|
||||
|
||||
setTimeout(() => {
|
||||
setSearchEngineStatus(prev => ({ ...prev, message: '', success: undefined }));
|
||||
}, 5000);
|
||||
} catch (error: any) {
|
||||
setSearchEngineStatus(prev => ({
|
||||
...prev,
|
||||
loading: false,
|
||||
message: error.message || 'Failed to toggle dual-write',
|
||||
success: false
|
||||
}));
|
||||
|
||||
setTimeout(() => {
|
||||
setSearchEngineStatus(prev => ({ ...prev, message: '', success: undefined }));
|
||||
}, 5000);
|
||||
}
|
||||
};
|
||||
|
||||
const handleOpenSearchReindex = async () => {
|
||||
setOpenSearchStatus(prev => ({
|
||||
@@ -624,36 +414,18 @@ export default function SystemSettings({}: SystemSettingsProps) {
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
{/* Search Engine Management */}
|
||||
{/* Search Management */}
|
||||
<div className="theme-card theme-shadow rounded-lg p-6">
|
||||
<h2 className="text-xl font-semibold theme-header mb-4">Search Engine Migration</h2>
|
||||
<h2 className="text-xl font-semibold theme-header mb-4">Search Management</h2>
|
||||
<p className="theme-text mb-6">
|
||||
Manage the transition from Typesense to OpenSearch. Switch between engines, enable dual-write mode, and perform maintenance operations.
|
||||
Manage OpenSearch indices for stories and authors. Use these tools if search isn't returning expected results.
|
||||
</p>
|
||||
|
||||
<div className="space-y-6">
|
||||
{/* Current Status */}
|
||||
<div className="border theme-border rounded-lg p-4">
|
||||
<h3 className="text-lg font-semibold theme-header mb-3">Current Configuration</h3>
|
||||
<h3 className="text-lg font-semibold theme-header mb-3">Search Status</h3>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3 text-sm">
|
||||
<div className="flex justify-between">
|
||||
<span>Primary Engine:</span>
|
||||
<span className={`font-medium ${searchEngineStatus.currentEngine === 'opensearch' ? 'text-blue-600 dark:text-blue-400' : 'text-green-600 dark:text-green-400'}`}>
|
||||
{searchEngineStatus.currentEngine.charAt(0).toUpperCase() + searchEngineStatus.currentEngine.slice(1)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span>Dual-Write:</span>
|
||||
<span className={`font-medium ${searchEngineStatus.dualWrite ? 'text-orange-600 dark:text-orange-400' : 'text-gray-600 dark:text-gray-400'}`}>
|
||||
{searchEngineStatus.dualWrite ? 'Enabled' : 'Disabled'}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span>Typesense:</span>
|
||||
<span className={`font-medium ${searchEngineStatus.typesenseAvailable ? 'text-green-600 dark:text-green-400' : 'text-red-600 dark:text-red-400'}`}>
|
||||
{searchEngineStatus.typesenseAvailable ? 'Available' : 'Unavailable'}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span>OpenSearch:</span>
|
||||
<span className={`font-medium ${searchEngineStatus.openSearchAvailable ? 'text-green-600 dark:text-green-400' : 'text-red-600 dark:text-red-400'}`}>
|
||||
@@ -663,52 +435,11 @@ export default function SystemSettings({}: SystemSettingsProps) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Engine Switching */}
|
||||
{/* Search Operations */}
|
||||
<div className="border theme-border rounded-lg p-4">
|
||||
<h3 className="text-lg font-semibold theme-header mb-3">Engine Controls</h3>
|
||||
<div className="flex flex-col sm:flex-row gap-3 mb-4">
|
||||
<Button
|
||||
onClick={() => handleSwitchEngine('typesense')}
|
||||
disabled={searchEngineStatus.loading || !searchEngineStatus.typesenseAvailable || searchEngineStatus.currentEngine === 'typesense'}
|
||||
variant={searchEngineStatus.currentEngine === 'typesense' ? 'primary' : 'ghost'}
|
||||
className="flex-1"
|
||||
>
|
||||
{searchEngineStatus.currentEngine === 'typesense' ? '✓ Typesense (Active)' : 'Switch to Typesense'}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => handleSwitchEngine('opensearch')}
|
||||
disabled={searchEngineStatus.loading || !searchEngineStatus.openSearchAvailable || searchEngineStatus.currentEngine === 'opensearch'}
|
||||
variant={searchEngineStatus.currentEngine === 'opensearch' ? 'primary' : 'ghost'}
|
||||
className="flex-1"
|
||||
>
|
||||
{searchEngineStatus.currentEngine === 'opensearch' ? '✓ OpenSearch (Active)' : 'Switch to OpenSearch'}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={handleToggleDualWrite}
|
||||
disabled={searchEngineStatus.loading}
|
||||
variant={searchEngineStatus.dualWrite ? 'secondary' : 'ghost'}
|
||||
className="flex-1"
|
||||
>
|
||||
{searchEngineStatus.dualWrite ? 'Disable Dual-Write' : 'Enable Dual-Write'}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{searchEngineStatus.message && (
|
||||
<div className={`text-sm p-3 rounded mb-3 ${
|
||||
searchEngineStatus.success
|
||||
? 'bg-green-50 dark:bg-green-900/20 text-green-800 dark:text-green-200'
|
||||
: 'bg-red-50 dark:bg-red-900/20 text-red-800 dark:text-red-200'
|
||||
}`}>
|
||||
{searchEngineStatus.message}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* OpenSearch Operations */}
|
||||
<div className="border theme-border rounded-lg p-4">
|
||||
<h3 className="text-lg font-semibold theme-header mb-3">OpenSearch Operations</h3>
|
||||
<h3 className="text-lg font-semibold theme-header mb-3">Search Operations</h3>
|
||||
<p className="text-sm theme-text mb-4">
|
||||
Perform maintenance operations on OpenSearch indices. Use these if OpenSearch isn't returning expected results.
|
||||
Perform maintenance operations on search indices. Use these if search isn't returning expected results.
|
||||
</p>
|
||||
|
||||
<div className="flex flex-col sm:flex-row gap-3 mb-4">
|
||||
@@ -719,7 +450,7 @@ export default function SystemSettings({}: SystemSettingsProps) {
|
||||
variant="ghost"
|
||||
className="flex-1"
|
||||
>
|
||||
{openSearchStatus.reindex.loading ? 'Reindexing...' : '🔄 Reindex OpenSearch'}
|
||||
{openSearchStatus.reindex.loading ? 'Reindexing...' : '🔄 Reindex All'}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={handleOpenSearchRecreate}
|
||||
@@ -732,7 +463,7 @@ export default function SystemSettings({}: SystemSettingsProps) {
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* OpenSearch Status Messages */}
|
||||
{/* Status Messages */}
|
||||
{openSearchStatus.reindex.message && (
|
||||
<div className={`text-sm p-3 rounded mb-3 ${
|
||||
openSearchStatus.reindex.success
|
||||
@@ -753,73 +484,12 @@ export default function SystemSettings({}: SystemSettingsProps) {
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Legacy Typesense Management */}
|
||||
<div className="theme-card theme-shadow rounded-lg p-6">
|
||||
<h2 className="text-xl font-semibold theme-header mb-4">Legacy Typesense Management</h2>
|
||||
<p className="theme-text mb-6">
|
||||
Manage Typesense search indexes (for backwards compatibility and during migration). These tools will be removed once migration is complete.
|
||||
</p>
|
||||
|
||||
<div className="space-y-6">
|
||||
{/* Simplified Operations */}
|
||||
<div className="border theme-border rounded-lg p-4">
|
||||
<h3 className="text-lg font-semibold theme-header mb-3">Search Operations</h3>
|
||||
<p className="text-sm theme-text mb-4">
|
||||
Perform maintenance operations on all search indexes (stories, authors, collections, etc.).
|
||||
</p>
|
||||
|
||||
<div className="flex flex-col sm:flex-row gap-3 mb-4">
|
||||
<Button
|
||||
onClick={handleFullReindex}
|
||||
disabled={typesenseStatus.reindex.loading || typesenseStatus.recreate.loading}
|
||||
loading={typesenseStatus.reindex.loading}
|
||||
variant="ghost"
|
||||
className="flex-1"
|
||||
>
|
||||
{typesenseStatus.reindex.loading ? 'Reindexing All...' : '🔄 Full Reindex'}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={handleRecreateAllCollections}
|
||||
disabled={typesenseStatus.reindex.loading || typesenseStatus.recreate.loading}
|
||||
loading={typesenseStatus.recreate.loading}
|
||||
variant="secondary"
|
||||
className="flex-1"
|
||||
>
|
||||
{typesenseStatus.recreate.loading ? 'Recreating All...' : '🏗️ Recreate All Collections'}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Status Messages */}
|
||||
{typesenseStatus.reindex.message && (
|
||||
<div className={`text-sm p-3 rounded mb-3 ${
|
||||
typesenseStatus.reindex.success
|
||||
? 'bg-green-50 dark:bg-green-900/20 text-green-800 dark:text-green-200'
|
||||
: 'bg-red-50 dark:bg-red-900/20 text-red-800 dark:text-red-200'
|
||||
}`}>
|
||||
{typesenseStatus.reindex.message}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{typesenseStatus.recreate.message && (
|
||||
<div className={`text-sm p-3 rounded mb-3 ${
|
||||
typesenseStatus.recreate.success
|
||||
? 'bg-green-50 dark:bg-green-900/20 text-green-800 dark:text-green-200'
|
||||
: 'bg-red-50 dark:bg-red-900/20 text-red-800 dark:text-red-200'
|
||||
}`}>
|
||||
{typesenseStatus.recreate.message}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="text-sm theme-text bg-blue-50 dark:bg-blue-900/20 p-3 rounded-lg">
|
||||
<p className="font-medium mb-1">When to use these tools:</p>
|
||||
<ul className="text-xs space-y-1 ml-4">
|
||||
<li>• <strong>Full Reindex:</strong> Refresh all search data while keeping existing schemas (fixes data sync issues)</li>
|
||||
<li>• <strong>Recreate All Collections:</strong> Delete and rebuild all search indexes from scratch (fixes schema and structure issues)</li>
|
||||
<li>• <strong>Operations run in parallel</strong> across all index types for better performance</li>
|
||||
<li>• <strong>Reindex All:</strong> Refresh all search data while keeping existing schemas (fixes data sync issues)</li>
|
||||
<li>• <strong>Recreate Indices:</strong> Delete and rebuild all search indexes from scratch (fixes schema and structure issues)</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user