fix(api): remove conflicting [id] route causing build error
Next.js error: "You cannot use different slug names for the same dynamic path ('id' !== 'slug')"
Removed /api/tools/[id]/route.ts to resolve conflict with /api/tools/[slug]/route.ts
The [slug] route already handles fetching tools by package name
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
aff28c4de9
commit
7f5d307c0f
3 changed files with 505 additions and 63 deletions
41
.env.vercel.production
Normal file
41
.env.vercel.production
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
# Created by Vercel CLI
|
||||
CRON_SECRET="CRON_SECRET=6c806d35cf6212f489c76414d38d2b6acbc44590ac78bb08aadea28dd04a29d0\n"
|
||||
DATABASE_URL="postgresql://neondb_owner:npg_euvYo4OTi1lX@ep-broad-darkness-a4lml85k-pooler.us-east-1.aws.neon.tech/neondb?sslmode=require"
|
||||
DATABASE_URL_UNPOOLED="postgresql://neondb_owner:npg_euvYo4OTi1lX@ep-broad-darkness-a4lml85k.us-east-1.aws.neon.tech/neondb?sslmode=require"
|
||||
NEXT_PUBLIC_STACK_PROJECT_ID="d786bd3a-a31d-4c6b-9497-5d6803dd9d86"
|
||||
NEXT_PUBLIC_STACK_PUBLISHABLE_CLIENT_KEY="pck_hafmpkaj047z331x5azv8bk5zggfnbgdedbj9pfqh1rn0"
|
||||
NX_DAEMON="false"
|
||||
PGDATABASE="neondb"
|
||||
PGHOST="ep-broad-darkness-a4lml85k-pooler.us-east-1.aws.neon.tech"
|
||||
PGHOST_UNPOOLED="ep-broad-darkness-a4lml85k.us-east-1.aws.neon.tech"
|
||||
PGPASSWORD="npg_euvYo4OTi1lX"
|
||||
PGUSER="neondb_owner"
|
||||
POSTGRES_DATABASE="neondb"
|
||||
POSTGRES_HOST="ep-broad-darkness-a4lml85k-pooler.us-east-1.aws.neon.tech"
|
||||
POSTGRES_PASSWORD="npg_euvYo4OTi1lX"
|
||||
POSTGRES_PRISMA_URL="postgresql://neondb_owner:npg_euvYo4OTi1lX@ep-broad-darkness-a4lml85k-pooler.us-east-1.aws.neon.tech/neondb?connect_timeout=15&sslmode=require"
|
||||
POSTGRES_URL="postgresql://neondb_owner:npg_euvYo4OTi1lX@ep-broad-darkness-a4lml85k-pooler.us-east-1.aws.neon.tech/neondb?sslmode=require"
|
||||
POSTGRES_URL_NON_POOLING="postgresql://neondb_owner:npg_euvYo4OTi1lX@ep-broad-darkness-a4lml85k.us-east-1.aws.neon.tech/neondb?sslmode=require"
|
||||
POSTGRES_URL_NO_SSL="postgresql://neondb_owner:npg_euvYo4OTi1lX@ep-broad-darkness-a4lml85k-pooler.us-east-1.aws.neon.tech/neondb"
|
||||
POSTGRES_USER="neondb_owner"
|
||||
STACK_SECRET_SERVER_KEY="ssk_p05kwe938wx13rpera9xf1fewc816dwkbq658xcsbwj1g"
|
||||
TURBO_CACHE="remote:rw"
|
||||
TURBO_DOWNLOAD_LOCAL_ENABLED="true"
|
||||
TURBO_REMOTE_ONLY="true"
|
||||
TURBO_RUN_SUMMARY="true"
|
||||
VERCEL="1"
|
||||
VERCEL_ENV="production"
|
||||
VERCEL_GIT_COMMIT_AUTHOR_LOGIN=""
|
||||
VERCEL_GIT_COMMIT_AUTHOR_NAME=""
|
||||
VERCEL_GIT_COMMIT_MESSAGE=""
|
||||
VERCEL_GIT_COMMIT_REF=""
|
||||
VERCEL_GIT_COMMIT_SHA=""
|
||||
VERCEL_GIT_PREVIOUS_SHA=""
|
||||
VERCEL_GIT_PROVIDER=""
|
||||
VERCEL_GIT_PULL_REQUEST_ID=""
|
||||
VERCEL_GIT_REPO_ID=""
|
||||
VERCEL_GIT_REPO_OWNER=""
|
||||
VERCEL_GIT_REPO_SLUG=""
|
||||
VERCEL_OIDC_TOKEN="eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Im1yay00MzAyZWMxYjY3MGY0OGE5OGFkNjFkYWRlNGEyM2JlNyJ9.eyJpc3MiOiJodHRwczovL29pZGMudmVyY2VsLmNvbS90cG1qcyIsInN1YiI6Im93bmVyOnRwbWpzOnByb2plY3Q6dHBtanMtd2ViOmVudmlyb25tZW50OmRldmVsb3BtZW50Iiwic2NvcGUiOiJvd25lcjp0cG1qczpwcm9qZWN0OnRwbWpzLXdlYjplbnZpcm9ubWVudDpkZXZlbG9wbWVudCIsImF1ZCI6Imh0dHBzOi8vdmVyY2VsLmNvbS90cG1qcyIsIm93bmVyIjoidHBtanMiLCJvd25lcl9pZCI6InRlYW1femtHV0NXYjdWakhvbmk2VmJ5ZmQyc3c4IiwicHJvamVjdCI6InRwbWpzLXdlYiIsInByb2plY3RfaWQiOiJwcmpfNWd1MEkwVzFjUFhkQ3ozd1RjQ0ZIejQzNUJ0MCIsImVudmlyb25tZW50IjoiZGV2ZWxvcG1lbnQiLCJwbGFuIjoicHJvIiwidXNlcl9pZCI6IkxKZk05VzdIdlljb2gyclVCaXRWd283ViIsIm5iZiI6MTc2NDM4OTAxMiwiaWF0IjoxNzY0Mzg5MDEyLCJleHAiOjE3NjQ0MzIyMTJ9.OF4IHrcmteA2lU1tkqHO1a9ITGGrCjCo29G8jI991q8_SQjgHZHVqcBj3AYVKZJDh6BjHib4HyNKdjO8nwUblF2dCFbYDv6y4hwB6jHNpsz32BE1JDKcXEJOKPtg_tBOFUDKtzMkPk7VOPWDVYw8Tz4_HZ_MR3SNoy1Pk9AFL-hEl3E-zR3bAYMDB8tKrIm9y9K4sZF6efMU7BR_J6Bf-i3IsbbrH-Axgq5dewlpogf-xHWmWaTXoUp6UFejNKhSMXqg3sAWTnizYeSGc2Ut6zNuAYPumUPBdQ37Kk7vuRNwS1h7RJz3vtEg6aOuw0-Ld0LdF-tWkfDGsVqanR_sxw"
|
||||
VERCEL_TARGET_ENV="production"
|
||||
VERCEL_URL=""
|
||||
464
HOW_TO_PUBLISH_A_TOOL.md
Normal file
464
HOW_TO_PUBLISH_A_TOOL.md
Normal file
|
|
@ -0,0 +1,464 @@
|
|||
# How to Publish a TPMJS Tool
|
||||
|
||||
This guide shows you how to create and publish an AI tool that will be automatically discovered and listed on tpmjs.com.
|
||||
|
||||
## Quick Start
|
||||
|
||||
1. Create a new NPM package
|
||||
2. Add `"tpmjs-tool"` to the `keywords` array in package.json
|
||||
3. Add a `tpmjs` field with your tool's metadata
|
||||
4. Publish to NPM
|
||||
5. Your tool will automatically appear on tpmjs.com within 15 minutes
|
||||
|
||||
## Step-by-Step Guide
|
||||
|
||||
### 1. Create Your NPM Package
|
||||
|
||||
Create a standard NPM package with your tool implementation:
|
||||
|
||||
```bash
|
||||
mkdir my-awesome-tool
|
||||
cd my-awesome-tool
|
||||
npm init -y
|
||||
```
|
||||
|
||||
### 2. Add the Required Keyword
|
||||
|
||||
In your `package.json`, add `"tpmjs-tool"` to the keywords array:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "@yourname/my-awesome-tool",
|
||||
"version": "1.0.0",
|
||||
"keywords": ["tpmjs-tool", "ai", "other-keywords"],
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
**Important:** The `"tpmjs-tool"` keyword is REQUIRED for automatic discovery!
|
||||
|
||||
### 3. Add TPMJS Metadata
|
||||
|
||||
Add a `tpmjs` field to your `package.json` with your tool's metadata. There are three tiers:
|
||||
|
||||
#### Tier 1: Minimal (Required Fields Only)
|
||||
|
||||
The bare minimum to get listed:
|
||||
|
||||
```json
|
||||
{
|
||||
"tpmjs": {
|
||||
"category": "text-analysis",
|
||||
"description": "A concise description of what your tool does",
|
||||
"example": "const result = await myTool({ input: 'hello' });"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Required fields:**
|
||||
- `category` - One of: `text-analysis`, `code-generation`, `data-processing`, `image-generation`, `audio-processing`, `search`, `integration`, `other`
|
||||
- `description` - Clear description of what the tool does (1-3 sentences)
|
||||
- `example` - Simple code example showing how to use the tool
|
||||
|
||||
#### Tier 2: Basic (Recommended)
|
||||
|
||||
Add parameter and return type information:
|
||||
|
||||
```json
|
||||
{
|
||||
"tpmjs": {
|
||||
"category": "text-analysis",
|
||||
"description": "Analyzes sentiment in text and returns a score",
|
||||
"example": "const result = await analyzeSentiment({ text: 'I love this!' });",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "text",
|
||||
"type": "string",
|
||||
"description": "The text to analyze",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"name": "language",
|
||||
"type": "string",
|
||||
"description": "Language code (e.g., 'en', 'es')",
|
||||
"required": false,
|
||||
"default": "en"
|
||||
}
|
||||
],
|
||||
"returns": {
|
||||
"type": "SentimentResult",
|
||||
"description": "Object containing score (-1 to 1) and label (positive/negative/neutral)"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Tier 3: Rich (Full Documentation)
|
||||
|
||||
Complete metadata for maximum visibility:
|
||||
|
||||
```json
|
||||
{
|
||||
"tpmjs": {
|
||||
"category": "text-analysis",
|
||||
"description": "Advanced sentiment analysis with emotion detection",
|
||||
"example": "const result = await analyzeSentiment({ text: 'I love this!', includeEmotions: true });",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "text",
|
||||
"type": "string",
|
||||
"description": "The text to analyze",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"name": "language",
|
||||
"type": "string",
|
||||
"description": "Language code",
|
||||
"required": false,
|
||||
"default": "en"
|
||||
},
|
||||
{
|
||||
"name": "includeEmotions",
|
||||
"type": "boolean",
|
||||
"description": "Whether to include emotion breakdown",
|
||||
"required": false,
|
||||
"default": false
|
||||
}
|
||||
],
|
||||
"returns": {
|
||||
"type": "SentimentResult",
|
||||
"description": "Object with score, label, and optional emotions array"
|
||||
},
|
||||
"authentication": {
|
||||
"type": "api-key",
|
||||
"required": true
|
||||
},
|
||||
"pricing": {
|
||||
"model": "pay-per-use",
|
||||
"cost": "$0.001 per request"
|
||||
},
|
||||
"frameworks": ["vercel-ai", "langchain"],
|
||||
"links": {
|
||||
"documentation": "https://docs.example.com",
|
||||
"repository": "https://github.com/yourname/tool",
|
||||
"homepage": "https://yourwebsite.com"
|
||||
},
|
||||
"tags": ["sentiment", "nlp", "emotions", "ai"],
|
||||
"status": "stable",
|
||||
"aiAgent": {
|
||||
"useCase": "Use this tool when users need to analyze sentiment in text, detect emotions, or understand the tone of customer feedback, reviews, or social media posts.",
|
||||
"limitations": "Only supports English and Spanish. Maximum 10,000 characters per request.",
|
||||
"examples": [
|
||||
"Analyze customer review sentiment",
|
||||
"Detect emotions in user feedback",
|
||||
"Monitor social media sentiment"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Implement Your Tool
|
||||
|
||||
Write your tool's implementation. Here's the example from `@tpmjs/createblogpost`:
|
||||
|
||||
```typescript
|
||||
// src/index.ts
|
||||
export interface BlogPostOptions {
|
||||
title: string;
|
||||
author: string;
|
||||
content: string;
|
||||
tags?: string[];
|
||||
format?: 'markdown' | 'mdx';
|
||||
excerpt?: string;
|
||||
}
|
||||
|
||||
export interface BlogPost {
|
||||
frontmatter: {
|
||||
title: string;
|
||||
author: string;
|
||||
date: string;
|
||||
tags: string[];
|
||||
excerpt?: string;
|
||||
slug: string;
|
||||
wordCount: number;
|
||||
readingTime: number;
|
||||
};
|
||||
content: string;
|
||||
formattedOutput: string;
|
||||
}
|
||||
|
||||
export async function createBlogPost(options: BlogPostOptions): Promise<BlogPost> {
|
||||
// Your implementation here
|
||||
const { title, author, content, tags = [], format = 'markdown', excerpt } = options;
|
||||
|
||||
// Validate inputs
|
||||
if (!title || !author || !content) {
|
||||
throw new Error('Title, author, and content are required');
|
||||
}
|
||||
|
||||
// Process and return result
|
||||
return {
|
||||
frontmatter: { /* ... */ },
|
||||
content,
|
||||
formattedOutput: '...'
|
||||
};
|
||||
}
|
||||
|
||||
export default createBlogPost;
|
||||
```
|
||||
|
||||
### 5. Build and Publish
|
||||
|
||||
Build your package and publish to NPM:
|
||||
|
||||
```bash
|
||||
# Build your package
|
||||
npm run build
|
||||
|
||||
# Publish to NPM
|
||||
npm publish --access public
|
||||
```
|
||||
|
||||
### 6. Verification
|
||||
|
||||
Your tool will be automatically discovered through:
|
||||
|
||||
1. **Keyword Search** - Runs every 15 minutes, searches NPM for `"tpmjs-tool"`
|
||||
2. **Changes Feed** - Monitors NPM publishes in real-time (every 2 minutes)
|
||||
|
||||
After publishing, your tool should appear on https://tpmjs.com within 15 minutes!
|
||||
|
||||
You can verify by searching: https://tpmjs.com/api/tools?q=yourpackagename
|
||||
|
||||
## Real Example: @tpmjs/createblogpost
|
||||
|
||||
Here's the complete `package.json` from the published example:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "@tpmjs/createblogpost",
|
||||
"version": "0.2.0",
|
||||
"description": "A tool for creating structured blog posts with AI-generated content",
|
||||
"type": "module",
|
||||
"keywords": ["tpmjs-tool", "blog", "content", "ai", "writing"],
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"default": "./dist/index.js"
|
||||
}
|
||||
},
|
||||
"files": ["dist"],
|
||||
"scripts": {
|
||||
"build": "tsup",
|
||||
"dev": "tsup --watch",
|
||||
"type-check": "tsc --noEmit"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/ajaxdavis/tpmjs.git",
|
||||
"directory": "packages/tools/createBlogPost"
|
||||
},
|
||||
"homepage": "https://tpmjs.com",
|
||||
"license": "MIT",
|
||||
"tpmjs": {
|
||||
"category": "text-analysis",
|
||||
"description": "Creates structured blog posts with customizable frontmatter, content sections, and SEO metadata. Supports multiple output formats including Markdown and MDX.",
|
||||
"example": "const post = await createBlogPost({ title: 'My First Post', author: 'John Doe', content: 'Hello World!', tags: ['intro', 'blog'] });",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "title",
|
||||
"type": "string",
|
||||
"description": "The title of the blog post",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"name": "author",
|
||||
"type": "string",
|
||||
"description": "The author of the blog post",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"name": "content",
|
||||
"type": "string",
|
||||
"description": "The main content of the blog post",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"name": "tags",
|
||||
"type": "string[]",
|
||||
"description": "Array of tags for categorization",
|
||||
"required": false,
|
||||
"default": []
|
||||
},
|
||||
{
|
||||
"name": "format",
|
||||
"type": "'markdown' | 'mdx'",
|
||||
"description": "Output format for the blog post",
|
||||
"required": false,
|
||||
"default": "markdown"
|
||||
},
|
||||
{
|
||||
"name": "excerpt",
|
||||
"type": "string",
|
||||
"description": "Short excerpt or summary of the post",
|
||||
"required": false
|
||||
}
|
||||
],
|
||||
"returns": {
|
||||
"type": "BlogPost",
|
||||
"description": "A structured blog post object with frontmatter, content, and metadata including slug, wordCount, readingTime, and formattedOutput"
|
||||
},
|
||||
"authentication": {
|
||||
"required": false,
|
||||
"type": "api-key"
|
||||
},
|
||||
"pricing": {
|
||||
"model": "free"
|
||||
},
|
||||
"frameworks": ["vercel-ai", "langchain"],
|
||||
"links": {
|
||||
"documentation": "https://tpmjs.com/tools/createblogpost",
|
||||
"repository": "https://github.com/ajaxdavis/tpmjs/tree/main/packages/tools/createBlogPost",
|
||||
"homepage": "https://tpmjs.com"
|
||||
},
|
||||
"tags": ["blog", "content", "markdown", "mdx", "writing", "seo"],
|
||||
"status": "stable",
|
||||
"aiAgent": {
|
||||
"useCase": "Use this tool when users need to generate blog posts, articles, or structured content with proper frontmatter and metadata. Ideal for content management systems, static site generators, and documentation sites.",
|
||||
"limitations": "Does not include AI content generation - you must provide the content. Only formats and structures existing content.",
|
||||
"examples": [
|
||||
"Create a blog post about TypeScript best practices",
|
||||
"Generate a tutorial post with code examples",
|
||||
"Format an article with SEO metadata"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Field Reference
|
||||
|
||||
### Required Fields (Tier 1 - Minimal)
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `category` | string | Tool category (see categories below) |
|
||||
| `description` | string | Clear description (1-3 sentences) |
|
||||
| `example` | string | Code example showing usage |
|
||||
|
||||
### Optional Fields (Tier 2 - Basic)
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `parameters` | array | Array of parameter objects |
|
||||
| `returns` | object | Return type information |
|
||||
|
||||
### Optional Fields (Tier 3 - Rich)
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `authentication` | object | Auth requirements |
|
||||
| `pricing` | object | Pricing information |
|
||||
| `frameworks` | array | Compatible frameworks |
|
||||
| `links` | object | Related URLs |
|
||||
| `tags` | array | Additional tags |
|
||||
| `status` | string | `stable`, `beta`, or `experimental` |
|
||||
| `aiAgent` | object | AI agent integration info |
|
||||
|
||||
### Categories
|
||||
|
||||
Choose one of these for the `category` field:
|
||||
|
||||
- `text-analysis` - NLP, sentiment, summarization
|
||||
- `code-generation` - Code generation and transformation
|
||||
- `data-processing` - Data manipulation and transformation
|
||||
- `image-generation` - Image creation and editing
|
||||
- `audio-processing` - Audio/speech processing
|
||||
- `search` - Search and retrieval
|
||||
- `integration` - Third-party integrations
|
||||
- `other` - Anything else
|
||||
|
||||
### Authentication Types
|
||||
|
||||
If your tool requires authentication:
|
||||
|
||||
```json
|
||||
"authentication": {
|
||||
"type": "api-key", // or "oauth", "bearer-token", "basic"
|
||||
"required": true
|
||||
}
|
||||
```
|
||||
|
||||
### Pricing Models
|
||||
|
||||
```json
|
||||
"pricing": {
|
||||
"model": "free" // or "pay-per-use", "subscription", "freemium"
|
||||
}
|
||||
```
|
||||
|
||||
For paid tools, add cost information:
|
||||
|
||||
```json
|
||||
"pricing": {
|
||||
"model": "pay-per-use",
|
||||
"cost": "$0.001 per request"
|
||||
}
|
||||
```
|
||||
|
||||
## Quality Score
|
||||
|
||||
Your tool gets a quality score based on:
|
||||
|
||||
- **Tier**: Rich (1.0) > Basic (0.5) > Minimal (0.25)
|
||||
- **Downloads**: Logarithmic scale based on monthly NPM downloads
|
||||
- **GitHub Stars**: Logarithmic scale based on repository stars
|
||||
|
||||
Higher scores = better visibility on tpmjs.com!
|
||||
|
||||
## Tips for Success
|
||||
|
||||
1. **Use descriptive names** - Make your package name clear and searchable
|
||||
2. **Complete metadata** - Tier 3 (Rich) tools get 4x the base score
|
||||
3. **Good documentation** - Link to docs in the `links.documentation` field
|
||||
4. **Active maintenance** - Regular updates boost download counts
|
||||
5. **AI-friendly descriptions** - Write the `aiAgent.useCase` field as guidance for AI agents
|
||||
|
||||
## Testing Locally
|
||||
|
||||
Before publishing, you can validate your `tpmjs` field using the validation schema:
|
||||
|
||||
```bash
|
||||
# In the tpmjs monorepo
|
||||
pnpm --filter=@tpmjs/types test
|
||||
```
|
||||
|
||||
Or manually check the structure matches the examples above.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**Tool not appearing after 15 minutes?**
|
||||
- Check that you added `"tpmjs-tool"` to keywords
|
||||
- Verify your `tpmjs` field has required fields (category, description, example)
|
||||
- Check the NPM package is public: `npm view yourpackage`
|
||||
|
||||
**Tool showing as "minimal" tier?**
|
||||
- Add `parameters` and `returns` fields for Basic tier
|
||||
- Add all Rich tier fields for maximum visibility
|
||||
|
||||
**Want to force a sync?**
|
||||
You can manually trigger a sync (requires auth):
|
||||
```bash
|
||||
curl -X POST "https://tpmjs.com/api/sync/keyword" \
|
||||
-H "Authorization: Bearer YOUR_CRON_SECRET"
|
||||
```
|
||||
|
||||
## Support
|
||||
|
||||
Questions or issues?
|
||||
- File an issue: https://github.com/ajaxdavis/tpmjs/issues
|
||||
- Check the API: https://tpmjs.com/api/tools
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
import { prisma } from '@tpmjs/db';
|
||||
import { type NextRequest, NextResponse } from 'next/server';
|
||||
|
||||
export const runtime = 'nodejs';
|
||||
export const dynamic = 'force-dynamic';
|
||||
export const maxDuration = 60;
|
||||
|
||||
/**
|
||||
* GET /api/tools/[id]
|
||||
* Get tool details by ID or package name
|
||||
*
|
||||
* Params:
|
||||
* - id: Tool ID (number) or NPM package name (string)
|
||||
*/
|
||||
export async function GET(_request: NextRequest, { params }: { params: Promise<{ id: string }> }) {
|
||||
try {
|
||||
const { id } = await params;
|
||||
|
||||
if (!id) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: 'Missing ID parameter',
|
||||
},
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
// Try to find by ID first (cuid), then by package name
|
||||
const tool = await prisma.tool.findFirst({
|
||||
where: {
|
||||
OR: [{ id }, { npmPackageName: id }],
|
||||
},
|
||||
});
|
||||
|
||||
if (!tool) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: 'Tool not found',
|
||||
message: `No tool found with ID or package name: ${id}`,
|
||||
},
|
||||
{ status: 404 }
|
||||
);
|
||||
}
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
data: tool,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error fetching tool details:', error);
|
||||
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: 'Failed to fetch tool details',
|
||||
message: error instanceof Error ? error.message : 'Unknown error',
|
||||
},
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue