From 5229765ea43c644d57e6704bbf954f97a166bed2 Mon Sep 17 00:00:00 2001 From: PianoNic <79938743+Pianonic@users.noreply.github.com> Date: Fri, 25 Jul 2025 02:31:50 +0200 Subject: [PATCH] Refactor badge tracking system to use tags instead of URLs, updating related schemas, services, and frontend components. --- src/main.py | 33 ++++++++++++++++----------------- src/models.py | 4 ++-- src/schemas.py | 9 ++++----- src/services.py | 20 ++++++++++---------- static/script.js | 35 ++++++++++------------------------- templates/index.html | 9 ++++----- 6 files changed, 46 insertions(+), 64 deletions(-) diff --git a/src/main.py b/src/main.py index b34136a..5130b6f 100644 --- a/src/main.py +++ b/src/main.py @@ -8,8 +8,8 @@ import logging import json from .models import initialize_database, close_database -from .schemas import BadgeParams, UrlStatsResponse, SystemStatsResponse -from .services import update_visit_count, get_url_visit_count, get_system_statistics, get_app_info, load_template +from .schemas import BadgeParams, TagStatsResponse, SystemStatsResponse +from .services import update_visit_count, get_tag_visit_count, get_system_statistics, get_app_info, load_template from .utils import build_shields_url, get_security_headers LOG_LEVEL = os.getenv("LOG_LEVEL", "INFO").upper() @@ -71,48 +71,48 @@ def get_app_version(): async def badge( request: Request, response: Response, - url: str, + tag: str, label: str = "visits", color: str = "4ade80", style: str = "flat", logo: str = "", ): try: - params = BadgeParams(url=url, label=label, color=color, style=style, logo=logo) + params = BadgeParams(tag=tag, label=label, color=color, style=style, logo=logo) except Exception: raise HTTPException(status_code=400, detail="Invalid parameters.") cookie_id = request.cookies.get("visitor_id") try: - count, was_incremented, new_cookie_id = update_visit_count(cookie_id, params.url) + count, was_incremented, new_cookie_id = update_visit_count(cookie_id, params.tag) if new_cookie_id: response.set_cookie(key="visitor_id", value=new_cookie_id, max_age=31536000, httponly=True, samesite="Lax") except ValueError as e: raise HTTPException(status_code=429, detail=str(e)) except Exception as e: logger.error(f"Error updating visit count: {e}") - count = get_url_visit_count(params.url) + count = get_tag_visit_count(params.tag) shields_url = build_shields_url(params.label, count, params.color, params.style, params.logo) headers = get_security_headers() return RedirectResponse(shields_url, headers=headers) -@app.get("/api/stats/{url}", response_model=UrlStatsResponse) -async def get_url_stats_endpoint(url: str): +@app.get("/api/stats/{tag}", response_model=TagStatsResponse) +async def get_tag_stats_endpoint(tag: str): try: - if not url or len(url) > 200: - raise HTTPException(status_code=400, detail="Invalid URL parameter") + if not tag or len(tag) > 200: + raise HTTPException(status_code=400, detail="Invalid tag parameter") - count = get_url_visit_count(url) - return UrlStatsResponse( - url=url, + count = get_tag_visit_count(tag) + return TagStatsResponse( + tag=tag, visit_count=count, last_updated=int(time.time()) ) except Exception as e: - logger.error(f"Error getting URL stats: {e}") + logger.error(f"Error getting tag stats: {e}") raise HTTPException(status_code=500, detail="Internal server error") @app.get("/api/stats", response_model=SystemStatsResponse) @@ -121,10 +121,9 @@ async def get_system_stats_endpoint(): stats = get_system_statistics() return SystemStatsResponse( - total_tracked_urls=stats["total_tracked_urls"], + total_tracked_tags=stats["total_tracked_tags"], total_visits=stats["total_visits"], - new_badges_today=stats["new_badges_today"], - rate_limit_window_hours=0 + new_badges_today=stats["new_badges_today"] ) except Exception as e: logger.error(f"Error getting system stats: {e}") diff --git a/src/models.py b/src/models.py index 05c3206..3588f4e 100644 --- a/src/models.py +++ b/src/models.py @@ -18,8 +18,8 @@ class Meta: database = db class Badge(BaseModel): - """Badge URLs with their total visit counts""" - url = CharField(max_length=200, unique=True) + """Badge tags with their total visit counts""" + tag = CharField(max_length=200, unique=True) visits = IntegerField(default=0) created = IntegerField() # When first created diff --git a/src/schemas.py b/src/schemas.py index 78fd6e0..3a51c86 100644 --- a/src/schemas.py +++ b/src/schemas.py @@ -2,22 +2,21 @@ from typing import Annotated class BadgeParams(BaseModel): - url: Annotated[str, Field(strip_whitespace=True, min_length=1, max_length=200)] + tag: Annotated[str, Field(strip_whitespace=True, min_length=1, max_length=200)] label: Annotated[str, Field(strip_whitespace=True, min_length=1, max_length=20)] = "visits" color: Annotated[str, Field(strip_whitespace=True, min_length=3, max_length=10)] = "4ade80" style: Annotated[str, Field(strip_whitespace=True, min_length=2, max_length=10)] = "flat" logo: Annotated[str, Field(strip_whitespace=True, max_length=20)] = "" -class UrlStatsResponse(BaseModel): - url: str +class TagStatsResponse(BaseModel): + tag: str visit_count: int last_updated: int class SystemStatsResponse(BaseModel): - total_tracked_urls: int + total_tracked_tags: int total_visits: int new_badges_today: int - rate_limit_window_hours: int class AppInfoResponse(BaseModel): version: str diff --git a/src/services.py b/src/services.py index 63e570e..d0587ac 100644 --- a/src/services.py +++ b/src/services.py @@ -9,15 +9,15 @@ logger = logging.getLogger(__name__) -def update_visit_count(cookie_id: str, url_str: str) -> Tuple[int, bool, str]: - """Update visit count for a URL and cookie combination""" +def update_visit_count(cookie_id: str, tag_str: str) -> Tuple[int, bool, str]: + """Update visit count for a tag and cookie combination""" current_time = int(time.time()) new_cookie_id = None try: with db.atomic(): badge, badge_created = Badge.get_or_create( - url=url_str, + tag=tag_str, defaults={'created': current_time} ) @@ -41,15 +41,15 @@ def update_visit_count(cookie_id: str, url_str: str) -> Tuple[int, bool, str]: except Exception as e: logger.error(f"Error updating visit count: {e}") try: - badge = Badge.get(Badge.url == url_str) + badge = Badge.get(Badge.tag == tag_str) return badge.visits, False, new_cookie_id except Badge.DoesNotExist: return 0, False, new_cookie_id -def get_url_visit_count(url_str: str) -> int: - """Get total visit count for a URL""" +def get_tag_visit_count(tag_str: str) -> int: + """Get total visit count for a tag""" try: - badge = Badge.get(Badge.url == url_str) + badge = Badge.get(Badge.tag == tag_str) return badge.visits except Badge.DoesNotExist: return 0 @@ -57,7 +57,7 @@ def get_url_visit_count(url_str: str) -> int: def get_system_statistics() -> dict: """Get system-wide statistics""" try: - total_urls = Badge.select().count() + total_tags = Badge.select().count() total_visits = Badge.select(fn.SUM(Badge.visits)).scalar() or 0 # Count badges created in last 24 hours @@ -67,14 +67,14 @@ def get_system_statistics() -> dict: ).count() return { - "total_tracked_urls": total_urls, + "total_tracked_tags": total_tags, "total_visits": total_visits, "new_badges_today": recent_badges, } except Exception as e: logger.error(f"Error getting system statistics: {e}") return { - "total_tracked_urls": 0, + "total_tracked_tags": 0, "total_visits": 0, "new_badges_today": 0, } diff --git a/static/script.js b/static/script.js index d3b8c24..1dae0cc 100644 --- a/static/script.js +++ b/static/script.js @@ -6,7 +6,7 @@ class BadgeGenerator { } initializeElements() { this.form = document.getElementById('badge-form'); - this.urlInput = document.getElementById('url'); + this.tagInput = document.getElementById('tag'); this.labelInput = document.getElementById('label'); this.colorInput = document.getElementById('color'); this.colorPicker = document.getElementById('color-picker'); @@ -33,16 +33,9 @@ class BadgeGenerator { option.addEventListener('click', () => { this.selectColor(option); }); - }); - - // Copy button events - document.querySelectorAll('.copy-btn').forEach((btn) => { - btn.addEventListener('click', (e) => { - this.copyToClipboard(e.target); - }); }); // Real-time preview [ - this.urlInput, + this.tagInput, this.labelInput, this.colorInput, this.styleSelect, @@ -127,8 +120,8 @@ class BadgeGenerator { updatePreview() { if (!this.badgePreview) return; - const url = this.urlInput.value.trim(); - if (!url) return; + const tag = this.tagInput.value.trim(); + if (!tag) return; const badgeUrl = this.buildBadgeUrl(); this.badgePreview.innerHTML = `Badge Preview`; @@ -137,7 +130,7 @@ class BadgeGenerator { buildBadgeUrl() { const baseUrl = window.location.origin; const params = new URLSearchParams({ - url: this.urlInput.value.trim(), + tag: this.tagInput.value.trim(), label: this.labelInput.value.trim() || 'visits', color: this.colorInput.value.trim() || '247e62', style: this.styleSelect.value || 'flat', @@ -151,17 +144,9 @@ class BadgeGenerator { } generateBadge() { - const url = this.urlInput.value.trim(); - if (!url) { - this.showToast('Please enter a URL'); - return; - } - - // Validate URL format - try { - new URL(url.startsWith('http') ? url : `https://${url}`); - } catch { - this.showToast('Please enter a valid URL'); + const tag = this.tagInput.value.trim(); + if (!tag) { + this.showToast('Please enter a tracking tag'); return; } @@ -173,11 +158,11 @@ class BadgeGenerator { this.badgeUrl.textContent = badgeUrl; // Generate markdown - const markdown = `[![${label}](${badgeUrl})](${url})`; + const markdown = `![${label}](${badgeUrl})`; this.markdownCode.textContent = markdown; // Generate HTML - const html = `${label}`; + const html = `${label}`; this.htmlCode.textContent = html; // Show results diff --git a/templates/index.html b/templates/index.html index 85e773b..5ef2358 100644 --- a/templates/index.html +++ b/templates/index.html @@ -41,9 +41,9 @@

Badge Generator

- - + +
@@ -96,8 +96,7 @@

Badge Generator

How to Use

  1. - Enter the URL you want to track visits for (your GitHub repo, - website, etc.) + Enter a unique tracking tag for your project
  2. Customize the badge label, color, and style to match your project