{"id":29208932,"url":"https://github.com/yaodingyd/bounty-board","last_synced_at":"2025-10-18T21:52:33.718Z","repository":{"id":301820007,"uuid":"1010402621","full_name":"yaodingyd/bounty-board","owner":"yaodingyd","description":"Self-hosted bounty issue tracking system, powered by Cloudflare worker.","archived":false,"fork":false,"pushed_at":"2025-06-29T02:10:27.000Z","size":52,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-06-29T03:29:12.814Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://bounty-board.yaodingyd.workers.dev/","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/yaodingyd.png","metadata":{"files":{"readme":"README.md","changelog":null,"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,"zenodo":null}},"created_at":"2025-06-29T01:53:43.000Z","updated_at":"2025-06-29T02:10:30.000Z","dependencies_parsed_at":"2025-06-29T03:29:42.356Z","dependency_job_id":"ba993689-8c8e-4157-ba1e-3d97dc51d282","html_url":"https://github.com/yaodingyd/bounty-board","commit_stats":null,"previous_names":["yaodingyd/bounty-board"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/yaodingyd/bounty-board","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yaodingyd%2Fbounty-board","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yaodingyd%2Fbounty-board/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yaodingyd%2Fbounty-board/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yaodingyd%2Fbounty-board/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/yaodingyd","download_url":"https://codeload.github.com/yaodingyd/bounty-board/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yaodingyd%2Fbounty-board/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":263212436,"owners_count":23431514,"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-07-02T20:37:23.908Z","updated_at":"2025-10-18T21:52:28.694Z","avatar_url":"https://github.com/yaodingyd.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Bounty Board\n\nSelf-hosted bounty issue tracking system, powered by Cloudflare worker.\n\n**note** 99.99% of the code are written by AI but it works perfectly, and free tier Cloudflare account make just suit personal usage.\n\n## Features\n\n- Searches GitHub issues with \"bounty\" labels or comments\n- Ranks issues based on workability criteria:\n  1. Has a bounty label or comment\n  2. Contains clear implementation details\n  3. Not yet completed (no payout/rewarded comments)\n  4. Has fewer than 10 comments\n- Returns a sorted list of issues with their workability scores\n\n## Setup\n\n### Prerequisites\n\n- Node.js (v18+ recommended, v24 for best performance)\n- pnpm package manager (`npm install -g pnpm`)\n- Cloudflare account with Workers access\n- Wrangler CLI (`npm install -g wrangler` or `pnpm add -g wrangler`)\n- GitHub Personal Access Token (for API access)\n\n### Technical Details\n\nThis project uses:\n\n- TypeScript with native support from Cloudflare Workers\n- No build step required - TypeScript is compiled at runtime\n- Cloudflare Workers for serverless execution\n- D1 Database for persistent storage with full-text search\n- Modular architecture for maintainability\n\n### Project Structure\n\n```\n├── src/\n│   ├── index.ts       # Main entry point \u0026 routing (HTTP + cron)\n│   ├── types.ts       # TypeScript interfaces\n│   ├── github.ts      # GitHub API functions (single + multi-page)\n│   ├── database.ts    # D1 database operations (search + upsert)\n│   ├── ranking.ts     # Issue scoring logic\n│   ├── search.ts      # Search API handler (D1-first)\n│   └── cron.ts        # Scheduled GitHub data refresh\n├── public/\n│   └── index.html     # Frontend UI\n├── migrations/\n│   └── 0001_create_bounty_issues.sql\n├── .vscode/           # VSCode configuration\n├── wrangler.toml      # Cloudflare Worker config (with cron)\n└── package.json       # Dependencies \u0026 scripts\n```\n\n### Installation\n\n1. **Clone and setup**:\n\n```bash\ngit clone \u003crepository-url\u003e\ncd bounty-board\npnpm install\n```\n\n2. **Authenticate with Cloudflare**:\n\n```bash\nwrangler login\n```\n\n3. **Configure environment variables**:\n\n   **Option A: Using wrangler.toml (for development)**\n\n   ```toml\n   [vars]\n   GITHUB_TOKEN = \"your_github_token_here\"\n   ```\n\n   **Option B: Using wrangler secrets (recommended for production)**\n\n   ```bash\n   wrangler secret put GITHUB_TOKEN\n   # Enter your token when prompted\n   ```\n\n\u003e **Important**: A GitHub token is required for the application to function properly. Without a token, the GitHub API limits requests to 60 per hour, which is insufficient for this application. For production, use `wrangler secret` instead of storing tokens in `wrangler.toml`.\n\n### Getting a GitHub Token\n\n1. Go to [GitHub Settings \u003e Developer settings \u003e Personal access tokens](https://github.com/settings/tokens)\n2. Click \"Generate new token\" (classic)\n3. Give it a name like \"Bounty Board\"\n4. Select the \"public_repo\" scope (or just \"repo\" if you want to include private repositories)\n5. Click \"Generate token\"\n6. Copy the token and add it to your wrangler.toml file\n\n## Development\n\n### Local Development\n\n1. **Start local development server**:\n\n```bash\npnpm start\n# or\nwrangler dev\n```\n\n2. **Local D1 database setup**:\n\n```bash\n# Create local database\nwrangler d1 execute bounty-board-db --local --file=./migrations/0001_create_bounty_issues.sql\n\n# Run with local database\nwrangler dev --local\n```\n\n### Environment-specific Configuration\n\nCreate different configurations for development/staging/production:\n\n```toml\n# wrangler.toml\n[env.staging]\nname = \"bounty-board-staging\"\nvars = { GITHUB_TOKEN = \"staging_token\" }\n\n[env.production]\nname = \"bounty-board-prod\"\n# Use secrets for production: wrangler secret put GITHUB_TOKEN --env production\n```\n\n## Deployment\n\n### Deploy to Production\n\n```bash\n# Deploy with environment-specific settings\nwrangler deploy --env production\n\n# Or use the npm script\npnpm deploy\n```\n\n### CI/CD Best Practices\n\n1. **Use GitHub Actions**:\n\n```yaml\n# .github/workflows/deploy.yml\nname: Deploy\non:\n  push:\n    branches: [main]\njobs:\n  deploy:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v3\n      - uses: actions/setup-node@v3\n        with:\n          node-version: \"18\"\n      - run: npm install -g wrangler\n      - run: pnpm install\n      - run: wrangler deploy --env production\n        env:\n          CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}\n```\n\n2. **Set up secrets in GitHub**:\n   - `CLOUDFLARE_API_TOKEN`: Your Cloudflare API token\n   - Use Wrangler secrets for sensitive data like `GITHUB_TOKEN`\n\n## Cron Job Management\n\n### Testing Cron Jobs Locally\n\n```bash\n# Test the scheduled function during development\npnpm run cron:trigger\n\n# Monitor cron job logs\npnpm run logs:cron\n```\n\n### Production Monitoring\n\n```bash\n# View all worker logs\npnpm run logs\n\n# Query data freshness\npnpm run db:query \"SELECT MAX(last_fetched_at) as last_fetch, COUNT(*) as total_issues FROM bounty_issues\"\n```\n\n### Cron Job Status\n\nThe cron job runs automatically every hour. You can verify it's working by:\n\n1. Checking the worker logs for cron activity\n2. Querying the `last_fetched_at` timestamp in D1\n3. Monitoring issue counts in the database\n\n## API Usage\n\n### Endpoint\n\n```\nGET https://your-worker-subdomain.workers.dev/\n```\n\n### Query Parameters\n\n- `query`: Search keywords (default: empty string - shows all issues)\n- `sort`: Sort parameter (default: `created`)\n- `order`: Sort order (default: `desc`)\n- `per_page`: Number of results per page (default: `10`)\n- `page`: Page number (default: `1`)\n\n### Search Functionality\n\nThe application now provides a simple, user-friendly search interface:\n\n- **Default View**: Shows all bounty issues sorted by workability score\n- **Keyword Search**: Search across issue titles, descriptions, and repository names\n- **No GitHub Syntax**: Users don't need to know GitHub query syntax\n- **Instant Results**: All searches are performed against the local D1 database\n- **Full-text Search**: Powered by SQLite FTS for fast, relevant results\n\n**Example searches:**\n\n- `react` - Find issues related to React\n- `bug fix` - Find bug fixing opportunities\n- `typescript` - Find TypeScript-related issues\n- `ethereum` - Find blockchain/Ethereum issues\n\n### Example Requests\n\n```bash\n# Get all issues (default view)\nGET https://your-worker-subdomain.workers.dev/search\n\n# Search for React-related issues\nGET https://your-worker-subdomain.workers.dev/search?query=react\n\n# Search with pagination\nGET https://your-worker-subdomain.workers.dev/search?query=typescript\u0026page=2\u0026per_page=20\n```\n\n### Example Response\n\n```json\n[\n  {\n    \"id\": 123456789,\n    \"number\": 42,\n    \"title\": \"Implement feature X\",\n    \"html_url\": \"https://github.com/owner/repo/issues/42\",\n    \"body\": \"Detailed description of the issue...\",\n    \"state\": \"open\",\n    \"comments\": 5,\n    \"labels\": [{ \"name\": \"bounty\" }],\n    \"repository_url\": \"https://api.github.com/repos/owner/repo\",\n    \"created_at\": \"2023-01-01T00:00:00Z\",\n    \"updated_at\": \"2023-01-02T00:00:00Z\",\n    \"comments_url\": \"https://api.github.com/repos/owner/repo/issues/42/comments\",\n    \"user\": {\n      \"login\": \"username\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/12345678\"\n    },\n    \"score\": 100,\n    \"repository\": \"owner/repo\",\n    \"hasBountyLabel\": true,\n    \"hasBountyComment\": false,\n    \"hasPayoutComment\": false,\n    \"commentCount\": 5,\n    \"hasImplementationDetails\": true\n  }\n]\n```\n\n## Ranking Algorithm\n\nIssues are ranked based on the following criteria:\n\n1. **Bounty Presence** (30 points): Issue has a \"bounty\" label or a comment mentioning a bounty\n2. **Implementation Details** (25 points): Issue has clear implementation details\n3. **Not Completed** (20 points): Issue has no \"payout\" or \"rewarded\" comments\n4. **Comment Count** (25 points): Issue has fewer than 10 comments\n\nThe maximum score is 100 points. Issues are sorted by score in descending order.\n\n## Troubleshooting\n\n### API Rate Limiting\n\nIf you encounter 403 errors or \"API rate limit exceeded\" messages, it's likely because you're hitting GitHub's API rate limits. Solutions:\n\n1. Add a GitHub token to your wrangler.toml file\n2. Reduce the number of requests by using smaller page sizes\n3. Implement caching to reduce API calls\n\n### 500 Internal Server Errors\n\nIf you're getting 500 errors:\n\n1. Check the worker logs for detailed error messages\n2. Verify your GitHub token is valid and has the correct permissions\n3. Try reducing the complexity of your query or the number of results requested\n\n### Slow Response Times\n\nIf the API is responding slowly:\n\n1. Reduce the `per_page` parameter to fetch fewer issues at once\n2. Consider implementing caching for frequently accessed data\n3. Optimize the ranking algorithm to process fewer comments\n\n# D1 Database Setup Instructions\n\n## Prerequisites\n\n- Cloudflare account with Workers/D1 access\n- wrangler CLI installed and authenticated\n\n## Setup Steps\n\n### 1. Create D1 Database\n\n```bash\n# Create the D1 database\nwrangler d1 create bounty-board-db\n```\n\nThis will output something like:\n\n```\n✅ Successfully created DB 'bounty-board-db'\n\n[[d1_databases]]\nbinding = \"DB\"\ndatabase_name = \"bounty-board-db\"\ndatabase_id = \"your-database-id-here\"\n```\n\n### 2. Update wrangler.toml\n\nCopy the database_id from the output above and update the `database_id` field in your `wrangler.toml` file.\n\n### 3. Run Migration\n\n```bash\n# Execute the migration to create tables\nwrangler d1 execute bounty-board-db --file=./migrations/0001_create_bounty_issues.sql\n```\n\n### 4. Verify Setup\n\n```bash\n# Check if tables were created successfully\nwrangler d1 execute bounty-board-db --command=\"SELECT name FROM sqlite_master WHERE type='table';\"\n```\n\nYou should see output showing the `bounty_issues` and `bounty_issues_fts` tables.\n\n### 5. Deploy Worker\n\n```bash\n# Deploy the updated worker\nwrangler deploy\n```\n\n## Local Development\n\nFor local development, you can create a local D1 database:\n\n```bash\n# Create local database\nwrangler d1 execute bounty-board-db --local --file=./migrations/0001_create_bounty_issues.sql\n\n# Run worker locally\nwrangler dev\n```\n\n## Database Schema\n\nThe `bounty_issues` table stores:\n\n- GitHub issue metadata (id, title, body, etc.)\n- Repository information\n- User information\n- Scoring/ranking data\n- Labels as JSON\n- Timestamps for cache management\n\nThe `bounty_issues_fts` virtual table enables full-text search across:\n\n- Issue titles\n- Issue bodies\n- Repository names\n- User logins\n\n## How It Works\n\n1. **Cron-based Data Refresh**: A Cloudflare Workers cron job runs every hour to fetch up to 2000 GitHub issues\n2. **Upsert Strategy**: New issues are inserted, existing issues are updated with latest data\n3. **Fast Searches**: All user searches query the D1 database directly (no GitHub API delays)\n4. **Simple Search Interface**: Users search with keywords instead of GitHub query syntax\n5. **Full-text Search**: Searches across issue titles, descriptions, and repository names\n6. **Default View**: Shows all issues sorted by workability score when no search query is provided\n7. **Emergency Fallback**: If D1 is empty (first deploy), falls back to GitHub API temporarily\n\n## Cron Architecture\n\n- **Schedule**: `0 * * * *` (every hour at minute 0)\n- **Process**: Fetch → Rank → Upsert to D1\n- **Performance**: Up to 2000 issues processed per hour\n- **Resilience**: Continues on individual page/issue failures\n- **Monitoring**: Comprehensive logging for debugging\n\n## User Settings \u0026 Configuration\n\n### Configurable GitHub Search Query\n\nThe application now allows users to customize the GitHub search query used to fetch bounties through the settings interface.\n\n**Features:**\n\n- **Default Query**: `is:issue is:open label:bounty`\n- **Custom Queries**: Users can modify the search query to find different types of issues\n- **Real-time Updates**: Changes are applied immediately to both the cron job and search functionality\n- **Persistent Storage**: Settings are stored in the D1 database and persist across sessions\n\n**Examples of custom queries:**\n\n- `is:issue is:open label:good-first-issue` - Find beginner-friendly issues\n- `is:issue is:open label:help-wanted` - Find issues looking for contributors\n- `is:issue is:open label:bug bounty` - Find bug bounties specifically\n- `is:issue is:open label:enhancement bounty` - Find feature request bounties\n\n**Accessing Settings:**\n\n1. Open the bounty board application\n2. Click the settings gear icon (⚙️) in the top-right corner\n3. Modify the \"GitHub Search Query\" field\n4. Click \"Save Settings\" to apply changes\n\n**Technical Implementation:**\n\n- Settings are stored in the `user_settings` table with key `search_query`\n- Both the cron job (`cron.ts`) and fallback search (`search.ts`) use the configured query\n- The default query is used if no custom query is set\n- Settings are validated to ensure they contain a valid query string\n\n### Other User Settings\n\nThe application also includes:\n\n- **Hidden Repositories**: Hide specific repositories from search results\n- **Issue Status Filter**: Control which issues are displayed based on status (interested, in progress, unwanted, etc.)\n\n## License\n\nISC\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyaodingyd%2Fbounty-board","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fyaodingyd%2Fbounty-board","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyaodingyd%2Fbounty-board/lists"}