Skip to main content
Glama
mcp-schema.ts40.1 kB
import { createId } from "@paralleldrive/cuid2" import { relations } from "drizzle-orm" import { boolean, index, integer, jsonb, pgTable, primaryKey, real, text, timestamp, uniqueIndex, varchar, } from "drizzle-orm/pg-core"; import { users } from "./auth-schema"; export const rolesEnumArray = ["user", "admin", "member"] as const; // 活动表 - 用户活动记录 export const activities = pgTable("activities", { id: text("id").primaryKey().notNull().$defaultFn(() => createId()), userId: text("user_id") .notNull(), title: varchar("title", { length: 255 }).notNull(), type: varchar("type", { length: 50 }).notNull(), // package, deploy, update, delete resourceId: text("resource_id"), // 相关资源ID resourceType: varchar("resource_type", { length: 50 }), // model, repository, service resourceName: varchar("resource_name", { length: 255 }), status: varchar("status", { length: 50 }).notNull(), // completed, failed, in-progress details: jsonb("details"), summary: varchar("summary"), link: varchar("link", { length: 255 }).notNull(), startTime: timestamp("start_time", { mode: "date" }).defaultNow().notNull(), endTime: timestamp("end_time", { mode: "date" }), duration: varchar("duration", { length: 50 }), createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(), }, (table) => [ index("activities_user_id_idx").on(table.userId), ]); // 应用类型枚举 // 应用表,每个应用只有一个分类,不采用多分类机制, export const apps = pgTable( "apps", { id: text("id") .primaryKey() .notNull() .$defaultFn(() => createId()), slug: varchar("slug", { length: 255 }).notNull().unique(), name: varchar("name", { length: 255 }).notNull(), categoryId: text("category_id"), // 分类Id description: text("description").notNull(), descriptionZh: text("description_zh"), // 从github上拉取的readme中的内容,中文 longDescription: text("long_description"), //概要介绍的内容 readme: text("readme"), // 从github上拉取的readme中的内容 readmeZh: text("readme_zh"), // 从github上拉取的readme中的内容,中文 type: varchar("type", { length: 20, enum: ["client", "server", "application"] }).notNull(), deployable: boolean("deployable").default(false), //是否可以部署为服务 source: varchar("source", { length: 20, enum: ["automatic", "submitted", "admin"] }).notNull(), // automatic, submitted, admin,即自动,提交,管理员录入 status: varchar("status", { length: 20, enum: ["pending", "approved", "rejected", "archived"] }).notNull(), // pending, approved, rejected, archived publishStatus: varchar("publish_status", { length: 20, enum: ["online", "offline"] }).notNull(), // online, offline analysed: boolean("analysed").notNull().default(false), //是否处理过,即是否分析过 icon: text("icon"), banner: varchar("banner", { length: 255 }), pics: text("pics").array(), // 图片列表 website: text("website"), github: text("github"), docs: text("docs"), version: varchar("version", { length: 50 }), license: varchar("license", { length: 255 }), stars: integer("stars").default(0), featured: boolean("featured").notNull().default(false), scenario: varchar("scenario", { length: 50 }), forks: integer("forks").default(0), watchers: integer("watchers").default(0), primaryLanguage: varchar("primary_language", { length: 200 }), // 主要语言 languages: text("languages").array(), // 语言列表 commits: integer("commits").default(0), releases: integer("releases").default(0), issues: integer("issues").default(0), pullRequests: integer("pull_requests").default(0), contributors: integer("contributors").default(0), lastCommit: timestamp("last_commit", { mode: "date" }), supportedServers: text("supported_servers").array(), features: text("features").array(), tools: jsonb("tools"), ownerId: text("owner_id"), // 应用的所有者,即声称应用的人,并且被批准了 userId: text("user_id"), //应用的提交人,即submission 的userid ownerName: varchar("owner_name", { length: 255 }), verified: boolean("verified").default(false), deleted: boolean("deleted").default(false), createdBy: varchar("created_by", { length: 255 }), updatedBy: varchar("updated_by", { length: 255 }), repoCreatedAt: timestamp("repo_created_at", { mode: "date" }), // 仓库创建时间,即仓库在github的创建时间 repoId: text("repo_id"), // 仓库ID,用于关联仓库,如果为空,则表示没有仓库 publishedAt: timestamp("published_at", { mode: "date" }), lastAnalyzedAt: timestamp("last_analyzed_at", { mode: "date" }), // 上次分析时间 createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(), // 创建时间,即添加到数据库的时间,用于记录应用的添加时间 updatedAt: timestamp("updated_at", { mode: "date" }).defaultNow().notNull(), // 更新时间 }, (table) => [index("apps_slug_idx").on(table.slug), index("apps_type_idx").on(table.type), index("apps_owner_id_idx").on(table.ownerId), uniqueIndex("apps_name_type_unique_idx").on(table.name, table.type), uniqueIndex("apps_website_idx").on(table.website), uniqueIndex("apps_github_idx").on(table.github)] ); // 标签表 export const tags = pgTable("tags", { id: text("id").primaryKey().notNull().$defaultFn(() => createId()), name: varchar("name", { length: 255 }).notNull().unique(), source: varchar("source", { length: 20, enum: ["ai", "user", "admin"] }).notNull(), // ai, user, admin description: text("description"), type: varchar("type", { length: 20, enum: ["client", "server", "application", "all"] }).default("all"), // client, server, application totalApps: integer("total_apps").default(0), deleted: boolean("deleted").default(false), slug: varchar("slug", { length: 255 }).notNull().unique(), createdBy: text("created_by"), deletedBy: text("deleted_by"), createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(), updatedAt: timestamp("updated_at", { mode: "date" }).defaultNow().notNull(), }, (table) => [ index("tags_slug_idx").on(table.slug), uniqueIndex("tags_name_source_unique_idx").on(table.name), ]); // 应用标签关联表 export const appTags = pgTable("app_tags", { id: text("id").primaryKey().notNull().$defaultFn(() => createId()), appId: text("app_id").notNull(), tagId: text("tag_id").notNull(), createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(), }, (table) => [ index("app_tags_app_id_idx").on(table.appId), index("app_tags_tag_id_idx").on(table.tagId), uniqueIndex("app_tags_unique_idx").on(table.appId, table.tagId), ]); // 分类表 export const categories = pgTable("categories", { id: text("id").primaryKey().notNull().$defaultFn(() => createId()), parentId: text("parent_id"), name: varchar("name", { length: 255 }).notNull().unique(), description: text("description"), slug: varchar("slug", { length: 255 }).notNull().unique(), appsCount: integer("apps_count").default(0), icon: text("icon"), status: varchar("status", { length: 20, enum: ["offline", "online"] }).notNull().default("offline"), // offline, online deleted: boolean("deleted").default(false), createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(), updatedAt: timestamp("updated_at", { mode: "date" }).defaultNow().notNull(), }, (table) => [ index("categories_slug_idx").on(table.slug), ]); // 应用分类关联表 export const appCategories = pgTable("app_categories", { id: text("id").primaryKey().notNull().$defaultFn(() => createId()), appId: text("app_id").notNull(), categoryId: text("category_id").notNull(), createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(), }, (table) => [ index("app_categories_app_id_idx").on(table.appId), index("app_categories_category_id_idx").on(table.categoryId), uniqueIndex("app_categories_unique_idx").on(table.appId, table.categoryId), ]); // 建议表 export const suggestions = pgTable( "suggestions", { id: text("id") .primaryKey() .notNull() .$defaultFn(() => createId()), appId: text("app_id").notNull(), appName: varchar("app_name", { length: 255 }).notNull(), appSlug: varchar("app_slug", { length: 255 }).notNull(), appType: varchar("app_type", { length: 20, enum: ["client", "server", "application"] }).notNull(), userId: text("user_id") .notNull(), userName: varchar("user_name", { length: 255 }).notNull(), userEmail: varchar("user_email", { length: 255 }).notNull(), title: varchar("title", { length: 255 }).notNull(), description: text("description").notNull(), type: varchar("type", { length: 20, enum: ["feature", "bug", "improvement", "other"] }).notNull(), status: varchar("status", { length: 20, enum: ["pending", "reviewing", "accepted", "implemented", "rejected", "duplicate"] }) .notNull() .default("pending"), upvotes: integer("upvotes").default(0), priority: varchar("priority", { length: 20 }), reproducible: boolean("reproducible"), stepsToReproduce: text("steps_to_reproduce"), expectedBehavior: text("expected_behavior"), actualBehavior: text("actual_behavior"), attachmentUrl: text("attachment_url"), responseText: text("response_text"), responseUserId: text("response_user_id"), responseUserName: varchar("response_user_name", { length: 255 }), responseAt: timestamp("response_at", { mode: "date" }), implementedAt: timestamp("implemented_at", { mode: "date" }), imageUrl: text("image_url"), adminRemarks: text("admin_remarks"), createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(), updatedAt: timestamp("updated_at", { mode: "date" }).defaultNow().notNull(), }, (table) => [index("suggestions_app_id_idx").on(table.appId), index("suggestions_user_id_idx").on(table.userId), index("suggestions_status_idx").on(table.status)] ); // 所有权请求状态枚举 // 所有权请求表 export const claims = pgTable( "claims", { id: text("id") .primaryKey() .notNull() .$defaultFn(() => createId()), appId: text("app_id") .notNull(), userId: text("user_id") .notNull(), userName: varchar("user_name", { length: 255 }).notNull(), title: varchar("title", { length: 255 }), userEmail: varchar("user_email", { length: 255 }).notNull(), appName: varchar("app_name", { length: 255 }).notNull(), appSlug: varchar("app_slug", { length: 255 }).notNull(), reason: text("reason"), appType: varchar("app_type", { length: 20, enum: ["client", "server", "application"] }).notNull(), appIcon: text("app_icon"), proofUrl: text("proof_url").notNull(), proofType: varchar("proof_type", { length: 20 }).notNull(), additionalInfo: text("additional_info"), status: varchar("status", { length: 20, enum: ["pending", "approved", "rejected"] }).notNull().default("pending"), reviewedAt: timestamp("reviewed_at", { mode: "date" }), reviewedBy: text("reviewed_by"), updatedBy: text("updated_by"), reviewNotes: text("review_notes"), createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(), updatedAt: timestamp("updated_at", { mode: "date" }).defaultNow().notNull(), }, (table) => [index("ownership_requests_app_id_idx").on(table.appId), index("ownership_requests_user_id_idx").on(table.userId), index("ownership_requests_status_idx").on(table.status)] ); // 广告表 export const ads = pgTable("ads", { id: text("id").primaryKey().notNull().$defaultFn(() => createId()), userId: text("user_id").notNull(), type: varchar("type", { length: 20, enum: ["listing", "banner"] }).notNull(), title: varchar("title", { length: 255 }).notNull(), description: text("description").notNull(), url: text("url").notNull(), imageUrl: text("image_url"), placement: varchar("placement", { enum: ["top", "middle", "bottom"] }).notNull().default("bottom"), startDate: timestamp("start_date", { mode: "date" }).notNull(), endDate: timestamp("end_date", { mode: "date" }).notNull(), status: varchar("status", { length: 20, enum: ["pending", "active", "completed", "rejected", "paused"] }).notNull().default("pending"), price: real("price").notNull().default(0), budget: real("budget").notNull(), spent: real("spent").notNull().default(0), cpc: real("cpc").notNull().default(0), impressions: integer("impressions").default(0), clicks: integer("clicks").default(0), ctr: real("ctr").default(0), appId: text("app_id"), approvedAt: timestamp("approved_at", { mode: "date" }), approvedBy: text("approved_by"), rejectionReason: text("rejection_reason"), createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(), updatedAt: timestamp("updated_at", { mode: "date" }).defaultNow().notNull(), }, (table) => [ index("ads_user_id_idx").on(table.userId), index("ads_status_idx").on(table.status), index("ads_dates_idx").on(table.startDate, table.endDate), ]); // 支付表 export const payments = pgTable( "payments", { id: text("id") .primaryKey() .notNull() .$defaultFn(() => createId()), userId: text("user_id") .notNull(), type: varchar("type", { length: 20, enum: ["ad", "subscription", "service", "other"] }).notNull(), relatedId: text("related_id"), amount: real("amount").notNull(), currency: varchar("currency", { length: 10 }).default("CNY"), method: varchar("method", { length: 20, enum: ["wechat", "alipay", "bank_transfer"] }).notNull(), status: varchar("status", { length: 20, enum: ["pending", "completed", "failed", "refunded"] }).notNull().default("pending"), transactionId: text("transaction_id"), invoiceNumber: text("invoice_number"), completedAt: timestamp("completed_at", { mode: "date" }), refundedAt: timestamp("refunded_at", { mode: "date" }), refundReason: text("refund_reason"), metadata: jsonb("metadata"), createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(), updatedAt: timestamp("updated_at", { mode: "date" }).defaultNow().notNull(), }, (table) => [index("payments_user_id_idx").on(table.userId), index("payments_status_idx").on(table.status), index("payments_type_idx").on(table.type)] ); // 发票表 export const invoices = pgTable("invoices", { id: text("id").primaryKey().notNull().$defaultFn(() => createId()), userId: text("user_id").notNull(), paymentId: text("payment_id").notNull(), type: varchar("type", { length: 20, enum: ["personal", "company"] }).notNull(), title: varchar("title", { length: 255 }).notNull(), taxNumber: varchar("tax_number", { length: 255 }), address: text("address"), email: varchar("email", { length: 255 }).notNull(), status: varchar("status", { length: 20, enum: ["pending", "issued", "sent"] }).notNull().default("pending"), issuedAt: timestamp("issued_at", { mode: "date" }), sentAt: timestamp("sent_at", { mode: "date" }), createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(), updatedAt: timestamp("updated_at", { mode: "date" }).defaultNow().notNull(), }, (table) => [ index("invoices_user_id_idx").on(table.userId), index("invoices_payment_id_idx").on(table.paymentId), index("invoices_status_idx").on(table.status), ]); export const tagsRelations = relations(tags, ({ many }) => ({ appTags: many(appTags), })) export const categoriesRelations = relations(categories, ({ many, one }) => ({ appCategories: many(appCategories), parent: one(categories, { fields: [categories.parentId], references: [categories.id], }), })) export const appTagsRelations = relations(appTags, ({ one }) => ({ app: one(apps, { fields: [appTags.appId], references: [apps.id], }), tag: one(tags, { fields: [appTags.tagId], references: [tags.id], }), })) export const appCategoriesRelations = relations(appCategories, ({ one }) => ({ app: one(apps, { fields: [appCategories.appId], references: [apps.id], }), category: one(categories, { fields: [appCategories.categoryId], references: [categories.id], }), })) export const suggestionsRelations = relations(suggestions, ({ one }) => ({ app: one(apps, { fields: [suggestions.appId], references: [apps.id], }), submitter: one(users, { fields: [suggestions.userId], references: [users.id], }), responseUser: one(users, { fields: [suggestions.responseUserId], references: [users.id], }), })); export const claimsRelations = relations(claims, ({ one }) => ({ app: one(apps, { fields: [claims.appId], references: [apps.id], }), user: one(users, { fields: [claims.userId], references: [users.id], }), reviewer: one(users, { fields: [claims.reviewedBy], references: [users.id], }), })) export const adsRelations = relations(ads, ({ one }) => ({ user: one(users, { fields: [ads.userId], references: [users.id], }), approver: one(users, { fields: [ads.approvedBy], references: [users.id], }), app: one(apps, { fields: [ads.appId], references: [apps.id], }), })) export const paymentsRelations = relations(payments, ({ one, many }) => ({ user: one(users, { fields: [payments.userId], references: [users.id], }), invoices: many(invoices), })) export const invoicesRelations = relations(invoices, ({ one }) => ({ user: one(users, { fields: [invoices.userId], references: [users.id], }), payment: one(payments, { fields: [invoices.paymentId], references: [payments.id], }), })) export const activitiesRelations = relations(activities, ({ one }) => ({ user: one(users, { fields: [activities.userId], references: [users.id], }), })) // 推荐应用表,首页展示时使用,即推荐应用,也可以为每个分类设置推荐应用或者排行 // 分类推荐:category,即在分类列表中显示的体检列表 // rank:是排行推荐,即各种日/周/月等的排行或者product hunt排行等等 // app相关的推荐不在这里维护,在relatedApp表中维护 export const recommendations = pgTable("recommendations", { id: text("id").primaryKey().notNull().$defaultFn(() => createId()), categoryId: text("category_id"), // 如果type === "category",则此值肯定不为空。 appId: text("app_id"), // 如果type === "app",则此值不能为空 type: varchar("type", { length: 20, enum: ["rank", "popular", "new", "related", "category"] }).notNull(), title: varchar("title", { length: 255 }).notNull(), appCount: integer("app_count").notNull().default(0), status: varchar("status", { length: 20, enum: ["pending", "active"] }).notNull().default("pending"), description: text("description"), createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(), updatedAt: timestamp("updated_at", { mode: "date" }).defaultNow().notNull(), }, (table) => [ index("recommendations_type_idx").on(table.type), ]); // 分类推荐应用关联表 export const recommendationApps = pgTable("recommendation_apps", { id: text("id").primaryKey().notNull().$defaultFn(() => createId()), recommendationId: text("recommendation_id").notNull(), // 推荐ID appId: text("app_id").notNull(), // 应用ID order: integer("order").notNull().default(0), // 排序 status: varchar("status", { length: 20, enum: ["pending", "active"] }).notNull().default("pending"), createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(), }, (table) => [ index("recommendation_apps_app_id_idx").on(table.appId), uniqueIndex("recommendation_apps_unique_idx").on(table.recommendationId, table.appId), ]); // 相关应用表,即应用之间的关系表,在详情页中使用和展示 // 其他分类,排行等等推荐信息在recommendation表中维护 export const relatedApps = pgTable("related_apps", { id: text("id").primaryKey().notNull().$defaultFn(() => createId()), appId: text("app_id").notNull(), type: varchar("type", { length: 20, enum: ["similar", "related"] }).default("related").notNull(), relatedAppId: text("related_app_id").notNull(), similarity: real("similarity").default(0), // 相似度分数 createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(), }, (table) => [ index("related_apps_app_id_idx").on(table.appId), index("related_apps_related_app_id_idx").on(table.relatedAppId), uniqueIndex("related_apps_unique_idx").on(table.appId, table.relatedAppId), ]); // 应用分析历史记录表 export const appAnalysisHistory = pgTable("app_analysis_history", { id: text("id").primaryKey().notNull().$defaultFn(() => createId()), //此即为jobId type: varchar("type", { length: 20, enum: ["website", "github"] }).notNull(), appId: text("app_id").notNull(), url: text("url").notNull(), // 分析的url version: varchar("version", { length: 50 }), analysisResult: jsonb("analysis_result").notNull(), // 分析结果 features: text("features").array(), // 提取的特征 tools: jsonb("tools"), // 提取的工具 readme: text("readme"), // 提取的readme,优先取中文 tags: text("tags").array(), // 提取的标签 categories: text("categories").array(), // 提取的分类 status: varchar("status", { length: 50, enum: ["pending", "in_progress", "completed", "stopped", "failed"] }).notNull().default("pending"), // 分析状态 message: text("message"), // 分析消息 error: text("error"), // 错误信息 startTime: timestamp("start_time", { mode: "date" }).defaultNow().notNull(), endTime: timestamp("end_time", { mode: "date" }), createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(), }, (table) => [ index("app_analysis_history_app_id_idx").on(table.appId), index("app_analysis_history_status_idx").on(table.status), ]); // 用户邮件订阅表 export const emailSubscriptions = pgTable("email_subscriptions", { id: text("id").primaryKey().notNull().$defaultFn(() => createId()), userId: text("user_id"), email: varchar("email", { length: 255 }).notNull().unique(), name: varchar("name", { length: 255 }), // 用户姓名 referringSite: varchar("referring_site", { length: 255 }), // 来源网站,头信息中获取 utmSource: varchar("utm_source", { length: 255 }).default("openmcp"), utmMedium: varchar("utm_medium").default("subscribe_form"), utmCampaign: varchar("utm_campaign").default("organic"), preferences: jsonb("preferences").default({}), // 订阅偏好 isVerified: boolean("is_verified").default(false), verificationToken: text("verification_token"), verificationExpires: timestamp("verification_expires", { mode: "date" }), lastEmailSent: timestamp("last_email_sent", { mode: "date" }), createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(), updatedAt: timestamp("updated_at", { mode: "date" }).defaultNow().notNull(), }, (table) => [ index("email_subscriptions_user_id_idx").on(table.userId), index("email_subscriptions_email_idx").on(table.email), ]); // 应用RSS表 export const appRss = pgTable("app_rss", { id: text("id").primaryKey().notNull().$defaultFn(() => createId()), appId: text("app_id").notNull(), title: varchar("title", { length: 255 }).notNull(), description: text("description"), feedUrl: text("feed_url").notNull(), lastFetched: timestamp("last_fetched", { mode: "date" }), lastUpdated: timestamp("last_updated", { mode: "date" }), isActive: boolean("is_active").default(true), createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(), updatedAt: timestamp("updated_at", { mode: "date" }).defaultNow().notNull(), }, (table) => [ index("app_rss_app_id_idx").on(table.appId), uniqueIndex("app_rss_feed_url_idx").on(table.feedUrl), ]); // RSS条目表 export const rssItems = pgTable("rss_items", { id: text("id").primaryKey().notNull().$defaultFn(() => createId()), rssId: text("rss_id").notNull(), title: varchar("title", { length: 255 }).notNull(), description: text("description"), content: text("content"), link: text("link").notNull(), guid: text("guid").notNull(), pubDate: timestamp("pub_date", { mode: "date" }).notNull(), createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(), }, (table) => [ index("rss_items_rss_id_idx").on(table.rssId), index("rss_items_pub_date_idx").on(table.pubDate), uniqueIndex("rss_items_guid_idx").on(table.guid), ]); // 应用提交状态枚举 // 用户应用提交表 export const appSubmissions = pgTable("app_submissions", { id: text("id").primaryKey().notNull().$defaultFn(() => createId()), userId: text("user_id").notNull(), name: varchar("name", { length: 255 }).notNull(), description: text("description").notNull(), longDescription: text("long_description"), // 长描述 在抓取之后更新 type: varchar("type", { length: 20, enum: ["client", "server", "application"] }).notNull(), website: text("website"), github: text("github"), docs: text("docs"), // readme文档,在抓取之后更新 favicon: text("favicon"), //应用图标 logo: text("logo"), //应用logo appId: text("app_id"), // 应用Id,认证通过之后,会生成appid version: varchar("version", { length: 50 }), // 版本号 在抓取之后更新 license: varchar("license", { length: 255 }), // 许可证 在抓取之后更新 scenario: varchar("scenario", { length: 50 }), // 应用场景 在抓取之后更新 features: text("features").array(), // 应用特点 在抓取之后更新 tags: text("tags").array(), // 应用标签 v在抓取之后更新 status: varchar("status", { length: 20, enum: ["pending", "approved", "rejected", "archived"] }).notNull().default("pending"), rejectionReason: text("rejection_reason"), reviewedAt: timestamp("reviewed_at", { mode: "date" }), reviewedBy: text("reviewed_by"), approvedAppId: text("approved_app_id"), approvedAt: timestamp("approved_at", { mode: "date" }), approvedBy: text("approved_by"), createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(), updatedAt: timestamp("updated_at", { mode: "date" }).defaultNow().notNull(), }, (table) => [ index("app_submissions_user_id_idx").on(table.userId), index("app_submissions_status_idx").on(table.status), index("app_submissions_reviewed_by_idx").on(table.reviewedBy), index("app_submissions_approved_app_id_idx").on(table.approvedAppId), ]); // 添加关系定义 export const recommendationsRelations = relations(recommendations, ({ one, many }) => ({ category: one(categories, { fields: [recommendations.categoryId], references: [categories.id], }), apps: many(recommendationApps), })); export const recommendationAppsRelations = relations(recommendationApps, ({ one }) => ({ app: one(apps, { fields: [recommendationApps.appId], references: [apps.id], }), recommendation: one(recommendations, { fields: [recommendationApps.recommendationId], references: [recommendations.id], }), })); export const relatedAppsRelations = relations(relatedApps, ({ one }) => ({ app: one(apps, { fields: [relatedApps.appId], references: [apps.id], }), relatedApp: one(apps, { fields: [relatedApps.relatedAppId], references: [apps.id], }), })); export const appAnalysisHistoryRelations = relations(appAnalysisHistory, ({ one }) => ({ app: one(apps, { fields: [appAnalysisHistory.appId], references: [apps.id], }), })); export const emailSubscriptionsRelations = relations(emailSubscriptions, ({ one }) => ({ user: one(users, { fields: [emailSubscriptions.userId], references: [users.id], }), })); export const appRssRelations = relations(appRss, ({ one, many }) => ({ app: one(apps, { fields: [appRss.appId], references: [apps.id], }), items: many(rssItems), })); export const rssItemsRelations = relations(rssItems, ({ one }) => ({ rss: one(appRss, { fields: [rssItems.rssId], references: [appRss.id], }), })); export const appSubmissionsRelations = relations(appSubmissions, ({ one }) => ({ user: one(users, { fields: [appSubmissions.userId], references: [users.id], }), reviewer: one(users, { fields: [appSubmissions.reviewedBy], references: [users.id], }), approvedApp: one(apps, { fields: [appSubmissions.approvedAppId], references: [apps.id], }), })); // 更新应用关系 export const appsRelations = relations(apps, ({ many, one }) => ({ tags: many(appTags), categories: many(appCategories), suggestions: many(suggestions), claims: many(claims), category: one(categories, { fields: [apps.categoryId], references: [categories.id], }), owner: one(users, { fields: [apps.ownerId], references: [users.id], }), relatedApps: many(relatedApps, { relationName: "app" }), relatedToApps: many(relatedApps, { relationName: "relatedApp" }), analysisHistory: many(appAnalysisHistory), rss: many(appRss), approvedSubmissions: many(appSubmissions, { relationName: "approvedApp" }), })) // 资产表 export const assets = pgTable("assets", { id: text("id").primaryKey().notNull().$defaultFn(() => createId()), userId: text("user_id").notNull(), assetType: varchar("asset_type", { length: 20, enum: ["unknown", "avatar", "banner", "icon", "logo", "image", "document"] }).notNull(), contentType: varchar("content_type", { length: 255 }).notNull(), fileName: varchar("file_name", { length: 255 }).notNull(), size: integer("size").notNull(), path: text("path").notNull(), url: text("url"), bookmarkId: text("bookmark_id"), createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(), updatedAt: timestamp("updated_at", { mode: "date" }).defaultNow().notNull(), }, (table) => [ index("assets_user_id_idx").on(table.userId), index("assets_asset_type_idx").on(table.assetType), index("assets_bookmark_id_idx").on(table.bookmarkId), ]); // 资产关系 export const assetsRelations = relations(assets, ({ one }) => ({ user: one(users, { fields: [assets.userId], references: [users.id], }), })); /** * 排行表,用于存储排行数据。 */ export const rankings = pgTable("rankings", { id: text("id").primaryKey().notNull().$defaultFn(() => createId()), name: varchar("name", { length: 100 }).notNull(), source: varchar("source", { length: 100, enum: ["github", "openmcp", "producthunt"] }).notNull(), description: text("description"), recordsCount: integer("records_count").default(0), type: varchar("type", { length: 50, enum: ["daily", "weekly", "monthly", "yearly"] }).notNull(), status: boolean("status").default(true), periodKey: varchar("period_key", { length: 20 }).notNull(), createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(), updatedAt: timestamp("updated_at", { mode: "date" }).defaultNow(), }, (table) => [ uniqueIndex("unique_source_type_period_key").on(table.source, table.type, table.periodKey), ]); /** * 排行记录表,用于存储排行记录数据。 */ export const rankingRecords = pgTable("ranking_records", { id: text("id").primaryKey().notNull().$defaultFn(() => createId()), rankingId: text("ranking_id").references(() => rankings.id), entityId: text("entity_id").notNull(), entityName: varchar("entity_name", { length: 255 }).notNull(), entityType: varchar("entity_type", { length: 50, enum: ["apps"] }).notNull(), score: integer("score").notNull(), // Drizzle 不支持 DECIMAL,可以用 integer 或 float rank: integer("rank").notNull().default(0), createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(), updatedAt: timestamp("updated_at", { mode: "date" }).defaultNow(), }, (table) => [ uniqueIndex("unique_ranking_records_key").on(table.rankingId, table.entityId, table.entityType), ]); // 排行-应用关系 export const rankingRecordsRelations = relations(rankingRecords, ({ one }) => ({ app: one(apps, { fields: [rankingRecords.entityId], references: [apps.id], }), })); /** * 仓库快照表,用于存储仓库的快照数据。按照年月日存储。即存储的是每天的快照数据。 */ export const snapshots = pgTable( "snapshots", { id: text("id").primaryKey().notNull().$defaultFn(() => createId()), repoId: text("repo_id").notNull(), createdAt: timestamp("created_at").notNull().defaultNow(), updatedAt: timestamp("updated_at"), year: integer("year").notNull(), months: jsonb("months"), day: integer("day").notNull(), month: integer("month").notNull(), forks: integer("forks"), stars: integer("stars"), watchers: integer("watchers"), openIssues: integer("open_issues"), subscribers: integer("subscribers"), contributors: integer("contributors"), pullRequests: integer("pull_requests"), releases: integer("releases"), commits: integer("commits"), }, (table) => [ index("snapshots_repo_id_idx").on(table.repoId), index("snapshots_year_idx").on(table.year), ] ); /* * 按月存储的快照数据,用于存储仓库的快照数据。 */ export const snapshotsMonthly = pgTable( "snapshots_monthly", { id: text("id").primaryKey().notNull().$defaultFn(() => createId()), repoId: text("repo_id").notNull(), createdAt: timestamp("created_at").notNull().defaultNow(), updatedAt: timestamp("updated_at"), year: integer("year").notNull(), month: integer("month").notNull(), forks: integer("forks"), stars: integer("stars"), watchers: integer("watchers"), openIssues: integer("open_issues"), subscribers: integer("subscribers"), contributors: integer("contributors"), pullRequests: integer("pull_requests"), releases: integer("releases"), commits: integer("commits"), }, (table) => [ index("snapshots_monthly_repo_id_idx").on(table.repoId), index("snapshots_monthly_year_idx").on(table.year), index("snapshots_monthly_month_idx").on(table.month), uniqueIndex("snapshots_monthly_unique_idx").on(table.repoId, table.year, table.month), ] ); /* * 按周存储的快照数据,用于存储仓库的快照数据。 */ export const snapshotsWeekly = pgTable( "snapshots_weekly", { id: text("id").primaryKey().notNull().$defaultFn(() => createId()), repoId: text("repo_id").notNull(), createdAt: timestamp("created_at").notNull().defaultNow(), updatedAt: timestamp("updated_at"), year: integer("year").notNull(), week: integer("week").notNull(), forks: integer("forks"), stars: integer("stars"), watchers: integer("watchers"), openIssues: integer("open_issues"), subscribers: integer("subscribers"), contributors: integer("contributors"), pullRequests: integer("pull_requests"), releases: integer("releases"), commits: integer("commits"), }, (table) => [ index("snapshots_weekly_repo_id_idx").on(table.repoId), index("snapshots_weekly_year_idx").on(table.year), index("snapshots_weekly_week_idx").on(table.week), uniqueIndex("snapshots_weekly_unique_idx").on(table.repoId, table.year, table.week), ] ); export const snapshotsRelations = relations(snapshots, ({ one }) => ({ repo: one(repos, { fields: [snapshots.repoId], references: [repos.id], }), })); export const snapshotsWeeklyRelations = relations(snapshotsWeekly, ({ one }) => ({ repo: one(repos, { fields: [snapshotsWeekly.repoId], references: [repos.id], }), })); export const snapshotsMonthlyRelations = relations(snapshotsMonthly, ({ one }) => ({ repo: one(repos, { fields: [snapshotsMonthly.repoId], references: [repos.id], }), })); /** * github代码仓库表,用于存储github代码仓库的基本信息。 */ export const repos = pgTable( "repos", { id: text("id").primaryKey(), // Date of addition to Best of JS added_at: timestamp("added_at").notNull().defaultNow(), // Last update (by the daily task) updated_at: timestamp("updated_at"), // From GitHub REST API archived: boolean("archived"), default_branch: text("default_branch"), description: text("description"), homepage: text("homepage"), name: text("name").notNull(), fullName: text("full_name").notNull(), owner: text("owner").notNull(), owner_id: integer("owner_id").notNull(), // used in GitHub users avatar URLs stars: integer("stargazers_count"), topics: jsonb("topics"), pushed_at: timestamp("pushed_at").notNull(), created_at: timestamp("created_at").notNull(), // From GitHub GraphQL API last_commit: timestamp("last_commit"), commit_count: integer("commit_count"), // From scrapping contributor_count: integer("contributor_count"), // New fields from GitHub GraphQL API mentionable_users_count: integer("mentionable_users_count"), watchers_count: integer("watchers_count"), license_spdx_id: text("license_spdx_id"), pull_requests_count: integer("pull_requests_count"), releases_count: integer("releases_count"), languages: jsonb("languages"), open_graph_image_url: text("open_graph_image_url"), uses_custom_open_graph_image: boolean("uses_custom_open_graph_image"), latest_release_name: text("latest_release_name"), latest_release_tag_name: text("latest_release_tag_name"), latest_release_published_at: timestamp("latest_release_published_at"), latest_release_url: text("latest_release_url"), latest_release_description: text("latest_release_description"), forks: integer("forks"), // 新增字段:README内容和翻译 readme_content: text("readme_content"), // 英文README内容 readme_content_zh: text("readme_content_zh"), // 中文README内容 description_zh: text("description_zh"), // 翻译后的描述 icon_url: text("icon_url"), // 上传到OSS的图标URL open_graph_image_oss_url: text("open_graph_image_oss_url"), // 上传到OSS的Open Graph图片URL latest_release_description_zh: text("latest_release_description_zh"), // 翻译后的Release Note内容 }, (table) => [uniqueIndex("name_owner_index").on(table.owner, table.name)] ); /** * 仓库关系表,用于存储仓库和名人堂的关系。 */ export const reposRelations = relations(repos, ({ many, one }) => ({ apps: many(apps), snapshots: many(snapshots), snapshotsWeekly: many(snapshotsWeekly), snapshotsMonthly: many(snapshotsMonthly), hallOfFameMember: many(hallOfFameToRepos), })); /** * 名人堂表,用于存储名人堂的基本信息。 */ export const hallOfFame = pgTable("hall_of_fame", { username: text("username").primaryKey(), name: text("name").notNull(), createdAt: timestamp("created_at").notNull().defaultNow(), updatedAt: timestamp("updated_at"), followers: integer("followers"), bio: text("bio"), homepage: text("homepage"), twitter: text("twitter"), github: text("github"), discord: text("discord"), avatar: text("avatar"), npmUsername: text("npm_username"), npmPackageCount: integer("npm_package_count"), status: text("status", { enum: ["active", "inactive", "archived"] }) .default("active") .notNull(), }); /** * 名人堂-仓库关系表,用于存储名人堂和仓库的关系。 */ export const hallOfFameToRepos = pgTable( "hall_of_fame_to_repos", { username: text("username") .notNull() .references(() => hallOfFame.username, { onDelete: "cascade" }), repoId: text("repo_id") .notNull() .references(() => repos.id, { onDelete: "cascade" }), }, (t) => [primaryKey({ columns: [t.username, t.repoId] })] ); /** * 名人堂关系表,用于存储名人堂和仓库的关系。 */ export const hallOfFameRelations = relations(hallOfFame, ({ many }) => ({ hallOfFameToRepos: many(hallOfFameToRepos), repos: many(repos), }));

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