From 9266de1f3e437b3ba4dd0a4602e7f254281ed164 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yusuf=20=C4=B0pek?= Date: Sun, 23 Nov 2025 14:26:07 +0300 Subject: [PATCH] docs: Add comprehensive package contribution guide and validation tooling - Add bilingual README with English/Turkish language switcher - Create detailed package contribution guidelines with platform-specific naming conventions - Add validation script for verifying package presets against database - Include best practices and example PR format for contributors - Add tsx dependency for running TypeScript validation scripts - Document validation workflow with manual and automated verification options --- README.md | 145 ++++++ README.tr.md | 252 +++++++++ package.json | 4 +- pnpm-lock.yaml | 298 ++++++++++- scripts/validate-presets.js | 188 +++++++ scripts/validate-presets.ts | 194 +++++++ src/app/api/recommendations/route.ts | 6 +- src/components/OnboardingModal.tsx | 6 - src/components/RecommendationCard.tsx | 6 - src/components/RecommendationListItem.tsx | 1 - src/components/RecommendationsSection.tsx | 2 +- src/data/recommendationPresets.ts | 594 +++++++++++++++++----- src/middleware.ts | 12 +- src/services/recommendationService.ts | 83 ++- 14 files changed, 1594 insertions(+), 197 deletions(-) create mode 100644 README.tr.md create mode 100755 scripts/validate-presets.js create mode 100644 scripts/validate-presets.ts diff --git a/README.md b/README.md index 543d417..6c595c9 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # RepoHub - Cross-Platform Package Manager +**🇬🇧 English** | [🇹🇷 Türkçe](./README.tr.md) + **Simplify software installation across Linux, Windows, and macOS with official repositories.** RepoHub provides a unified interface for package discovery and installation across different operating systems. @@ -96,6 +98,149 @@ curl -X POST http://localhost:3000/api/sync \ -d '{"platform": "all"}' ``` +## 📦 Contributing to Package Recommendations + +[🇹🇷 Türkçe README](./README.tr.md) | **🇬🇧 English** + +RepoHub uses curated package lists to provide personalized recommendations to users. You can help improve these recommendations by adding packages! + +### How to Add Packages + +Package recommendations are stored in `/src/data/recommendationPresets.ts`. Here's how to add a package: + +#### 1. Find the Right Location + +Navigate to the platform and category where your package belongs: + +```typescript +export const PACKAGE_PRESETS = { + windows: { + development: ["Git.Git", "Microsoft.VisualStudioCode"], + design: ["GIMP.GIMP", "Inkscape.Inkscape"], + // ... other categories + }, + // ... other platforms +} +``` + +**Available Platforms:** +- `windows` - Windows (Winget) +- `macos` - macOS (Homebrew) +- `ubuntu` - Ubuntu (APT) +- `debian` - Debian (APT) +- `arch` - Arch Linux (Pacman/AUR) +- `fedora` - Fedora (DNF) + +**Available Categories:** +- `development` - Dev tools, IDEs, compilers +- `design` - Graphics, creative software +- `multimedia` - Media players, editors +- `system-tools` - System utilities +- `gaming` - Game launchers, platforms +- `productivity` - Office, browsers, productivity apps +- `education` - Educational software + +#### 2. Get the Correct Package Name + +**⚠️ CRITICAL:** Package names must match **exactly** as they appear in the database. + +**Package name formats by platform:** + +- **Windows**: `Publisher.PackageName` (e.g., `Microsoft.VisualStudioCode`) +- **macOS**: lowercase-with-hyphens (e.g., `visual-studio-code`) +- **Linux**: lowercase, varies by distro (e.g., `code`, `docker.io`) + +#### 3. Verify the Package Exists + +**Option A: Using the Validation Script (Recommended)** + +If you have access to the database: + +```bash +# Validate specific platform +npm run validate:presets -- windows + +# Validate multiple platforms +npm run validate:presets -- ubuntu debian arch + +# Validate all platforms +npm run validate:presets -- --all +``` + +The script will show: +- ✅ Packages found in database +- ❌ Packages not found +- 💡 Similar package suggestions + +**Option B: Manual Verification** + +If you don't have database access: + +1. Search on the live RepoHub website +2. Find your package in the search results +3. Copy the **exact package name** displayed +4. Or check official package repositories: + - Windows: [winget.run](https://winget.run/) + - macOS: `brew search ` + - Ubuntu/Debian: `apt search ` + - Arch: [archlinux.org/packages](https://archlinux.org/packages/) + - Fedora: [packages.fedoraproject.org](https://packages.fedoraproject.org/) + +#### 4. Add the Package + +Simply add the package name to the array: + +```typescript +windows: { + development: [ + "Git.Git", + "Microsoft.VisualStudioCode", + "Docker.DockerDesktop" // ← Your new package + ] +} +``` + +#### 5. Test Your Changes + +1. Run validation: + ```bash + npm run validate:presets -- windows + ``` + +2. Start the dev server: + ```bash + npm run dev + ``` + +3. Test in the app: + - Complete onboarding + - Select the relevant category + - Check if your package appears in recommendations + +### Best Practices + +**DO ✅** +- Verify package names using the validation script +- Add popular, well-maintained packages +- Test before submitting +- Use exact package names from official repos + +**DON'T ❌** +- Don't guess package names +- Don't add deprecated packages +- Don't skip verification +- Don't add duplicates across categories + +### Example Pull Request + +``` +Add Popular Development Tools to Windows Recommendations + +- Added Docker.DockerDesktop to development +- Added Postman.Postman to development +- Validation: ✅ All packages verified (100% found) +``` + ## 🤝 Contributing Contributions are welcome! Please feel free to submit a Pull Request. diff --git a/README.tr.md b/README.tr.md new file mode 100644 index 0000000..10095b5 --- /dev/null +++ b/README.tr.md @@ -0,0 +1,252 @@ +# RepoHub - Çok Platformlu Paket Yöneticisi + +[🇬🇧 English](./README.md) | **🇹🇷 Türkçe** + +**Linux, Windows ve macOS'te resmi depolardan yazılım kurulumunu basitleştirin.** + +RepoHub, farklı işletim sistemlerinde paket keşfi ve kurulumu için birleşik bir arayüz sağlar. + +## 🚀 Özellikler + +- **Çok Platformlu Destek**: Linux (Debian, Ubuntu, Arch, Fedora), Windows ve macOS'te çalışır. +- **Resmi Depolar**: Yazılımlara yalnızca güvenilir, resmi kaynaklardan erişin. +- **Script Oluşturma**: Seçtiğiniz platform için idempotent kurulum scriptleri oluşturun. +- **Akıllı Filtreleme**: Paketleri verimli bir şekilde bulun ve filtreleyin. + +## 🛠️ Teknoloji Yığını + +### Frontend +- **Framework**: Next.js 14+ (React) +- **Stil**: Tailwind CSS +- **İkonlar**: Lucide React +- **Durum Yönetimi**: React Query + Zustand + +### Backend +- **Runtime**: Node.js (TypeScript) +- **Veritabanı**: PostgreSQL +- **Altyapı**: Docker + +## 🏁 Başlangıç + +### Gereksinimler + +- Node.js 18+ +- pnpm +- Docker (isteğe bağlı, veritabanı için) + +### Kurulum + +1. **Depoyu klonlayın:** + ```bash + git clone https://github.com/yusufipk/RepoHub.git + cd RepoHub + ``` + +2. **Bağımlılıkları yükleyin:** + ```bash + pnpm install + ``` + +3. **Ortam Değişkenlerini Ayarlayın:** + `.env.example` dosyasını `.env` olarak kopyalayın ve veritabanı bağlantınızı yapılandırın. + ```bash + cp .env.example .env + ``` + +4. **Veritabanını Başlatın:** + Veritabanı şemasını kurmak ve migrasyonları uygulamak için başlatma scriptini çalıştırın. + ```bash + pnpm init:db + ``` + +5. **Geliştirme sunucusunu çalıştırın:** + ```bash + pnpm dev + ``` + + Tarayıcınızda [http://localhost:3000](http://localhost:3000) adresini açın. + +## 🔄 API Kullanımı + +### Depoları Senkronize Etme + +API kullanarak depo senkronizasyonunu tetikleyebilirsiniz. Bu, paket veritabanını güncellemek için kullanışlıdır. + +**Endpoint:** `POST /api/sync` + +**Başlıklar:** +- `Content-Type`: `application/json` +- `x-sync-secret`: Senkronizasyon gizli anahtarınız (`SYNC_SERVER_ONLY=true` ise gerekli) + +**Body Parametreleri:** +- `platform`: Senkronize edilecek platform. Seçenekler: + - `debian`: Debian paketlerini senkronize et (Resmi Repo) + - `ubuntu`: Ubuntu paketlerini senkronize et (Resmi Repo) + - `arch`: Arch Linux paketlerini senkronize et (Resmi Repo) + - `aur`: Arch User Repository (AUR) paketlerini senkronize et + - `fedora`: Fedora paketlerini senkronize et (Resmi Repo) + - `windows`: Windows paketlerini senkronize et (Winget) + - `macos`: macOS paketlerini senkronize et (Homebrew) + - `all`: Tüm platformları senkronize et + +**Örnek İstek:** + +```bash +curl -X POST http://localhost:3000/api/sync \ + -H "Content-Type: application/json" \ + -H "x-sync-secret: gizli_anahtariniz" \ + -d '{"platform": "all"}' +``` + +## 📦 Paket Önerilerine Katkıda Bulunma + +[🇬🇧 English](./README.md) | **🇹🇷 Türkçe** + +RepoHub, kullanıcılara kişiselleştirilmiş öneriler sunmak için düzenlenmiş paket listeleri kullanır. Paket ekleyerek bu önerileri geliştirmeye yardımcı olabilirsiniz! + +### Paket Nasıl Eklenir + +Paket önerileri `/src/data/recommendationPresets.ts` dosyasında saklanır. İşte bir paket ekleme adımları: + +#### 1. Doğru Konumu Bulun + +Paketinizin ait olduğu platform ve kategoriye gidin: + +```typescript +export const PACKAGE_PRESETS = { + windows: { + development: ["Git.Git", "Microsoft.VisualStudioCode"], + design: ["GIMP.GIMP", "Inkscape.Inkscape"], + // ... diğer kategoriler + }, + // ... diğer platformlar +} +``` + +**Mevcut Platformlar:** +- `windows` - Windows (Winget) +- `macos` - macOS (Homebrew) +- `ubuntu` - Ubuntu (APT) +- `debian` - Debian (APT) +- `arch` - Arch Linux (Pacman/AUR) +- `fedora` - Fedora (DNF) + +**Mevcut Kategoriler:** +- `development` - Geliştirme araçları, IDE'ler, derleyiciler +- `design` - Grafik, kreatif yazılımlar +- `multimedia` - Medya oynatıcılar, editörler +- `system-tools` - Sistem araçları +- `gaming` - Oyun başlatıcıları, platformlar +- `productivity` - Ofis, tarayıcılar, üretkenlik uygulamaları +- `education` - Eğitim yazılımları + +#### 2. Doğru Paket Adını Alın + +**⚠️ KRİTİK:** Paket adları veritabanında göründükleri gibi **tam olarak** eşleşmelidir. + +**Platformlara göre paket adı formatları:** + +- **Windows**: `Yayinci.PaketAdi` (örn., `Microsoft.VisualStudioCode`) +- **macOS**: kucuk-harf-tireli (örn., `visual-studio-code`) +- **Linux**: küçük harf, dağıtıma göre değişir (örn., `code`, `docker.io`) + +#### 3. Paketin Var Olduğunu Doğrulayın + +**Seçenek A: Doğrulama Scriptini Kullanma (Önerilen)** + +Veritabanı erişiminiz varsa: + +```bash +# Belirli bir platformu doğrula +npm run validate:presets -- windows + +# Birden fazla platformu doğrula +npm run validate:presets -- ubuntu debian arch + +# Tüm platformları doğrula +npm run validate:presets -- --all +``` + +Script şunları gösterecek: +- ✅ Veritabanında bulunan paketler +- ❌ Bulunamayan paketler +- 💡 Benzer paket önerileri + +**Seçenek B: Manuel Doğrulama** + +Veritabanı erişiminiz yoksa: + +1. Canlı RepoHub web sitesinde arama yapın +2. Arama sonuçlarında paketinizi bulun +3. Görüntülenen **tam paket adını** kopyalayın +4. Veya resmi paket depolarını kontrol edin: + - Windows: [winget.run](https://winget.run/) + - macOS: `brew search ` + - Ubuntu/Debian: `apt search ` + - Arch: [archlinux.org/packages](https://archlinux.org/packages/) + - Fedora: [packages.fedoraproject.org](https://packages.fedoraproject.org/) + +#### 4. Paketi Ekleyin + +Paket adını diziye ekleyin: + +```typescript +windows: { + development: [ + "Git.Git", + "Microsoft.VisualStudioCode", + "Docker.DockerDesktop" // ← Yeni paketiniz + ] +} +``` + +#### 5. Değişikliklerinizi Test Edin + +1. Doğrulamayı çalıştırın: + ```bash + npm run validate:presets -- windows + ``` + +2. Geliştirme sunucusunu başlatın: + ```bash + npm run dev + ``` + +3. Uygulamada test edin: + - Onboarding'i tamamlayın + - İlgili kategoriyi seçin + - Paketinizin önerilerde görünüp görünmediğini kontrol edin + +### En İyi Uygulamalar + +**YAPIN ✅** +- Doğrulama scriptini kullanarak paket adlarını doğrulayın +- Popüler, iyi bakımlı paketler ekleyin +- Göndermeden önce test edin +- Resmi depolardan tam paket adlarını kullanın + +**YAPMAYIN ❌** +- Paket adlarını tahmin etmeyin +- Kullanımdan kaldırılmış paketler eklemeyin +- Doğrulamayı atlamayın +- Kategoriler arasında tekrar eklemeyin + +### Örnek Pull Request + +``` +Windows Önerilerine Popüler Geliştirme Araçları Eklendi + +- development'a Docker.DockerDesktop eklendi +- development'a Postman.Postman eklendi +- Doğrulama: ✅ Tüm paketler doğrulandı (%100 bulundu) +``` + +## 🤝 Katkıda Bulunma + +Katkılar hoş karşılanır! Lütfen Pull Request göndermekten çekinmeyin. + +1. Projeyi fork edin +2. Feature branch'inizi oluşturun (`git checkout -b feature/HarikaBirOzellik`) +3. Değişikliklerinizi commit edin (`git commit -m 'Harika bir özellik ekle'`) +4. Branch'inizi push edin (`git push origin feature/HarikaBirOzellik`) +5. Bir Pull Request açın diff --git a/package.json b/package.json index 22c8fa4..c305c50 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,8 @@ "lint": "next lint", "type-check": "tsc --noEmit", "test:db": "node scripts/test-db.js", - "init:db": "node scripts/init-db.js" + "init:db": "node scripts/init-db.js", + "validate:presets": "tsx scripts/validate-presets.ts" }, "dependencies": { "@radix-ui/react-checkbox": "^1.0.4", @@ -31,6 +32,7 @@ "react-query": "^3.39.3", "tailwind-merge": "^2.1.0", "tailwindcss-animate": "^1.0.7", + "tsx": "^4.20.6", "undici": "^6.6.2", "zustand": "^4.4.7" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7c8b52e..e064d92 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -64,7 +64,10 @@ importers: version: 2.6.0 tailwindcss-animate: specifier: ^1.0.7 - version: 1.0.7(tailwindcss@3.4.18) + version: 1.0.7(tailwindcss@3.4.18(tsx@4.20.6)) + tsx: + specifier: ^4.20.6 + version: 4.20.6 undici: specifier: ^6.6.2 version: 6.22.0 @@ -98,7 +101,7 @@ importers: version: 8.5.6 tailwindcss: specifier: ^3.3.0 - version: 3.4.18 + version: 3.4.18(tsx@4.20.6) typescript: specifier: ^5 version: 5.9.3 @@ -122,6 +125,162 @@ packages: '@emnapi/wasi-threads@1.1.0': resolution: {integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==} + '@esbuild/aix-ppc64@0.25.12': + resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.25.12': + resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.25.12': + resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.25.12': + resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.25.12': + resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.25.12': + resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.25.12': + resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.25.12': + resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.25.12': + resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.25.12': + resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.25.12': + resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.25.12': + resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.25.12': + resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.25.12': + resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.25.12': + resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.25.12': + resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.25.12': + resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.25.12': + resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.25.12': + resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.25.12': + resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.25.12': + resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.25.12': + resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.25.12': + resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.25.12': + resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.25.12': + resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.25.12': + resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + '@eslint-community/eslint-utils@4.9.0': resolution: {integrity: sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -1166,6 +1325,11 @@ packages: resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} engines: {node: '>= 0.4'} + esbuild@0.25.12: + resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==} + engines: {node: '>=18'} + hasBin: true + escalade@3.2.0: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} @@ -2314,6 +2478,11 @@ packages: tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + tsx@4.20.6: + resolution: {integrity: sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg==} + engines: {node: '>=18.0.0'} + hasBin: true + type-check@0.4.0: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} @@ -2494,6 +2663,84 @@ snapshots: tslib: 2.8.1 optional: true + '@esbuild/aix-ppc64@0.25.12': + optional: true + + '@esbuild/android-arm64@0.25.12': + optional: true + + '@esbuild/android-arm@0.25.12': + optional: true + + '@esbuild/android-x64@0.25.12': + optional: true + + '@esbuild/darwin-arm64@0.25.12': + optional: true + + '@esbuild/darwin-x64@0.25.12': + optional: true + + '@esbuild/freebsd-arm64@0.25.12': + optional: true + + '@esbuild/freebsd-x64@0.25.12': + optional: true + + '@esbuild/linux-arm64@0.25.12': + optional: true + + '@esbuild/linux-arm@0.25.12': + optional: true + + '@esbuild/linux-ia32@0.25.12': + optional: true + + '@esbuild/linux-loong64@0.25.12': + optional: true + + '@esbuild/linux-mips64el@0.25.12': + optional: true + + '@esbuild/linux-ppc64@0.25.12': + optional: true + + '@esbuild/linux-riscv64@0.25.12': + optional: true + + '@esbuild/linux-s390x@0.25.12': + optional: true + + '@esbuild/linux-x64@0.25.12': + optional: true + + '@esbuild/netbsd-arm64@0.25.12': + optional: true + + '@esbuild/netbsd-x64@0.25.12': + optional: true + + '@esbuild/openbsd-arm64@0.25.12': + optional: true + + '@esbuild/openbsd-x64@0.25.12': + optional: true + + '@esbuild/openharmony-arm64@0.25.12': + optional: true + + '@esbuild/sunos-x64@0.25.12': + optional: true + + '@esbuild/win32-arm64@0.25.12': + optional: true + + '@esbuild/win32-ia32@0.25.12': + optional: true + + '@esbuild/win32-x64@0.25.12': + optional: true + '@eslint-community/eslint-utils@4.9.0(eslint@8.57.1)': dependencies: eslint: 8.57.1 @@ -3589,6 +3836,35 @@ snapshots: is-date-object: 1.1.0 is-symbol: 1.1.1 + esbuild@0.25.12: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.12 + '@esbuild/android-arm': 0.25.12 + '@esbuild/android-arm64': 0.25.12 + '@esbuild/android-x64': 0.25.12 + '@esbuild/darwin-arm64': 0.25.12 + '@esbuild/darwin-x64': 0.25.12 + '@esbuild/freebsd-arm64': 0.25.12 + '@esbuild/freebsd-x64': 0.25.12 + '@esbuild/linux-arm': 0.25.12 + '@esbuild/linux-arm64': 0.25.12 + '@esbuild/linux-ia32': 0.25.12 + '@esbuild/linux-loong64': 0.25.12 + '@esbuild/linux-mips64el': 0.25.12 + '@esbuild/linux-ppc64': 0.25.12 + '@esbuild/linux-riscv64': 0.25.12 + '@esbuild/linux-s390x': 0.25.12 + '@esbuild/linux-x64': 0.25.12 + '@esbuild/netbsd-arm64': 0.25.12 + '@esbuild/netbsd-x64': 0.25.12 + '@esbuild/openbsd-arm64': 0.25.12 + '@esbuild/openbsd-x64': 0.25.12 + '@esbuild/openharmony-arm64': 0.25.12 + '@esbuild/sunos-x64': 0.25.12 + '@esbuild/win32-arm64': 0.25.12 + '@esbuild/win32-ia32': 0.25.12 + '@esbuild/win32-x64': 0.25.12 + escalade@3.2.0: {} escape-string-regexp@4.0.0: {} @@ -4463,12 +4739,13 @@ snapshots: camelcase-css: 2.0.1 postcss: 8.5.6 - postcss-load-config@6.0.1(jiti@1.21.7)(postcss@8.5.6): + postcss-load-config@6.0.1(jiti@1.21.7)(postcss@8.5.6)(tsx@4.20.6): dependencies: lilconfig: 3.1.3 optionalDependencies: jiti: 1.21.7 postcss: 8.5.6 + tsx: 4.20.6 postcss-nested@6.2.0(postcss@8.5.6): dependencies: @@ -4819,11 +5096,11 @@ snapshots: tailwind-merge@2.6.0: {} - tailwindcss-animate@1.0.7(tailwindcss@3.4.18): + tailwindcss-animate@1.0.7(tailwindcss@3.4.18(tsx@4.20.6)): dependencies: - tailwindcss: 3.4.18 + tailwindcss: 3.4.18(tsx@4.20.6) - tailwindcss@3.4.18: + tailwindcss@3.4.18(tsx@4.20.6): dependencies: '@alloc/quick-lru': 5.2.0 arg: 5.0.2 @@ -4842,7 +5119,7 @@ snapshots: postcss: 8.5.6 postcss-import: 15.1.0(postcss@8.5.6) postcss-js: 4.1.0(postcss@8.5.6) - postcss-load-config: 6.0.1(jiti@1.21.7)(postcss@8.5.6) + postcss-load-config: 6.0.1(jiti@1.21.7)(postcss@8.5.6)(tsx@4.20.6) postcss-nested: 6.2.0(postcss@8.5.6) postcss-selector-parser: 6.1.2 resolve: 1.22.11 @@ -4885,6 +5162,13 @@ snapshots: tslib@2.8.1: {} + tsx@4.20.6: + dependencies: + esbuild: 0.25.12 + get-tsconfig: 4.13.0 + optionalDependencies: + fsevents: 2.3.3 + type-check@0.4.0: dependencies: prelude-ls: 1.2.1 diff --git a/scripts/validate-presets.js b/scripts/validate-presets.js new file mode 100755 index 0000000..d2087c1 --- /dev/null +++ b/scripts/validate-presets.js @@ -0,0 +1,188 @@ +#!/usr/bin/env node + +/** + * Validate package names in recommendation presets against the database + * + * Usage: + * node scripts/validate-presets.js --all + * node scripts/validate-presets.js windows macos + * node scripts/validate-presets.js ubuntu + */ + +const fetch = require('node-fetch'); + +// Import the presets +const { PACKAGE_PRESETS } = require('../src/data/recommendationPresets.ts'); + +const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3000'; + +// Platform IDs +const PLATFORMS = ['windows', 'macos', 'ubuntu', 'debian', 'arch', 'fedora']; + +async function checkPackageExists(platformId, packageName) { + try { + const response = await fetch( + `${API_BASE_URL}/api/packages?platform_id=${platformId}&search=${encodeURIComponent(packageName)}&limit=10` + ); + + if (!response.ok) { + throw new Error(`API error: ${response.status}`); + } + + const data = await response.json(); + + // Check for exact match (case-insensitive) + const exactMatch = data.packages?.find( + pkg => pkg.name.toLowerCase() === packageName.toLowerCase() + ); + + return { + exists: !!exactMatch, + foundName: exactMatch?.name || null, + similarMatches: data.packages?.slice(0, 3).map(p => p.name) || [] + }; + } catch (error) { + console.error(`Error checking ${packageName} on ${platformId}:`, error.message); + return { exists: false, error: error.message }; + } +} + +async function validatePlatform(platformId) { + console.log(`\n${'='.repeat(60)}`); + console.log(`Validating ${platformId.toUpperCase()}`); + console.log('='.repeat(60)); + + const platformPresets = PACKAGE_PRESETS[platformId]; + if (!platformPresets) { + console.log(`❌ No presets found for platform: ${platformId}`); + return { total: 0, found: 0, missing: [] }; + } + + const results = { + total: 0, + found: 0, + missing: [] + }; + + // Check each category + for (const [category, packages] of Object.entries(platformPresets)) { + console.log(`\n📁 Category: ${category}`); + + for (const packageName of packages) { + results.total++; + + const check = await checkPackageExists(platformId, packageName); + + if (check.error) { + console.log(` ⚠️ ${packageName} - Error: ${check.error}`); + results.missing.push({ category, packageName, reason: 'API Error' }); + } else if (check.exists) { + console.log(` ✅ ${packageName}${check.foundName !== packageName ? ` (found as: ${check.foundName})` : ''}`); + results.found++; + } else { + console.log(` ❌ ${packageName} - NOT FOUND`); + if (check.similarMatches?.length > 0) { + console.log(` Similar: ${check.similarMatches.join(', ')}`); + } + results.missing.push({ + category, + packageName, + similar: check.similarMatches + }); + } + + // Small delay to avoid overwhelming the API + await new Promise(resolve => setTimeout(resolve, 100)); + } + } + + return results; +} + +async function main() { + const args = process.argv.slice(2); + + if (args.length === 0) { + console.log('Usage:'); + console.log(' node scripts/validate-presets.js --all'); + console.log(' node scripts/validate-presets.js windows macos ubuntu'); + console.log('\nAvailable platforms:', PLATFORMS.join(', ')); + process.exit(1); + } + + let platformsToCheck = []; + + if (args.includes('--all')) { + platformsToCheck = PLATFORMS; + } else { + // Validate platform names + for (const platform of args) { + if (!PLATFORMS.includes(platform)) { + console.error(`❌ Invalid platform: ${platform}`); + console.log('Available platforms:', PLATFORMS.join(', ')); + process.exit(1); + } + } + platformsToCheck = args; + } + + console.log(`\n🔍 Validating package presets for: ${platformsToCheck.join(', ')}\n`); + + const allResults = {}; + + for (const platform of platformsToCheck) { + const results = await validatePlatform(platform); + allResults[platform] = results; + } + + // Summary + console.log(`\n${'='.repeat(60)}`); + console.log('SUMMARY'); + console.log('='.repeat(60)); + + let totalPackages = 0; + let totalFound = 0; + let totalMissing = 0; + + for (const [platform, results] of Object.entries(allResults)) { + totalPackages += results.total; + totalFound += results.found; + totalMissing += results.missing.length; + + const successRate = results.total > 0 + ? ((results.found / results.total) * 100).toFixed(1) + : 0; + + console.log(`\n${platform.toUpperCase()}:`); + console.log(` Total: ${results.total}`); + console.log(` Found: ${results.found} (${successRate}%)`); + console.log(` Missing: ${results.missing.length}`); + + if (results.missing.length > 0) { + console.log(` Missing packages:`); + for (const { category, packageName, similar } of results.missing) { + console.log(` - ${packageName} (${category})`); + if (similar?.length > 0) { + console.log(` Try: ${similar.join(', ')}`); + } + } + } + } + + console.log(`\n${'='.repeat(60)}`); + console.log(`OVERALL: ${totalFound}/${totalPackages} packages found (${((totalFound / totalPackages) * 100).toFixed(1)}%)`); + console.log('='.repeat(60)); + + if (totalMissing > 0) { + console.log(`\n⚠️ Found ${totalMissing} missing packages. Review the output above and update recommendationPresets.ts`); + process.exit(1); + } else { + console.log('\n✅ All packages validated successfully!'); + process.exit(0); + } +} + +main().catch(error => { + console.error('Fatal error:', error); + process.exit(1); +}); diff --git a/scripts/validate-presets.ts b/scripts/validate-presets.ts new file mode 100644 index 0000000..22ec70e --- /dev/null +++ b/scripts/validate-presets.ts @@ -0,0 +1,194 @@ +/** + * Validate package names in recommendation presets against the database + * + * Usage: + * tsx scripts/validate-presets.ts --all + * tsx scripts/validate-presets.ts windows macos + * tsx scripts/validate-presets.ts ubuntu + */ + +import { PACKAGE_PRESETS } from '../src/data/recommendationPresets'; + +const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3002'; + +// Platform IDs +const PLATFORMS = ['windows', 'macos', 'ubuntu', 'debian', 'arch', 'fedora'] as const; +type PlatformId = typeof PLATFORMS[number]; + +interface PackageCheckResult { + exists: boolean; + foundName?: string; + similarMatches?: string[]; + error?: string; +} + +async function checkPackageExists( + platformId: string, + packageName: string +): Promise { + try { + const response = await fetch( + `${API_BASE_URL}/api/packages?platform_id=${platformId}&search=${encodeURIComponent(packageName)}&limit=10` + ); + + if (!response.ok) { + throw new Error(`API error: ${response.status}`); + } + + const data = await response.json(); + + // Check for exact match (case-insensitive) + const exactMatch = data.packages?.find( + (pkg: any) => pkg.name.toLowerCase() === packageName.toLowerCase() + ); + + return { + exists: !!exactMatch, + foundName: exactMatch?.name || undefined, + similarMatches: data.packages?.slice(0, 3).map((p: any) => p.name) || [] + }; + } catch (error: any) { + console.error(`Error checking ${packageName} on ${platformId}:`, error.message); + return { exists: false, error: error.message }; + } +} + +async function validatePlatform(platformId: PlatformId) { + console.log(`\n${'='.repeat(60)}`); + console.log(`Validating ${platformId.toUpperCase()}`); + console.log('='.repeat(60)); + + const platformPresets = PACKAGE_PRESETS[platformId]; + if (!platformPresets) { + console.log(`❌ No presets found for platform: ${platformId}`); + return { total: 0, found: 0, missing: [] as any[] }; + } + + const results = { + total: 0, + found: 0, + missing: [] as { category: string; packageName: string; similar?: string[]; reason?: string }[] + }; + + // Check each category + for (const [category, packages] of Object.entries(platformPresets)) { + console.log(`\n📁 Category: ${category}`); + + for (const packageName of packages) { + results.total++; + + const check = await checkPackageExists(platformId, packageName); + + if (check.error) { + console.log(` ⚠️ ${packageName} - Error: ${check.error}`); + results.missing.push({ category, packageName, reason: 'API Error' }); + } else if (check.exists) { + console.log(` ✅ ${packageName}${check.foundName !== packageName ? ` (found as: ${check.foundName})` : ''}`); + results.found++; + } else { + console.log(` ❌ ${packageName} - NOT FOUND`); + if (check.similarMatches && check.similarMatches.length > 0) { + console.log(` Similar: ${check.similarMatches.join(', ')}`); + } + results.missing.push({ + category, + packageName, + similar: check.similarMatches + }); + } + + // Delay to avoid rate limiting + await new Promise(resolve => setTimeout(resolve, 500)); + } + } + + return results; +} + +async function main() { + const args = process.argv.slice(2); + + if (args.length === 0) { + console.log('Usage:'); + console.log(' tsx scripts/validate-presets.ts --all'); + console.log(' tsx scripts/validate-presets.ts windows macos ubuntu'); + console.log('\nAvailable platforms:', PLATFORMS.join(', ')); + process.exit(1); + } + + let platformsToCheck: PlatformId[] = []; + + if (args.includes('--all')) { + platformsToCheck = [...PLATFORMS]; + } else { + // Validate platform names + for (const platform of args) { + if (!PLATFORMS.includes(platform as any)) { + console.error(`❌ Invalid platform: ${platform}`); + console.log('Available platforms:', PLATFORMS.join(', ')); + process.exit(1); + } + } + platformsToCheck = args as PlatformId[]; + } + + console.log(`\n🔍 Validating package presets for: ${platformsToCheck.join(', ')}\n`); + + const allResults: Record = {}; + + for (const platform of platformsToCheck) { + const results = await validatePlatform(platform); + allResults[platform] = results; + } + + // Summary + console.log(`\n${'='.repeat(60)}`); + console.log('SUMMARY'); + console.log('='.repeat(60)); + + let totalPackages = 0; + let totalFound = 0; + let totalMissing = 0; + + for (const [platform, results] of Object.entries(allResults)) { + totalPackages += results.total; + totalFound += results.found; + totalMissing += results.missing.length; + + const successRate = results.total > 0 + ? ((results.found / results.total) * 100).toFixed(1) + : '0'; + + console.log(`\n${platform.toUpperCase()}:`); + console.log(` Total: ${results.total}`); + console.log(` Found: ${results.found} (${successRate}%)`); + console.log(` Missing: ${results.missing.length}`); + + if (results.missing.length > 0) { + console.log(` Missing packages:`); + for (const { category, packageName, similar } of results.missing) { + console.log(` - ${packageName} (${category})`); + if (similar && similar.length > 0) { + console.log(` Try: ${similar.join(', ')}`); + } + } + } + } + + console.log(`\n${'='.repeat(60)}`); + console.log(`OVERALL: ${totalFound}/${totalPackages} packages found (${((totalFound / totalPackages) * 100).toFixed(1)}%)`); + console.log('='.repeat(60)); + + if (totalMissing > 0) { + console.log(`\n⚠️ Found ${totalMissing} missing packages. Review the output above and update recommendationPresets.ts`); + process.exit(1); + } else { + console.log('\n✅ All packages validated successfully!'); + process.exit(0); + } +} + +main().catch(error => { + console.error('Fatal error:', error); + process.exit(1); +}); diff --git a/src/app/api/recommendations/route.ts b/src/app/api/recommendations/route.ts index dc6a50a..2507f71 100644 --- a/src/app/api/recommendations/route.ts +++ b/src/app/api/recommendations/route.ts @@ -65,14 +65,14 @@ export async function POST(request: NextRequest) { } // Validate and set limit with better error message - if (body.limit !== undefined && (body.limit < 1 || body.limit > 50)) { + if (body.limit !== undefined && (body.limit < 1 || body.limit > 1000)) { return NextResponse.json( - { error: "Limit must be between 1 and 50" }, + { error: "Limit must be between 1 and 1000" }, { status: 400 } ); } const limit = - body.limit && body.limit > 0 && body.limit <= 50 ? body.limit : 20; + body.limit && body.limit > 0 && body.limit <= 1000 ? body.limit : 50; // Generate recommendations const recommendations = await RecommendationService.generateRecommendations( diff --git a/src/components/OnboardingModal.tsx b/src/components/OnboardingModal.tsx index 1617569..88506de 100644 --- a/src/components/OnboardingModal.tsx +++ b/src/components/OnboardingModal.tsx @@ -174,12 +174,6 @@ export function OnboardingModal({ ) })} - - {selectedCategories.length > 0 && ( -

- {t('onboarding.step1.selected', { count: selectedCategories.length })} -

- )} )} diff --git a/src/components/RecommendationCard.tsx b/src/components/RecommendationCard.tsx index 2aa34b2..1a3974c 100644 --- a/src/components/RecommendationCard.tsx +++ b/src/components/RecommendationCard.tsx @@ -30,12 +30,6 @@ export function RecommendationCard({ pkg, isSelected, onToggle }: Recommendation -

- {pkg.description} -

- - -