{"id":27918607,"url":"https://github.com/hannasdev/team-health-dashboard","last_synced_at":"2025-05-06T18:23:49.644Z","repository":{"id":252527817,"uuid":"834483488","full_name":"hannasdev/team-health-dashboard","owner":"hannasdev","description":null,"archived":false,"fork":false,"pushed_at":"2024-10-17T12:07:33.000Z","size":1424,"stargazers_count":0,"open_issues_count":2,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-10-20T14:29:34.936Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/hannasdev.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-07-27T12:06:12.000Z","updated_at":"2024-10-13T10:38:37.000Z","dependencies_parsed_at":"2024-09-05T13:49:10.005Z","dependency_job_id":"7d42aea2-f022-4276-8419-0af5ff4a5683","html_url":"https://github.com/hannasdev/team-health-dashboard","commit_stats":null,"previous_names":["hannasdev/team-health-dashboard"],"tags_count":18,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hannasdev%2Fteam-health-dashboard","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hannasdev%2Fteam-health-dashboard/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hannasdev%2Fteam-health-dashboard/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hannasdev%2Fteam-health-dashboard/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hannasdev","download_url":"https://codeload.github.com/hannasdev/team-health-dashboard/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252742119,"owners_count":21797158,"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":[],"created_at":"2025-05-06T18:23:48.692Z","updated_at":"2025-05-06T18:23:49.633Z","avatar_url":"https://github.com/hannasdev.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Team Health Dashboard\n\nGathers information from your github repository and from your google sheet, to render a dashboard with relevant team statistics.\n\n## Middleware Stack\n\nThe application uses several middleware layers to handle security, authentication, and request processing:\n\n### Security Middleware\n\n1. **Security Headers Middleware**\n\n   - Sets secure HTTP headers\n   - Configures Content Security Policy (CSP)\n   - Enables XSS protection\n   - Sets HSTS headers\n   - Controls frame options\n   - Prevents MIME type sniffing\n\n2. **Rate Limiting Middleware**\n\n   - Protects against brute force attacks\n   - Limits requests per IP address\n   - Configurable time windows and request limits\n   - Returns 429 (Too Many Requests) when limit exceeded\n\n3. **CORS Middleware**\n   - Manages Cross-Origin Resource Sharing\n   - Configurable allowed origins\n   - Supports wildcard or specific origin lists\n   - Handles preflight OPTIONS requests\n   - Sets appropriate CORS headers\n\n### Authentication Middleware\n\n1. **Auth Middleware**\n   - Validates JWT tokens\n   - Handles token expiration\n   - Manages token refresh\n   - Controls access to protected routes\n   - Provides user context to requests\n\n### Request Processing\n\n1. **Body Parser Middleware**\n\n   - Parses JSON request bodies\n   - Handles URL-encoded data\n   - Configurable size limits\n   - Protects against oversized payloads\n\n2. **Error Handler Middleware**\n   - Catches and processes all errors\n   - Provides consistent error responses\n   - Handles different error types\n   - Logs errors appropriately\n\n## APIs\n\n### Public Endpoints\n\n- `GET /health`: Returns the current health status of the API.\n\n### Protected Endpoints (Require Authentication)\n\n- `GET /api/metrics/sync`: Initiates a sync of metrics from GitHub and Google Sheets.\n\n- Method: GET\n- URL: `/api/metrics/sync`\n- Headers: `Authorization: Bearer \u003caccess_token\u003e`\n\n`Response`\n\n- Success (200 OK):\n\n```json\n{\n  \"success\": true,\n  \"data\": {\n    \"message\": \"Metrics synced successfully\"\n  }\n}\n```\n\n- `GET /api/metrics/reset-database`\n\n- Method: GET\n- URL: `/api/metrics/reset-database`\n- Headers: `Authorization: Bearer \u003caccess_token\u003e`\n\n`Response`\n\n- Success (200 OK):\n\n```json\n{\n  \"success\": true,\n  \"data\": {\n    \"message\": \"Database reset successfully\"\n  }\n}\n```\n\n- `GET /api/metrics`: Retrieves metrics from github and google sheet\n\n`Request`\n\n- Method: GET\n- URL: `/api/metrics`\n- Headers: `Authorization: Bearer \u003caccess_token\u003e`\n- Query Parameters:\n  - `timePeriod` (optional): Number of days to fetch data for. Default is 90 days.\n\n`Response`\n\n- Success (200 OK):\n\n```json\n{\n  \"success\": true,\n  \"data\": {\n    \"metrics\": [\n      {\n        \"_id\": \"670a76828d9d1330dac959e9\",\n        \"metric_category\": \"Code Quality\",\n        \"metric_name\": \"Build Success\",\n        \"value\": 95,\n        \"timestamp\": \"2023-08-01T00:00:00.000Z\",\n        \"unit\": \"percent\",\n        \"additional_info\": \"\",\n        \"source\": \"Google Sheets\"\n      },\n      {\n        \"_id\": \"670a76828d9d1330dac959ea\",\n        \"metric_category\": \"Quality\",\n        \"metric_name\": \"Bug Resolution\",\n        \"value\": 2.5,\n        \"timestamp\": \"2023-08-01T00:00:00.000Z\",\n        \"unit\": \"days\",\n        \"additional_info\": \"\",\n        \"source\": \"Google Sheets\"\n      },\n      {\n        \"_id\": \"670a76828d9d1330dac959e5\",\n        \"metric_category\": \"Sprint Metrics\",\n        \"metric_name\": \"Burndown\",\n        \"value\": 30,\n        \"timestamp\": \"2023-08-01T00:00:00.000Z\",\n        \"unit\": \"percent\",\n        \"additional_info\": \"Sprint 23, Day 3\",\n        \"source\": \"Google Sheets\"\n      },\n      {\n        \"_id\": \"670a76828d9d1330dac959e8\",\n        \"metric_category\": \"Code Quality\",\n        \"metric_name\": \"PR Size (avg)\",\n        \"value\": 250,\n        \"timestamp\": \"2023-08-01T00:00:00.000Z\",\n        \"unit\": \"lines\",\n        \"additional_info\": \"\",\n        \"source\": \"Google Sheets\"\n      },\n      {\n        \"_id\": \"670a76828d9d1330dac959e6\",\n        \"metric_category\": \"Efficiency\",\n        \"metric_name\": \"Cycle Time\",\n        \"value\": 3.5,\n        \"timestamp\": \"2023-08-01T00:00:00.000Z\",\n        \"unit\": \"days\",\n        \"additional_info\": \"\",\n        \"source\": \"Google Sheets\"\n      },\n      {\n        \"_id\": \"670a76828d9d1330dac959e4\",\n        \"metric_category\": \"Sprint Metrics\",\n        \"metric_name\": \"Sprint Velocity\",\n        \"value\": 45,\n        \"timestamp\": \"2023-08-01T00:00:00.000Z\",\n        \"unit\": \"points\",\n        \"additional_info\": \"Sprint 23\",\n        \"source\": \"Google Sheets\"\n      },\n      {\n        \"_id\": \"670a76828d9d1330dac959ec\",\n        \"metric_category\": \"Team Health\",\n        \"metric_name\": \"Happiness Index\",\n        \"value\": 4.2,\n        \"timestamp\": \"2023-08-01T00:00:00.000Z\",\n        \"unit\": \"score\",\n        \"additional_info\": \"Scale: 1-5\",\n        \"source\": \"Google Sheets\"\n      },\n      {\n        \"_id\": \"670a76828d9d1330dac959eb\",\n        \"metric_category\": \"Sprint Metrics\",\n        \"metric_name\": \"Goal Achievement\",\n        \"value\": 80,\n        \"timestamp\": \"2023-08-01T00:00:00.000Z\",\n        \"unit\": \"percent\",\n        \"additional_info\": \"Sprint 23\",\n        \"source\": \"Google Sheets\"\n      },\n      {\n        \"_id\": \"670a76828d9d1330dac959e2\",\n        \"metric_category\": \"Workflow\",\n        \"metric_name\": \"WIP Items\",\n        \"value\": 5,\n        \"timestamp\": \"2023-08-01T00:00:00.000Z\",\n        \"unit\": \"items\",\n        \"additional_info\": \"\",\n        \"source\": \"Google Sheets\"\n      },\n      {\n        \"_id\": \"670a76828d9d1330dac959e7\",\n        \"metric_category\": \"Efficiency\",\n        \"metric_name\": \"Code Review Time\",\n        \"value\": 1.2,\n        \"timestamp\": \"2023-08-01T00:00:00.000Z\",\n        \"unit\": \"days\",\n        \"additional_info\": \"\",\n        \"source\": \"Google Sheets\"\n      }\n    ],\n    \"githubStats\": {\n      \"totalPRs\": 168,\n      \"fetchedPRs\": 0,\n      \"timePeriod\": 90\n    },\n    \"totalMetrics\": 180\n  }\n}\n```\n\n### Authentication Endpoints\n\n- `POST /api/auth/register`: Registers a new user.\n- `POST /api/auth/login`: Authenticates a user and returns access and refresh tokens.\n- `POST /api/auth/refresh`: Refreshes an expired access token using a valid refresh token.\n\n`POST /api/auth/register`\n\n### Register a New User\n\n- **Endpoint**: `POST /api/auth/register`\n- **Body**:\n\n```json\n{\n  \"email\": \"user@example.com\",\n  \"password\": \"securepassword123\"\n}\n```\n\n`Response`\n\n- Success (201 Created):\n\n```json\n{\n  \"accessToken\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...\",\n  \"refreshToken\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...\",\n  \"user\": {\n    \"id\": \"user_id\",\n    \"email\": \"user@example.com\"\n  }\n}\n```\n\n- Error (409 Conflict):\n\n```json\n{\n  \"error\": \"User already exists\"\n}\n```\n\n### Login\n\n`POST /api/auth/login`\n\nAuthenticates a user and returns a JWT token.\n\n`Request`\n\n- Method: POST\n- URL: `/api/auth/login`\n- Body:\n\n```json\n{\n  \"email\": \"user@example.com\",\n  \"password\": \"securepassword123\"\n}\n```\n\n`Response`\n\n- Success (200 OK):\n\n```json\n{\n  \"accessToken\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...\",\n  \"refreshToken\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...\",\n  \"user\": {\n    \"id\": \"user_id\",\n    \"email\": \"user@example.com\"\n  }\n}\n```\n\n- Error (401 Unauthorized):\n\n```json\n{\n  \"error\": \"Invalid credentials\"\n}\n```\n\nNote: The JWT token returned by both register and login endpoints should be included in the Authorization header for subsequent authenticated requests.\n\n### Refresh Token\n\n`POST /api/auth/refresh`\n\nRefreshes access token with a refresh token.\n\n- Method: POST\n- URL: `/api/auth/refresh`\n- Body:\n\n```json\n{\n  \"refreshToken\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...\"\n}\n```\n\n- Success (200 OK):\n\n```json\n{\n  \"accessToken\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...\",\n  \"refreshToken\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...\"\n}\n```\n\n- Error (401 Unauthorized):\n\n```json\n{\n  \"error\": \"Invalid refresh token\"\n}\n```\n\n### Logout\n\n`POST /api/auth/logout`\n\n- Headers: `Authorization: Bearer \u003caccess_token\u003e`\n- Body:\n\n```json\n{\n  \"refreshToken\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...\"\n}\n```\n\n- Success (204 No Content)\n\n- Error (401 Unauthorized):\n\n```json\n{\n  \"error\": \"Invalid token\"\n}\n```\n\n## Configuration\n\n### Middleware Configuration\n\n```typescript\n// CORS Configuration\n{\n  CORS_ORIGIN: 'http://localhost:3000,http://example.com' // comma-separated list of allowed origins\n}\n\n// Rate Limit Configuration\n{\n  windowMs: 15 * 60 * 1000, // 15 minutes\n  maxRequests: 100 // requests per window\n}\n\n// Security Headers Configuration\n{\n  contentSecurityPolicy: {\n    directives: {\n      defaultSrc: [\"'self'\"],\n      scriptSrc: [\"'self'\", \"'unsafe-inline'\"],\n      styleSrc: [\"'self'\", \"'unsafe-inline'\"],\n      imgSrc: [\"'self'\", \"data:\", \"https:\"],\n      connectSrc: [\"'self'\"]\n    }\n  },\n  hsts: {\n    maxAge: 31536000,\n    includeSubDomains: true,\n    preload: true\n  }\n}\n```\n\n## Development\n\nHere's a basic example of how to use these endpoints in a JavaScript client:\n\n```javascript\nconst API_URL = 'http://your-api-url';\nlet accessToken = '';\nlet refreshToken = '';\n\n// Function to handle authentication\nasync function authenticate(email, password) {\n  const response = await fetch(`${API_URL}/api/auth/login`, {\n    method: 'POST',\n    headers: { 'Content-Type': 'application/json' },\n    body: JSON.stringify({ email, password }),\n  });\n  const data = await response.json();\n  if (response.ok) {\n    accessToken = data.accessToken;\n    refreshToken = data.refreshToken;\n    return true;\n  }\n  return false;\n}\n\n// Function to refresh the token\nasync function refreshAccessToken() {\n  const response = await fetch(`${API_URL}/api/auth/refresh`, {\n    method: 'POST',\n    headers: { 'Content-Type': 'application/json' },\n    body: JSON.stringify({ refreshToken }),\n  });\n  const data = await response.json();\n  if (response.ok) {\n    accessToken = data.accessToken;\n    refreshToken = data.refreshToken;\n    return true;\n  }\n  return false;\n}\n\n// Function to fetch metrics\nfunction fetchMetrics(timePeriod = 90) {\n  const eventSource = new EventSource(\n    `${API_URL}/api/metrics?timePeriod=${timePeriod}`,\n    {\n      headers: { Authorization: `Bearer ${accessToken}` },\n    },\n  );\n\n  eventSource.onmessage = event =\u003e {\n    const data = JSON.parse(event.data);\n    console.log('Received data:', data);\n  };\n\n  eventSource.addEventListener('progress', event =\u003e {\n    const progress = JSON.parse(event.data);\n    console.log('Progress:', progress);\n  });\n\n  eventSource.addEventListener('result', event =\u003e {\n    const result = JSON.parse(event.data);\n    console.log('Final result:', result);\n    eventSource.close();\n  });\n\n  eventSource.onerror = error =\u003e {\n    console.error('EventSource failed:', error);\n    eventSource.close();\n  };\n}\n\n// Usage\nauthenticate('user@example.com', 'password').then(success =\u003e {\n  if (success) {\n    fetchMetrics();\n  }\n});\n```\n\nUtilises the MVC-pattern with dependency injection and inversion of control for improved testability.\n\n## Conventional Commits\n\n```txt\nfeat: add user authentication API\nfix: resolve data race condition in job queue\nBREAKING CHANGE: refactor API response format\n```\n\n## Semantic Versioning\n\nMAJOR.MINOR.PATCH e.g. `1.2.3`\n\n- MAJOR version for incompatible API changes\n- MINOR version for backwards-compatible functionality additions\n- PATCH version for backwards-compatible bug fixes\n\n## Release Process\n\n1. Develop features in feature branches\n2. Merge feature branches into main using pull requests\n3. CI/CD pipeline automatically bumps version based on commit messages\n4. CI/CD pipeline generates changelog and creates a new Git tag\n5. CI/CD pipeline pushes the new version and changelog to the repository\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhannasdev%2Fteam-health-dashboard","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhannasdev%2Fteam-health-dashboard","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhannasdev%2Fteam-health-dashboard/lists"}