Professional View Counter
Advanced serverless analytics with enterprise-grade
admin management
⚡ High Performance
🔒 Secure Admin
📊 Advanced Analytics
💰 Cost Optimized
✨ 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
🎨 Badge Generator
Badge Preview:

📚 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
fetch('https://your-domain.com/api/views/my-project', {
method: 'POST' }) .then(response => response.json())
.then(data => console.log('✅ View tracked!',
data.totalViews));
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
<div> <span>Views: </span> <span
id="view-count">Loading...</span> </div>
<img
src="https://your-domain.com/api/views/my-project/badge"
alt="View Count" />
3. GitHub README Integration

[](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 |
POST /api/views/my-awesome-project
{
"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
GET /api/views/my-awesome-project
{
"success": true,
"projectName":
"my-awesome-project",
"totalViews": 1337,
"uniqueViews": 892,
"description": null,
"createdAt":
"2025-08-01T10:30:00Z"
}
{
"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) |
GET /api/views/my-project/badge
GET
/api/views/my-project/badge?color=brightgreen&label=downloads
💊 GET /health
Purpose: Health check endpoint
{
"success": true,
"status":
"ok",
"timestamp":
"2025-08-28T13:45:30Z",
"worker":
"cflaircounter-api",
"version":
"2.0.0"
}
⚡ Integration Examples
🌐 JavaScript (Browser)
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); } }
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); } }
document.addEventListener('DOMContentLoaded', () => {
trackView('my-website'); displayViewCount('my-website');
});
🚀 Node.js (Server-side)
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; } }
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; } }
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
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)}
counter = ViewCounter("https://your-domain.com")
result = counter.track_view("my-python-project") if
result.get("success"): print(f"📈 Total views:
{result['totalViews']}")
stats = counter.get_stats("my-python-project") if
stats.get("success"): print(f"📊 Views:
{stats['totalViews']}, Unique: {stats['uniqueViews']}")
🦀 Rust
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) }
#[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
git clone
https://github.com/Life-Experimentalist/CFlair-Counter.git
cd CFlair-Counter
npm install
cp .env.example .env # Edit .env with your settings
npm run db:create npm run db:init
npm run deploy
📊 Rate Limiting & Performance
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 } }
const tracker = new ViewTracker(30000); // 30 second
cooldown tracker.track('my-project');
🔄 Batch Operations
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; }
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); }
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
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
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; } }
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
💰 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
Access-Control-Allow-Origin: * Content-Security-Policy:
default-src 'self' X-Content-Type-Options: nosniff
Max requests per IP: 1000/hour Project name validation:
Alphanumeric + hyphens/underscores Max project name
length: 100 characters
IP address hashing: SHA-256 with salt No personal data
stored GDPR compliant by design