Compare commits

...

2 Commits

Author SHA1 Message Date
semantic-release-bot 04edfca6e0 🔖 chore(release): v1.105.2 [skip ci]
### [Version 1.105.2](https://github.com/lobehub/lobe-chat/compare/v1.105.1...v1.105.2)
<sup>Released on **2025-07-29**</sup>

#### ♻ Code Refactoring

- **misc**: Clean mcp sitemap, refactor jose-JWT to xor obfuscation.

#### 💄 Styles

- **misc**: Add more OpenAI SDK Text2Image providers, update i18n.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### Code refactoring

* **misc**: Clean mcp sitemap, closes [#8596](https://github.com/lobehub/lobe-chat/issues/8596) ([b9e3e66](https://github.com/lobehub/lobe-chat/commit/b9e3e66))
* **misc**: Refactor jose-JWT to xor obfuscation, closes [#8595](https://github.com/lobehub/lobe-chat/issues/8595) ([be98d56](https://github.com/lobehub/lobe-chat/commit/be98d56))

#### Styles

* **misc**: Add more OpenAI SDK Text2Image providers, closes [#8573](https://github.com/lobehub/lobe-chat/issues/8573) ([403aebd](https://github.com/lobehub/lobe-chat/commit/403aebd))
* **misc**: Update i18n, closes [#8593](https://github.com/lobehub/lobe-chat/issues/8593) ([356cf0c](https://github.com/lobehub/lobe-chat/commit/356cf0c))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-07-29 07:11:05 +00:00
Arvin Xu b9e3e6633e ♻️ refactor: clean mcp sitemap (#8596) 2025-07-29 14:55:46 +08:00
9 changed files with 38 additions and 140 deletions
+35
View File
@@ -2,6 +2,41 @@
# Changelog
### [Version 1.105.2](https://github.com/lobehub/lobe-chat/compare/v1.105.1...v1.105.2)
<sup>Released on **2025-07-29**</sup>
#### ♻ Code Refactoring
- **misc**: Clean mcp sitemap, refactor jose-JWT to xor obfuscation.
#### 💄 Styles
- **misc**: Add more OpenAI SDK Text2Image providers, update i18n.
<br/>
<details>
<summary><kbd>Improvements and Fixes</kbd></summary>
#### Code refactoring
- **misc**: Clean mcp sitemap, closes [#8596](https://github.com/lobehub/lobe-chat/issues/8596) ([b9e3e66](https://github.com/lobehub/lobe-chat/commit/b9e3e66))
- **misc**: Refactor jose-JWT to xor obfuscation, closes [#8595](https://github.com/lobehub/lobe-chat/issues/8595) ([be98d56](https://github.com/lobehub/lobe-chat/commit/be98d56))
#### Styles
- **misc**: Add more OpenAI SDK Text2Image providers, closes [#8573](https://github.com/lobehub/lobe-chat/issues/8573) ([403aebd](https://github.com/lobehub/lobe-chat/commit/403aebd))
- **misc**: Update i18n, closes [#8593](https://github.com/lobehub/lobe-chat/issues/8593) ([356cf0c](https://github.com/lobehub/lobe-chat/commit/356cf0c))
</details>
<div align="right">
[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
</div>
### [Version 1.105.1](https://github.com/lobehub/lobe-chat/compare/v1.105.0...v1.105.1)
<sup>Released on **2025-07-29**</sup>
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@lobehub/chat",
"version": "1.105.1",
"version": "1.105.2",
"description": "Lobe Chat - an open-source, high-performance chatbot framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.",
"keywords": [
"framework",
+1 -10
View File
@@ -17,10 +17,9 @@ export async function generateSitemaps() {
const staticSitemaps = sitemapModule.sitemapIndexs;
// 获取需要分页的类型的页数
const [pluginPages, assistantPages, mcpPages, modelPages] = await Promise.all([
const [pluginPages, assistantPages, modelPages] = await Promise.all([
sitemapModule.getPluginPageCount(),
sitemapModule.getAssistantPageCount(),
sitemapModule.getMcpPageCount(),
sitemapModule.getModelPageCount(),
]);
@@ -30,7 +29,6 @@ export async function generateSitemaps() {
...Array.from({ length: assistantPages }, (_, i) => ({
id: `assistants-${i + 1}` as SitemapType,
})),
...Array.from({ length: mcpPages }, (_, i) => ({ id: `mcp-${i + 1}` as SitemapType })),
...Array.from({ length: modelPages }, (_, i) => ({ id: `models-${i + 1}` as SitemapType })),
];
@@ -60,9 +58,6 @@ export default async function sitemap({ id }: { id: string }): Promise<MetadataR
case SitemapType.Assistants: {
return sitemapModule.getAssistants(page);
}
case SitemapType.Mcp: {
return sitemapModule.getMcp(page);
}
case SitemapType.Plugins: {
return sitemapModule.getPlugins(page);
}
@@ -82,10 +77,6 @@ export default async function sitemap({ id }: { id: string }): Promise<MetadataR
const pageNum = parseInt(id.split('-')[1], 10);
return sitemapModule.getAssistants(pageNum);
}
if (id.startsWith('mcp-')) {
const pageNum = parseInt(id.split('-')[1], 10);
return sitemapModule.getMcp(pageNum);
}
if (id.startsWith('models-')) {
const pageNum = parseInt(id.split('-')[1], 10);
return sitemapModule.getModels(pageNum);
-14
View File
@@ -173,20 +173,6 @@ export const marketRouter = router({
}
}),
getMcpIdentifiers: marketProcedure.query(async ({ ctx }) => {
log('getMcpIdentifiers called');
try {
return await ctx.discoverService.getMcpIdentifiers();
} catch (error) {
log('Error fetching mcp identifiers: %O', error);
throw new TRPCError({
code: 'INTERNAL_SERVER_ERROR',
message: 'Failed to fetch mcp identifiers',
});
}
}),
getMcpList: marketProcedure
.input(
z
-13
View File
@@ -462,19 +462,6 @@ export class DiscoverService {
return result;
};
getMcpIdentifiers = async (): Promise<IdentifiersResponse> => {
log('getMcpIdentifiers: fetching identifiers');
const result = await this.market.plugins.getPublishedIdentifiers({
cache: 'force-cache',
next: {
revalidate: CacheRevalidate.List,
tags: [CacheTag.Discover, CacheTag.MCP],
},
});
log('getMcpIdentifiers: returning %d identifiers', result.length);
return result;
};
getMcpList = async (params: McpQueryParams = {}): Promise<McpListResponse> => {
log('getMcpList: params=%O', params);
const { locale } = params;
-52
View File
@@ -13,7 +13,6 @@ describe('Sitemap', () => {
// Mock the page count methods to return specific values for testing
vi.spyOn(sitemap, 'getPluginPageCount').mockResolvedValue(2);
vi.spyOn(sitemap, 'getAssistantPageCount').mockResolvedValue(3);
vi.spyOn(sitemap, 'getMcpPageCount').mockResolvedValue(1);
vi.spyOn(sitemap, 'getModelPageCount').mockResolvedValue(2);
const index = await sitemap.getIndex();
@@ -31,7 +30,6 @@ describe('Sitemap', () => {
expect(index).toContain(`<loc>${getCanonicalUrl('/sitemap/assistants-1.xml')}</loc>`);
expect(index).toContain(`<loc>${getCanonicalUrl('/sitemap/assistants-2.xml')}</loc>`);
expect(index).toContain(`<loc>${getCanonicalUrl('/sitemap/assistants-3.xml')}</loc>`);
expect(index).toContain(`<loc>${getCanonicalUrl('/sitemap/mcp-1.xml')}</loc>`);
expect(index).toContain(`<loc>${getCanonicalUrl('/sitemap/models-1.xml')}</loc>`);
expect(index).toContain(`<loc>${getCanonicalUrl('/sitemap/models-2.xml')}</loc>`);
@@ -218,46 +216,6 @@ describe('Sitemap', () => {
});
});
describe('getMcp', () => {
it('should return a valid mcp sitemap without pagination', async () => {
vi.spyOn(sitemap['discoverService'], 'getMcpIdentifiers').mockResolvedValue([
// @ts-ignore
{ identifier: 'test-mcp', lastModified: '2023-01-01' },
]);
const mcpSitemap = await sitemap.getMcp();
expect(mcpSitemap.length).toBe(15);
expect(mcpSitemap).toContainEqual(
expect.objectContaining({
url: getCanonicalUrl('/discover/mcp/test-mcp'),
lastModified: '2023-01-01T00:00:00.000Z',
}),
);
expect(mcpSitemap).toContainEqual(
expect.objectContaining({
url: getCanonicalUrl('/discover/mcp/test-mcp?hl=zh-CN'),
lastModified: '2023-01-01T00:00:00.000Z',
}),
);
});
it('should return a valid mcp sitemap with pagination', async () => {
const mockMcps = Array.from({ length: 80 }, (_, i) => ({
identifier: `test-mcp-${i}`,
lastModified: '2023-01-01',
}));
vi.spyOn(sitemap['discoverService'], 'getMcpIdentifiers').mockResolvedValue(
// @ts-ignore
mockMcps,
);
// Test first page (should have 80 items, all on first page)
const firstPageSitemap = await sitemap.getMcp(1);
expect(firstPageSitemap.length).toBe(80 * 15); // 80 items * 15 locales
});
});
describe('getProviders', () => {
it('should return a valid providers sitemap', async () => {
vi.spyOn(sitemap['discoverService'], 'getProviderIdentifiers').mockResolvedValue([
@@ -303,16 +261,6 @@ describe('Sitemap', () => {
expect(pageCount).toBe(3); // 250 items / 100 per page = ceil(2.5) = 3 pages
});
it('should return correct mcp page count', async () => {
vi.spyOn(sitemap['discoverService'], 'getMcpIdentifiers').mockResolvedValue(
// @ts-ignore
Array.from({ length: 50 }, (_, i) => ({ identifier: `mcp-${i}` })),
);
const pageCount = await sitemap.getMcpPageCount();
expect(pageCount).toBe(1); // 50 items / 100 per page = 1 page
});
it('should return correct model page count', async () => {
vi.spyOn(sitemap['discoverService'], 'getModelIdentifiers').mockResolvedValue(
// @ts-ignore
+1 -38
View File
@@ -52,12 +52,6 @@ export class Sitemap {
return Math.ceil(list.length / ITEMS_PER_PAGE);
}
// 获取MCP总页数
async getMcpPageCount(): Promise<number> {
const list = await this.discoverService.getMcpIdentifiers();
return Math.ceil(list.length / ITEMS_PER_PAGE);
}
// 获取模型总页数
async getModelPageCount(): Promise<number> {
const list = await this.discoverService.getModelIdentifiers();
@@ -171,10 +165,9 @@ export class Sitemap {
);
// 获取需要分页的类型的页数
const [pluginPages, assistantPages, mcpPages, modelPages] = await Promise.all([
const [pluginPages, assistantPages, modelPages] = await Promise.all([
this.getPluginPageCount(),
this.getAssistantPageCount(),
this.getMcpPageCount(),
this.getModelPageCount(),
]);
@@ -193,11 +186,6 @@ export class Sitemap {
),
),
),
...Array.from({ length: mcpPages }, (_, i) =>
this._generateSitemapLink(
getCanonicalUrl(SITEMAP_BASE_URL, isDev ? `mcp-${i + 1}` : `mcp-${i + 1}.xml`),
),
),
...Array.from({ length: modelPages }, (_, i) =>
this._generateSitemapLink(
getCanonicalUrl(SITEMAP_BASE_URL, isDev ? `models-${i + 1}` : `models-${i + 1}.xml`),
@@ -239,31 +227,6 @@ export class Sitemap {
return flatten(sitmap);
}
async getMcp(page?: number): Promise<MetadataRoute.Sitemap> {
const list = await this.discoverService.getMcpIdentifiers();
if (page !== undefined) {
const startIndex = (page - 1) * ITEMS_PER_PAGE;
const endIndex = startIndex + ITEMS_PER_PAGE;
const pageMcps = list.slice(startIndex, endIndex);
const sitmap = pageMcps.map((item) =>
this._genSitemap(urlJoin('/discover/mcp', item.identifier), {
lastModified: item?.lastModified || LAST_MODIFIED,
}),
);
return flatten(sitmap);
}
// 如果没有指定页数,返回所有(向后兼容)
const sitmap = list.map((item) =>
this._genSitemap(urlJoin('/discover/mcp', item.identifier), {
lastModified: item?.lastModified || LAST_MODIFIED,
}),
);
return flatten(sitmap);
}
async getPlugins(page?: number): Promise<MetadataRoute.Sitemap> {
const list = await this.discoverService.getPluginIdentifiers();
-4
View File
@@ -83,10 +83,6 @@ class DiscoverService {
});
};
getMcpIdentifiers = async (): Promise<IdentifiersResponse> => {
return lambdaClient.market.getMcpIdentifiers.query();
};
getMcpList = async (params: McpQueryParams = {}): Promise<McpListResponse> => {
const locale = globalHelpers.getCurrentLanguage();
return lambdaClient.market.getMcpList.query({
-8
View File
@@ -8,7 +8,6 @@ import { DiscoverStore } from '@/store/discover';
import { globalHelpers } from '@/store/global/helpers';
import {
DiscoverMcpDetail,
IdentifiersResponse,
McpListResponse,
McpQueryParams,
} from '@/types/discover';
@@ -20,7 +19,6 @@ export interface MCPAction {
}) => SWRResponse<DiscoverMcpDetail>;
useFetchMcpList: (params: McpQueryParams) => SWRResponse<McpListResponse>;
useMcpCategories: (params: CategoryListQuery) => SWRResponse<CategoryItem[]>;
useMcpIdentifiers: () => SWRResponse<IdentifiersResponse>;
}
export const createMCPSlice: StateCreator<
@@ -61,10 +59,4 @@ export const createMCPSlice: StateCreator<
},
);
},
useMcpIdentifiers: () => {
return useClientDataSWR('mcp-identifiers', async () => discoverService.getMcpIdentifiers(), {
revalidateOnFocus: false,
});
},
});