Skip to main content
Glama

MCP Social Network

by GrahamMcBain
database.ts9.89 kB
import { createClient } from '@supabase/supabase-js'; export interface User { id: string; username: string; bio: string | null; created_at: string; follower_count: number; following_count: number; post_count: number; } export interface Post { id: string; user_id: string; content: string; code: string | null; language: string | null; tags: string[] | null; like_count: number; reply_count: number; created_at: string; username?: string; // joined from users table } export interface Follow { follower_id: string; following_id: string; created_at: string; } export interface Like { user_id: string; post_id: string; created_at: string; } export interface Reply { id: string; post_id: string; user_id: string; content: string; created_at: string; username?: string; // joined from users table } export interface Notification { id: string; user_id: string; type: 'follow' | 'mention' | 'reply' | 'like'; from_user_id: string; post_id: string | null; content: string | null; read: boolean; created_at: string; from_username?: string; // joined from users table } export class Database { private supabase; constructor(url?: string, key?: string) { if (!url || !key) { throw new Error('Supabase URL and key are required. Set SUPABASE_URL and SUPABASE_KEY environment variables.'); } this.supabase = createClient(url, key); } // User operations async createUser(username: string, bio?: string, passwordHash?: string): Promise<User> { const { data, error } = await this.supabase .from('users') .insert({ username, bio, password_hash: passwordHash }) .select() .single(); if (error) { if (error.code === '23505') { // unique constraint violation throw new Error(`Username "${username}" is already taken`); } throw new Error(`Failed to create user: ${error.message}`); } return data; } async getUserByCredentials(username: string): Promise<{ id: string; username: string; password_hash: string } | null> { const { data, error } = await this.supabase .from('users') .select('id, username, password_hash') .eq('username', username) .single(); if (error) { if (error.code === 'PGRST116') { // no rows returned return null; } throw new Error(`Failed to get user credentials: ${error.message}`); } return data; } async getUser(username: string): Promise<User | null> { const { data, error } = await this.supabase .from('users') .select('*') .eq('username', username) .single(); if (error) { if (error.code === 'PGRST116') { // no rows returned return null; } throw new Error(`Failed to get user: ${error.message}`); } return data; } async updateUser(username: string, bio: string): Promise<User> { const { data, error } = await this.supabase .from('users') .update({ bio }) .eq('username', username) .select() .single(); if (error) { throw new Error(`Failed to update user: ${error.message}`); } return data; } async searchUsers(query: string, limit: number = 10): Promise<User[]> { const { data, error } = await this.supabase .from('users') .select('*') .ilike('username', `%${query}%`) .limit(limit) .order('created_at', { ascending: false }); if (error) { throw new Error(`Failed to search users: ${error.message}`); } return data || []; } // Post operations async createPost(userId: string, content: string, code?: string, language?: string, tags?: string[]): Promise<Post> { const { data, error } = await this.supabase .from('posts') .insert({ user_id: userId, content, code, language, tags }) .select(` *, users!posts_user_id_fkey(username) `) .single(); if (error) { throw new Error(`Failed to create post: ${error.message}`); } return { ...data, username: data.users.username }; } async getPost(postId: string): Promise<Post | null> { const { data, error } = await this.supabase .from('posts') .select(` *, users!posts_user_id_fkey(username) `) .eq('id', postId) .single(); if (error) { if (error.code === 'PGRST116') { return null; } throw new Error(`Failed to get post: ${error.message}`); } return { ...data, username: data.users.username }; } async getUserPosts(userId: string, limit: number = 20): Promise<Post[]> { const { data, error } = await this.supabase .from('posts') .select(` *, users!posts_user_id_fkey(username) `) .eq('user_id', userId) .order('created_at', { ascending: false }) .limit(limit); if (error) { throw new Error(`Failed to get user posts: ${error.message}`); } return (data || []).map(post => ({ ...post, username: post.users.username })); } async getGlobalFeed(limit: number = 20): Promise<Post[]> { const { data, error } = await this.supabase .from('posts') .select(` *, users!posts_user_id_fkey(username) `) .order('created_at', { ascending: false }) .limit(limit); if (error) { throw new Error(`Failed to get global feed: ${error.message}`); } return (data || []).map(post => ({ ...post, username: post.users.username })); } // Follow operations async followUser(followerId: string, followingId: string): Promise<void> { if (followerId === followingId) { throw new Error('Cannot follow yourself'); } const { error } = await this.supabase .from('follows') .insert({ follower_id: followerId, following_id: followingId }); if (error) { if (error.code === '23505') { // unique constraint violation throw new Error('Already following this user'); } throw new Error(`Failed to follow user: ${error.message}`); } } async unfollowUser(followerId: string, followingId: string): Promise<void> { const { error } = await this.supabase .from('follows') .delete() .eq('follower_id', followerId) .eq('following_id', followingId); if (error) { throw new Error(`Failed to unfollow user: ${error.message}`); } } async isFollowing(followerId: string, followingId: string): Promise<boolean> { const { data, error } = await this.supabase .from('follows') .select('*') .eq('follower_id', followerId) .eq('following_id', followingId) .single(); if (error) { if (error.code === 'PGRST116') { return false; } throw new Error(`Failed to check follow status: ${error.message}`); } return !!data; } async getFollowing(userId: string): Promise<User[]> { const { data, error } = await this.supabase .from('follows') .select(` users!follows_following_id_fkey(*) `) .eq('follower_id', userId) .order('created_at', { ascending: false }); if (error) { throw new Error(`Failed to get following: ${error.message}`); } return (data || []).map((item: any) => item.users); } async getFollowers(userId: string): Promise<User[]> { const { data, error } = await this.supabase .from('follows') .select(` users!follows_follower_id_fkey(*) `) .eq('following_id', userId) .order('created_at', { ascending: false }); if (error) { throw new Error(`Failed to get followers: ${error.message}`); } return (data || []).map((item: any) => item.users); } async getUserFeed(userId: string, limit: number = 20): Promise<Post[]> { const { data, error } = await this.supabase .from('posts') .select(` *, users!posts_user_id_fkey(username) `) .in('user_id', [ // Subquery to get followed users this.supabase .from('follows') .select('following_id') .eq('follower_id', userId) ]) .order('created_at', { ascending: false }) .limit(limit); if (error) { throw new Error(`Failed to get user feed: ${error.message}`); } return (data || []).map(post => ({ ...post, username: post.users.username })); } // Like operations async likePost(userId: string, postId: string): Promise<void> { const { error } = await this.supabase .from('likes') .insert({ user_id: userId, post_id: postId }); if (error) { if (error.code === '23505') { // unique constraint violation throw new Error('You have already liked this post'); } throw new Error(`Failed to like post: ${error.message}`); } } async unlikePost(userId: string, postId: string): Promise<void> { const { error } = await this.supabase .from('likes') .delete() .eq('user_id', userId) .eq('post_id', postId); if (error) { throw new Error(`Failed to unlike post: ${error.message}`); } } async isPostLiked(userId: string, postId: string): Promise<boolean> { const { data, error } = await this.supabase .from('likes') .select('*') .eq('user_id', userId) .eq('post_id', postId) .single(); if (error) { if (error.code === 'PGRST116') { return false; } throw new Error(`Failed to check like status: ${error.message}`); } return !!data; } // Utility method to get user ID by username async getUserId(username: string): Promise<string | null> { const user = await this.getUser(username); return user?.id || null; } }

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/GrahamMcBain/MCP-Social'

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