← back to inzamulhaque1__my-blog

Function bodies 345 total

All specs Real LLM only Function bodies
Callout function · typescript · L5-L17 (13 LOC)
src/components/mdx/index.tsx
function Callout({ children, type = 'info' }: { children: React.ReactNode; type?: 'info' | 'warning' | 'error' }) {
  const styles = {
    info: 'border-blue-500 bg-blue-50 dark:bg-blue-950',
    warning: 'border-yellow-500 bg-yellow-50 dark:bg-yellow-950',
    error: 'border-red-500 bg-red-50 dark:bg-red-950',
  };

  return (
    <div className={`my-6 rounded-lg border-l-4 p-4 ${styles[type]}`}>
      {children}
    </div>
  );
}
CustomImage function · typescript · L19-L37 (19 LOC)
src/components/mdx/index.tsx
function CustomImage({ src, alt, ...props }: ComponentProps<typeof Image>) {
  return (
    <figure className="my-8">
      <Image
        src={src}
        alt={alt || ''}
        width={800}
        height={450}
        className="rounded-lg"
        {...props}
      />
      {alt && (
        <figcaption className="mt-2 text-center text-sm text-gray-500 dark:text-gray-400">
          {alt}
        </figcaption>
      )}
    </figure>
  );
}
CustomLink function · typescript · L39-L55 (17 LOC)
src/components/mdx/index.tsx
function CustomLink({ href, children, ...props }: ComponentProps<'a'>) {
  const isInternal = href?.startsWith('/') || href?.startsWith('#');

  if (isInternal) {
    return (
      <Link href={href || '#'} {...props}>
        {children}
      </Link>
    );
  }

  return (
    <a href={href} target="_blank" rel="noopener noreferrer" {...props}>
      {children}
    </a>
  );
}
SessionProvider function · typescript · L5-L7 (3 LOC)
src/components/providers/SessionProvider.tsx
export function SessionProvider({ children }: { children: React.ReactNode }) {
  return <NextAuthSessionProvider>{children}</NextAuthSessionProvider>;
}
TenantFooter function · typescript · L37-L220 (184 LOC)
src/components/tenant/TenantFooter.tsx
export default function TenantFooter({
  tenantSlug,
  basePath,
  siteName,
  siteLogo,
  primaryColor,
  accentColor = '#8b5cf6',
  socialLinks,
  contactEmail,
  contactPhone,
  contactAddress,
  copyrightText,
  footerLinks = [],
  hideBranding = false,
}: TenantFooterProps) {
  const enabledLinks = footerLinks.filter(link => link.enabled);
  const hasSocialLinks = Object.values(socialLinks).some(Boolean);
  const hasContactInfo = contactEmail || contactPhone || contactAddress;

  // Determine the base path
  const urlBase = basePath !== undefined ? basePath : (tenantSlug ? `/t/${tenantSlug}` : '');

  // Helper to build proper URL for tenant pages
  const buildUrl = (url: string) => {
    if (url.startsWith('http://') || url.startsWith('https://')) {
      return url;
    }
    if (url.startsWith('/')) {
      return `${urlBase}${url}`;
    }
    return `${urlBase}/${url}`;
  };

  return (
    <footer className="border-t border-gray-200 dark:border-gray-800 bg-white dark:bg-gray-
TenantPageLayout function · typescript · L56-L278 (223 LOC)
src/components/tenant/TenantPageLayout.tsx
export function TenantPageLayout({
  children,
  basePath,
  settings,
  categories,
  breakingNews = [],
  activeNav = 'home',
  showFooter = true,
}: TenantPageLayoutProps) {
  const {
    siteName,
    siteLogo,
    primaryColor,
    accentColor,
    googleAnalyticsId,
    footerText,
    copyrightText,
    socialLinks = {},
    contactEmail,
    contactPhone,
    contactAddress,
    breakingNews: breakingNewsSettings,
  } = settings;

  const breakingNewsEnabled = breakingNewsSettings?.enabled ?? true;
  const breakingNewsLabel = breakingNewsSettings?.label || 'Latest';
  const breakingNewsSpeed = breakingNewsSettings?.speed || 30;

  return (
    <main className={`min-h-screen bg-gray-50 dark:bg-gray-950 ${breakingNewsEnabled ? 'pt-10' : ''}`}>
      {/* Breaking News Ticker - Fixed at top */}
      {breakingNewsEnabled && breakingNews.length > 0 && (
        <BreakingNewsTicker
          news={breakingNews}
          basePath={basePath}
          label={breakingNewsLabel}
       
CategoryBadge function · typescript · L11-L32 (22 LOC)
src/components/ui/CategoryBadge.tsx
export function CategoryBadge({ category, href, className }: CategoryBadgeProps) {
  const categoryConfig = categories.find(
    (c) => c.slug.toLowerCase() === category.toLowerCase()
  );
  const colorClass = categoryConfig?.color || 'bg-gray-500';

  const baseClasses = cn(
    'inline-flex items-center rounded-full px-3 py-1 text-xs font-medium text-white',
    colorClass,
    className
  );

  if (href) {
    return (
      <Link href={href} className={baseClasses}>
        {categoryConfig?.name || category}
      </Link>
    );
  }

  return <span className={baseClasses}>{categoryConfig?.name || category}</span>;
}
Repobility analyzer · published findings · https://repobility.com
ImageCropper function · typescript · L13-L158 (146 LOC)
src/components/ui/ImageCropper.tsx
export function ImageCropper({
  image,
  onCropComplete,
  onCancel,
  aspectRatio = 1,
}: ImageCropperProps) {
  const [crop, setCrop] = useState({ x: 0, y: 0 });
  const [zoom, setZoom] = useState(1);
  const [croppedAreaPixels, setCroppedAreaPixels] = useState<Area | null>(null);
  const [loading, setLoading] = useState(false);

  const onCropChange = useCallback((crop: { x: number; y: number }) => {
    setCrop(crop);
  }, []);

  const onZoomChange = useCallback((zoom: number) => {
    setZoom(zoom);
  }, []);

  const onCropAreaComplete = useCallback((_: Area, croppedAreaPixels: Area) => {
    setCroppedAreaPixels(croppedAreaPixels);
  }, []);

  const createCroppedImage = async (): Promise<Blob> => {
    const img = new Image();
    img.src = image;

    await new Promise((resolve) => {
      img.onload = resolve;
    });

    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');

    if (!ctx || !croppedAreaPixels) {
      throw new Error('C
ImageUpload function · typescript · L14-L318 (305 LOC)
src/components/ui/ImageUpload.tsx
export function ImageUpload({
  value,
  onChange,
  placeholder = 'Upload an image or paste URL',
  aspectRatio = 'video',
  enableCrop = true,
}: ImageUploadProps) {
  const [uploading, setUploading] = useState(false);
  const [error, setError] = useState('');
  const [dragOver, setDragOver] = useState(false);
  const [cropImage, setCropImage] = useState<string | null>(null);
  const fileInputRef = useRef<HTMLInputElement>(null);

  const isValidUrl = (url: string) => {
    if (!url) return false;
    return url.startsWith('/') || url.startsWith('http://') || url.startsWith('https://');
  };

  const uploadToServer = async (file: File | Blob, fileName?: string, skipWatermark?: boolean) => {
    const formData = new FormData();
    if (file instanceof Blob && !(file instanceof File)) {
      formData.append('image', file, fileName || 'cropped-image.jpg');
    } else {
      formData.append('image', file);
    }

    // Skip watermark for avatar uploads
    if (skipWatermark) {
      f
ImageWithBranding function · typescript · L16-L65 (50 LOC)
src/components/ui/ImageWithBranding.tsx
export function ImageWithBranding({
  src,
  alt,
  className = '',
  fill = false,
  width,
  height,
  priority = false,
}: ImageWithBrandingProps) {
  const [imageError, setImageError] = useState(false);

  const isValidSrc = src && (src.startsWith('/') || src.startsWith('http://') || src.startsWith('https://'));

  if (!isValidSrc || imageError) {
    return (
      <div className={`flex items-center justify-center bg-gray-200 dark:bg-gray-700 ${className}`}>
        <svg className="h-12 w-12 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
          <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" />
        </svg>
      </div>
    );
  }

  // Watermark is now permanently baked into images during upload
  // No CSS overlay needed
  return (
    <div className={`relative overflow-hidde
Pagination function · typescript · L10-L63 (54 LOC)
src/components/ui/Pagination.tsx
export function Pagination({ currentPage, totalPages, basePath }: PaginationProps) {
  if (totalPages <= 1) return null;

  const pages = Array.from({ length: totalPages }, (_, i) => i + 1);

  return (
    <nav className="flex items-center justify-center space-x-2" aria-label="Pagination">
      {/* Previous */}
      <Link
        href={currentPage > 1 ? `${basePath}?page=${currentPage - 1}` : '#'}
        className={cn(
          'rounded-lg px-3 py-2 text-sm font-medium',
          currentPage === 1
            ? 'pointer-events-none text-gray-400 dark:text-gray-600'
            : 'text-gray-700 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-800'
        )}
        aria-disabled={currentPage === 1}
      >
        Previous
      </Link>

      {/* Page Numbers */}
      {pages.map((page) => (
        <Link
          key={page}
          href={`${basePath}?page=${page}`}
          className={cn(
            'rounded-lg px-4 py-2 text-sm font-medium',
            currentPage
SearchBar function · typescript · L6-L41 (36 LOC)
src/components/ui/SearchBar.tsx
export function SearchBar() {
  const [query, setQuery] = useState('');
  const router = useRouter();

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    if (query.trim()) {
      router.push(`/search?q=${encodeURIComponent(query.trim())}`);
    }
  };

  return (
    <form onSubmit={handleSubmit} className="relative">
      <input
        type="text"
        value={query}
        onChange={(e) => setQuery(e.target.value)}
        placeholder="Search..."
        className="w-full rounded-full border border-gray-200 bg-gray-50 py-2 pl-10 pr-4 text-sm transition-all focus:border-indigo-500 focus:bg-white focus:outline-none focus:ring-2 focus:ring-indigo-500/20 dark:border-white/10 dark:bg-white/5 dark:text-white dark:placeholder-gray-500 dark:focus:border-indigo-500/50 dark:focus:bg-white/10 md:w-44 lg:w-52"
      />
      <svg
        className="absolute left-3.5 top-1/2 h-4 w-4 -translate-y-1/2 text-gray-400"
        fill="none"
        stroke="currentColor"
 
Tag function · typescript · L10-L25 (16 LOC)
src/components/ui/Tag.tsx
export function Tag({ name, href, className }: TagProps) {
  const baseClasses = cn(
    'inline-flex items-center rounded-full bg-gray-100 px-3 py-1 text-xs font-medium text-gray-700 transition-colors hover:bg-gray-200 dark:bg-gray-800 dark:text-gray-300 dark:hover:bg-gray-700',
    className
  );

  if (href) {
    return (
      <Link href={href} className={baseClasses}>
        #{name}
      </Link>
    );
  }

  return <span className={baseClasses}>#{name}</span>;
}
ThemeToggle function · typescript · L5-L64 (60 LOC)
src/components/ui/ThemeToggle.tsx
export function ThemeToggle() {
  const [theme, setTheme] = useState<'light' | 'dark'>('light');
  const [mounted, setMounted] = useState(false);

  useEffect(() => {
    setMounted(true);
    const savedTheme = localStorage.getItem('theme');
    // Validate that savedTheme is actually 'light' or 'dark'
    const validTheme = savedTheme === 'light' || savedTheme === 'dark' ? savedTheme : null;
    const systemTheme = window.matchMedia('(prefers-color-scheme: dark)').matches
      ? 'dark'
      : 'light';
    const initialTheme = validTheme || systemTheme;
    setTheme(initialTheme);
    document.documentElement.classList.toggle('dark', initialTheme === 'dark');
  }, []);

  const toggleTheme = () => {
    const newTheme = theme === 'light' ? 'dark' : 'light';
    setTheme(newTheme);
    localStorage.setItem('theme', newTheme);
    document.documentElement.classList.toggle('dark', newTheme === 'dark');
  };

  if (!mounted) {
    return (
      <button className="rounded-full p-2" aria
buildSystemPrompt function · typescript · L121-L266 (146 LOC)
src/lib/ai/reviewGenerator.ts
function buildSystemPrompt(): string {
  return `You are a world-class affiliate product reviewer who writes for major publications like Wirecutter, TechRadar, and CNET. Your reviews are known for being thorough, specific, and genuinely helpful.

## YOUR WRITING STYLE
- Write like a trusted expert talking to a friend
- Be confident and authoritative - you've done the research
- Use short paragraphs (2-4 sentences max)
- Mix sentence lengths for rhythm
- Include specific numbers, stats, and data from the provided information
- Write substantial content - aim for 2500-4000 words total

## CRITICAL RULES - FOLLOW EXACTLY:

### 1. USE REAL DATA ONLY
- Every price, rating, spec MUST come from the provided data
- NEVER invent statistics, testimonials, or fake claims
- If data is missing, say "check the official website" - NEVER make things up

### 2. WRITE GENUINE PROS & CONS
Use real limitations from the data. Cons must be REAL issues.

**BANNED FAKE CONS (INSTANT REJECTION):**
- "Learning 
Repobility's GitHub App fixes findings like these · https://github.com/apps/repobility-bot
buildUserPrompt function · typescript · L271-L420 (150 LOC)
src/lib/ai/reviewGenerator.ts
function buildUserPrompt(options: GenerateOptions): string {
  const { product, sections, affiliateLink, customInstructions, productImages } = options;

  // Ensure product has all required properties with defaults
  const safeProduct = {
    name: product?.name || 'Unknown Product',
    vendor: product?.vendor || 'Not specified',
    price: product?.price || 'Check website',
    originalPrice: product?.originalPrice || '',
    launchDate: product?.launchDate || '',
    category: product?.category || '',
    commission: product?.commission || '',
    guarantee: product?.guarantee || '',
    description: product?.description || '',
    features: Array.isArray(product?.features) ? product.features.filter(Boolean) : [],
    bonuses: Array.isArray(product?.bonuses) ? product.bonuses.filter(Boolean) : [],
    funnel: Array.isArray(product?.funnel) ? product.funnel.filter(f => f && f.name) : [],
    testimonials: Array.isArray(product?.testimonials) ? product.testimonials.filter(t => t && t.
generateSlug function · typescript · L519-L527 (9 LOC)
src/lib/ai/reviewGenerator.ts
function generateSlug(title: string): string {
  return title
    .toLowerCase()
    .replace(/[^a-z0-9\s-]/g, '')
    .replace(/\s+/g, '-')
    .replace(/-+/g, '-')
    .replace(/^-|-$/g, '')
    .substring(0, 100);
}
extractReviewData function · typescript · L532-L541 (10 LOC)
src/lib/ai/reviewGenerator.ts
function extractReviewData(content: string): {
  title: string;
  slug: string;
  excerpt: string;
  rating: number;
  pros: string[];
  cons: string[];
  verdict: string;
  sections: string[];
} {
generateReview function · typescript · L665-L732 (68 LOC)
src/lib/ai/reviewGenerator.ts
export async function generateReview(options: GenerateOptions): Promise<GeneratedReview> {
  const startTime = Date.now();

  try {
    const systemPrompt = buildSystemPrompt();
    const userPrompt = buildUserPrompt(options);

    const defaultModels: Record<string, string> = {
      openai: 'gpt-4o',
      anthropic: 'claude-3-5-sonnet-latest',
      google: 'gemini-2.0-flash',
      groq: 'llama-3.3-70b-versatile'
    };

    const model = options.model || defaultModels[options.provider];
    let result: { content: string; tokensUsed: number };

    switch (options.provider) {
      case 'openai':
        result = await generateWithOpenAI(options.apiKey, model, systemPrompt, userPrompt);
        break;
      case 'anthropic':
        result = await generateWithAnthropic(options.apiKey, model, systemPrompt, userPrompt);
        break;
      case 'google':
        result = await generateWithGoogle(options.apiKey, model, systemPrompt, userPrompt);
        break;
      case 'groq':
    
ensureReviewCategory function · typescript · L11-L41 (31 LOC)
src/lib/categories.ts
export async function ensureReviewCategory(tenantId: mongoose.Types.ObjectId | string) {
  await connectDB();

  const tenantObjectId = typeof tenantId === 'string'
    ? new mongoose.Types.ObjectId(tenantId)
    : tenantId;

  // Check if Review category already exists for this tenant
  let reviewCategory = await Category.findOne({
    tenantId: tenantObjectId,
    slug: 'review',
  });

  if (!reviewCategory) {
    // Create the Review category as a system category
    reviewCategory = await Category.create({
      tenantId: tenantObjectId,
      name: 'Review',
      slug: 'review',
      description: 'AI-generated product reviews',
      color: 'bg-purple-500',
      isSystem: true,
    });
  } else if (!reviewCategory.isSystem) {
    // If it exists but isn't marked as system, update it
    reviewCategory.isSystem = true;
    await reviewCategory.save();
  }

  return reviewCategory;
}
getCategoriesWithReviewFirst function · typescript · L48-L73 (26 LOC)
src/lib/categories.ts
export async function getCategoriesWithReviewFirst(tenantId: mongoose.Types.ObjectId | string) {
  await connectDB();

  // Ensure Review category exists
  await ensureReviewCategory(tenantId);

  const tenantObjectId = typeof tenantId === 'string'
    ? new mongoose.Types.ObjectId(tenantId)
    : tenantId;

  // Get all categories for this tenant
  const categories = await Category.find({ tenantId: tenantObjectId })
    .sort({ name: 1 })
    .lean();

  // Separate Review category from others
  const reviewCategory = categories.find((c: any) => c.slug === 'review');
  const otherCategories = categories.filter((c: any) => c.slug !== 'review');

  // Return with Review first
  if (reviewCategory) {
    return [reviewCategory, ...otherCategories];
  }

  return otherCategories;
}
getJwtSecret function · typescript · L12-L25 (14 LOC)
src/lib/constants.ts
function getJwtSecret(): string {
  const secret = process.env.JWT_SECRET || process.env.NEXTAUTH_SECRET;

  if (!secret) {
    if (process.env.NODE_ENV === 'production') {
      throw new Error('JWT_SECRET or NEXTAUTH_SECRET must be set in production');
    }
    // Only allow fallback in development
    console.warn('WARNING: Using default JWT secret. Set JWT_SECRET in production!');
    return 'development-secret-do-not-use-in-production';
  }

  return secret;
}
validateCoupon function · typescript · L22-L98 (77 LOC)
src/lib/coupon-utils.ts
export async function validateCoupon({
  code,
  tenantId,
  planSlug,
  originalPrice,
}: ValidateCouponParams): Promise<CouponValidationResult> {
  // Normalize code
  const normalizedCode = code.toUpperCase().trim();

  // Find the coupon
  const coupon = await Coupon.findOne({ code: normalizedCode });

  if (!coupon) {
    return { valid: false, error: 'Invalid coupon code' };
  }

  // Check if coupon is active
  if (!coupon.isActive) {
    return { valid: false, error: 'This coupon is no longer active' };
  }

  // Check valid from date
  const now = new Date();
  if (coupon.validFrom > now) {
    return { valid: false, error: 'This coupon is not yet valid' };
  }

  // Check expiry date
  if (coupon.validUntil && coupon.validUntil < now) {
    return { valid: false, error: 'This coupon has expired' };
  }

  // Check total usage limit
  if (
    coupon.maxUsageTotal &&
    coupon.currentUsageCount >= coupon.maxUsageTotal
  ) {
    return { valid: false, error: 'This coupon has r
Same scanner, your repo: https://repobility.com — Repobility
calculateDiscount function · typescript · L103-L112 (10 LOC)
src/lib/coupon-utils.ts
export function calculateDiscount(
  discountType: 'percentage' | 'fixed',
  discountValue: number,
  originalPrice: number
): number {
  if (discountType === 'percentage') {
    return Math.round((originalPrice * discountValue) / 100 * 100) / 100;
  }
  return Math.min(discountValue, originalPrice);
}
recordCouponUsage function · typescript · L117-L134 (18 LOC)
src/lib/coupon-utils.ts
export async function recordCouponUsage(
  couponId: string,
  tenantId: string,
  planRequestId: string
): Promise<void> {
  // Create usage record
  await CouponUsage.create({
    couponId,
    tenantId,
    planRequestId,
    usedAt: new Date(),
  });

  // Increment total usage count
  await Coupon.findByIdAndUpdate(couponId, {
    $inc: { currentUsageCount: 1 },
  });
}
getCouponStats function · typescript · L139-L144 (6 LOC)
src/lib/coupon-utils.ts
export async function getCouponStats(): Promise<{
  total: number;
  active: number;
  expired: number;
  usedThisMonth: number;
}> {
formatDiscount function · typescript · L172-L180 (9 LOC)
src/lib/coupon-utils.ts
export function formatDiscount(
  discountType: 'percentage' | 'fixed',
  discountValue: number
): string {
  if (discountType === 'percentage') {
    return `${discountValue}%`;
  }
  return `$${discountValue.toFixed(2)}`;
}
getSessionWithImpersonation function · typescript · L32-L83 (52 LOC)
src/lib/get-session.ts
export async function getSessionWithImpersonation(): Promise<SessionResult> {
  // First, check for impersonation session
  const cookieStore = await cookies();
  const impersonationToken = cookieStore.get('impersonation-session')?.value;
  const isImpersonated = cookieStore.get('is-impersonated')?.value === 'true';

  if (isImpersonated && impersonationToken) {
    try {
      const decoded = jwt.verify(impersonationToken, JWT_SECRET) as any;

      return {
        user: {
          id: decoded.userId,
          email: decoded.email,
          name: decoded.name,
          role: decoded.role || 'admin',
          tenantId: decoded.tenantId,
          isImpersonating: true,
          impersonatedBy: decoded.impersonatedBy,
        },
        isImpersonating: true,
      };
    } catch (err) {
      // Invalid impersonation token, fall through to regular session
      console.error('Invalid impersonation token:', err);
    }
  }

  // Fall back to regular NextAuth session
  const sessi
getAdminSession function · typescript · L90-L112 (23 LOC)
src/lib/get-session.ts
export async function getAdminSession(): Promise<SessionResult | null> {
  const result = await getSessionWithImpersonation();

  if (!result.user) {
    return null;
  }

  // Impersonating super admins always have access
  if (result.isImpersonating) {
    return result;
  }

  // Check for admin/editor roles, author role, or tenant ownership
  const isAdminOrEditor = ['admin', 'editor'].includes(result.user.role || '');
  const isAuthor = result.user.role === 'author';
  const isTenantOwner = !!result.user.tenantId && !isAuthor;

  if (!isAdminOrEditor && !isAuthor && !isTenantOwner) {
    return null;
  }

  return result;
}
getOwnerSession function · typescript · L119-L146 (28 LOC)
src/lib/get-session.ts
export async function getOwnerSession(): Promise<SessionResult | null> {
  const result = await getSessionWithImpersonation();

  if (!result.user) {
    return null;
  }

  // Impersonating super admins always have access
  if (result.isImpersonating) {
    return result;
  }

  // Authors are NOT allowed - only admin, editor, or tenant owners
  const isAuthor = result.user.role === 'author';
  if (isAuthor) {
    return null;
  }

  // Check for admin/editor roles or tenant ownership
  const isAdminOrEditor = ['admin', 'editor'].includes(result.user.role || '');
  const isTenantOwner = !!result.user.tenantId;

  if (!isAdminOrEditor && !isTenantOwner) {
    return null;
  }

  return result;
}
getTenantBySlug function · typescript · L93-L106 (14 LOC)
src/lib/get-tenant.ts
export async function getTenantBySlug(slug: string): Promise<ITenant | null> {
  try {
    await connectDB();
    const tenant = await Tenant.findOne({
      slug: slug.toLowerCase(),
      status: 'active',
    }).lean() as ITenant | null;

    return tenant;
  } catch (error) {
    console.error('Error fetching tenant by slug:', error);
    return null;
  }
}
Repobility · code-quality intelligence · https://repobility.com
getTenantById function · typescript · L111-L120 (10 LOC)
src/lib/get-tenant.ts
export async function getTenantById(tenantId: string): Promise<ITenant | null> {
  try {
    await connectDB();
    const tenant = await Tenant.findById(tenantId).lean() as ITenant | null;
    return tenant;
  } catch (error) {
    console.error('Error fetching tenant by ID:', error);
    return null;
  }
}
isSlugAvailable function · typescript · L125-L145 (21 LOC)
src/lib/get-tenant.ts
export async function isSlugAvailable(slug: string): Promise<boolean> {
  const reserved = [
    'app', 'www', 'api', 'admin', 'super', 'dashboard',
    'login', 'register', 'pricing', 'blog', 'support',
    'help', 'docs', 'mail', 'email', 'ftp', 'cdn',
    'static', 'assets', 'images', 'fonts', 'js', 'css',
  ];

  if (reserved.includes(slug.toLowerCase())) {
    return false;
  }

  try {
    await connectDB();
    const existing = await Tenant.findOne({ slug: slug.toLowerCase() });
    return !existing;
  } catch (error) {
    console.error('Error checking slug availability:', error);
    return false;
  }
}
checkTenantLimits function · typescript · L150-L153 (4 LOC)
src/lib/get-tenant.ts
export async function checkTenantLimits(
  tenantId: string,
  resource: 'posts' | 'authors' | 'users' | 'storage'
): Promise<{ allowed: boolean; current: number; limit: number; message?: string }> {
updateTenantUsage function · typescript · L195-L208 (14 LOC)
src/lib/get-tenant.ts
export async function updateTenantUsage(
  tenantId: string,
  resource: 'postsCount' | 'authorsCount' | 'usersCount' | 'storageUsed',
  increment: number = 1
): Promise<void> {
  try {
    await connectDB();
    await Tenant.findByIdAndUpdate(tenantId, {
      $inc: { [`usage.${resource}`]: increment },
    });
  } catch (error) {
    console.error('Error updating tenant usage:', error);
  }
}
buildTenantFilter function · typescript · L217-L228 (12 LOC)
src/lib/get-tenant.ts
export function buildTenantFilter(
  tenantId: string | null,
  additionalFilters: Record<string, unknown> = {}
): Record<string, unknown> {
  const filter: Record<string, unknown> = { ...additionalFilters };

  if (tenantId) {
    filter.tenantId = tenantId;
  }

  return filter;
}
getEffectiveTenantId function · typescript · L237-L248 (12 LOC)
src/lib/get-tenant.ts
export async function getEffectiveTenantId(
  sessionTenantId?: string | null
): Promise<string | null> {
  // Session tenant ID takes precedence
  if (sessionTenantId) {
    return sessionTenantId;
  }

  // Fall back to header-based tenant
  const { tenantId } = await getTenantFromHeaders();
  return tenantId;
}
buildPublishedFilter function · typescript · L254-L262 (9 LOC)
src/lib/get-tenant.ts
export function buildPublishedFilter(
  tenantId: string | null,
  additionalFilters: Record<string, unknown> = {}
): Record<string, unknown> {
  return buildTenantFilter(tenantId, {
    published: true,
    ...additionalFilters,
  });
}
compileMdxContent function · typescript · L7-L21 (15 LOC)
src/lib/mdx.ts
export async function compileMdxContent(content: string) {
  const { content: compiledContent } = await compileMDX({
    source: content,
    components: MDXComponents,
    options: {
      parseFrontmatter: false,
      mdxOptions: {
        remarkPlugins: [remarkGfm],
        rehypePlugins: [rehypeSlug, rehypeHighlight],
      },
    },
  });

  return compiledContent;
}
Repobility analyzer · published findings · https://repobility.com
connectDB function · typescript · L24-L52 (29 LOC)
src/lib/mongodb.ts
export async function connectDB() {
  if (cached.conn) {
    return cached.conn;
  }

  if (!cached.promise) {
    const opts = {
      bufferCommands: false,
      maxPoolSize: 10,
      serverSelectionTimeoutMS: 5000,
      socketTimeoutMS: 45000,
    };

    cached.promise = mongoose.connect(MONGODB_URI, opts).then((mongoose) => {
      console.log('MongoDB connected successfully');
      return mongoose;
    });
  }

  try {
    cached.conn = await cached.promise;
  } catch (e) {
    cached.promise = null;
    console.error('MongoDB connection error:', e);
    throw e;
  }

  return cached.conn;
}
mapPost function · typescript · L33-L54 (22 LOC)
src/lib/posts.ts
function mapPost(post: any): PostData {
  return {
    _id: post._id.toString(),
    slug: post.slug,
    title: post.title,
    description: post.description,
    content: post.content,
    date: post.createdAt.toISOString(),
    author: post.author ? {
      _id: post.author._id.toString(),
      name: post.author.name,
      slug: post.author.slug,
      avatar: post.author.avatar,
    } : null,
    category: post.category,
    tags: post.tags,
    image: post.image,
    featured: post.featured,
    readingTime: post.readingTime,
    views: post.views,
  };
}
incrementPostViews function · typescript · L102-L133 (32 LOC)
src/lib/posts.ts
export async function incrementPostViews(slug: string, tenantIdOverride?: string): Promise<void> {
  try {
    await connectDB();

    // Get tenant context from headers or use override
    const { tenantId: headerTenantId } = await getTenantFromHeaders();
    const tenantId = tenantIdOverride || headerTenantId;

    // Build filter with tenant isolation
    const filter: Record<string, unknown> = { slug, published: true };
    if (tenantId) {
      filter.tenantId = tenantId;
    }

    // Find the post to get its ID
    const post = await Post.findOne(filter).select('_id tenantId').lean();

    if (!post) {
      return; // Post not found, nothing to increment
    }

    // Use the new tracking system with deduplication
    const { trackPostView } = await import('./view-tracking');
    await trackPostView(
      (post as any)._id.toString(),
      (post as any).tenantId?.toString() || tenantId || ''
    );
  } catch (err) {
    console.error('Failed to increment post views:', err);
 
mapReviewToPost function · typescript · L411-L431 (21 LOC)
src/lib/posts.ts
function mapReviewToPost(review: any): PostData {
  return {
    _id: review._id.toString(),
    // Use the actual review slug or ID - reviews are now at /blog/{slug}
    slug: review.generatedReview?.slug || review._id.toString(),
    title: review.generatedReview?.title || review.product.name,
    description: review.generatedReview?.excerpt || `Review of ${review.product.name}`,
    content: review.generatedReview?.content || '',
    date: (review.publishedAt || review.createdAt).toISOString(),
    author: null,
    category: 'Review',
    tags: ['review', 'product'],
    image: review.featuredImage || review.product.images?.[0] || '',
    featured: false,
    readingTime: `${Math.ceil((review.generatedReview?.content?.length || 0) / 1000)} min read`,
    views: 0,
    // Reviews now use the same /blog/ path as regular posts
    isReview: true,
    rating: review.generatedReview?.rating || 0,
  };
}
startCleanupTimer function · typescript · L29-L45 (17 LOC)
src/lib/rate-limit.ts
function startCleanupTimer() {
  if (cleanupTimer) return;

  cleanupTimer = setInterval(() => {
    const now = Date.now();
    rateLimitStore.forEach((entry, key) => {
      if (entry.resetTime < now) {
        rateLimitStore.delete(key);
      }
    });
  }, CLEANUP_INTERVAL);

  // Don't prevent Node from exiting
  if (cleanupTimer.unref) {
    cleanupTimer.unref();
  }
}
checkRateLimit function · typescript · L63-L106 (44 LOC)
src/lib/rate-limit.ts
export function checkRateLimit(
  identifier: string,
  config: RateLimitConfig
): RateLimitResult {
  startCleanupTimer();

  const key = config.keyPrefix ? `${config.keyPrefix}:${identifier}` : identifier;
  const now = Date.now();

  const entry = rateLimitStore.get(key);

  // No existing entry or expired entry
  if (!entry || entry.resetTime < now) {
    rateLimitStore.set(key, {
      count: 1,
      resetTime: now + config.windowMs,
    });

    return {
      success: true,
      remaining: config.maxRequests - 1,
      resetTime: now + config.windowMs,
    };
  }

  // Check if limit exceeded
  if (entry.count >= config.maxRequests) {
    return {
      success: false,
      remaining: 0,
      resetTime: entry.resetTime,
      retryAfter: Math.ceil((entry.resetTime - now) / 1000),
    };
  }

  // Increment count
  entry.count++;

  return {
    success: true,
    remaining: config.maxRequests - entry.count,
    resetTime: entry.resetTime,
  };
}
getClientIdentifier function · typescript · L111-L132 (22 LOC)
src/lib/rate-limit.ts
export function getClientIdentifier(request: Request): string {
  // Try to get IP from various headers
  const headers = request.headers;

  // Cloudflare
  const cfConnectingIp = headers.get('cf-connecting-ip');
  if (cfConnectingIp) return cfConnectingIp;

  // Standard proxy headers
  const xForwardedFor = headers.get('x-forwarded-for');
  if (xForwardedFor) {
    // Take the first IP in the chain (original client)
    return xForwardedFor.split(',')[0].trim();
  }

  const xRealIp = headers.get('x-real-ip');
  if (xRealIp) return xRealIp;

  // Fallback to a hash of user-agent for basic protection
  const userAgent = headers.get('user-agent') || 'unknown';
  return `ua:${hashString(userAgent)}`;
}
hashString function · typescript · L137-L145 (9 LOC)
src/lib/rate-limit.ts
function hashString(str: string): string {
  let hash = 0;
  for (let i = 0; i < str.length; i++) {
    const char = str.charCodeAt(i);
    hash = ((hash << 5) - hash) + char;
    hash = hash & hash; // Convert to 32bit integer
  }
  return hash.toString(36);
}
Repobility's GitHub App fixes findings like these · https://github.com/apps/repobility-bot
createRateLimiter function · typescript · L150-L199 (50 LOC)
src/lib/rate-limit.ts
export function createRateLimiter(config: RateLimitConfig) {
  return {
    check: (identifier: string) => checkRateLimit(identifier, config),

    /**
     * Middleware-style function that returns a response if rate limited
     */
    limit: (request: Request): NextResponse | null => {
      const identifier = getClientIdentifier(request);
      const result = checkRateLimit(identifier, config);

      if (!result.success) {
        return NextResponse.json(
          {
            error: 'Too many requests',
            message: `Rate limit exceeded. Please try again in ${result.retryAfter} seconds.`,
            retryAfter: result.retryAfter,
          },
          {
            status: 429,
            headers: {
              'Retry-After': String(result.retryAfter),
              'X-RateLimit-Limit': String(config.maxRequests),
              'X-RateLimit-Remaining': '0',
              'X-RateLimit-Reset': String(Math.ceil(result.resetTime / 1000)),
            },
          }
   
sanitizeHtml function · typescript · L6-L27 (22 LOC)
src/lib/sanitize.ts
export function sanitizeHtml(html: string): string {
  return DOMPurify.sanitize(html, {
    ALLOWED_TAGS: [
      'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
      'p', 'br', 'hr',
      'ul', 'ol', 'li',
      'blockquote', 'pre', 'code',
      'a', 'strong', 'em', 'b', 'i', 'u', 's', 'strike',
      'img', 'figure', 'figcaption',
      'table', 'thead', 'tbody', 'tr', 'th', 'td',
      'div', 'span',
      'iframe', // For embedded content like YouTube
    ],
    ALLOWED_ATTR: [
      'href', 'target', 'rel',
      'src', 'alt', 'title', 'width', 'height',
      'class', 'id',
      'frameborder', 'allowfullscreen', 'allow',
    ],
    ALLOW_DATA_ATTR: false,
  });
}
escapeSvgText function · typescript · L32-L39 (8 LOC)
src/lib/sanitize.ts
export function escapeSvgText(text: string): string {
  return text
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&#39;');
}
‹ prevpage 6 / 7next ›