更新 host_ollama.py
This commit is contained in:
parent
c7464e0e4d
commit
70b1d02cba
129
host_ollama.py
129
host_ollama.py
@ -1,51 +1,79 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""
|
"""
|
||||||
Ollama 本地镜像服务器
|
Ollama 本地镜像服务器 - 增强版
|
||||||
功能:
|
新增功能:
|
||||||
1. 自动检测并Host下载的镜像目录
|
1. 同时托管 ollama_releases 和 install.sh
|
||||||
2. 提供文件浏览和下载接口
|
2. 自动生成安装指引
|
||||||
3. 自动生成友好的下载页面
|
3. 显示本机IP方便局域网访问
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
import socket
|
||||||
from http.server import HTTPServer, SimpleHTTPRequestHandler
|
from http.server import HTTPServer, SimpleHTTPRequestHandler
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import threading
|
import threading
|
||||||
import webbrowser
|
import webbrowser
|
||||||
|
|
||||||
MIRROR_DIR = "ollama_releases" # 与mirror_ollama.sh保持一致
|
MIRROR_DIR = "ollama_releases"
|
||||||
|
INSTALL_SCRIPT = "install.sh"
|
||||||
PORT = 8000
|
PORT = 8000
|
||||||
|
|
||||||
class OllamaRequestHandler(SimpleHTTPRequestHandler):
|
class OllamaRequestHandler(SimpleHTTPRequestHandler):
|
||||||
"""自定义请求处理器,增强目录浏览功能"""
|
"""自定义请求处理器,支持多目录托管"""
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def translate_path(self, path):
|
||||||
super().__init__(*args, directory=MIRROR_DIR, **kwargs)
|
# 优先检查是否是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):
|
def list_directory(self, path):
|
||||||
"""生成增强型目录列表页面"""
|
"""生成增强型目录列表页面"""
|
||||||
try:
|
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 = []
|
items = []
|
||||||
for item in Path(path).iterdir():
|
for item in Path(path).iterdir():
|
||||||
|
if item.is_file():
|
||||||
mtime = item.stat().st_mtime
|
mtime = item.stat().st_mtime
|
||||||
size = item.stat().st_size
|
size = item.stat().st_size
|
||||||
items.append((item.name, mtime, size))
|
items.append((item.name, mtime, size))
|
||||||
|
|
||||||
# 按版本号排序(降序)
|
|
||||||
items.sort(key=lambda x: x[0], reverse=True)
|
items.sort(key=lambda x: x[0], reverse=True)
|
||||||
|
|
||||||
# 生成HTML
|
html = [
|
||||||
html = ["<html><head><title>Ollama 本地镜像</title>"]
|
"<html><head><title>Ollama 本地镜像</title>",
|
||||||
html.append("<style>")
|
"<style>",
|
||||||
html.append("body { font-family: Arial, sans-serif; margin: 20px; }")
|
"body { font-family: Arial, sans-serif; margin: 20px; }",
|
||||||
html.append("table { border-collapse: collapse; width: 100%; }")
|
"table { border-collapse: collapse; width: 100%; margin-bottom: 20px; }",
|
||||||
html.append("th, td { padding: 8px; text-align: left; border-bottom: 1px solid #ddd; }")
|
"th, td { padding: 8px; text-align: left; border-bottom: 1px solid #ddd; }",
|
||||||
html.append("tr:hover { background-color: #f5f5f5; }")
|
"tr:hover { background-color: #f5f5f5; }",
|
||||||
html.append("</style></head><body>")
|
".box { background: #f8f9fa; padding: 15px; border-radius: 5px; margin: 20px 0; }",
|
||||||
html.append("<h1>Ollama 本地镜像库</h1>")
|
"</style></head><body>",
|
||||||
html.append(f"<p>当前目录: {path}</p>")
|
f"<h1>Ollama 本地镜像库</h1>",
|
||||||
html.append("<table><tr><th>文件名</th><th>大小</th><th>修改时间</th></tr>")
|
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:
|
for name, mtime, size in items:
|
||||||
human_size = f"{size/1024/1024:.1f} MB" if size > 1024*1024 else f"{size/1024:.1f} KB"
|
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>")
|
html.append("</table></body></html>")
|
||||||
|
|
||||||
self.send_response(200)
|
self.send_response(200)
|
||||||
self.send_header("Content-type", "text/html; charset=utf-8")
|
self.send_header("Content-type", "text/html; charset=utf-8")
|
||||||
self.end_headers()
|
self.end_headers()
|
||||||
self.wfile.write("\n".join(html).encode('utf-8'))
|
self.wfile.write("\n".join(html).encode('utf-8'))
|
||||||
|
|
||||||
except Exception as e:
|
def get_local_ip():
|
||||||
self.send_error(500, f"服务器错误: {str(e)}")
|
"""获取本机局域网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):
|
def start_server(port=PORT):
|
||||||
"""启动HTTP服务器"""
|
"""启动HTTP服务器"""
|
||||||
|
local_ip = get_local_ip()
|
||||||
server_address = ('', port)
|
server_address = ('', port)
|
||||||
httpd = HTTPServer(server_address, OllamaRequestHandler)
|
httpd = HTTPServer(server_address, OllamaRequestHandler)
|
||||||
|
|
||||||
print(f"\n🎯 Ollama 本地镜像服务已启动")
|
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(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:
|
try:
|
||||||
httpd.serve_forever()
|
httpd.serve_forever()
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
print("\n服务器已停止")
|
print("\n服务器已停止")
|
||||||
|
|
||||||
def ensure_mirror_dir():
|
def check_files():
|
||||||
"""确保镜像目录存在"""
|
"""检查必要文件是否存在"""
|
||||||
if not os.path.exists(MIRROR_DIR):
|
if not os.path.exists(MIRROR_DIR):
|
||||||
os.makedirs(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 False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def open_browser():
|
|
||||||
"""自动打开浏览器"""
|
|
||||||
import time
|
|
||||||
time.sleep(1)
|
|
||||||
webbrowser.open(f"http://localhost:{PORT}")
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
# 检查并获取端口参数
|
port = int(sys.argv[1]) if len(sys.argv) > 1 else PORT
|
||||||
port = PORT
|
|
||||||
if len(sys.argv) > 1:
|
if not check_files():
|
||||||
try:
|
|
||||||
port = int(sys.argv[1])
|
|
||||||
except ValueError:
|
|
||||||
print("错误: 端口号必须是数字")
|
|
||||||
sys.exit(1)
|
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)
|
start_server(port)
|
||||||
Loading…
Reference in New Issue
Block a user