<!-- 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

# With custom styling
[](https://your-domain.com)
// 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
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');
});
# 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');
// 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:
๐ Admin Panel
0
Total Views
0
Total Projects
โก Quick Actions
๐ Project Management
Loading projects...
โ Add New Project
Only alphanumeric characters, hyphens, and
underscores