Professional View Counter

Advanced serverless analytics with enterprise-grade admin management

⚡ High Performance 🔒 Secure Admin 📊 Advanced Analytics 💰 Cost Optimized

📊 Global Statistics

0
Total Views
0
Active Projects
0
Unique Visitors

✨ Key Features

Ultra Fast

Optimized for minimal latency with intelligent caching and efficient database queries.

🔒

Secure Admin

Password-protected admin panel with environment-based configuration and role management.

📊

Rich Analytics

Detailed view tracking, visitor analytics, and comprehensive reporting dashboard.

💰

Cost Effective

Designed to stay within Cloudflare's free tier with optional premium features.

🚀 Live Demo

Try out the view counter with real-time updates

🎯 Quick Test

🎨 Badge Generator

📚 Complete Documentation

Everything you need to integrate CFlairCounter into your projects

🚀 Quick Start 📖 API Reference 💡 Examples ⚙️ Configuration

🚀 Quick Start

Get your view counter running in under 2 minutes:

1. Basic Setup

// Step 1: Track a view (POST request) fetch('https://your-domain.com/api/views/my-project', { method: 'POST' }) .then(response => response.json()) .then(data => console.log('✅ View tracked!', data.totalViews)); // Step 2: Display view count (GET request) fetch('https://your-domain.com/api/views/my-project') .then(response => response.json()) .then(data => { document.getElementById('view-count').textContent = data.totalViews; });

2. Add to Your HTML

<!-- View counter display --> <div> <span>Views: </span> <span id="view-count">Loading...</span> </div> <!-- Or use a badge --> <img src="https://your-domain.com/api/views/my-project/badge" alt="View Count" />

3. GitHub README Integration

# Add to your README.md ![Views](https://your-domain.com/api/views/my-repo/badge?color=blue&label=views) # With custom styling [![Project Views](https://your-domain.com/api/views/my-repo/badge?color=brightgreen&style=flat&label=project%20views)](https://your-domain.com)

🔗 Complete API Reference

📈 POST /api/views/{project-name}

Purpose: Increment view count for a project

Parameter Type Description Required
project-name string Unique project identifier (max 100 chars) ✅ Yes
// Request POST /api/views/my-awesome-project // Response (200 OK) { "success": true, "projectName": "my-awesome-project", "totalViews": 1337, "uniqueViews": 892, "timestamp": "2025-08-28T13:45:30Z" }

📊 GET /api/views/{project-name}

Purpose: Get current statistics for a project

// Request GET /api/views/my-awesome-project // Response (200 OK) { "success": true, "projectName": "my-awesome-project", "totalViews": 1337, "uniqueViews": 892, "description": null, "createdAt": "2025-08-01T10:30:00Z" } // Response for non-existent project { "success": true, "projectName": "new-project", "totalViews": 0, "uniqueViews": 0, "description": null, "createdAt": null }

🎨 GET /api/views/{project-name}/badge

Purpose: Generate SVG badge with view count

Parameter Type Default Options
color string blue blue, green, red, orange, purple, brightgreen
label string views Any custom text (URL encoded)
// Basic badge GET /api/views/my-project/badge // Custom color and label GET /api/views/my-project/badge?color=brightgreen&label=downloads // Response: SVG image (image/svg+xml) // Cached for 1 hour

💊 GET /health

Purpose: Health check endpoint

// Response (200 OK) { "success": true, "status": "ok", "timestamp": "2025-08-28T13:45:30Z", "worker": "cflaircounter-api", "version": "2.0.0" }

⚡ Integration Examples

🌐 JavaScript (Browser)

// Modern async/await approach async function trackView(projectName) { try { const response = await fetch(`/api/views/${projectName}`, { method: 'POST' }); const data = await response.json(); if (data.success) { console.log(`📈 Views: ${data.totalViews}`); updateViewCounter(data.totalViews); } } catch (error) { console.error('❌ Failed to track view:', error); } } // Get and display current stats async function displayViewCount(projectName) { try { const response = await fetch(`/api/views/${projectName}`); const data = await response.json(); document.getElementById('view-count').textContent = data.totalViews.toLocaleString(); document.getElementById('unique-count').textContent = data.uniqueViews.toLocaleString(); } catch (error) { console.error('❌ Failed to load stats:', error); } } // Auto-track when page loads document.addEventListener('DOMContentLoaded', () => { trackView('my-website'); displayViewCount('my-website'); });

🚀 Node.js (Server-side)

// Using fetch (Node.js 18+) async function trackServerView(projectName) { try { const response = await fetch(`https://your-domain.com/api/views/${projectName}`, { method: 'POST', headers: { 'User-Agent': 'YourApp/1.0.0' } }); const data = await response.json(); console.log(`✅ Tracked view for ${projectName}: ${data.totalViews} total`); return data; } catch (error) { console.error('❌ Tracking failed:', error); throw error; } } // Using axios const axios = require('axios'); async function getProjectStats(projectName) { try { const { data } = await axios.get(`https://your-domain.com/api/views/${projectName}`); return { views: data.totalViews, unique: data.uniqueViews, created: data.createdAt }; } catch (error) { if (error.response?.status === 404) { return { views: 0, unique: 0, created: null }; } throw error; } } // Express.js middleware const trackViews = (projectName) => async (req, res, next) => { try { await trackServerView(projectName); } catch (error) { console.warn('View tracking failed:', error.message); } next(); }; app.use('/my-project', trackViews('my-project'), myProjectRoutes);

🐍 Python

# Using requests library import requests from typing import Dict, Optional class ViewCounter: def __init__(self, base_url: str): self.base_url = base_url.rstrip('/') def track_view(self, project_name: str) -> Dict: """Track a view for the given project.""" url = f"{self.base_url}/api/views/{project_name}" try: response = requests.post(url, timeout=5) response.raise_for_status() return response.json() except requests.RequestException as e: print(f"❌ Failed to track view: {e}") return {"success": False, "error": str(e)} def get_stats(self, project_name: str) -> Dict: """Get current statistics for a project.""" url = f"{self.base_url}/api/views/{project_name}" try: response = requests.get(url, timeout=5) response.raise_for_status() return response.json() except requests.RequestException as e: print(f"❌ Failed to get stats: {e}") return {"success": False, "error": str(e)} # Usage counter = ViewCounter("https://your-domain.com") # Track a view result = counter.track_view("my-python-project") if result.get("success"): print(f"📈 Total views: {result['totalViews']}") # Get current stats stats = counter.get_stats("my-python-project") if stats.get("success"): print(f"📊 Views: {stats['totalViews']}, Unique: {stats['uniqueViews']}")

🦀 Rust

// Add to Cargo.toml: reqwest = { version = "0.11", features = ["json"] } use reqwest; use serde::{Deserialize, Serialize}; #[derive(Debug, Deserialize)] struct ViewResponse { success: bool, #[serde(rename = "totalViews")] total_views: u32, #[serde(rename = "uniqueViews")] unique_views: u32, #[serde(rename = "projectName")] project_name: String, } async fn track_view(project_name: &str) -> Result<ViewResponse, reqwest::Error> { let url = format!("https://your-domain.com/api/views/{}", project_name); let client = reqwest::Client::new(); let response = client .post(&url) .send() .await? .json::<ViewResponse>() .await?; println!("✅ Tracked view for {}: {} total", project_name, response.total_views); Ok(response) } async fn get_stats(project_name: &str) -> Result<ViewResponse, reqwest::Error> { let url = format!("https://your-domain.com/api/views/{}", project_name); let response = reqwest::get(&url) .await? .json::<ViewResponse>() .await?; Ok(response) } // Usage in main function #[tokio::main] async fn main() -> Result<(), Box<dyn std::error::Error>> { let stats = track_view("my-rust-project").await?; println!("📊 Project stats: {:?}", stats); Ok(()) }

🎯 Advanced Usage

⚙️ Environment Configuration

Configure your CFlairCounter instance with these environment variables:

Variable Default Description
ADMIN_PASSWORD admin123 Password for admin panel access
ENABLE_ADMIN true Enable/disable admin functionality
ENABLE_ANALYTICS false Enable detailed visitor tracking (uses more DB queries)
MAX_PROJECTS 100 Maximum number of projects to prevent abuse

🏗️ Self-Hosting

# Clone the repository git clone https://github.com/Life-Experimentalists/CFlair-Counter.git cd CFlair-Counter # Install dependencies npm install # Set up environment variables cp .env.example .env # Edit .env with your settings # Create Cloudflare D1 database npm run db:create npm run db:init # Deploy to Cloudflare Pages npm run deploy

📊 Rate Limiting & Performance

// Implement client-side rate limiting class ViewTracker { constructor(cooldown = 60000) { // 1 minute cooldown this.cooldown = cooldown; this.lastTrack = new Map(); } async track(projectName) { const now = Date.now(); const last = this.lastTrack.get(projectName) || 0; if (now - last < this.cooldown) { console.log('⏱️ Cooldown active, skipping track'); return this.getStats(projectName); } this.lastTrack.set(projectName, now); return this.trackView(projectName); } async trackView(projectName) { // Implementation here } } // Usage const tracker = new ViewTracker(30000); // 30 second cooldown tracker.track('my-project');

🔄 Batch Operations

// Track multiple projects efficiently async function trackMultiple(projects) { const promises = projects.map(project => fetch(`/api/views/${project}`, { method: 'POST' }) .then(r => r.json()) .catch(err => ({ success: false, project, error: err.message })) ); const results = await Promise.all(promises); return results; } // Get stats for multiple projects async function getMultipleStats(projects) { const promises = projects.map(async project => { try { const response = await fetch(`/api/views/${project}`); const data = await response.json(); return { ...data, project }; } catch (error) { return { success: false, project, error: error.message }; } }); return Promise.all(promises); } // Usage const projects = ['project-1', 'project-2', 'project-3']; const stats = await getMultipleStats(projects); console.table(stats);

🔧 Troubleshooting

❌ Common Issues

Issue Cause Solution
CORS Error Cross-origin request blocked Use server-side tracking or proxy requests
404 Not Found Project name contains invalid characters Use alphanumeric characters, hyphens, underscores only
Admin Access Denied Wrong password or admin disabled Check ADMIN_PASSWORD environment variable
High Response Times Database not optimized Ensure D1 database is properly initialized

🐛 Debugging

// Enable detailed logging const DEBUG = true; async function debugTrack(projectName) { if (DEBUG) console.log(`🔍 Tracking view for: ${projectName}`); try { const start = performance.now(); const response = await fetch(`/api/views/${projectName}`, { method: 'POST' }); const duration = performance.now() - start; if (DEBUG) console.log(`⏱️ Request took: ${duration.toFixed(2)}ms`); const data = await response.json(); if (DEBUG) console.log('📊 Response:', data); return data; } catch (error) { if (DEBUG) console.error('❌ Error:', error); throw error; } }

📈 Monitoring

// Check system health async function healthCheck() { try { const response = await fetch('/health'); const data = await response.json(); console.log('✅ System Status:', data.status); console.log('📅 Last Check:', new Date(data.timestamp)); console.log('🔢 Version:', data.version); return data.status === 'ok'; } catch (error) { console.error('❌ Health check failed:', error); return false; } } // Monitor with alerts setInterval(async () => { const isHealthy = await healthCheck(); if (!isHealthy) { console.warn('⚠️ System health check failed!'); // Implement your alerting logic here } }, 300000); // Check every 5 minutes

⚡ Performance & Limits

📊 System Specifications

< 100ms
Average Response Time
1000+
RPS Capacity
99.9%
Uptime
1 Hour
Badge Cache

💰 Cost Optimization

CFlairCounter is designed to stay within Cloudflare's free tier limits:

  • Database: Optimized queries (1-3 per request)
  • Functions: Minimal compute time
  • Caching: 1-hour badge cache reduces load
  • Analytics: Optional to save resources

🔒 Security Features

# Security headers automatically included Access-Control-Allow-Origin: * Content-Security-Policy: default-src 'self' X-Content-Type-Options: nosniff # Rate limiting built-in Max requests per IP: 1000/hour Project name validation: Alphanumeric + hyphens/underscores Max project name length: 100 characters # Privacy-friendly visitor tracking IP address hashing: SHA-256 with salt No personal data stored GDPR compliant by design