feat: support white list for discover assistant (#5216)

*  feat: support white list for discover assistant

* improve error status
This commit is contained in:
Arvin Xu
2024-12-29 01:04:42 +08:00
committed by GitHub
parent 5b0bd683f1
commit 90bb20d333
7 changed files with 95 additions and 30 deletions
@@ -1,6 +1,5 @@
import { NextResponse } from 'next/server';
import { DEFAULT_LANG } from '@/const/locale';
import { AssistantStore } from '@/server/modules/AssistantStore';
export const runtime = 'edge';
@@ -11,18 +10,10 @@ export const GET = async (req: Request) => {
const market = new AssistantStore();
let res: Response;
const data = await market.getAgentIndex(locale as any);
res = await fetch(market.getAgentIndexUrl(locale as any));
if (res.status === 404) {
res = await fetch(market.getAgentIndexUrl(DEFAULT_LANG));
}
const data = await res.json();
return NextResponse.json(data);
} catch (e) {
console.error(e);
} catch {
return new Response(`failed to fetch agent market index`, {
headers: {
'Access-Control-Allow-Origin': '*',
+4
View File
@@ -47,6 +47,8 @@ export const getAppConfig = () => {
PLUGIN_SETTINGS: z.string().optional(),
APP_URL: z.string().optional(),
VERCEL_EDGE_CONFIG: z.string().optional(),
CDN_USE_GLOBAL: z.boolean().optional(),
CUSTOM_FONT_FAMILY: z.string().optional(),
CUSTOM_FONT_URL: z.string().optional(),
@@ -75,6 +77,8 @@ export const getAppConfig = () => {
PLUGIN_SETTINGS: process.env.PLUGIN_SETTINGS,
VERCEL_EDGE_CONFIG: process.env.VERCEL_EDGE_CONFIG,
APP_URL,
CUSTOM_FONT_FAMILY: process.env.CUSTOM_FONT_FAMILY,
CUSTOM_FONT_URL: process.env.CUSTOM_FONT_URL,
@@ -7,19 +7,19 @@ const baseURL = 'https://registry.npmmirror.com/@lobehub/agents-index/v1/files/p
describe('AssistantStore', () => {
it('should return the default index URL when no language is provided', () => {
const agentMarket = new AssistantStore();
const url = agentMarket.getAgentIndexUrl();
const url = agentMarket['getAgentIndexUrl']();
expect(url).toBe(`${baseURL}/index.en-US.json`);
});
it('should return the index URL for a not supported language', () => {
const agentMarket = new AssistantStore();
const url = agentMarket.getAgentIndexUrl('xxx' as any);
const url = agentMarket['getAgentIndexUrl']('xxx' as any);
expect(url).toBe('https://registry.npmmirror.com/@lobehub/agents-index/v1/files/public');
});
it('should return the zh-CN URL for zh locale', () => {
const agentMarket = new AssistantStore();
const url = agentMarket.getAgentIndexUrl('zh' as any);
const url = agentMarket['getAgentIndexUrl']('zh' as any);
expect(url).toBe(
'https://registry.npmmirror.com/@lobehub/agents-index/v1/files/public/index.zh-CN.json',
);
@@ -27,7 +27,7 @@ describe('AssistantStore', () => {
it('should return the default URL for en locale', () => {
const agentMarket = new AssistantStore();
const url = agentMarket.getAgentIndexUrl('en' as any);
const url = agentMarket['getAgentIndexUrl']('en' as any);
expect(url).toBe(
'https://registry.npmmirror.com/@lobehub/agents-index/v1/files/public/index.en-US.json',
);
@@ -35,7 +35,7 @@ describe('AssistantStore', () => {
it('should return the base URL if the provided language is not supported', () => {
const agentMarket = new AssistantStore();
const url = agentMarket.getAgentIndexUrl('fr' as any);
const url = agentMarket['getAgentIndexUrl']('fr' as any);
expect(url).toBe(baseURL);
});
+39 -1
View File
@@ -3,6 +3,8 @@ import urlJoin from 'url-join';
import { appEnv } from '@/config/app';
import { DEFAULT_LANG, isLocaleNotSupport } from '@/const/locale';
import { Locales, normalizeLocale } from '@/locales/resources';
import { EdgeConfig } from '@/server/modules/EdgeConfig';
import { AgentStoreIndex } from '@/types/discover';
export class AssistantStore {
private readonly baseUrl: string;
@@ -11,7 +13,7 @@ export class AssistantStore {
this.baseUrl = baseUrl || appEnv.AGENTS_INDEX_URL;
}
getAgentIndexUrl = (lang: Locales = DEFAULT_LANG) => {
private getAgentIndexUrl = (lang: Locales = DEFAULT_LANG) => {
if (isLocaleNotSupport(lang)) return this.baseUrl;
return urlJoin(this.baseUrl, `index.${normalizeLocale(lang)}.json`);
@@ -22,4 +24,40 @@ export class AssistantStore {
return urlJoin(this.baseUrl, `${identifier}.${normalizeLocale(lang)}.json`);
};
getAgentIndex = async (locale: Locales = DEFAULT_LANG, revalidate?: number) => {
try {
let res: Response;
res = await fetch(this.getAgentIndexUrl(locale as any), { next: { revalidate } });
if (res.status === 404) {
res = await fetch(this.getAgentIndexUrl(DEFAULT_LANG), { next: { revalidate } });
}
if (!res.ok) {
console.error('fetch agent index error:', await res.text());
return [];
}
const data: AgentStoreIndex = await res.json();
// Get the assistant whitelist from Edge Config
const edgeConfig = new EdgeConfig();
if (!!appEnv.VERCEL_EDGE_CONFIG) {
const assistantWhitelist = await edgeConfig.getAgentWhitelist();
if (assistantWhitelist && assistantWhitelist?.length > 0) {
data.agents = data.agents.filter((item) => assistantWhitelist.includes(item.identifier));
}
}
return data;
} catch (e) {
console.error('fetch agent index error:', e);
throw e;
}
};
}
+23
View File
@@ -0,0 +1,23 @@
import { EdgeConfigClient, createClient } from '@vercel/edge-config';
import { appEnv } from '@/config/app';
enum EdgeConfigKeys {
/**
* Assistant whitelist
*/
AssistantWhitelist = 'assistant_whitelist',
}
export class EdgeConfig {
get client(): EdgeConfigClient {
if (!appEnv.VERCEL_EDGE_CONFIG) {
throw new Error('VERCEL_EDGE_CONFIG is not set');
}
return createClient(appEnv.VERCEL_EDGE_CONFIG);
}
getAgentWhitelist = async (): Promise<string[] | undefined> => {
return this.client.get<string[]>(EdgeConfigKeys.AssistantWhitelist);
};
}
+2 -13
View File
@@ -50,20 +50,9 @@ export class DiscoverService {
};
getAssistantList = async (locale: Locales): Promise<DiscoverAssistantItem[]> => {
let res = await fetch(this.assistantStore.getAgentIndexUrl(locale), {
next: { revalidate },
});
if (!res.ok) {
res = await fetch(this.assistantStore.getAgentIndexUrl(DEFAULT_LANG), {
next: { revalidate },
});
}
if (!res.ok) return [];
const json = await res.json();
const json = await this.assistantStore.getAgentIndex(locale, revalidate);
// @ts-expect-error 目前类型不一致,未来要统一
return json.agents;
};
+20
View File
@@ -154,3 +154,23 @@ export interface FilterBy {
token?: number;
vision?: boolean;
}
interface AgentIndexItem {
author: string;
createAt: string;
createdAt: string;
homepage: string;
identifier: string;
meta: {
avatar: string;
category: string;
description: string;
tags: string[];
title: string;
};
}
export interface AgentStoreIndex {
agents: AgentIndexItem[];
schemaVersion: number;
}