Skip to content

Commit 38fa102

Browse files
author
Lasim
committed
fix(frontend): implement category caching and loading logic
1 parent b8459cd commit 38fa102

File tree

3 files changed

+51
-17
lines changed

3 files changed

+51
-17
lines changed

services/frontend/src/components/mcp-server/CategoryDisplay.vue

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
<script setup lang="ts">
2-
import { ref, onMounted, computed } from 'vue'
2+
import { ref, onMounted, computed, watch } from 'vue'
33
import { useI18n } from 'vue-i18n'
44
import { DynamicIcon } from '@/components/ui/dynamic-icon'
55
import { McpCatalogService } from '@/services/mcpCatalogService'
6+
import { useEventBus } from '@/composables/useEventBus'
67
import type { McpCategory } from '@/services/mcpCategoriesService'
78
89
const { t } = useI18n()
10+
const eventBus = useEventBus()
11+
12+
// Storage key for categories cache
13+
const CATEGORIES_STORAGE_KEY = 'mcp_categories_cache'
914
1015
interface Props {
1116
categoryId?: string | null
@@ -29,25 +34,56 @@ const category = ref<McpCategory | null>(props.category)
2934
const isLoading = ref(false)
3035
const error = ref<string | null>(null)
3136
32-
// Fetch category by ID if not provided as prop
33-
async function fetchCategory(categoryId: string): Promise<McpCategory | null> {
37+
// Find category from cached categories in storage
38+
function findCategoryFromCache(categoryId: string): McpCategory | null {
39+
const cached = eventBus.getState<McpCategory[]>(CATEGORIES_STORAGE_KEY)
40+
if (cached && Array.isArray(cached)) {
41+
return cached.find(cat => cat.id === categoryId) || null
42+
}
43+
return null
44+
}
45+
46+
// Fetch categories and cache them in storage
47+
async function fetchAndCacheCategories(): Promise<McpCategory[]> {
48+
const categories = await McpCatalogService.getCategories()
49+
eventBus.setState(CATEGORIES_STORAGE_KEY, categories)
50+
return categories
51+
}
52+
53+
// Load category - check storage cache first, then fetch if needed
54+
async function loadCategory(categoryId: string): Promise<void> {
55+
// First, try to find in storage cache (prevents redundant API calls)
56+
const cachedCategory = findCategoryFromCache(categoryId)
57+
if (cachedCategory) {
58+
category.value = cachedCategory
59+
return
60+
}
61+
62+
// Not in cache, fetch from API
3463
try {
3564
isLoading.value = true
36-
const categories = await McpCatalogService.getCategories()
37-
return categories.find(cat => cat.id === categoryId) || null
65+
error.value = null
66+
const categories = await fetchAndCacheCategories()
67+
category.value = categories.find(cat => cat.id === categoryId) || null
3868
} catch (err) {
3969
error.value = err instanceof Error ? err.message : 'Failed to fetch category'
4070
console.error('Failed to fetch category:', err)
41-
return null
4271
} finally {
4372
isLoading.value = false
4473
}
4574
}
4675
76+
// Watch for categoryId changes (handles re-renders without remount)
77+
watch(() => props.categoryId, (newId) => {
78+
if (newId && !props.category) {
79+
loadCategory(newId)
80+
}
81+
}, { immediate: false })
82+
4783
// Load category on mount if categoryId is provided but category is not
48-
onMounted(async () => {
84+
onMounted(() => {
4985
if (props.categoryId && !props.category) {
50-
category.value = await fetchCategory(props.categoryId)
86+
loadCategory(props.categoryId)
5187
}
5288
})
5389

services/frontend/src/services/mcpCatalogService.ts

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import type {
77
UpdateMcpServerRequest,
88
McpServerFilters
99
} from '@/views/admin/mcp-server-catalog/types'
10-
import type { McpCategory } from '@/services/mcpCategoriesService'
10+
import { McpCategoriesService, type McpCategory } from '@/services/mcpCategoriesService'
1111
import type { McpServerSearchParams, McpServerSearchResponse } from '@/types/mcp-catalog'
1212

1313
export interface PaginationParams {
@@ -284,6 +284,7 @@ export class McpCatalogService {
284284
static async updateGlobalServer(serverId: string, serverData: UpdateMcpServerRequest): Promise<McpServer> {
285285
const response = await fetch(`${this.baseUrl}/api/mcp/servers/global/${serverId}`, {
286286
method: 'PUT',
287+
mode: 'cors',
287288
credentials: 'include',
288289
headers: {
289290
'Content-Type': 'application/json',
@@ -568,15 +569,10 @@ export class McpCatalogService {
568569
// This delegates to the dedicated McpCategoriesService
569570
export class McpCategoriesCache {
570571
static async getCategories(forceRefresh = false): Promise<McpCategory[]> {
571-
// Import here to avoid circular dependencies
572-
const { McpCategoriesService } = await import('@/services/mcpCategoriesService')
573572
return McpCategoriesService.getCategories(forceRefresh)
574573
}
575574

576575
static clearCache(): void {
577-
// Import here to avoid circular dependencies
578-
import('@/services/mcpCategoriesService').then(({ McpCategoriesService }) => {
579-
McpCategoriesService.clearCache()
580-
})
576+
McpCategoriesService.clearCache()
581577
}
582578
}

services/frontend/src/views/admin/mcp-server-catalog/edit/[id].vue

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -242,10 +242,12 @@ const handleSubmit = async (formData: McpServerFormData) => {
242242
git_branch: repositorySetupData?.git_branch !== undefined ? repositorySetupData.git_branch : formData.repository.git_branch
243243
}
244244
245-
// Get README markdown from storage and convert to base64
245+
// Get README markdown from storage and convert to base64 (UTF-8 safe)
246246
const readmeData = eventBus.getState<{ readme_markdown: string }>('edit_readme_data')
247247
const readmeMarkdown = readmeData?.readme_markdown || ''
248-
const readmeBase64 = readmeMarkdown ? btoa(readmeMarkdown) : ''
248+
const readmeBase64 = readmeMarkdown
249+
? btoa(new TextEncoder().encode(readmeMarkdown).reduce((data, byte) => data + String.fromCharCode(byte), ''))
250+
: ''
249251
250252
// CRITICAL FIX: Synchronize environment variables from installation_methods to team_env_schema
251253
let finalConfigurationSchema = { ...formData.configuration_schema }

0 commit comments

Comments
 (0)