Initial release v1.0.0
Open-source marketplace for AI Agent skills. Features: - Next.js 15 web app with i18n (en/fa) - CLI tool for skill installation (npx skillhub) - GitHub crawler/indexer with multi-strategy discovery - Security scanning for all indexed skills - Self-hostable with Docker Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
326
docs/API.md
Normal file
326
docs/API.md
Normal file
@@ -0,0 +1,326 @@
|
||||
# API Reference
|
||||
|
||||
SkillHub provides a public REST API for searching, browsing, and installing AI agent skills.
|
||||
|
||||
**Base URL:** `https://skills.palebluedot.live/api` (or your self-hosted instance)
|
||||
|
||||
---
|
||||
|
||||
## Authentication
|
||||
|
||||
Most read endpoints are public. Write operations require GitHub OAuth authentication via NextAuth.js.
|
||||
|
||||
### Rate Limits
|
||||
|
||||
| Tier | Limit | Description |
|
||||
|------|-------|-------------|
|
||||
| Anonymous | 100/min | Unauthenticated requests |
|
||||
| Authenticated | 200/min | Logged-in users |
|
||||
| Search | 60/min | Search queries |
|
||||
|
||||
---
|
||||
|
||||
## Skills
|
||||
|
||||
### Search Skills
|
||||
|
||||
```
|
||||
GET /api/skills?q={query}&limit={limit}&offset={offset}
|
||||
```
|
||||
|
||||
| Parameter | Type | Default | Description |
|
||||
|-----------|------|---------|-------------|
|
||||
| `q` | string | - | Search query |
|
||||
| `limit` | number | 20 | Results per page |
|
||||
| `offset` | number | 0 | Pagination offset |
|
||||
|
||||
**Example:**
|
||||
```bash
|
||||
curl "https://skills.palebluedot.live/api/skills?q=pdf&limit=10"
|
||||
```
|
||||
|
||||
### Get Skill Details
|
||||
|
||||
```
|
||||
GET /api/skills/{owner}/{repo}/{skill-name}
|
||||
```
|
||||
|
||||
Skill IDs use the format `owner/repo/skill-name`.
|
||||
|
||||
**Example:**
|
||||
```bash
|
||||
curl https://skills.palebluedot.live/api/skills/anthropics/skills/pdf
|
||||
```
|
||||
|
||||
### Featured Skills
|
||||
|
||||
```
|
||||
GET /api/skills/featured
|
||||
```
|
||||
|
||||
Returns a curated list of featured skills. Response is cached.
|
||||
|
||||
### Recent Skills
|
||||
|
||||
```
|
||||
GET /api/skills/recent
|
||||
```
|
||||
|
||||
Returns recently updated skills. Response is cached.
|
||||
|
||||
### Track Installation
|
||||
|
||||
```
|
||||
POST /api/skills/install
|
||||
```
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `skillId` | string | Skill ID (`owner/repo/skill-name`) |
|
||||
| `platform` | string | Target platform |
|
||||
| `method` | string | Installation method |
|
||||
|
||||
### Submit Skill Addition Request
|
||||
|
||||
```
|
||||
POST /api/skills/add-request
|
||||
```
|
||||
|
||||
Requires authentication.
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `gitHubUrl` | string | GitHub repository URL |
|
||||
| `skillPath` | string | Path to SKILL.md in repo |
|
||||
| `reason` | string | Reason for submission |
|
||||
|
||||
### Submit Skill Removal Request
|
||||
|
||||
```
|
||||
POST /api/skills/removal-request
|
||||
```
|
||||
|
||||
Requires authentication.
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `skillId` | string | Skill to remove |
|
||||
| `reason` | string | Reason for removal |
|
||||
|
||||
### Verify Ownership
|
||||
|
||||
```
|
||||
GET /api/skills/verify-ownership?owner={owner}&repo={repo}
|
||||
```
|
||||
|
||||
Requires authentication. Checks if the authenticated user owns the specified GitHub repository.
|
||||
|
||||
---
|
||||
|
||||
## Skill Files
|
||||
|
||||
### Get Skill Files
|
||||
|
||||
```
|
||||
GET /api/skill-files?skillId={skillId}
|
||||
```
|
||||
|
||||
Fetches skill file contents from GitHub (with caching).
|
||||
|
||||
### Download as ZIP
|
||||
|
||||
```
|
||||
GET /api/skill-files/zip?skillId={skillId}&platform={platform}
|
||||
```
|
||||
|
||||
| Parameter | Type | Description |
|
||||
|-----------|------|-------------|
|
||||
| `skillId` | string | Skill ID |
|
||||
| `platform` | string | `claude`, `codex`, `copilot`, `cursor`, or `windsurf` |
|
||||
|
||||
---
|
||||
|
||||
## Categories
|
||||
|
||||
### List Categories
|
||||
|
||||
```
|
||||
GET /api/categories
|
||||
```
|
||||
|
||||
Returns all skill categories with skill counts.
|
||||
|
||||
**Example:**
|
||||
```bash
|
||||
curl https://skills.palebluedot.live/api/categories
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Ratings
|
||||
|
||||
### Get Reviews
|
||||
|
||||
```
|
||||
GET /api/ratings?skillId={skillId}&limit={limit}&offset={offset}
|
||||
```
|
||||
|
||||
| Parameter | Type | Default | Description |
|
||||
|-----------|------|---------|-------------|
|
||||
| `skillId` | string | required | Skill ID |
|
||||
| `limit` | number | 10 | Results per page |
|
||||
| `offset` | number | 0 | Pagination offset |
|
||||
|
||||
### Submit Rating
|
||||
|
||||
```
|
||||
POST /api/ratings
|
||||
```
|
||||
|
||||
Requires authentication.
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `skillId` | string | Skill ID |
|
||||
| `rating` | number | Rating (1-5) |
|
||||
| `review` | string | Review text |
|
||||
|
||||
### Get My Rating
|
||||
|
||||
```
|
||||
GET /api/ratings/me?skillId={skillId}
|
||||
```
|
||||
|
||||
Requires authentication. Returns the current user's rating for a skill.
|
||||
|
||||
---
|
||||
|
||||
## Favorites
|
||||
|
||||
### Get My Favorites
|
||||
|
||||
```
|
||||
GET /api/favorites
|
||||
```
|
||||
|
||||
Requires authentication. Returns the user's favorited skills.
|
||||
|
||||
### Check Favorites
|
||||
|
||||
```
|
||||
POST /api/favorites/check
|
||||
```
|
||||
|
||||
Requires authentication.
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `skillIds` | string[] | Array of skill IDs to check |
|
||||
|
||||
---
|
||||
|
||||
## Newsletter
|
||||
|
||||
### Subscribe
|
||||
|
||||
```
|
||||
GET /api/newsletter/subscribe?email={email}&locale={locale}
|
||||
```
|
||||
|
||||
One-click subscription from email links.
|
||||
|
||||
### Unsubscribe
|
||||
|
||||
```
|
||||
POST /api/newsletter/unsubscribe
|
||||
```
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `email` | string | Email to unsubscribe |
|
||||
|
||||
---
|
||||
|
||||
## Platform
|
||||
|
||||
### Statistics
|
||||
|
||||
```
|
||||
GET /api/stats
|
||||
```
|
||||
|
||||
Returns platform-wide statistics (cached).
|
||||
|
||||
**Example:**
|
||||
```bash
|
||||
curl https://skills.palebluedot.live/api/stats
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"totalSkills": 178056,
|
||||
"totalDownloads": 2053,
|
||||
"totalCategories": 30,
|
||||
"totalContributors": 9552
|
||||
}
|
||||
```
|
||||
|
||||
### Health Check
|
||||
|
||||
```
|
||||
GET /api/health
|
||||
```
|
||||
|
||||
Returns health status of all services (database, Redis, Meilisearch).
|
||||
|
||||
### Attribution
|
||||
|
||||
```
|
||||
GET /api/attribution
|
||||
```
|
||||
|
||||
Returns attribution data including license distribution and discovery sources.
|
||||
|
||||
---
|
||||
|
||||
## Error Responses
|
||||
|
||||
All endpoints return standard HTTP status codes:
|
||||
|
||||
| Status | Description |
|
||||
|--------|-------------|
|
||||
| `200` | Success |
|
||||
| `400` | Bad request (missing/invalid parameters) |
|
||||
| `401` | Authentication required |
|
||||
| `403` | Forbidden |
|
||||
| `404` | Not found |
|
||||
| `429` | Rate limit exceeded |
|
||||
| `500` | Internal server error |
|
||||
|
||||
Error response format:
|
||||
```json
|
||||
{
|
||||
"error": "Description of the error"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## CLI Usage
|
||||
|
||||
The [SkillHub CLI](https://www.npmjs.com/package/skillhub) uses these APIs internally:
|
||||
|
||||
```bash
|
||||
# Install CLI
|
||||
npm install -g skillhub
|
||||
|
||||
# Search skills
|
||||
npx skillhub search pdf
|
||||
|
||||
# Install a skill
|
||||
npx skillhub install anthropics/skills/pdf
|
||||
|
||||
# List installed skills
|
||||
npx skillhub list
|
||||
```
|
||||
196
docs/MANUAL_TEST_CHECKLIST.md
Normal file
196
docs/MANUAL_TEST_CHECKLIST.md
Normal file
@@ -0,0 +1,196 @@
|
||||
# SkillHub Manual Test Checklist
|
||||
|
||||
This checklist covers features NOT covered by automated E2E tests (43 Playwright tests).
|
||||
Complete before production deployment.
|
||||
|
||||
## Already Automated - SKIP These
|
||||
|
||||
The following are covered by E2E tests in `apps/web/e2e/*.spec.ts`:
|
||||
|
||||
| Area | E2E Coverage |
|
||||
|------|--------------|
|
||||
| **Homepage** | Page load, stats, featured skills, navigation links |
|
||||
| **Browse** | Skills list, search input, search filter, platform filter, skill cards, detail navigation |
|
||||
| **Categories** | Page load, category list, names, skill counts, navigation |
|
||||
| **Skill Detail** | Name, description, platforms, install command, GitHub link |
|
||||
| **i18n** | EN default, language switcher, FA switch, RTL, Persian text, context preserve |
|
||||
| **Mobile** | Mobile nav, menu toggle, single column, text size, touch targets |
|
||||
| **API** | `/api/health`, `/api/stats`, `/api/categories`, `/api/skills/*`, `/api/skills/featured`, `/api/skills/recent` |
|
||||
|
||||
**To run E2E tests:** `pnpm --filter @skillhub/web test:e2e`
|
||||
|
||||
---
|
||||
|
||||
**Tester:** ________________
|
||||
**Date:** ________________
|
||||
**Environment:** [ ] Local Docker [ ] Staging [ ] Production
|
||||
|
||||
---
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- [ ] All Docker containers running (`docker ps`)
|
||||
- [ ] Database has data (`docker exec skillhub-db psql -U postgres -d skillhub -c "SELECT COUNT(*) FROM skills;"`)
|
||||
- [ ] Health endpoint OK (`curl http://localhost:3000/api/health`)
|
||||
- [ ] GitHub OAuth configured (for auth tests)
|
||||
|
||||
---
|
||||
|
||||
## 1. Authentication (GitHub OAuth)
|
||||
|
||||
### Sign In Flow
|
||||
| Test | Expected | Pass |
|
||||
|------|----------|------|
|
||||
| Click "Sign In" in header | Redirects to GitHub OAuth | [ ] |
|
||||
| Approve on GitHub | Returns to app, user dropdown visible | [ ] |
|
||||
| User dropdown shows avatar + name | GitHub profile info displayed | [ ] |
|
||||
| Refresh page while signed in | Session persists | [ ] |
|
||||
|
||||
### Sign Out Flow
|
||||
| Test | Expected | Pass |
|
||||
|------|----------|------|
|
||||
| Click user dropdown → Sign Out | Logged out, "Sign In" button reappears | [ ] |
|
||||
| Refresh after sign out | Still logged out | [ ] |
|
||||
|
||||
---
|
||||
|
||||
## 2. Favorites System (Auth Required)
|
||||
|
||||
### API Endpoints
|
||||
| Test | Expected | Pass |
|
||||
|------|----------|------|
|
||||
| `GET /api/favorites` (unauthenticated) | 401 Unauthorized | [ ] |
|
||||
| `GET /api/favorites` (authenticated) | Returns user's favorites array | [ ] |
|
||||
| `POST /api/favorites` with `{skillId}` | 200, skill added | [ ] |
|
||||
| `DELETE /api/favorites` with `{skillId}` | 200, skill removed | [ ] |
|
||||
| `POST /api/favorites/check` with `{skillIds}` | Returns favorited status | [ ] |
|
||||
|
||||
### UI Tests
|
||||
| Test | Expected | Pass |
|
||||
|------|----------|------|
|
||||
| Click heart on skill detail (unauthenticated) | Redirects to sign in | [ ] |
|
||||
| Click heart on skill detail (authenticated) | Heart fills red, added to favorites | [ ] |
|
||||
| Click filled heart | Heart unfills, removed from favorites | [ ] |
|
||||
| Go to `/favorites` page | Shows all favorited skills | [ ] |
|
||||
| Click heart on favorites page | Skill removed from list | [ ] |
|
||||
| Empty favorites state | "No favorites" message + Browse button | [ ] |
|
||||
|
||||
---
|
||||
|
||||
## 3. Ratings System (Auth Required)
|
||||
|
||||
### API Endpoints
|
||||
| Test | Expected | Pass |
|
||||
|------|----------|------|
|
||||
| `GET /api/ratings?skillId=...` | Returns ratings array + summary | [ ] |
|
||||
| `POST /api/ratings` with `{skillId, rating: 5}` | 200, rating saved | [ ] |
|
||||
| `POST /api/ratings` with invalid rating (0 or 6) | 400 error | [ ] |
|
||||
| `GET /api/ratings/me?skillId=...` (authenticated) | Returns user's rating | [ ] |
|
||||
|
||||
### UI Tests
|
||||
| Test | Expected | Pass |
|
||||
|------|----------|------|
|
||||
| Click stars (unauthenticated) | Redirects to sign in | [ ] |
|
||||
| Click stars to rate (authenticated) | Stars fill, rating submitted | [ ] |
|
||||
| Change rating | New rating saved, average updates | [ ] |
|
||||
| Refresh page | Rating persists | [ ] |
|
||||
|
||||
---
|
||||
|
||||
## 4. Interactive UI Elements
|
||||
|
||||
### Browse Page Filters
|
||||
| Test | Expected | Pass |
|
||||
|------|----------|------|
|
||||
| Sort dropdown (Stars/Downloads/Recent/Rating) | Results re-sorted, URL updates | [ ] |
|
||||
| Verified checkbox | Only verified skills shown | [ ] |
|
||||
| Combined filters (platform + sort + verified) | All filters applied together | [ ] |
|
||||
| Load More button | Next page loads, more skills appear | [ ] |
|
||||
|
||||
### Skill Detail - Install Section
|
||||
| Test | Expected | Pass |
|
||||
|------|----------|------|
|
||||
| Platform tabs (Claude/Codex/Copilot) | Command changes for each platform | [ ] |
|
||||
| Copy button | Command copied to clipboard, checkmark appears | [ ] |
|
||||
| Download ZIP button | Opens GitHub ZIP download in new tab | [ ] |
|
||||
| Select Folder button (Chrome) | File picker opens, skill files created | [ ] |
|
||||
|
||||
---
|
||||
|
||||
## 5. Pages Not in E2E Tests
|
||||
|
||||
### Featured Page (`/featured`)
|
||||
| Test | Expected | Pass |
|
||||
|------|----------|------|
|
||||
| Page loads | Shows top featured skills | [ ] |
|
||||
| Skills sorted by popularity | Highest stars first | [ ] |
|
||||
| Skill cards clickable | Navigate to detail page | [ ] |
|
||||
|
||||
### New Skills Page (`/new`)
|
||||
| Test | Expected | Pass |
|
||||
|------|----------|------|
|
||||
| Page loads | Shows recently added skills | [ ] |
|
||||
| Skills sorted by date | Most recent first | [ ] |
|
||||
| Timestamps displayed | "X hours/days ago" format | [ ] |
|
||||
|
||||
### Favorites Page (`/favorites`)
|
||||
| Test | Expected | Pass |
|
||||
|------|----------|------|
|
||||
| Unauthenticated access | Redirects to sign in | [ ] |
|
||||
| Authenticated + no favorites | Empty state shown | [ ] |
|
||||
| Authenticated + has favorites | Skills grid displayed | [ ] |
|
||||
|
||||
---
|
||||
|
||||
## 6. Error States
|
||||
|
||||
| Test | Expected | Pass |
|
||||
|------|----------|------|
|
||||
| Navigate to `/nonexistent-page` | 404 page displayed | [ ] |
|
||||
| Navigate to `/skill/invalid/id` | Error or 404 shown | [ ] |
|
||||
| API returns error | Graceful error message | [ ] |
|
||||
|
||||
---
|
||||
|
||||
## 7. Docker Health Checks
|
||||
|
||||
| Service | Command | Expected | Pass |
|
||||
|---------|---------|----------|------|
|
||||
| Web | `curl http://localhost:3000/api/health` | `status: ok` | [ ] |
|
||||
| Database | `docker exec skillhub-db pg_isready` | Ready | [ ] |
|
||||
| Redis | `docker exec skillhub-redis redis-cli ping` | PONG | [ ] |
|
||||
| Meilisearch | `curl http://localhost:7700/health` | `available` | [ ] |
|
||||
| Indexer | `docker logs skillhub-indexer --tail=5` | No errors | [ ] |
|
||||
|
||||
---
|
||||
|
||||
## 8. Real Data Verification
|
||||
|
||||
| Test | Expected | Pass |
|
||||
|------|----------|------|
|
||||
| Skills in database | `SELECT COUNT(*) FROM skills` > 0 | [ ] |
|
||||
| Skills have real GitHub data | `github_stars`, `github_owner` populated | [ ] |
|
||||
| Search returns results | Search for existing skill works | [ ] |
|
||||
| Meilisearch synced | `curl localhost:7700/indexes/skills/stats` shows documents | [ ] |
|
||||
|
||||
---
|
||||
|
||||
## Notes
|
||||
|
||||
_Record any issues found:_
|
||||
|
||||
```
|
||||
______________________________________________________________
|
||||
______________________________________________________________
|
||||
______________________________________________________________
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Sign-off
|
||||
|
||||
- [ ] All tests pass
|
||||
- [ ] No blocking issues
|
||||
- [ ] Ready for production
|
||||
|
||||
**Signature:** ________________ **Date:** ________________
|
||||
93
docs/features/claim-system.md
Normal file
93
docs/features/claim-system.md
Normal file
@@ -0,0 +1,93 @@
|
||||
# Claim System
|
||||
|
||||
## Overview
|
||||
|
||||
The claim system allows users to:
|
||||
1. **Add Skills**: Request to add their GitHub repositories containing SKILL.md files
|
||||
2. **Remove Skills**: Request to remove their skills from SkillHub (auto-approved for verified owners)
|
||||
|
||||
## Add Skill Flow
|
||||
|
||||
### User Actions
|
||||
1. Sign in with GitHub OAuth
|
||||
2. Navigate to `/claim#add`
|
||||
3. Enter GitHub repository URL
|
||||
4. Optionally provide reason for adding the skill
|
||||
5. Submit request
|
||||
|
||||
### Backend Processing
|
||||
1. Validate GitHub URL format
|
||||
2. Check if repository exists and is public
|
||||
3. Scan repository for SKILL.md files (recursive auto-scan)
|
||||
4. Create pending add request in database
|
||||
5. Send confirmation email ONLY if skills found (`skillCount > 0`)
|
||||
|
||||
### Email Behavior
|
||||
- **Skills found**: Email sent with count
|
||||
- **No skills found**: No email sent (manual review needed, user notified on screen)
|
||||
|
||||
### Success States
|
||||
- **1 skill found**: "SKILL.md was found and will be indexed soon"
|
||||
- **Multiple skills**: "Found N skills that will be indexed soon"
|
||||
- **No skills**: "No SKILL.md found - repository will be reviewed manually. You will receive an email if skills are approved for indexing."
|
||||
|
||||
### Field Requirements
|
||||
- **Repository URL**: Required
|
||||
- **Reason**: Optional (defaults to "No reason provided")
|
||||
|
||||
## Remove Skill Flow
|
||||
|
||||
### User Actions
|
||||
1. Sign in with GitHub OAuth
|
||||
2. Navigate to `/claim#remove`
|
||||
3. Enter skill ID (format: `owner/repo/skill-name`)
|
||||
4. Optionally provide reason for removal
|
||||
5. Submit request
|
||||
|
||||
### Backend Processing
|
||||
1. Verify skill exists in database
|
||||
2. Verify user owns the repository (via GitHub API)
|
||||
3. Auto-approve and block skill from indexing
|
||||
4. Send confirmation email
|
||||
|
||||
### Field Requirements
|
||||
- **Skill ID**: Required
|
||||
- **Reason**: Optional (defaults to "No reason provided")
|
||||
|
||||
## Error Handling
|
||||
|
||||
| Error Code | Message | Trigger |
|
||||
|------------|---------|---------|
|
||||
| `INVALID_URL` | Invalid GitHub URL | Malformed URL |
|
||||
| `INVALID_REPO` | Repository not found or not accessible | 404 from GitHub or private repo |
|
||||
| `RATE_LIMIT_EXCEEDED` | GitHub API rate limit exceeded | 403 with rate limit header |
|
||||
| `NETWORK_TIMEOUT` | Request timed out | Timeout during validation |
|
||||
| `ALREADY_PENDING` | Pending request exists | Duplicate request |
|
||||
| `NOT_OWNER` | Not repository owner | Ownership verification failed (remove only) |
|
||||
| `SKILL_NOT_FOUND` | Skill not in database | Skill ID doesn't exist (remove only) |
|
||||
| `GITHUB_ERROR` | GitHub verification failed | General GitHub API error |
|
||||
| `INVALID_SKILL` | Invalid GitHub info | Skill missing repo data |
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
### Key Features
|
||||
- **Auto-scan**: Entire repository is scanned recursively for all SKILL.md files
|
||||
- **No manual path input**: Removed confusing "Skill Path" field
|
||||
- **Optional reasons**: Users don't have to explain their requests
|
||||
- **Conditional emails**: Only send when skills are actually found
|
||||
- **Bilingual**: Full support for English and Farsi (RTL)
|
||||
- **Specific errors**: Each edge case has a clear, actionable error message
|
||||
|
||||
### Technical Details
|
||||
- **API Routes**: `/api/skills/add-request` and `/api/skills/removal-request`
|
||||
- **Component**: `ClaimForm.tsx` with tab-based UI
|
||||
- **i18n**: Uses next-intl for translations
|
||||
- **Email**: Resend library with bilingual templates
|
||||
- **Security**: CSRF protection, GitHub OAuth verification
|
||||
|
||||
### Recent Improvements (Feb 2026)
|
||||
- ✅ Fixed email logic to only send when skills found
|
||||
- ✅ Made reason fields optional
|
||||
- ✅ Removed confusing skill path field
|
||||
- ✅ Improved success messages for different states
|
||||
- ✅ Added specific error messages for rate limits, timeouts, and private repos
|
||||
240
docs/self-hosting.md
Normal file
240
docs/self-hosting.md
Normal file
@@ -0,0 +1,240 @@
|
||||
# Self-Hosting Guide
|
||||
|
||||
This guide explains how to deploy SkillHub on your own server.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Docker and Docker Compose
|
||||
- A server with at least 2GB RAM and 10GB disk space
|
||||
- A domain name (optional, but recommended)
|
||||
- GitHub Personal Access Token (for indexing skills)
|
||||
|
||||
## Quick Start
|
||||
|
||||
### 1. Clone the repository
|
||||
|
||||
```bash
|
||||
git clone https://github.com/airano-ir/skillhub.git
|
||||
cd skillhub
|
||||
```
|
||||
|
||||
### 2. Configure environment
|
||||
|
||||
```bash
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
Edit `.env` and set the following required variables:
|
||||
|
||||
```env
|
||||
# Required: GitHub token for API access
|
||||
GITHUB_TOKEN=ghp_your_token_here
|
||||
|
||||
# Required: Database password (change in production!)
|
||||
POSTGRES_PASSWORD=your_secure_password
|
||||
|
||||
# Required: Meilisearch key (change in production!)
|
||||
MEILI_MASTER_KEY=your_secure_key
|
||||
|
||||
# Optional: Your domain
|
||||
NEXT_PUBLIC_APP_URL=https://skills.yourdomain.com
|
||||
```
|
||||
|
||||
### 3. Start the services
|
||||
|
||||
```bash
|
||||
# Development mode (without nginx)
|
||||
docker compose up -d
|
||||
|
||||
# Production mode (with nginx reverse proxy)
|
||||
docker compose --profile production up -d
|
||||
```
|
||||
|
||||
### 4. Initialize the database
|
||||
|
||||
The database is automatically initialized when the container starts. You can verify by checking the logs:
|
||||
|
||||
```bash
|
||||
docker compose logs db
|
||||
```
|
||||
|
||||
### 5. Access the application
|
||||
|
||||
- Web interface: http://localhost:3000
|
||||
- Meilisearch: http://localhost:7700
|
||||
|
||||
## Production Deployment
|
||||
|
||||
### SSL/TLS with Let's Encrypt
|
||||
|
||||
1. Install certbot:
|
||||
|
||||
```bash
|
||||
apt install certbot
|
||||
```
|
||||
|
||||
2. Generate certificates:
|
||||
|
||||
```bash
|
||||
certbot certonly --standalone -d skills.yourdomain.com
|
||||
```
|
||||
|
||||
3. Copy certificates to nginx directory:
|
||||
|
||||
```bash
|
||||
mkdir -p nginx/ssl
|
||||
cp /etc/letsencrypt/live/skills.yourdomain.com/fullchain.pem nginx/ssl/
|
||||
cp /etc/letsencrypt/live/skills.yourdomain.com/privkey.pem nginx/ssl/
|
||||
```
|
||||
|
||||
4. Update `nginx/nginx.conf` with your domain name.
|
||||
|
||||
5. Start with production profile:
|
||||
|
||||
```bash
|
||||
docker compose --profile production up -d
|
||||
```
|
||||
|
||||
### Auto-renewal
|
||||
|
||||
Add to crontab:
|
||||
|
||||
```bash
|
||||
0 0 1 * * certbot renew --post-hook "docker compose restart nginx"
|
||||
```
|
||||
|
||||
## Configuration Options
|
||||
|
||||
### Environment Variables
|
||||
|
||||
| Variable | Description | Default |
|
||||
|----------|-------------|---------|
|
||||
| `DATABASE_URL` | PostgreSQL connection string | `postgresql://postgres:postgres@db:5432/skillhub` |
|
||||
| `REDIS_URL` | Redis connection string | `redis://redis:6379` |
|
||||
| `GITHUB_TOKEN` | GitHub PAT for API access | Required |
|
||||
| `MEILI_MASTER_KEY` | Meilisearch master key | `skillhub-dev-key` |
|
||||
| `INDEXER_CONCURRENCY` | Number of concurrent indexing jobs | `5` |
|
||||
| `INDEXER_MIN_STARS` | Minimum GitHub stars for indexing | `2` |
|
||||
|
||||
### Scaling
|
||||
|
||||
#### Horizontal Scaling
|
||||
|
||||
You can run multiple instances of the web service behind a load balancer:
|
||||
|
||||
```bash
|
||||
docker compose up -d --scale web=3
|
||||
```
|
||||
|
||||
#### Resource Limits
|
||||
|
||||
Add resource limits in `docker-compose.yml`:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
web:
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '1'
|
||||
memory: 1G
|
||||
reservations:
|
||||
cpus: '0.5'
|
||||
memory: 512M
|
||||
```
|
||||
|
||||
## Maintenance
|
||||
|
||||
### Backup
|
||||
|
||||
```bash
|
||||
# Backup PostgreSQL
|
||||
docker compose exec db pg_dump -U postgres skillhub > backup.sql
|
||||
|
||||
# Backup Redis
|
||||
docker compose exec redis redis-cli BGSAVE
|
||||
|
||||
# Copy Redis dump
|
||||
docker cp skillhub-redis:/data/dump.rdb ./backup-redis.rdb
|
||||
```
|
||||
|
||||
### Restore
|
||||
|
||||
```bash
|
||||
# Restore PostgreSQL
|
||||
cat backup.sql | docker compose exec -T db psql -U postgres skillhub
|
||||
|
||||
# Restore Redis
|
||||
docker compose stop redis
|
||||
docker cp ./backup-redis.rdb skillhub-redis:/data/dump.rdb
|
||||
docker compose start redis
|
||||
```
|
||||
|
||||
### Update
|
||||
|
||||
```bash
|
||||
git pull origin main
|
||||
docker compose build
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
### Logs
|
||||
|
||||
```bash
|
||||
# All services
|
||||
docker compose logs -f
|
||||
|
||||
# Specific service
|
||||
docker compose logs -f web
|
||||
docker compose logs -f indexer
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Database connection issues
|
||||
|
||||
```bash
|
||||
# Check if database is healthy
|
||||
docker compose exec db pg_isready -U postgres
|
||||
|
||||
# Check database logs
|
||||
docker compose logs db
|
||||
```
|
||||
|
||||
### Indexer not running
|
||||
|
||||
```bash
|
||||
# Check indexer status
|
||||
docker compose ps indexer
|
||||
|
||||
# View indexer logs
|
||||
docker compose logs -f indexer
|
||||
|
||||
# Restart indexer
|
||||
docker compose restart indexer
|
||||
```
|
||||
|
||||
### Search not working
|
||||
|
||||
```bash
|
||||
# Check Meilisearch status
|
||||
curl http://localhost:7700/health
|
||||
|
||||
# View Meilisearch logs
|
||||
docker compose logs meilisearch
|
||||
```
|
||||
|
||||
## Security Recommendations
|
||||
|
||||
1. **Change default passwords** in `.env`
|
||||
2. **Use HTTPS** in production
|
||||
3. **Restrict database access** to internal network only
|
||||
4. **Enable firewall** and only expose necessary ports
|
||||
5. **Regular backups** to external storage
|
||||
6. **Monitor logs** for suspicious activity
|
||||
7. **Keep Docker and images updated**
|
||||
|
||||
## Support
|
||||
|
||||
- GitHub Issues: https://github.com/airano-ir/skillhub/issues
|
||||
- Documentation: https://skills.palebluedot.live/docs
|
||||
Reference in New Issue
Block a user