Skip to main content
Glama
apps.ts12.2 kB
import { appsDataAccess, recommendationDataAccess } from "@repo/db/database/admin"; import { zAppSourceEnum, zCreateAppSchema, zSearchAppsSchema, zUpdateAppSchema, zPublishStatusEnum, } from "@repo/db/types"; import { z } from "zod"; import { adminProcedure, router } from "../../trpc"; import { getRepository } from "@repo/github"; // 生成slug的辅助函数 function generateSlug(input: string): string { return input .toLowerCase() .replace(/[^a-z0-9]+/g, '-') .replace(/^-+|-+$/g, '') .replace(/[^\w-]+/g, ''); } export const appsRouter = router({ // 创建应用 create: adminProcedure.input(zCreateAppSchema).mutation(async ({ input, ctx }) => { const data = { ...input, source: zAppSourceEnum.enum.admin, publishStatus: zPublishStatusEnum.enum.offline }; return appsDataAccess.create(data, ctx.user.id); }), /** * 从github创建应用。 */ createFromGitHub: adminProcedure.input(z.object({ gitHubURL: z.string().url().startsWith("https://github.com/"), type: z.enum(["client", "server", "application"]), categoryId: z.string().optional(), })).mutation(async ({ input, ctx }) => { try { // 1. 从GitHub URL提取仓库信息 const repoInfo = getRepository(input.gitHubURL); if (!repoInfo || !repoInfo.owner || !repoInfo.name) { throw new Error("无效的GitHub URL"); } // 2. 生成slug const slug = generateSlug(`${repoInfo.owner}-${repoInfo.name}`); // 3. 检查应用是否已存在 const existingApp = await appsDataAccess.getByGitHubURL(input.gitHubURL); if (existingApp) { throw new Error("该GitHub仓库已存在对应的应用"); } console.log("process.env.PROD_CREATE_API_URL", process.env.PROD_CREATE_API_URL); console.log("process.env.PROD_CREATE_API_TOKEN", process.env.PROD_CREATE_API_TOKEN); if (process.env.PROD_CREATE_API_URL && process.env.PROD_CREATE_API_TOKEN) { try { const response = await fetch(process.env.PROD_CREATE_API_URL, { method: "POST", headers: { "Authorization": `Bearer ${process.env.PROD_CREATE_API_TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify({ githubUrl: input.gitHubURL, }), }); if (response.ok) { const data = await response.json(); if (data.success && data.data?.project?.id) { // 8. 创建应用记录 const app = await appsDataAccess.createFromGitHub({ gitHubURL: input.gitHubURL, slug: slug, name: repoInfo.name, description: data.data.project.description || "从GitHub导入的应用", type: input.type, source: "admin", repoId: data.data.project.id, owner: repoInfo.owner, categoryId: input.categoryId, }, ctx.user.id); return app; } } } catch (error) { console.warn("Vercel API请求失败,使用默认项目ID:", error); } } else { throw new Error("Vercel API请求失败"); } } catch (error) { console.error("创建GitHub应用失败:", error); throw new Error(`创建应用失败: ${(error as Error).message}`); } }), // 更新应用 update: adminProcedure.input(zUpdateAppSchema).mutation(async ({ input, ctx }) => { const { id, ...data } = input; // @ts-expect-error return appsDataAccess.update(id, data, ctx.user.id); }), // 获取应用 getById: adminProcedure.input(z.object({ id: z.string() })).query(async ({ input }) => { return appsDataAccess.getById(input.id); }), getByIdWithRelations: adminProcedure.input(z.object({ id: z.string() })).query(async ({ input }) => { return appsDataAccess.getByIdWithRelations(input.id); }), // 搜索应用 search: adminProcedure.input(zSearchAppsSchema).query(async ({ input }) => { return appsDataAccess.search(input); }), searchByRecommendation: adminProcedure.input(z.object({ query: z.string(), recommendationId: z.string(), page: z.coerce.number().optional(), limit: z.coerce.number().optional(), })).query(async ({ input }) => { return appsDataAccess.searchByRecommendationId(input); }), searchByRanking: adminProcedure.input(z.object({ query: z.string(), rankingId: z.string(), page: z.coerce.number().optional(), limit: z.coerce.number().optional(), })).query(async ({ input }) => { return appsDataAccess.searchByRankingId(input); }), // 删除应用 delete: adminProcedure.input(z.object({ id: z.string() })).mutation(async ({ input }) => { return appsDataAccess.delete(input.id); }), // 获取相关应用 getRelatedApps: adminProcedure.input(z.object({ id: z.string() })).query(async ({ input }) => { return appsDataAccess.getRelatedApps(input.id); }), // 获取 RSS 订阅 getRssFeeds: adminProcedure.input(z.object({ id: z.string() })).query(async ({ input }) => { return appsDataAccess.getRssFeeds(input.id); }), // 获取广告 getAds: adminProcedure.input(z.object({ id: z.string() })).query(async ({ input }) => { return appsDataAccess.getAds(input.id); }), // 获取建议 getSuggestions: adminProcedure.input(z.object({ id: z.string() })).query(async ({ input }) => { return appsDataAccess.getSuggestions(input.id); }), // 获取所有权声明 getClaims: adminProcedure.input(z.object({ id: z.string() })).query(async ({ input }) => { return appsDataAccess.getClaims(input.id); }), // 获取所有者 getOwner: adminProcedure.input(z.object({ id: z.string().optional().nullish() })).query(async ({ input }) => { if (!input.id) { return null; } return appsDataAccess.getOwner(input.id); }), // 获取该应用被推荐的列表,即会创建多个recommandation,每个recommendation会有多个应用, getRecommendations: adminProcedure.input(z.object({ id: z.string() })).query(async ({ input }) => { return recommendationDataAccess.getRecommendationsByAppid(input.id); }), // 更新相关应用 updateRelatedApps: adminProcedure .input( z.object({ id: z.string(), relatedAppIds: z.array(z.string()), }) ) .mutation(async ({ input }) => { return appsDataAccess.updateRelatedApps(input.id, input.relatedAppIds); }), // 更新 RSS 订阅 updateRssFeeds: adminProcedure .input( z.object({ id: z.string(), feeds: z.array( z.object({ title: z.string(), feedUrl: z.string(), description: z.string().optional(), }) ), }) ) .mutation(async ({ input }) => { return appsDataAccess.updateRssFeeds(input.id, input.feeds); }), getTags: adminProcedure.input(z.object({ appId: z.string() })).query(async ({ input }) => { return appsDataAccess.getTags(input.appId); }), getCategories: adminProcedure.input(z.object({ appId: z.string() })).query(async ({ input }) => { return appsDataAccess.getCategories(input.appId); }), // 添加标签 addTag: adminProcedure.input(z.object({ appId: z.string(), tagId: z.string().optional(), tagName: z.string() })).mutation(async ({ input }) => { if (input.tagId && input.tagId.trim().length > 0) { //用户选择了指定的tag, return appsDataAccess.addTag(input.appId, input.tagId); } else { const slug = generateSlug(input.tagName); return appsDataAccess.addNewTag(input.appId, input.tagName, slug); } }), // 添加分类 addCategory: adminProcedure.input(z.object({ appId: z.string(), categoryId: z.string() })).mutation(async ({ input }) => { return appsDataAccess.addCategory(input.appId, input.categoryId); }), removeTag: adminProcedure.input(z.object({ appId: z.string(), tagId: z.string() })).mutation(async ({ input }) => { appsDataAccess.removeTag(input.appId, input.tagId); }), removeCategory: adminProcedure.input(z.object({ appId: z.string(), categoryId: z.string() })).mutation(async ({ input }) => { appsDataAccess.removeCategory(input.appId, input.categoryId); }), // 更新标签 updateTags: adminProcedure .input( z.object({ appId: z.string(), tagIds: z.array(z.string()), }) ) .mutation(async ({ input }) => { return appsDataAccess.updateTags(input.appId, input.tagIds); }), // 更新分类 updateCategories: adminProcedure .input( z.object({ appId: z.string(), categoryIds: z.array(z.string()), }) ) .mutation(async ({ input }) => { return appsDataAccess.updateCategories(input.appId, input.categoryIds); }), // 获取提交 getSubmission: adminProcedure.input(z.object({ id: z.string().optional() })).query(async ({ input }) => { if (!input.id) { return null; } return appsDataAccess.getSubmission(input.id); }), // 更新提交 updateSubmission: adminProcedure .input( z.object({ id: z.string(), data: z.object({ status: z.string(), reason: z.string(), }), }) ) .mutation(async ({ input }) => { // return appsDataAccess.updateSubmission(input.id, input.data); }), // 删除提交 deleteSubmission: adminProcedure.input(z.object({ id: z.string() })).mutation(async ({ input }) => { // return appsDataAccess.deleteSubmission(input.id); }), rejectClaim: adminProcedure.input(z.object({ claimId: z.string(), appId: z.string() })).mutation(async ({ input }) => { return appsDataAccess.rejectClaim(input.claimId, input.appId); }), approveClaim: adminProcedure.input(z.object({ claimId: z.string(), appId: z.string() })).mutation(async ({ input }) => { return appsDataAccess.approveClaim(input.claimId, input.appId); }), searchApps: adminProcedure .input( z.object({ query: z.string(), page: z.coerce.number().optional(), limit: z.coerce.number().optional(), }) ) .query(async ({ input }) => { return appsDataAccess.searchApps(input.query, input.page || 1, input.limit || 10); }), // addRelatedApp: adminProcedure .input( z.object({ appId: z.string(), relatedAppId: z.string(), }) ) .mutation(async ({ input }) => { // return appsDataAccess.addRelatedApp(input.appId, input.relatedAppId); }), removeRelatedApp: adminProcedure .input( z.object({ appId: z.string(), relatedAppId: z.string(), }) ) .mutation(async ({ input }) => { // return appsDataAccess.removeRelatedApp(input.appId, input.relatedAppId); }), // 添加推荐 addRecommendation: adminProcedure.input(z.object({ appId: z.string(), recommendedAppId: z.string(), recommendationType: z.string(), })).mutation(async ({ input }) => { // return appsDataAccess.addRecommendation({ appId: input.appId, recommendationId: input.recommendedAppId }); }), removeRecommendation: adminProcedure.input(z.object({ appId: z.string(), recommendedAppId: z.string(), })).mutation(async ({ input }) => { // return appsDataAccess.removeRecommendation(input.recommendedAppId); }), //上线/下线 updatePublishStatus: adminProcedure.input(z.object({ id: z.string(), status: z.enum(["online", "offline"]), })).mutation(async ({ input }) => { return appsDataAccess.updatePublishStatus(input.id, input.status); }), //审核状态 updateStatus: adminProcedure.input(z.object({ id: z.string(), status: z.enum(["pending", "approved", "rejected", "archived"]), })).mutation(async ({ input }) => { return appsDataAccess.updateStatus(input.id, input.status); }), getListByUserId: adminProcedure.input(z.object({ userId: z.string(), })).query(async ({ input }) => { return appsDataAccess.getSubmittedApps(input.userId); }), });

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/metacode0602/open-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server