mirror of
https://github.com/0x5t4l1n/AURHub.git
synced 2026-05-26 19:26:35 +00:00
Initial commit: ArchStore package manager for Arch Linux
This commit is contained in:
@@ -0,0 +1 @@
|
||||
# api package
|
||||
@@ -0,0 +1 @@
|
||||
# api.routes package
|
||||
@@ -0,0 +1,33 @@
|
||||
"""
|
||||
Categories API routes for ArchStore.
|
||||
Handles package category browsing.
|
||||
"""
|
||||
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from services import package_service
|
||||
|
||||
router = APIRouter(prefix="/api/categories", tags=["categories"])
|
||||
|
||||
|
||||
@router.get("")
|
||||
async def list_categories():
|
||||
"""Get all available package categories."""
|
||||
try:
|
||||
categories = await package_service.get_categories()
|
||||
return {"results": categories, "count": len(categories)}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=f"Failed to get categories: {str(e)}")
|
||||
|
||||
|
||||
@router.get("/{name}")
|
||||
async def get_category_packages(name: str):
|
||||
"""Get packages in a specific category."""
|
||||
try:
|
||||
packages = await package_service.get_category_packages(name)
|
||||
if not packages and name not in [c["name"] for c in await package_service.get_categories()]:
|
||||
raise HTTPException(status_code=404, detail=f"Category '{name}' not found")
|
||||
return {"category": name, "results": packages, "count": len(packages)}
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=f"Failed to get category: {str(e)}")
|
||||
@@ -0,0 +1,109 @@
|
||||
"""
|
||||
Package API routes for ArchStore.
|
||||
Handles search, info, install, and remove endpoints.
|
||||
"""
|
||||
|
||||
from fastapi import APIRouter, HTTPException, Query
|
||||
from services import package_service, aur_service
|
||||
from scanner.security import scan_pkgbuild
|
||||
from utils.sanitize import sanitize_package_name, sanitize_search_query
|
||||
|
||||
router = APIRouter(prefix="/api/packages", tags=["packages"])
|
||||
|
||||
|
||||
@router.get("/search")
|
||||
async def search_packages(
|
||||
q: str = Query(..., min_length=1, max_length=128, description="Search query"),
|
||||
source: str = Query("all", description="Source filter: all, pacman, aur"),
|
||||
):
|
||||
"""Search packages across pacman and AUR."""
|
||||
try:
|
||||
query = sanitize_search_query(q)
|
||||
results = await package_service.search_packages(query, source)
|
||||
return {"results": results, "count": len(results), "query": query, "source": source}
|
||||
except ValueError as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=f"Search failed: {str(e)}")
|
||||
|
||||
|
||||
@router.get("/installed")
|
||||
async def list_installed():
|
||||
"""List all installed packages."""
|
||||
try:
|
||||
packages = await package_service.list_installed()
|
||||
return {"results": packages, "count": len(packages)}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=f"Failed to list packages: {str(e)}")
|
||||
|
||||
|
||||
@router.get("/{name}")
|
||||
async def get_package_info(name: str):
|
||||
"""Get detailed info about a specific package."""
|
||||
try:
|
||||
name = sanitize_package_name(name)
|
||||
info = await package_service.get_package_info(name)
|
||||
if not info:
|
||||
raise HTTPException(status_code=404, detail=f"Package '{name}' not found")
|
||||
return info
|
||||
except ValueError as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=f"Failed to get package info: {str(e)}")
|
||||
|
||||
|
||||
@router.get("/{name}/scan")
|
||||
async def scan_package(name: str):
|
||||
"""Scan an AUR package's PKGBUILD for security issues."""
|
||||
try:
|
||||
name = sanitize_package_name(name)
|
||||
pkgbuild = await aur_service.get_pkgbuild(name)
|
||||
if not pkgbuild:
|
||||
return {
|
||||
"package_name": name,
|
||||
"risk_score": 0,
|
||||
"findings": [],
|
||||
"scanned": False,
|
||||
"risk_level": "unknown",
|
||||
"message": "PKGBUILD not found (may be a pacman package)",
|
||||
}
|
||||
result = scan_pkgbuild(name, pkgbuild)
|
||||
return result.to_dict()
|
||||
except ValueError as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
|
||||
|
||||
@router.post("/{name}/install")
|
||||
async def install_package(name: str):
|
||||
"""Install a package."""
|
||||
try:
|
||||
name = sanitize_package_name(name)
|
||||
result = await package_service.install_package(name)
|
||||
if not result["success"]:
|
||||
raise HTTPException(status_code=500, detail=result["message"])
|
||||
return result
|
||||
except ValueError as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=f"Install failed: {str(e)}")
|
||||
|
||||
|
||||
@router.post("/{name}/remove")
|
||||
async def remove_package(name: str):
|
||||
"""Remove an installed package."""
|
||||
try:
|
||||
name = sanitize_package_name(name)
|
||||
result = await package_service.remove_package(name)
|
||||
if not result["success"]:
|
||||
raise HTTPException(status_code=500, detail=result["message"])
|
||||
return result
|
||||
except ValueError as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=f"Remove failed: {str(e)}")
|
||||
@@ -0,0 +1,53 @@
|
||||
"""
|
||||
Updates API routes for ArchStore.
|
||||
Handles update checking and applying updates.
|
||||
"""
|
||||
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from services import package_service
|
||||
|
||||
router = APIRouter(prefix="/api/updates", tags=["updates"])
|
||||
|
||||
|
||||
@router.get("/check")
|
||||
async def check_updates():
|
||||
"""Check for available package updates (pacman + AUR)."""
|
||||
try:
|
||||
updates = await package_service.check_updates()
|
||||
return {
|
||||
"results": updates,
|
||||
"count": len(updates),
|
||||
"pacman_count": sum(1 for u in updates if u["source"] == "pacman"),
|
||||
"aur_count": sum(1 for u in updates if u["source"] == "aur"),
|
||||
}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=f"Update check failed: {str(e)}")
|
||||
|
||||
|
||||
@router.post("/apply")
|
||||
async def apply_updates():
|
||||
"""Apply all available updates. This is a long-running operation."""
|
||||
try:
|
||||
import asyncio
|
||||
from utils.sanitize import sanitize_package_name
|
||||
|
||||
async def _run_command(cmd, timeout=600):
|
||||
proc = await asyncio.create_subprocess_exec(
|
||||
*cmd,
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stderr=asyncio.subprocess.PIPE,
|
||||
)
|
||||
stdout, stderr = await asyncio.wait_for(proc.communicate(), timeout=timeout)
|
||||
return stdout.decode(), stderr.decode(), proc.returncode
|
||||
|
||||
# Run system update via yay (handles both pacman and AUR)
|
||||
stdout, stderr, code = await _run_command(
|
||||
["yay", "-Syu", "--noconfirm"], timeout=600
|
||||
)
|
||||
|
||||
return {
|
||||
"success": code == 0,
|
||||
"message": stdout if code == 0 else stderr,
|
||||
}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=f"Update failed: {str(e)}")
|
||||
Reference in New Issue
Block a user