更新 host_ollama.py

This commit is contained in:
kanshan 2025-03-27 16:46:32 +08:00
parent c7464e0e4d
commit 70b1d02cba

View File

@ -1,51 +1,79 @@
#!/usr/bin/env python3
"""
Ollama 本地镜像服务器
功能
1. 自动检测并Host下载的镜像目录
2. 提供文件浏览和下载接口
3. 自动生成友好的下载页面
Ollama 本地镜像服务器 - 增强版
新增功能
1. 同时托管 ollama_releases install.sh
2. 自动生成安装指引
3. 显示本机IP方便局域网访问
"""
import os
import sys
import socket
from http.server import HTTPServer, SimpleHTTPRequestHandler
from pathlib import Path
import threading
import webbrowser
MIRROR_DIR = "ollama_releases" # 与mirror_ollama.sh保持一致
MIRROR_DIR = "ollama_releases"
INSTALL_SCRIPT = "install.sh"
PORT = 8000
class OllamaRequestHandler(SimpleHTTPRequestHandler):
"""自定义请求处理器,增强目录浏览功能"""
"""自定义请求处理器,支持多目录托管"""
def __init__(self, *args, **kwargs):
super().__init__(*args, directory=MIRROR_DIR, **kwargs)
def translate_path(self, path):
# 优先检查是否是install.sh
if path.endswith('/' + INSTALL_SCRIPT) or path == '/' + INSTALL_SCRIPT:
return os.path.abspath(INSTALL_SCRIPT)
# 默认处理镜像目录
path = path.split('?',1)[0]
path = path.split('#',1)[0]
return os.path.abspath(os.path.join(MIRROR_DIR, path.lstrip('/')))
def list_directory(self, path):
"""生成增强型目录列表页面"""
try:
# 特殊处理根目录
if os.path.abspath(path) == os.path.abspath(MIRROR_DIR):
return self.generate_index_page(path)
return super().list_directory(path)
except Exception as e:
self.send_error(500, f"服务器错误: {str(e)}")
def generate_index_page(self, path):
"""生成带安装指引的首页"""
local_ip = get_local_ip()
install_url = f"http://{local_ip}:{self.server.server_port}/{INSTALL_SCRIPT}"
items = []
for item in Path(path).iterdir():
if item.is_file():
mtime = item.stat().st_mtime
size = item.stat().st_size
items.append((item.name, mtime, size))
# 按版本号排序(降序)
items.sort(key=lambda x: x[0], reverse=True)
# 生成HTML
html = ["<html><head><title>Ollama 本地镜像</title>"]
html.append("<style>")
html.append("body { font-family: Arial, sans-serif; margin: 20px; }")
html.append("table { border-collapse: collapse; width: 100%; }")
html.append("th, td { padding: 8px; text-align: left; border-bottom: 1px solid #ddd; }")
html.append("tr:hover { background-color: #f5f5f5; }")
html.append("</style></head><body>")
html.append("<h1>Ollama 本地镜像库</h1>")
html.append(f"<p>当前目录: {path}</p>")
html.append("<table><tr><th>文件名</th><th>大小</th><th>修改时间</th></tr>")
html = [
"<html><head><title>Ollama 本地镜像</title>",
"<style>",
"body { font-family: Arial, sans-serif; margin: 20px; }",
"table { border-collapse: collapse; width: 100%; margin-bottom: 20px; }",
"th, td { padding: 8px; text-align: left; border-bottom: 1px solid #ddd; }",
"tr:hover { background-color: #f5f5f5; }",
".box { background: #f8f9fa; padding: 15px; border-radius: 5px; margin: 20px 0; }",
"</style></head><body>",
f"<h1>Ollama 本地镜像库</h1>",
f"<div class='box'>",
"<h3>📌 安装指引</h3>",
f"<p>在其他机器上执行以下命令安装:</p>",
f"<code>curl -sSL {install_url} | sudo bash</code>",
f"</div>",
"<h3>📂 可用文件列表</h3>",
"<table><tr><th>文件名</th><th>大小</th><th>修改时间</th></tr>"
]
for name, mtime, size in items:
human_size = f"{size/1024/1024:.1f} MB" if size > 1024*1024 else f"{size/1024:.1f} KB"
@ -56,58 +84,67 @@ class OllamaRequestHandler(SimpleHTTPRequestHandler):
)
html.append("</table></body></html>")
self.send_response(200)
self.send_header("Content-type", "text/html; charset=utf-8")
self.end_headers()
self.wfile.write("\n".join(html).encode('utf-8'))
except Exception as e:
self.send_error(500, f"服务器错误: {str(e)}")
def get_local_ip():
"""获取本机局域网IP"""
try:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect(("8.8.8.8", 80))
ip = s.getsockname()[0]
s.close()
return ip
except:
return "localhost"
def start_server(port=PORT):
"""启动HTTP服务器"""
local_ip = get_local_ip()
server_address = ('', port)
httpd = HTTPServer(server_address, OllamaRequestHandler)
print(f"\n🎯 Ollama 本地镜像服务已启动")
print(f"👉 访问地址: http://localhost:{port}")
print(f"👉 本机访问: http://localhost:{port}")
print(f"👉 局域网访问: http://{local_ip}:{port}")
print(f"📁 镜像目录: {os.path.abspath(MIRROR_DIR)}")
print("🛑 按 Ctrl+C 停止服务\n")
print(f"📜 安装脚本: {os.path.abspath(INSTALL_SCRIPT)}")
print("\n🔧 安装命令:")
print(f" curl -sSL http://{local_ip}:{port}/{INSTALL_SCRIPT} | sudo bash")
print("\n🛑 按 Ctrl+C 停止服务\n")
try:
httpd.serve_forever()
except KeyboardInterrupt:
print("\n服务器已停止")
def ensure_mirror_dir():
"""确保镜像目录存在"""
def check_files():
"""检查必要文件是否存在"""
if not os.path.exists(MIRROR_DIR):
os.makedirs(MIRROR_DIR)
print(f"创建镜像目录: {MIRROR_DIR}")
print(f"⚠️ 创建空镜像目录: {MIRROR_DIR}")
print("请先运行 mirror_ollama.sh 下载文件")
if not os.path.exists(INSTALL_SCRIPT):
print(f"❌ 错误: {INSTALL_SCRIPT} 不存在")
print("请确保安装脚本与host_ollama.py在同一目录")
return False
return True
def open_browser():
"""自动打开浏览器"""
import time
time.sleep(1)
webbrowser.open(f"http://localhost:{PORT}")
if __name__ == '__main__':
# 检查并获取端口参数
port = PORT
if len(sys.argv) > 1:
try:
port = int(sys.argv[1])
except ValueError:
print("错误: 端口号必须是数字")
port = int(sys.argv[1]) if len(sys.argv) > 1 else PORT
if not check_files():
sys.exit(1)
if not ensure_mirror_dir():
print("⚠️ 注意: 镜像目录为空,请先运行 mirror_ollama.sh 下载文件")
# 自动打开浏览器
threading.Thread(target=open_browser, daemon=True).start()
threading.Thread(
target=webbrowser.open,
args=(f"http://localhost:{port}",),
daemon=True
).start()
# 启动服务器
start_server(port)