Jack 2 ماه پیش
والد
کامیت
71353903b6
58فایلهای تغییر یافته به همراه335 افزوده شده و 121 حذف شده
  1. 0 0
      backend/alembic/env.py
  2. 38 9
      backend/app/api/categories.py
  3. 75 0
      backend/app/api/executions.py
  4. 165 0
      backend/app/api/spiders.py
  5. 0 0
      backend/app/celery_app/scheduler.py
  6. 6 5
      backend/app/core/config.py
  7. 0 0
      backend/app/core/security.py
  8. 0 0
      backend/app/crud/__init__.py
  9. 0 0
      backend/app/crud/categories.py
  10. 0 0
      backend/app/crud/executions.py
  11. 0 0
      backend/app/crud/spiders.py
  12. 0 0
      backend/app/dependencies.py
  13. 8 16
      backend/app/main.py
  14. 26 0
      backend/app/services/category_service.py
  15. 12 9
      backend/app/services/file_service.py
  16. 0 0
      backend/app/services/scheduler_service.py
  17. 5 0
      backend/run.py
  18. BIN
      backend/spider_scheduler.db
  19. 0 0
      backend/uploaded_scripts/.gitkeep
  20. 0 0
      docker/Dockerfile.backend
  21. 0 0
      docker/Dockerfile.frontend
  22. 0 0
      docker/docker-compose.yml
  23. 0 0
      frontend/index.html
  24. 0 0
      frontend/package.json
  25. 0 0
      frontend/public/favicon.ico
  26. 0 0
      frontend/public/index.html
  27. 0 0
      frontend/src/App.vue
  28. 0 0
      frontend/src/api/categories.js
  29. 0 0
      frontend/src/api/executions.js
  30. 0 0
      frontend/src/api/index.js
  31. 0 0
      frontend/src/api/spiders.js
  32. 0 0
      frontend/src/components/Common/CodeEditor.vue
  33. 0 0
      frontend/src/components/Common/CronEditor.vue
  34. 0 0
      frontend/src/components/Common/LogViewer.vue
  35. 0 0
      frontend/src/components/Dialogs/CategoryDialog.vue
  36. 0 0
      frontend/src/components/Dialogs/SpiderDialog.vue
  37. 0 0
      frontend/src/components/Layout/Header.vue
  38. 0 0
      frontend/src/components/Layout/Sidebar.vue
  39. 0 0
      frontend/src/main.js
  40. 0 0
      frontend/src/router/index.js
  41. 0 0
      frontend/src/stores/categories.js
  42. 0 0
      frontend/src/stores/executions.js
  43. 0 0
      frontend/src/stores/index.js
  44. 0 0
      frontend/src/stores/spiders.js
  45. 0 0
      frontend/src/utils/helpers.js
  46. 0 0
      frontend/src/utils/request.js
  47. 0 0
      frontend/src/views/Categories.vue
  48. 0 0
      frontend/src/views/Dashboard.vue
  49. 0 0
      frontend/src/views/Executions.vue
  50. 0 0
      frontend/src/views/Spiders.vue
  51. 0 0
      frontend/vite.config.js
  52. 0 82
      mk.sh
  53. 0 0
      nginx/conf.d/spider-scheduler.conf
  54. 0 0
      nginx/nginx.conf
  55. 0 0
      requirements.txt
  56. 0 0
      scripts/backup.sh
  57. 0 0
      scripts/init_db.py
  58. 0 0
      scripts/start.sh

+ 0 - 0
backend/alembic/env.py


+ 38 - 9
backend/app/api/categories.py

@@ -4,16 +4,15 @@ from sqlalchemy.orm import Session
 from typing import List
 
 from ..database import get_db
-from ..models import Category
-from ..schemas import CategoryCreate, CategoryUpdate, Category, ListResponse
+from ..models import Category, Spider
+from ..schemas import CategoryCreate, CategoryUpdate, Category, ListResponse, SuccessResponse
+from ..services.category_service import CategoryService
 
 router = APIRouter(prefix="/api/categories", tags=["categories"])
 
 @router.post("/", response_model=Category)
 def create_category(category: CategoryCreate, db: Session = Depends(get_db)):
-    # 检查名称是否已存在
-    db_category = db.query(Category).filter(Category.name == category.name).first()
-    if db_category:
+    if CategoryService.check_category_name_exists(db, category.name):
         raise HTTPException(
             status_code=status.HTTP_400_BAD_REQUEST,
             detail="分类名称已存在"
@@ -28,11 +27,41 @@ def create_category(category: CategoryCreate, db: Session = Depends(get_db)):
 @router.get("/", response_model=ListResponse)
 def get_categories(db: Session = Depends(get_db)):
     categories = db.query(Category).all()
-    # 为每个分类计算脚本数量
     for category in categories:
-        from ..models import Spider
-        category.spider_count = db.query(Spider).filter(Spider.category_id == category.id).count()
+        category.spider_count = CategoryService.get_spider_count(db, category.id)
     
     return ListResponse(total=len(categories), items=categories)
 
-# ... 其他分类接口(更新、删除等)
+@router.put("/{category_id}", response_model=Category)
+def update_category(category_id: int, category: CategoryUpdate, db: Session = Depends(get_db)):
+    db_category = CategoryService.get_category_by_id(db, category_id)
+    
+    if category.name and category.name != db_category.name:
+        if CategoryService.check_category_name_exists(db, category.name, exclude_id=category_id):
+            raise HTTPException(
+                status_code=status.HTTP_400_BAD_REQUEST,
+                detail="分类名称已存在"
+            )
+    
+    update_data = category.dict(exclude_unset=True)
+    for field, value in update_data.items():
+        setattr(db_category, field, value)
+    
+    db.commit()
+    db.refresh(db_category)
+    return db_category
+
+@router.delete("/{category_id}")
+def delete_category(category_id: int, db: Session = Depends(get_db)):
+    db_category = CategoryService.get_category_by_id(db, category_id)
+    
+    spider_count = CategoryService.get_spider_count(db, category_id)
+    if spider_count > 0:
+        raise HTTPException(
+            status_code=status.HTTP_400_BAD_REQUEST,
+            detail=f"该分类下还有 {spider_count} 个脚本,无法删除"
+        )
+    
+    db.delete(db_category)
+    db.commit()
+    return SuccessResponse(success=True, message="分类删除成功")

+ 75 - 0
backend/app/api/executions.py

@@ -0,0 +1,75 @@
+# backend/app/api/executions.py
+from fastapi import APIRouter, Depends, HTTPException, status
+from sqlalchemy.orm import Session
+from typing import Optional
+from datetime import datetime
+
+from ..database import get_db
+from ..models import TaskExecutionLog, Spider
+from ..schemas import TaskExecutionLog as TaskExecutionLogSchema, ListResponse, SuccessResponse
+
+# 修复:确保变量名是 router
+router = APIRouter(prefix="/api/executions", tags=["executions"])
+
+@router.post("/spiders/{spider_id}/run")
+def run_spider(spider_id: int, db: Session = Depends(get_db)):
+    spider = db.query(Spider).filter(Spider.id == spider_id).first()
+    if not spider:
+        raise HTTPException(
+            status_code=status.HTTP_404_NOT_FOUND,
+            detail="脚本不存在"
+        )
+    
+    # 创建执行记录
+    execution_log = TaskExecutionLog(
+        spider_id=spider_id,
+        status="RUNNING",
+        trigger_method="MANUAL",
+        started_at=datetime.now()
+    )
+    db.add(execution_log)
+    db.commit()
+    db.refresh(execution_log)
+    
+    # TODO: 调用Celery任务
+    # run_spider_task.delay(spider_id, "MANUAL")
+    
+    return SuccessResponse(
+        success=True, 
+        message=f"脚本 {spider.name} 开始执行,执行ID: {execution_log.id}"
+    )
+
+@router.get("/spiders/{spider_id}", response_model=ListResponse)
+def get_spider_executions(spider_id: int, limit: Optional[int] = None, db: Session = Depends(get_db)):
+    # 验证spider存在
+    spider = db.query(Spider).filter(Spider.id == spider_id).first()
+    if not spider:
+        raise HTTPException(
+            status_code=status.HTTP_404_NOT_FOUND,
+            detail="脚本不存在"
+        )
+    
+    query = db.query(TaskExecutionLog).filter(TaskExecutionLog.spider_id == spider_id)
+    
+    if limit:
+        query = query.limit(limit)
+    
+    executions = query.order_by(TaskExecutionLog.started_at.desc()).all()
+    
+    # 加载spider信息
+    for execution in executions:
+        execution.spider
+    
+    return ListResponse(total=len(executions), items=executions)
+
+@router.get("/{execution_id}", response_model=TaskExecutionLogSchema)
+def get_execution_log(execution_id: int, db: Session = Depends(get_db)):
+    execution = db.query(TaskExecutionLog).filter(TaskExecutionLog.id == execution_id).first()
+    if not execution:
+        raise HTTPException(
+            status_code=status.HTTP_404_NOT_FOUND,
+            detail="执行记录不存在"
+        )
+    
+    execution.spider
+    return execution

+ 165 - 0
backend/app/api/spiders.py

@@ -0,0 +1,165 @@
+# backend/app/api/spiders.py
+from fastapi import APIRouter, Depends, HTTPException, status, UploadFile, File, Form
+from sqlalchemy.orm import Session
+from typing import Optional
+import os
+
+from ..database import get_db
+from ..models import Spider, Category
+from ..schemas import SpiderCreate, SpiderUpdate, Spider, ListResponse, SuccessResponse
+from ..services.category_service import CategoryService
+from ..services.file_service import FileService
+
+router = APIRouter(prefix="/api/spiders", tags=["spiders"])
+
+@router.post("/", response_model=Spider)
+def create_spider(
+    name: str = Form(...),
+    description: Optional[str] = Form(None),
+    cron_expression: str = Form("0 0 * * *"),
+    enabled: bool = Form(True),
+    timeout: int = Form(300),
+    category_id: int = Form(...),
+    code_content: Optional[str] = Form(None),
+    file: Optional[UploadFile] = File(None),
+    db: Session = Depends(get_db)
+):
+    # 验证分类是否存在
+    category = CategoryService.get_category_by_id(db, category_id)
+    
+    # 检查脚本名称是否重复
+    existing_spider = db.query(Spider).filter(Spider.name == name).first()
+    if existing_spider:
+        raise HTTPException(
+            status_code=status.HTTP_400_BAD_REQUEST,
+            detail="脚本名称已存在"
+        )
+    
+    file_service = FileService()
+    filename = None
+    file_path = None
+    final_code_content = code_content
+    
+    if file and file.filename:
+        # 文件上传方式
+        filename, file_path = file_service.save_uploaded_file(file)
+        final_code_content = file_service.read_file_content(file_path)
+    
+    elif code_content:
+        # 代码粘贴方式
+        filename, file_path = file_service.save_code_content(code_content, name)
+    
+    else:
+        raise HTTPException(
+            status_code=status.HTTP_400_BAD_REQUEST,
+            detail="必须提供代码内容或上传文件"
+        )
+    
+    spider_data = {
+        "name": name,
+        "description": description,
+        "filename": filename,
+        "file_path": file_path,
+        "code_content": final_code_content,
+        "cron_expression": cron_expression,
+        "enabled": enabled,
+        "timeout": timeout,
+        "category_id": category_id
+    }
+    
+    db_spider = Spider(**spider_data)
+    db.add(db_spider)
+    db.commit()
+    db.refresh(db_spider)
+    return db_spider
+
+@router.get("/", response_model=ListResponse)
+def get_spiders(category_id: Optional[int] = None, db: Session = Depends(get_db)):
+    query = db.query(Spider)
+    if category_id:
+        query = query.filter(Spider.category_id == category_id)
+    
+    spiders = query.all()
+    
+    # 加载分类信息
+    for spider in spiders:
+        spider.category
+    
+    return ListResponse(total=len(spiders), items=spiders)
+
+@router.get("/{spider_id}", response_model=Spider)
+def get_spider(spider_id: int, db: Session = Depends(get_db)):
+    spider = db.query(Spider).filter(Spider.id == spider_id).first()
+    if not spider:
+        raise HTTPException(
+            status_code=status.HTTP_404_NOT_FOUND,
+            detail="脚本不存在"
+        )
+    
+    spider.category
+    return spider
+
+@router.put("/{spider_id}", response_model=Spider)
+def update_spider(spider_id: int, spider_update: SpiderUpdate, db: Session = Depends(get_db)):
+    db_spider = db.query(Spider).filter(Spider.id == spider_id).first()
+    if not db_spider:
+        raise HTTPException(
+            status_code=status.HTTP_404_NOT_FOUND,
+            detail="脚本不存在"
+        )
+    
+    update_data = spider_update.dict(exclude_unset=True)
+    file_service = FileService()
+    
+    # 如果更新了代码内容,需要更新文件
+    if 'code_content' in update_data and update_data['code_content']:
+        # 删除旧文件
+        file_service.delete_file(db_spider.file_path)
+        
+        # 保存新文件
+        filename, file_path = file_service.save_code_content(
+            update_data['code_content'], 
+            update_data.get('name', db_spider.name)
+        )
+        update_data['filename'] = filename
+        update_data['file_path'] = file_path
+    
+    # 更新字段
+    for field, value in update_data.items():
+        if field != 'code_content':
+            setattr(db_spider, field, value)
+    
+    db.commit()
+    db.refresh(db_spider)
+    return db_spider
+
+@router.delete("/{spider_id}")
+def delete_spider(spider_id: int, db: Session = Depends(get_db)):
+    db_spider = db.query(Spider).filter(Spider.id == spider_id).first()
+    if not db_spider:
+        raise HTTPException(
+            status_code=status.HTTP_404_NOT_FOUND,
+            detail="脚本不存在"
+        )
+    
+    # 删除服务器上的脚本文件
+    file_service = FileService()
+    file_service.delete_file(db_spider.file_path)
+    
+    db.delete(db_spider)
+    db.commit()
+    return SuccessResponse(success=True, message="脚本删除成功")
+
+@router.put("/{spider_id}/toggle", response_model=Spider)
+def toggle_spider(spider_id: int, db: Session = Depends(get_db)):
+    db_spider = db.query(Spider).filter(Spider.id == spider_id).first()
+    if not db_spider:
+        raise HTTPException(
+            status_code=status.HTTP_404_NOT_FOUND,
+            detail="脚本不存在"
+        )
+    
+    db_spider.enabled = not db_spider.enabled
+    db.commit()
+    db.refresh(db_spider)
+    return db_spider

+ 0 - 0
backend/app/celery_app/scheduler.py


+ 6 - 5
backend/app/core/config.py

@@ -1,6 +1,7 @@
 # backend/app/core/config.py
 from pydantic_settings import BaseSettings
 import os
+from pathlib import Path
 
 class Settings(BaseSettings):
     PROJECT_NAME: str = "爬虫任务调度系统"
@@ -11,12 +12,11 @@ class Settings(BaseSettings):
     DATABASE_URL: str = "sqlite:///./spider_scheduler.db"
     
     # 文件上传配置
+    BASE_DIR: Path = Path(__file__).parent.parent.parent
     UPLOAD_DIR: str = "uploaded_scripts"
-    MAX_FILE_SIZE: int = 10 * 1024 * 1024  # 10MB
     
-    # Celery配置
-    CELERY_BROKER_URL: str = "redis://localhost:6379/0"
-    CELERY_RESULT_BACKEND: str = "redis://localhost:6379/0"
+    # CORS配置
+    CORS_ORIGINS: list = ["*"]
     
     class Config:
         case_sensitive = True
@@ -24,4 +24,5 @@ class Settings(BaseSettings):
 settings = Settings()
 
 # 创建上传目录
-os.makedirs(settings.UPLOAD_DIR, exist_ok=True)
+upload_path = settings.BASE_DIR / settings.UPLOAD_DIR
+upload_path.mkdir(exist_ok=True)

+ 0 - 0
backend/app/core/security.py


+ 0 - 0
backend/app/crud/__init__.py


+ 0 - 0
backend/app/crud/categories.py


+ 0 - 0
backend/app/crud/executions.py


+ 0 - 0
backend/app/crud/spiders.py


+ 0 - 0
backend/alembic/alembic.ini → backend/app/dependencies.py


+ 8 - 16
backend/app/main.py

@@ -1,18 +1,10 @@
 # backend/app/main.py
-import sys
-import os
-
-# 添加当前目录到 Python 路径
-sys.path.append(os.path.dirname(os.path.abspath(__file__)))
-
 from fastapi import FastAPI
 from fastapi.middleware.cors import CORSMiddleware
 
-# 现在使用绝对导入
-from core.config import settings
-from database import engine, Base
-# 注意:暂时注释掉这些导入,我们先让基础部分运行起来
-# from api import categories, spiders, executions
+from .core.config import settings
+from .database import engine, Base
+from .api import categories, spiders, executions
 
 # 创建数据库表
 Base.metadata.create_all(bind=engine)
@@ -25,16 +17,16 @@ app = FastAPI(
 # CORS配置
 app.add_middleware(
     CORSMiddleware,
-    allow_origins=["*"],
+    allow_origins=settings.CORS_ORIGINS,
     allow_credentials=True,
     allow_methods=["*"],
     allow_headers=["*"],
 )
 
-# 暂时注释掉路由注册
-# app.include_router(categories.router)
-# app.include_router(spiders.router)
-# app.include_router(executions.router)
+# 注册路由
+app.include_router(categories.router)
+app.include_router(spiders.router)
+app.include_router(executions.router)
 
 @app.get("/")
 def read_root():

+ 26 - 0
backend/app/services/category_service.py

@@ -0,0 +1,26 @@
+# backend/app/services/category_service.py
+from sqlalchemy.orm import Session
+from fastapi import HTTPException, status
+from ..models import Category, Spider
+
+class CategoryService:
+    @staticmethod
+    def get_category_by_id(db: Session, category_id: int) -> Category:
+        category = db.query(Category).filter(Category.id == category_id).first()
+        if not category:
+            raise HTTPException(
+                status_code=status.HTTP_404_NOT_FOUND,
+                detail="分类不存在"
+            )
+        return category
+    
+    @staticmethod
+    def check_category_name_exists(db: Session, name: str, exclude_id: int = None) -> bool:
+        query = db.query(Category).filter(Category.name == name)
+        if exclude_id:
+            query = query.filter(Category.id != exclude_id)
+        return query.first() is not None
+    
+    @staticmethod
+    def get_spider_count(db: Session, category_id: int) -> int:
+        return db.query(Spider).filter(Spider.category_id == category_id).count()

+ 12 - 9
backend/app/services/file_service.py

@@ -3,11 +3,13 @@ import os
 import shutil
 import uuid
 from fastapi import UploadFile, HTTPException, status
+from pathlib import Path
+from ..core.config import settings
 
 class FileService:
-    def __init__(self, upload_dir: str):
-        self.upload_dir = upload_dir
-        os.makedirs(upload_dir, exist_ok=True)
+    def __init__(self):
+        self.upload_dir = settings.BASE_DIR / settings.UPLOAD_DIR
+        self.upload_dir.mkdir(exist_ok=True)
     
     def save_uploaded_file(self, file: UploadFile) -> tuple[str, str]:
         """保存上传的文件"""
@@ -18,22 +20,22 @@ class FileService:
             )
         
         filename = f"{uuid.uuid4().hex}_{file.filename}"
-        file_path = os.path.join(self.upload_dir, filename)
+        file_path = self.upload_dir / filename
         
         with open(file_path, "wb") as buffer:
             shutil.copyfileobj(file.file, buffer)
         
-        return filename, file_path
+        return filename, str(file_path)
     
     def save_code_content(self, code_content: str, name: str) -> tuple[str, str]:
         """保存代码内容为文件"""
         filename = f"{uuid.uuid4().hex}_{name.replace(' ', '_')}.py"
-        file_path = os.path.join(self.upload_dir, filename)
+        file_path = self.upload_dir / filename
         
         with open(file_path, 'w', encoding='utf-8') as f:
             f.write(code_content)
         
-        return filename, file_path
+        return filename, str(file_path)
     
     def read_file_content(self, file_path: str) -> str:
         """读取文件内容"""
@@ -42,5 +44,6 @@ class FileService:
     
     def delete_file(self, file_path: str):
         """删除文件"""
-        if os.path.exists(file_path):
-            os.remove(file_path)
+        path = Path(file_path)
+        if path.exists():
+            path.unlink()

+ 0 - 0
backend/app/services/scheduler_service.py


+ 5 - 0
backend/run.py

@@ -0,0 +1,5 @@
+# backend/run.py
+import uvicorn
+
+if __name__ == "__main__":
+    uvicorn.run("app.main:app", host="0.0.0.0", port=8000, reload=True)

BIN
backend/spider_scheduler.db


+ 0 - 0
backend/uploaded_scripts/.gitkeep


+ 0 - 0
docker/Dockerfile.backend


+ 0 - 0
docker/Dockerfile.frontend


+ 0 - 0
docker/docker-compose.yml


+ 0 - 0
frontend/index.html


+ 0 - 0
frontend/package.json


+ 0 - 0
frontend/public/favicon.ico


+ 0 - 0
frontend/public/index.html


+ 0 - 0
frontend/src/App.vue


+ 0 - 0
frontend/src/api/categories.js


+ 0 - 0
frontend/src/api/executions.js


+ 0 - 0
frontend/src/api/index.js


+ 0 - 0
frontend/src/api/spiders.js


+ 0 - 0
frontend/src/components/Common/CodeEditor.vue


+ 0 - 0
frontend/src/components/Common/CronEditor.vue


+ 0 - 0
frontend/src/components/Common/LogViewer.vue


+ 0 - 0
frontend/src/components/Dialogs/CategoryDialog.vue


+ 0 - 0
frontend/src/components/Dialogs/SpiderDialog.vue


+ 0 - 0
frontend/src/components/Layout/Header.vue


+ 0 - 0
frontend/src/components/Layout/Sidebar.vue


+ 0 - 0
frontend/src/main.js


+ 0 - 0
frontend/src/router/index.js


+ 0 - 0
frontend/src/stores/categories.js


+ 0 - 0
frontend/src/stores/executions.js


+ 0 - 0
frontend/src/stores/index.js


+ 0 - 0
frontend/src/stores/spiders.js


+ 0 - 0
frontend/src/utils/helpers.js


+ 0 - 0
frontend/src/utils/request.js


+ 0 - 0
frontend/src/views/Categories.vue


+ 0 - 0
frontend/src/views/Dashboard.vue


+ 0 - 0
frontend/src/views/Executions.vue


+ 0 - 0
frontend/src/views/Spiders.vue


+ 0 - 0
frontend/vite.config.js


+ 0 - 82
mk.sh

@@ -1,82 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-
-mkdir -p backend/app/{crud,api,core,services,celery_app}
-mkdir -p backend/{uploaded_scripts,alembic/versions}
-mkdir -p frontend/{public,src/{router,stores,components/{Layout,Common,Dialogs},views,api,utils}}
-mkdir -p nginx/conf.d docker scripts logs
-
-touch backend/app/__init__.py
-touch backend/app/main.py
-touch backend/app/models.py
-touch backend/app/schemas.py
-touch backend/app/database.py
-touch backend/app/crud/__init__.py
-touch backend/app/crud/categories.py
-touch backend/app/crud/spiders.py
-touch backend/app/crud/executions.py
-touch backend/app/api/__init__.py
-touch backend/app/api/categories.py
-touch backend/app/api/spiders.py
-touch backend/app/api/executions.py
-touch backend/app/core/__init__.py
-touch backend/app/core/config.py
-touch backend/app/core/security.py
-touch backend/app/services/__init__.py
-touch backend/app/services/spider_service.py
-touch backend/app/services/scheduler_service.py
-touch backend/app/services/file_service.py
-touch backend/app/celery_app/__init__.py
-touch backend/app/celery_app/celery.py
-touch backend/app/celery_app/tasks.py
-touch backend/app/celery_app/scheduler.py
-touch backend/uploaded_scripts/.gitkeep
-touch backend/requirements.txt
-touch backend/alembic/env.py
-touch backend/alembic/alembic.ini
-touch frontend/public/index.html
-touch frontend/public/favicon.ico
-touch frontend/src/main.js
-touch frontend/src/App.vue
-touch frontend/src/router/index.js
-touch frontend/src/stores/index.js
-touch frontend/src/stores/categories.js
-touch frontend/src/stores/spiders.js
-touch frontend/src/stores/executions.js
-touch frontend/src/components/Layout/Sidebar.vue
-touch frontend/src/components/Layout/Header.vue
-touch frontend/src/components/Common/CronEditor.vue
-touch frontend/src/components/Common/CodeEditor.vue
-touch frontend/src/components/Common/LogViewer.vue
-touch frontend/src/components/Dialogs/CategoryDialog.vue
-touch frontend/src/components/Dialogs/SpiderDialog.vue
-touch frontend/src/views/Dashboard.vue
-touch frontend/src/views/Categories.vue
-touch frontend/src/views/Spiders.vue
-touch frontend/src/views/Executions.vue
-touch frontend/src/api/index.js
-touch frontend/src/api/categories.js
-touch frontend/src/api/spiders.js
-touch frontend/src/api/executions.js
-touch frontend/src/utils/request.js
-touch frontend/src/utils/helpers.js
-touch frontend/package.json
-touch frontend/vite.config.js
-touch frontend/index.html
-touch nginx/nginx.conf
-touch nginx/conf.d/spider-scheduler.conf
-touch docker/Dockerfile.backend
-touch docker/Dockerfile.frontend
-touch docker/docker-compose.yml
-touch scripts/start.sh
-touch scripts/init_db.py
-touch scripts/backup.sh
-touch logs/app.log
-touch logs/celery.log
-touch logs/scheduler.log
-touch .env.example
-touch .gitignore
-touch README.md
-touch requirements.txt
-
-echo "✅ 当前目录骨架创建完成!"

+ 0 - 0
nginx/conf.d/spider-scheduler.conf


+ 0 - 0
nginx/nginx.conf


+ 0 - 0
backend/requirements.txt → requirements.txt


+ 0 - 0
scripts/backup.sh


+ 0 - 0
scripts/init_db.py


+ 0 - 0
scripts/start.sh