Professional View Counter

Advanced serverless analytics with enterprise-grade admin management

โšก High Performance ๐Ÿ”’ Secure Admin ๐Ÿ“Š Advanced Analytics ๐Ÿ’ฐ Cost Optimized
API status: checking...
Current endpoint:
Checking domain routing...

๐Ÿ“Š 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.

๐ŸŒ™

Dark Mode

Beautiful light and dark themes with smooth transitions and persistent user preferences.

๐ŸŽฏ

Easy Integration

Simple one-line webhook integration with RESTful API and auto-generated badges for any project.

๐Ÿš€ 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" }

๐Ÿงฉ Multi-Instance Setup (No Interference)

You can replicate this project for your own domain and data, without touching this instance.

# 1) Fork this repository git clone https://github.com/Life-Experimentalist/CFlair-Counter.git cd CFlair-Counter # 2) Create your own Cloudflare Pages project + D1 DB # Set your own project name and database IDs in wrangler.toml # 3) Use your own custom domain (recommended) # Example: counter.yourdomain.com # 4) Keep your secrets unique # ADMIN_PASSWORD, DB binding, and project endpoints should be yours # 5) API calls auto-bind to current origin # This UI uses window.location.origin, so your deployment uses your own API base
Important: Do not hardcode counter.vkrishna04.me in your fork. Use your own domain and Cloudflare project to keep data isolated.

โšก 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-Experimentalist/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

Self-Hosting Setup

Deploy your own CFlair Counter in minutes. Each fork gets its own isolated D1 database โ€” completely separate from any other instance.

1

Fork & Clone

Fork the repo on GitHub, then:

git clone https://github.com/YOUR_USERNAME/cflair-counter.git cd cflair-counter
2

Install Dependencies

npm install
3

Create D1 Database

wrangler d1 create cflaircounter-db

Copy the output database_id and paste it into wrangler.toml.

4

Configure wrangler.toml

[[d1_databases]] binding = "DB" database_name = "cflaircounter-db" database_id = "YOUR_DATABASE_ID_HERE" [vars] ADMIN_PASSWORD = "your-secure-password-here" # Your deployment will be at: YOUR_ORIGIN
5

Initialize Schema

npm run db:init
6

Deploy

npm run deploy

After deploy, Cloudflare will show your Pages URL.

7

Custom Domain (Optional)

In your Cloudflare dashboard: Pages โ†’ your project โ†’ Custom domains โ†’ Add domain.

For external DNS, add a CNAME record pointing to your Pages subdomain (e.g. yourproject.pages.dev).

๐Ÿงช Test Your Deployment

After deploying, verify your instance is working: