{"id":25188635,"url":"https://github.com/montasim/github-readme-counter","last_synced_at":"2026-04-12T13:03:01.806Z","repository":{"id":246035922,"uuid":"819871721","full_name":"montasim/github-readme-counter","owner":"montasim","description":"A simple web service that generates a dynamic SVG image showing a visitor count. It increments a counter every time the endpoint is called and returns an SVG image displaying the updated count.","archived":false,"fork":false,"pushed_at":"2024-07-25T05:17:32.000Z","size":25,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-02-09T20:36:44.956Z","etag":null,"topics":["api","counter","customizable-svg","dynamic-svg","expressjs","github","glitch","nodejs","real-time","server","svg","visitor-counter","web-application","web-service"],"latest_commit_sha":null,"homepage":"https://plum-meowing-freon.glitch.me/count.svg","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/montasim.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-06-25T10:48:55.000Z","updated_at":"2024-07-25T06:33:45.000Z","dependencies_parsed_at":"2024-06-25T13:55:59.679Z","dependency_job_id":"66b0496e-5d19-403a-bf1e-41c7a7ea12c5","html_url":"https://github.com/montasim/github-readme-counter","commit_stats":null,"previous_names":["montasim/github-readme-counter"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/montasim%2Fgithub-readme-counter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/montasim%2Fgithub-readme-counter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/montasim%2Fgithub-readme-counter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/montasim%2Fgithub-readme-counter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/montasim","download_url":"https://codeload.github.com/montasim/github-readme-counter/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247157276,"owners_count":20893221,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api","counter","customizable-svg","dynamic-svg","expressjs","github","glitch","nodejs","real-time","server","svg","visitor-counter","web-application","web-service"],"created_at":"2025-02-09T20:29:34.713Z","updated_at":"2026-04-12T13:03:01.801Z","avatar_url":"https://github.com/montasim.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# GitHub Readme Counter\r\n\r\n\u003c!-- repository summary badges start --\u003e\r\n\u003cdiv\u003e\r\n    \u003cimg alt=\"GitHub contributors badge\" src=\"https://img.shields.io/github/contributors/montasim/github-readme-counter?labelColor=EB008B\u0026color=00B8B5\"\u003e\r\n    \u003cimg alt=\"GitHub language count badge\" src=\"https://img.shields.io/github/languages/count/montasim/github-readme-counter?labelColor=EB008B\u0026color=00B8B5\"/\u003e\r\n    \u003cimg alt=\"GitHub top language badge\" src=\"https://img.shields.io/github/languages/top/montasim/github-readme-counter?labelColor=EB008B\u0026color=00B8B5\"\u003e\r\n    \u003cimg alt=\"GitHub code size badge\" src=\"https://img.shields.io/github/languages/code-size/montasim/github-readme-counter?labelColor=EB008B\u0026color=00B8B5\"\u003e\r\n    \u003cimg alt=\"GitHub repo size badge\" src=\"https://img.shields.io/github/repo-size/montasim/github-readme-counter?labelColor=EB008B\u0026color=00B8B5\"\u003e\r\n    \u003cimg alt=\"GitHub file count badge\" src=\"https://img.shields.io/github/directory-file-count/montasim/github-readme-counter?labelColor=EB008B\u0026color=00B8B5\"\u003e\r\n    \u003cimg alt=\"GitHub open issues badge\" src=\"https://img.shields.io/github/issues-raw/montasim/github-readme-counter?labelColor=EB008B\u0026color=00B8B5\"\u003e\r\n    \u003cimg alt=\"GitHub closed issues badge\" src=\"https://img.shields.io/github/issues-closed-raw/montasim/github-readme-counter?labelColor=EB008B\u0026color=00B8B5\"\u003e\r\n    \u003cimg alt=\"GitHub open pull requests badge\" src=\"https://img.shields.io/github/issues-pr-raw/montasim/github-readme-counter?labelColor=EB008B\u0026color=00B8B5\"\u003e\r\n    \u003cimg alt=\"GitHub closed pull requests badge\" src=\"https://img.shields.io/github/issues-pr-closed-raw/montasim/github-readme-counter?labelColor=EB008B\u0026color=00B8B5\"\u003e\r\n    \u003cimg alt=\"GitHub active milestones badge\" src=\"https://img.shields.io/github/milestones/open/montasim/github-readme-counter?labelColor=EB008B\u0026color=00B8B5\"\u003e\r\n    \u003cimg alt=\"GitHub completed milestones badge\" src=\"https://img.shields.io/github/milestones/closed/montasim/github-readme-counter?labelColor=EB008B\u0026color=00B8B5\"\u003e\r\n    \u003cimg alt=\"GitHub license badge\" src=\"https://img.shields.io/github/license/montasim/github-readme-counter?labelColor=EB008B\u0026color=00B8B5\"\u003e\r\n    \u003cimg alt=\"GitHub last commit badge\" src=\"https://img.shields.io/github/last-commit/montasim/github-readme-counter?labelColor=EB008B\u0026color=00B8B5\"\u003e\r\n    \u003cimg alt=\"GitHub Discussions badge\" src=\"https://img.shields.io/github/discussions/montasim/github-readme-counter?labelColor=EB008B\u0026color=00B8B5\"\u003e\r\n\u003c/div\u003e\r\n\u003c!-- repository summary badges end --\u003e\r\n\r\nA modern, well-architected web service that generates dynamic SVG images showing visitor counts. Built with Express.js following Clean Code principles, SOLID design patterns, and industry best practices.\r\n\r\n## ✨ Features\r\n\r\n- **Dynamic SVG Generation**: Creates customizable SVG images with visitor counts\r\n- **Real-time Counter**: Increments on each request with persistent storage\r\n- **Customizable Appearance**: Customize background and text colors via URL parameters\r\n- **Health Check Endpoint**: Monitor service status and uptime\r\n- **Comprehensive Error Handling**: Proper HTTP status codes and error messages\r\n- **Request Logging**: Detailed logging with color-coded status codes\r\n- **Input Validation**: Validates hex color codes with proper error messages\r\n- **Graceful Shutdown**: Handles termination signals properly\r\n- **Backward Compatible**: Supports both old and new query parameter formats\r\n\r\n## 🏗️ Architecture\r\n\r\nThis project follows a modular, layered architecture with clear separation of concerns:\r\n\r\n```\r\nsrc/\r\n├── config/                    # Configuration management\r\n│   ├── constants.js          # Magic numbers, default values, SVG config\r\n│   ├── paths.js              # Path configurations\r\n│   └── index.js              # Export all configs\r\n├── services/                  # Business logic layer\r\n│   ├── CounterService.js     # Counter operations (read/write/increment)\r\n│   └── ImageService.js       # SVG generation\r\n├── controllers/               # Request handling layer\r\n│   └── CounterController.js  # HTTP request handler\r\n├── routes/                    # Route definitions\r\n│   ├── counterRoutes.js      # Counter endpoint routes\r\n│   ├── healthRoutes.js       # Health check endpoint\r\n│   └── index.js              # Export all routes\r\n├── middleware/                # Express middleware\r\n│   ├── errorHandler.js       # Global error handling\r\n│   ├── logger.js             # Request/response logging\r\n│   └── notFound.js           # 404 handler\r\n├── utils/                     # Utility functions\r\n│   ├── validators.js         # Input validation\r\n│   └── helpers.js            # Helper functions\r\n├── app.js                    # Express app setup\r\n├── index.js                  # Entry point with graceful shutdown\r\n└── counter.txt               # Counter storage\r\n```\r\n\r\n### Design Principles\r\n\r\n- **Single Responsibility Principle**: Each module has a single, well-defined purpose\r\n- **Open/Closed Principle**: Easy to extend without modifying existing code\r\n- **Dependency Inversion**: High-level modules don't depend on low-level modules\r\n- **Clean Code**: Meaningful names, small functions, comprehensive documentation\r\n- **Express.js Best Practices**: Proper middleware chain, error handling, and logging\r\n\r\n## 🛠️ Requirements\r\n\r\n- [Node.js](https://nodejs.org/en) (\u003e= 20.x)\r\n- [pnpm](https://pnpm.io/) (\u003e= 8.x)\r\n\r\n## 🚀 Installation\r\n\r\n1. **Clone the repository**:\r\n   ```bash\r\n   git clone https://github.com/montasim/github-readme-counter.git\r\n   cd github-readme-counter\r\n   ```\r\n\r\n2. **Install dependencies**:\r\n   ```bash\r\n   pnpm install\r\n   ```\r\n\r\n3. **Start the server**:\r\n   ```bash\r\n   pnpm start\r\n   ```\r\n\r\n   For development with auto-reload:\r\n   ```bash\r\n   pnpm dev\r\n   ```\r\n\r\nThe server will start on the port specified in your environment variables or default to port 3000.\r\n\r\n## 📡 API Endpoints\r\n\r\n### Health Check\r\n\r\nCheck the service status and uptime:\r\n\r\n```bash\r\nGET /health\r\n```\r\n\r\n**Response**:\r\n```json\r\n{\r\n  \"status\": \"ok\",\r\n  \"timestamp\": \"2026-02-23T05:00:00.000Z\",\r\n  \"uptime\": 123.456\r\n}\r\n```\r\n\r\n### Counter SVG\r\n\r\nGet a dynamic SVG image with the visitor count:\r\n\r\n```bash\r\nGET /count.svg\r\n```\r\n\r\n**Query Parameters**:\r\n\r\n| Parameter | Short | Description | Default |\r\n|-----------|-------|-------------|---------|\r\n| Background Color | `bg` | Hex code for background color | `000000` (black) |\r\n| Text Color | `tc` | Hex code for text color | `EB008B` (magenta) |\r\n\r\n\u003e **Note**: For backward compatibility, the old parameter names (`backgroundColor` and `textColor`) are still supported. The new short names take precedence if both are provided.\r\n\r\n**Examples**:\r\n\r\nDefault colors:\r\n```bash\r\nhttp://localhost:3000/count.svg\r\n```\r\n\r\nCustom colors (new parameters):\r\n```bash\r\nhttp://localhost:3000/count.svg?bg=FFFFFF\u0026tc=0000FF\r\n```\r\n\r\nCustom colors (old parameters - still supported):\r\n```bash\r\nhttp://localhost:3000/count.svg?backgroundColor=FFFFFF\u0026textColor=0000FF\r\n```\r\n\r\nMixed parameters (new takes precedence):\r\n```bash\r\nhttp://localhost:3000/count.svg?bg=FFFFFF\u0026textColor=0000FF\r\n```\r\n\r\n![Default Visitor Count](./default.png)\r\n![Customized Visitor Count](./customized-example.png)\r\n\r\n## 🎨 Customization\r\n\r\n### Color Guidelines\r\n\r\n- Provide hex codes **without** the `#` symbol (e.g., `FFFFFF`, not `#FFFFFF`)\r\n- Use 6-digit hex codes (e.g., `000000`, `FFFFFF`, `EB008B`)\r\n- Invalid or unsupported color values will revert to defaults\r\n\r\n### Color Examples\r\n\r\n| Color | Hex Code | Preview |\r\n|-------|----------|---------|\r\n| Black | `000000` | ⬛ |\r\n| White | `FFFFFF` | ⬜ |\r\n| Red | `FF0000` | 🟥 |\r\n| Green | `00FF00` | 🟩 |\r\n| Blue | `0000FF` | 🟦 |\r\n| Magenta | `EB008B` | 💜 |\r\n| Cyan | `00B8B5` | 💠 |\r\n\r\n## 🧪 Testing\r\n\r\nAll features have been tested and verified:\r\n\r\n✅ **Health Check Endpoint**\r\n- Returns 200 status\r\n- Includes service status, timestamp, and uptime\r\n\r\n✅ **Counter Endpoint**\r\n- Returns SVG image with incremented counter\r\n- Default colors work correctly\r\n- Custom colors apply properly\r\n\r\n✅ **Query Parameters**\r\n- New short parameters (`bg`, `tc`) work\r\n- Old long parameters (`backgroundColor`, `textColor`) work\r\n- Mixed parameters work (new takes precedence)\r\n- No parameters use defaults\r\n\r\n✅ **Error Handling**\r\n- Invalid hex codes return 400 status\r\n- Proper error messages displayed\r\n- Error logging with stack traces\r\n\r\n✅ **404 Handler**\r\n- Undefined routes return 404 status\r\n- Consistent 404 response format\r\n\r\n✅ **Logging**\r\n- All requests logged with timestamp and method\r\n- All responses logged with status code and duration\r\n- Color-coded status codes (green for 2xx, yellow for 4xx, red for 5xx)\r\n\r\n## 🔧 Error Handling\r\n\r\nThe service implements comprehensive error handling:\r\n\r\n- **400 Bad Request**: Invalid input (e.g., invalid hex color codes)\r\n- **404 Not Found**: Route not found\r\n- **500 Internal Server Error**: Unexpected server errors\r\n\r\nAll errors are logged with full stack traces for debugging.\r\n\r\n## 📊 Logging\r\n\r\nThe service provides detailed logging:\r\n\r\n- **Request Logging**: Timestamp, HTTP method, and path\r\n- **Response Logging**: Status code and response duration\r\n- **Error Logging**: Full error messages and stack traces\r\n- **Color-coded Output**: Green for success, yellow for warnings, red for errors\r\n\r\n## 🔄 Graceful Shutdown\r\n\r\nThe service handles termination signals properly:\r\n\r\n- **SIGTERM**: Graceful shutdown on termination signal\r\n- **SIGINT**: Graceful shutdown on interrupt signal (Ctrl+C)\r\n- **Timeout**: Forces shutdown after 10 seconds if graceful shutdown fails\r\n- **Cleanup**: Properly closes server connections\r\n\r\n## 🤝 Contributing\r\n\r\nWe welcome contributions! Please read [CONTRIBUTION.md](./CONTRIBUTION.md) for details on:\r\n- How to contribute\r\n- Coding style guidelines\r\n- Commit message conventions\r\n- Code review process\r\n\r\n## 📖 Author\r\n\r\n\u003ctable\u003e\r\n  \u003ctr\u003e\r\n    \u003ctd  align=center\u003e\r\n        \u003cimg src=\"https://avatars.githubusercontent.com/u/95298623?v=4\" width=\"100px\" alt=\"Moon\"\u003e\r\n        \u003ca href=\"https://github.com/montasim\"\u003e\r\n          \u003cbr\u003e\r\n            Ｍ♢ＮＴΛＳＩＭ\r\n          \u003c/br\u003e\r\n        \u003c/a\u003e\r\n    \u003c/td\u003e\r\n  \u003c/tr\u003e\r\n\u003c/table\u003e\r\n\r\n## 👥 Contributors\r\n\r\n[![Contributors Display](https://badges.pufler.dev/contributors/montasim/github-readme-counter?size=50\u0026padding=5\u0026perRow=10\u0026bots=true)](https://badges.pufler.dev)\r\n\r\n## 📝 License\r\n\r\nThis project is licensed under the MIT License - see the [LICENSE](./LICENSE) file for details.\r\n\r\n## 🙏 Acknowledgments\r\n\r\n1. [sagar-viradiya](https://github.com/sagar-viradiya/sagar-viradiya)\r\n2. [pufler.dev/badge-it/](https://pufler.dev/badge-it/)\r\n3. [github-readme-counter](https://github.com/iamskok/github-readme-counter)\r\n\r\n## 📚 Additional Documentation\r\n\r\n- [IMPLEMENTATION_SUMMARY.md](./IMPLEMENTATION_SUMMARY.md) - Details on query parameter simplification\r\n- [REFACTORING_SUMMARY.md](./REFACTORING_SUMMARY.md) - Complete refactoring documentation\r\n- [SECURITY.md](./SECURITY.md) - Security policy and reporting\r\n- [CONTRIBUTION.md](./CONTRIBUTION.md) - Contribution guidelines\r\n\r\n## 🌟 Show Your Support\r\n\r\nIf you find this project helpful, please consider:\r\n- ⭐ Starring the repository\r\n- 🍴 Forking and contributing\r\n- 📢 Sharing with others\r\n\r\n---\r\n\r\nBuilt with ❤️ using Express.js, following Clean Code principles and SOLID design patterns.\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmontasim%2Fgithub-readme-counter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmontasim%2Fgithub-readme-counter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmontasim%2Fgithub-readme-counter/lists"}