import type { SocialFeedDashboard, SocialFeedDashboardCreate, SocialFeedDashboardUpdate } from '@nexumi/shared'; import type { SocialFeed } from '@prisma/client'; import { prisma } from '../prisma'; import { assertPremiumLimit, PremiumLimitError } from '../premium'; export class FeedPremiumError extends Error { constructor(public readonly code: 'premium_limit' | 'premium_feature') { super(code); } } function toDashboard(feed: SocialFeed): SocialFeedDashboard { return { id: feed.id, type: feed.type as SocialFeedDashboard['type'], sourceId: feed.sourceId, channelId: feed.channelId, rolePingId: feed.rolePingId ?? '', template: feed.template, enabled: feed.enabled }; } export async function listFeeds(guildId: string): Promise { const feeds = await prisma.socialFeed.findMany({ where: { guildId }, orderBy: { createdAt: 'asc' } }); return feeds.map(toDashboard); } export async function createFeed( guildId: string, input: SocialFeedDashboardCreate ): Promise { await prisma.guild.upsert({ where: { id: guildId }, update: {}, create: { id: guildId } }); const currentCount = await prisma.socialFeed.count({ where: { guildId } }); try { await assertPremiumLimit(prisma, guildId, 'feeds', currentCount); } catch (error) { if (error instanceof PremiumLimitError) { throw new FeedPremiumError(error.code === 'feature_disabled' ? 'premium_feature' : 'premium_limit'); } throw error; } const feed = await prisma.socialFeed.create({ data: { guildId, type: input.type, sourceId: input.sourceId, channelId: input.channelId, rolePingId: input.rolePingId || null, template: input.template ?? null, enabled: input.enabled } }); return toDashboard(feed); } export async function updateFeed( guildId: string, feedId: string, patch: SocialFeedDashboardUpdate ): Promise { const existing = await prisma.socialFeed.findFirst({ where: { id: feedId, guildId } }); if (!existing) { return null; } const { rolePingId, ...rest } = patch; const updated = await prisma.socialFeed.update({ where: { id: feedId }, data: { ...rest, ...(rolePingId !== undefined ? { rolePingId: rolePingId === '' ? null : rolePingId } : {}) } }); return toDashboard(updated); } export async function deleteFeed(guildId: string, feedId: string): Promise { const existing = await prisma.socialFeed.findFirst({ where: { id: feedId, guildId } }); if (!existing) { return false; } await prisma.socialFeed.delete({ where: { id: feedId } }); return true; }