<!-- 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
đ Admin Panel
0
Total Views
0
Total Projects
⥠Quick Actions
đ Project Management
Loading projects...
â Add New Project
Only alphanumeric characters, hyphens, and
underscores