Function bodies 345 total
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('CImageUpload 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) {
fImageWithBranding 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-hiddePagination 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',
currentPageSearchBar 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" ariabuildSystemPrompt 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 rSame 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 sessigetAdminSession 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, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}