fix(marketplace): isolate api under marketplace prefix

This commit is contained in:
unclecode
2025-10-09 22:26:15 +08:00
parent 2c373f0642
commit 5a4f21fad9
7 changed files with 42 additions and 38 deletions

View File

@@ -30,10 +30,10 @@ const { API_BASE, API_ORIGIN } = (() => {
if (origin) {
const normalized = cleanOrigin(origin);
return { API_BASE: `${normalized}/api`, API_ORIGIN: normalized };
return { API_BASE: `${normalized}/marketplace/api`, API_ORIGIN: normalized };
}
return { API_BASE: '/api', API_ORIGIN: '' };
return { API_BASE: '/marketplace/api', API_ORIGIN: '' };
})();
const resolveAssetUrl = (path) => {

View File

@@ -210,6 +210,6 @@
</div>
</div>
<script src="admin.js?v=1759334000"></script>
<script src="admin.js?v=1759335000"></script>
</body>
</html>

View File

@@ -1,5 +1,5 @@
// App Detail Page JavaScript
const API_BASE = '/api';
const API_BASE = '/marketplace/api';
class AppDetailPage {
constructor() {

View File

@@ -1,4 +1,4 @@
from fastapi import FastAPI, HTTPException, Query, Depends, Body, UploadFile, File, Form
from fastapi import FastAPI, HTTPException, Query, Depends, Body, UploadFile, File, Form, APIRouter
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
from fastapi.staticfiles import StaticFiles
@@ -16,6 +16,7 @@ from datetime import datetime, timedelta
from config import Config
app = FastAPI(title="Crawl4AI Marketplace API")
router = APIRouter(prefix="/marketplace/api")
# Security setup
security = HTTPBearer()
@@ -84,7 +85,7 @@ def to_int(value, default=0):
# ============= PUBLIC ENDPOINTS =============
@app.get("/api/apps")
@router.get("/apps")
async def get_apps(
category: Optional[str] = None,
type: Optional[str] = None,
@@ -114,7 +115,7 @@ async def get_apps(
return json_response(apps)
@app.get("/api/apps/{slug}")
@router.get("/apps/{slug}")
async def get_app(slug: str):
"""Get single app by slug"""
apps = db.get_all('apps', where=f"slug = '{slug}'", limit=1)
@@ -127,7 +128,7 @@ async def get_app(slug: str):
return json_response(app)
@app.get("/api/articles")
@router.get("/articles")
async def get_articles(
category: Optional[str] = None,
limit: int = Query(default=20, le=10000),
@@ -146,7 +147,7 @@ async def get_articles(
return json_response(articles)
@app.get("/api/articles/{slug}")
@router.get("/articles/{slug}")
async def get_article(slug: str):
"""Get single article by slug"""
articles = db.get_all('articles', where=f"slug = '{slug}'", limit=1)
@@ -161,7 +162,7 @@ async def get_article(slug: str):
return json_response(article)
@app.get("/api/categories")
@router.get("/categories")
async def get_categories():
"""Get all categories ordered by index"""
categories = db.get_all('categories', limit=50)
@@ -170,7 +171,7 @@ async def get_categories():
categories.sort(key=lambda x: x.get('order_index', 0))
return json_response(categories, cache_time=7200)
@app.get("/api/sponsors")
@router.get("/sponsors")
async def get_sponsors(active: Optional[bool] = True):
"""Get sponsors, default active only"""
where = f"active = {1 if active else 0}" if active is not None else None
@@ -185,7 +186,7 @@ async def get_sponsors(active: Optional[bool] = True):
return json_response(sponsors)
@app.get("/api/search")
@router.get("/search")
async def search(q: str = Query(min_length=2)):
"""Search across apps and articles"""
if len(q) < 2:
@@ -206,7 +207,7 @@ async def search(q: str = Query(min_length=2)):
return json_response(results, cache_time=1800)
@app.get("/api/stats")
@router.get("/stats")
async def get_stats():
"""Get marketplace statistics"""
stats = {
@@ -227,7 +228,7 @@ def verify_token(credentials: HTTPAuthorizationCredentials = Depends(security)):
return token
@app.post("/api/admin/upload-image", dependencies=[Depends(verify_token)])
@router.post("/admin/upload-image", dependencies=[Depends(verify_token)])
async def upload_image(file: UploadFile = File(...), folder: str = Form("sponsors")):
"""Upload image files for admin assets"""
folder = (folder or "").strip().lower()
@@ -251,7 +252,7 @@ async def upload_image(file: UploadFile = File(...), folder: str = Form("sponsor
return {"url": f"/uploads/{folder}/{filename}"}
@app.post("/api/admin/login")
@router.post("/admin/login")
async def admin_login(password: str = Body(..., embed=True)):
"""Admin login with password"""
provided_hash = hashlib.sha256(password.encode()).hexdigest()
@@ -272,7 +273,7 @@ async def admin_login(password: str = Body(..., embed=True)):
# ============= ADMIN ENDPOINTS =============
@app.get("/api/admin/stats", dependencies=[Depends(verify_token)])
@router.get("/admin/stats", dependencies=[Depends(verify_token)])
async def get_admin_stats():
"""Get detailed admin statistics"""
stats = {
@@ -292,7 +293,7 @@ async def get_admin_stats():
return stats
# Apps CRUD
@app.post("/api/admin/apps", dependencies=[Depends(verify_token)])
@router.post("/admin/apps", dependencies=[Depends(verify_token)])
async def create_app(app_data: Dict[str, Any]):
"""Create new app"""
try:
@@ -311,7 +312,7 @@ async def create_app(app_data: Dict[str, Any]):
except Exception as e:
raise HTTPException(status_code=400, detail=str(e))
@app.put("/api/admin/apps/{app_id}", dependencies=[Depends(verify_token)])
@router.put("/admin/apps/{app_id}", dependencies=[Depends(verify_token)])
async def update_app(app_id: int, app_data: Dict[str, Any]):
"""Update app"""
try:
@@ -329,7 +330,7 @@ async def update_app(app_id: int, app_data: Dict[str, Any]):
except Exception as e:
raise HTTPException(status_code=400, detail=str(e))
@app.delete("/api/admin/apps/{app_id}", dependencies=[Depends(verify_token)])
@router.delete("/admin/apps/{app_id}", dependencies=[Depends(verify_token)])
async def delete_app(app_id: int):
"""Delete app"""
cursor = db.conn.cursor()
@@ -338,7 +339,7 @@ async def delete_app(app_id: int):
return {"message": "App deleted"}
# Articles CRUD
@app.post("/api/admin/articles", dependencies=[Depends(verify_token)])
@router.post("/admin/articles", dependencies=[Depends(verify_token)])
async def create_article(article_data: Dict[str, Any]):
"""Create new article"""
try:
@@ -356,7 +357,7 @@ async def create_article(article_data: Dict[str, Any]):
except Exception as e:
raise HTTPException(status_code=400, detail=str(e))
@app.put("/api/admin/articles/{article_id}", dependencies=[Depends(verify_token)])
@router.put("/admin/articles/{article_id}", dependencies=[Depends(verify_token)])
async def update_article(article_id: int, article_data: Dict[str, Any]):
"""Update article"""
try:
@@ -373,7 +374,7 @@ async def update_article(article_id: int, article_data: Dict[str, Any]):
except Exception as e:
raise HTTPException(status_code=400, detail=str(e))
@app.delete("/api/admin/articles/{article_id}", dependencies=[Depends(verify_token)])
@router.delete("/admin/articles/{article_id}", dependencies=[Depends(verify_token)])
async def delete_article(article_id: int):
"""Delete article"""
cursor = db.conn.cursor()
@@ -382,7 +383,7 @@ async def delete_article(article_id: int):
return {"message": "Article deleted"}
# Categories CRUD
@app.post("/api/admin/categories", dependencies=[Depends(verify_token)])
@router.post("/admin/categories", dependencies=[Depends(verify_token)])
async def create_category(category_data: Dict[str, Any]):
"""Create new category"""
try:
@@ -399,7 +400,7 @@ async def create_category(category_data: Dict[str, Any]):
except Exception as e:
raise HTTPException(status_code=400, detail=str(e))
@app.put("/api/admin/categories/{cat_id}", dependencies=[Depends(verify_token)])
@router.put("/admin/categories/{cat_id}", dependencies=[Depends(verify_token)])
async def update_category(cat_id: int, category_data: Dict[str, Any]):
"""Update category"""
try:
@@ -417,7 +418,7 @@ async def update_category(cat_id: int, category_data: Dict[str, Any]):
raise HTTPException(status_code=400, detail=str(e))
@app.delete("/api/admin/categories/{cat_id}", dependencies=[Depends(verify_token)])
@router.delete("/admin/categories/{cat_id}", dependencies=[Depends(verify_token)])
async def delete_category(cat_id: int):
"""Delete category"""
try:
@@ -429,7 +430,7 @@ async def delete_category(cat_id: int):
raise HTTPException(status_code=400, detail=str(e))
# Sponsors CRUD
@app.post("/api/admin/sponsors", dependencies=[Depends(verify_token)])
@router.post("/admin/sponsors", dependencies=[Depends(verify_token)])
async def create_sponsor(sponsor_data: Dict[str, Any]):
"""Create new sponsor"""
try:
@@ -443,7 +444,7 @@ async def create_sponsor(sponsor_data: Dict[str, Any]):
except Exception as e:
raise HTTPException(status_code=400, detail=str(e))
@app.put("/api/admin/sponsors/{sponsor_id}", dependencies=[Depends(verify_token)])
@router.put("/admin/sponsors/{sponsor_id}", dependencies=[Depends(verify_token)])
async def update_sponsor(sponsor_id: int, sponsor_data: Dict[str, Any]):
"""Update sponsor"""
try:
@@ -457,7 +458,7 @@ async def update_sponsor(sponsor_id: int, sponsor_data: Dict[str, Any]):
raise HTTPException(status_code=400, detail=str(e))
@app.delete("/api/admin/sponsors/{sponsor_id}", dependencies=[Depends(verify_token)])
@router.delete("/admin/sponsors/{sponsor_id}", dependencies=[Depends(verify_token)])
async def delete_sponsor(sponsor_id: int):
"""Delete sponsor"""
try:
@@ -468,6 +469,9 @@ async def delete_sponsor(sponsor_id: int):
except Exception as e:
raise HTTPException(status_code=400, detail=str(e))
app.include_router(router)
@app.get("/")
async def root():
"""API info"""
@@ -475,12 +479,12 @@ async def root():
"name": "Crawl4AI Marketplace API",
"version": "1.0.0",
"endpoints": [
"/api/apps",
"/api/articles",
"/api/categories",
"/api/sponsors",
"/api/search?q=query",
"/api/stats"
"/marketplace/api/apps",
"/marketplace/api/articles",
"/marketplace/api/categories",
"/marketplace/api/sponsors",
"/marketplace/api/search?q=query",
"/marketplace/api/stats"
]
}

View File

@@ -1,5 +1,5 @@
// App Detail Page JavaScript
const API_BASE = '/api';
const API_BASE = '/marketplace/api';
class AppDetailPage {
constructor() {

View File

@@ -1,5 +1,5 @@
// Marketplace JS - Magazine Layout
const API_BASE = '/api';
const API_BASE = '/marketplace/api';
const CACHE_TTL = 3600000; // 1 hour in ms
class MarketplaceCache {

View File

@@ -3,9 +3,9 @@ const { API_BASE, API_ORIGIN } = (() => {
const { hostname, port } = window.location;
if ((hostname === 'localhost' || hostname === '127.0.0.1') && port === '8000') {
const origin = 'http://127.0.0.1:8100';
return { API_BASE: `${origin}/api`, API_ORIGIN: origin };
return { API_BASE: `${origin}/marketplace/api`, API_ORIGIN: origin };
}
return { API_BASE: '/api', API_ORIGIN: '' };
return { API_BASE: '/marketplace/api', API_ORIGIN: '' };
})();
const resolveAssetUrl = (path) => {