Skip to main content
Glama

Authenticated Next.js MCP Server

api-keys.ts7.83 kB
import { randomBytes } from 'crypto' import type { UserApiKey as PrismaUserApiKey } from '../generated/prisma' import { PrismaService } from './prisma' export interface UserApiKey { id: string userId: string name: string key: string createdAt: string lastUsed?: string scopes: string[] } const prisma = PrismaService.getInstance() // Helper function to convert Prisma model to interface function convertPrismaToInterface(prismaKey: PrismaUserApiKey): UserApiKey { return { id: prismaKey.id, userId: prismaKey.userId, name: prismaKey.name, key: prismaKey.key, createdAt: prismaKey.createdAt.toISOString(), lastUsed: prismaKey.lastUsed?.toISOString(), scopes: JSON.parse(prismaKey.scopes), } } // Helper function to generate unique API key export function generateApiKey(): string { return `mcp_${randomBytes(32).toString('hex')}` } // Helper function to get user's API keys export async function getUserApiKeys(userId: string): Promise<UserApiKey[]> { const keys = await prisma.userApiKey.findMany({ where: { userId }, orderBy: { createdAt: 'desc' }, }) return keys.map(convertPrismaToInterface) } // Helper function to create a new API key export async function createUserApiKey( userId: string, name: string, scopes: string[] ): Promise<UserApiKey> { const key = generateApiKey() const newKey = await prisma.userApiKey.create({ data: { userId, name, key, scopes: JSON.stringify(scopes), }, }) // Store activity event directly try { await prisma.webhookEvent.create({ data: { userId, eventType: 'api_key_created', payload: { keyId: newKey.id, keyName: name, scopes, timestamp: new Date().toISOString(), }, processed: false, }, }) } catch (error) { console.error('Failed to create webhook event for API key creation:', error) // Don't throw - continue with API key creation even if activity logging fails } return convertPrismaToInterface(newKey) } // Helper function to find API key by ID and user export async function findUserApiKey( keyId: string, userId: string ): Promise<UserApiKey | null> { const key = await prisma.userApiKey.findFirst({ where: { id: keyId, userId, }, }) return key ? convertPrismaToInterface(key) : null } // Helper function to delete API key export async function deleteUserApiKey( keyId: string, userId: string ): Promise<boolean> { try { // Get key info before deletion for webhook const keyToDelete = await prisma.userApiKey.findFirst({ where: { id: keyId, userId }, select: { name: true, scopes: true }, }) await prisma.userApiKey.delete({ where: { id: keyId, userId, }, }) // Store activity event directly for API key deletion if (keyToDelete) { try { await prisma.webhookEvent.create({ data: { userId, eventType: 'api_key_deleted', payload: { keyId, keyName: keyToDelete.name, scopes: JSON.parse(keyToDelete.scopes), timestamp: new Date().toISOString(), }, processed: false, }, }) } catch (error) { console.error('Failed to create webhook event for API key deletion:', error) // Don't throw - continue with deletion even if activity logging fails } } return true } catch (error) { console.error('deleteUserApiKey failed:', error) return false } } // Helper function to update API key export async function updateUserApiKey( keyId: string, userId: string, updates: { name?: string; scopes?: string[] } ): Promise<UserApiKey | null> { try { const updatedKey = await prisma.userApiKey.update({ where: { id: keyId, userId, }, data: { ...(updates.name && { name: updates.name }), ...(updates.scopes && { scopes: JSON.stringify(updates.scopes) }), }, }) // Store activity event directly for API key update try { await prisma.webhookEvent.create({ data: { userId, eventType: 'api_key_updated', payload: { keyId, keyName: updatedKey.name, updates, timestamp: new Date().toISOString(), }, processed: false, }, }) } catch (error) { console.error('Failed to create webhook event for API key update:', error) // Don't throw - continue with update even if activity logging fails } return convertPrismaToInterface(updatedKey) } catch { return null } } // Function to validate API keys and log requests export async function validateUserApiKey( key: string, requestInfo?: { endpoint?: string userAgent?: string ipAddress?: string } ): Promise<{ valid: boolean userId?: string scopes?: string[] keyId?: string }> { const apiKey = await prisma.userApiKey.findUnique({ where: { key }, }) if (!apiKey) { return { valid: false } } // Log the request and update last used timestamp await Promise.all([ // Update last used timestamp only prisma.userApiKey.update({ where: { id: apiKey.id }, data: { lastUsed: new Date(), }, }), // Create request log entry (single source of truth for request counting) prisma.apiKeyRequestLog.create({ data: { apiKeyId: apiKey.id, userId: apiKey.userId, endpoint: requestInfo?.endpoint, userAgent: requestInfo?.userAgent, ipAddress: requestInfo?.ipAddress, }, }), ]) // Store activity event directly try { await prisma.webhookEvent.create({ data: { userId: apiKey.userId, eventType: 'api_key_used', payload: { keyId: apiKey.id, keyName: apiKey.name, endpoint: requestInfo?.endpoint, ipAddress: requestInfo?.ipAddress, timestamp: new Date().toISOString(), }, processed: false, }, }) } catch (error) { console.error('Failed to create webhook event for API key usage:', error) // Don't throw - continue with API validation even if activity logging fails } return { valid: true, userId: apiKey.userId, scopes: JSON.parse(apiKey.scopes), keyId: apiKey.id, } } // Function to get user request statistics export async function getUserRequestStats(userId: string): Promise<{ totalRequests: number requestsThisMonth: number requestsToday: number }> { const now = new Date() const startOfMonth = new Date(now.getFullYear(), now.getMonth(), 1) const startOfDay = new Date(now.getFullYear(), now.getMonth(), now.getDate()) startOfDay.setHours(0, 0, 0, 0) try { // Use userId-based queries to include ALL requests (from active and deleted keys) const [totalRequests, requestsThisMonth, requestsToday] = await Promise.all([ // All requests for this user (including from deleted keys) prisma.apiKeyRequestLog.count({ where: { userId }, }), // This month's requests for this user prisma.apiKeyRequestLog.count({ where: { userId, timestamp: { gte: startOfMonth }, }, }), // Today's requests for this user prisma.apiKeyRequestLog.count({ where: { userId, timestamp: { gte: startOfDay }, }, }), ]) return { totalRequests, requestsThisMonth, requestsToday, } } catch (error) { console.log('Error in analytics calculation:', error) return { totalRequests: 0, requestsThisMonth: 0, requestsToday: 0 } } }

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/vedaterenoglu/ve-nextjs-mcp-server'

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