mirror of
https://github.com/setube/ogame-vue-ts.git
synced 2026-05-11 23:45:11 +08:00
197 lines
5.0 KiB
Go
197 lines
5.0 KiB
Go
package main
|
||
|
||
import (
|
||
"bufio"
|
||
"embed"
|
||
"flag"
|
||
"fmt"
|
||
"io/fs"
|
||
"net"
|
||
"net/http"
|
||
"os"
|
||
"os/exec"
|
||
"runtime"
|
||
"strconv"
|
||
"strings"
|
||
"time"
|
||
)
|
||
|
||
// 使用 go:embed 强制将 docs 文件夹及其所有内容打包进二进制文件
|
||
//go:embed docs/*
|
||
var content embed.FS
|
||
|
||
func main() {
|
||
// --- 1. 命令行参数配置 ---
|
||
// 定义 -port 参数,默认为 -1(表示未指定,需要交互选择)
|
||
portPtr := flag.Int("port", -1, "指定运行端口 (例如: 8080),不指定则显示交互菜单")
|
||
flag.Parse()
|
||
|
||
// 如果没有通过命令行指定端口,显示交互式菜单
|
||
port := *portPtr
|
||
if port == -1 {
|
||
port = showPortMenu()
|
||
}
|
||
|
||
// --- 2. 静态资源处理 ---
|
||
// 获取 docs 子目录的文件系统句柄
|
||
distFS, err := fs.Sub(content, "docs")
|
||
if err != nil {
|
||
fmt.Printf("错误: 无法访问嵌入的 docs 目录: %v\n", err)
|
||
return
|
||
}
|
||
|
||
fileServer := http.FileServer(http.FS(distFS))
|
||
|
||
// 自定义路由处理:支持静态文件和 SPA (单页应用) 回退
|
||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||
// 移除路径前缀的斜杠
|
||
path := strings.TrimPrefix(r.URL.Path, "/")
|
||
|
||
// 尝试打开文件
|
||
f, err := distFS.Open(path)
|
||
if err != nil {
|
||
// 如果文件不存在(或者是访问根目录),则回退到 index.html
|
||
// 这是为了支持 Vue Router 的 History 模式
|
||
indexData, err := fs.ReadFile(distFS, "index.html")
|
||
if err != nil {
|
||
http.Error(w, "Index.html not found in embedded docs", http.StatusNotFound)
|
||
return
|
||
}
|
||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||
http.ServeContent(w, r, "index.html", time.Now(), strings.NewReader(string(indexData)))
|
||
return
|
||
}
|
||
f.Close()
|
||
|
||
// 如果文件存在,使用标准文件服务器响应
|
||
fileServer.ServeHTTP(w, r)
|
||
})
|
||
|
||
// --- 3. 端口监听逻辑 ---
|
||
addr := fmt.Sprintf("0.0.0.0:%d", port)
|
||
listener, err := net.Listen("tcp", addr)
|
||
if err != nil {
|
||
fmt.Printf("错误: 端口 %d 已被占用或监听失败: %v\n", port, err)
|
||
// 停留 5 秒让用户看到错误信息
|
||
time.Sleep(5 * time.Second)
|
||
os.Exit(1)
|
||
}
|
||
|
||
actualPort := listener.Addr().(*net.TCPAddr).Port
|
||
localUrl := fmt.Sprintf("http://localhost:%d", actualPort)
|
||
lanUrl := fmt.Sprintf("http://%s:%d", getLocalIp(), actualPort)
|
||
|
||
// --- 4. 控制台信息展示 ---
|
||
fmt.Println("=======================================")
|
||
fmt.Printf("OGame Vue Ts 服务启动成功!\n")
|
||
fmt.Printf("启动时间: %s\n", time.Now().Format("2006-01-02 15:04:05"))
|
||
fmt.Printf("本地访问: %s\n", localUrl)
|
||
fmt.Printf("局域网访问: %s\n", lanUrl)
|
||
if port != 0 {
|
||
fmt.Printf("运行模式: 固定端口 (%d)\n", port)
|
||
} else {
|
||
fmt.Printf("运行模式: 自动分配端口\n")
|
||
}
|
||
fmt.Println("=======================================")
|
||
fmt.Println("提示: 请勿关闭此控制台窗口,否则服务将停止。")
|
||
fmt.Println("--- 实时访问日志 ---")
|
||
|
||
// --- 5. 自动打开浏览器并启动服务 ---
|
||
go openBrowser(localUrl)
|
||
|
||
err = http.Serve(listener, nil)
|
||
if err != nil {
|
||
fmt.Printf("服务运行异常: %v\n", err)
|
||
}
|
||
}
|
||
|
||
// 获取本机局域网 IP
|
||
func getLocalIp() string {
|
||
addrs, err := net.InterfaceAddrs()
|
||
if err != nil {
|
||
return "localhost"
|
||
}
|
||
for _, address := range addrs {
|
||
if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
|
||
if ipnet.IP.To4() != nil {
|
||
return ipnet.IP.String()
|
||
}
|
||
}
|
||
}
|
||
return "localhost"
|
||
}
|
||
|
||
// 根据不同操作系统自动打开默认浏览器
|
||
func openBrowser(url string) {
|
||
var cmd string
|
||
var args []string
|
||
|
||
switch runtime.GOOS {
|
||
case "windows":
|
||
cmd = "cmd"
|
||
args = []string{"/c", "start", url}
|
||
case "darwin":
|
||
cmd = "open"
|
||
args = []string{url}
|
||
default: // linux
|
||
cmd = "xdg-open"
|
||
args = []string{url}
|
||
}
|
||
|
||
_ = exec.Command(cmd, args...).Start()
|
||
}
|
||
|
||
// 显示端口选择菜单
|
||
func showPortMenu() int {
|
||
reader := bufio.NewReader(os.Stdin)
|
||
|
||
fmt.Println("=======================================")
|
||
fmt.Println(" OGame Vue Ts 服务器启动")
|
||
fmt.Println("=======================================")
|
||
fmt.Println()
|
||
fmt.Println("请选择端口模式:")
|
||
fmt.Println(" [1] 随机端口 (自动分配可用端口)")
|
||
fmt.Println(" [2] 自定义端口 (指定固定端口)")
|
||
fmt.Println()
|
||
fmt.Print("请输入选项 (1/2): ")
|
||
|
||
for {
|
||
input, _ := reader.ReadString('\n')
|
||
input = strings.TrimSpace(input)
|
||
|
||
switch input {
|
||
case "1", "":
|
||
fmt.Println("\n已选择: 随机端口模式")
|
||
return 0
|
||
case "2":
|
||
return inputCustomPort(reader)
|
||
default:
|
||
fmt.Print("无效输入,请输入 1 或 2: ")
|
||
}
|
||
}
|
||
}
|
||
|
||
// 输入自定义端口
|
||
func inputCustomPort(reader *bufio.Reader) int {
|
||
fmt.Print("请输入端口号 (1-65535,推荐: 8080): ")
|
||
|
||
for {
|
||
input, _ := reader.ReadString('\n')
|
||
input = strings.TrimSpace(input)
|
||
|
||
// 如果直接回车,使用默认端口 8080
|
||
if input == "" {
|
||
fmt.Println("\n已选择: 固定端口 8080")
|
||
return 8080
|
||
}
|
||
|
||
port, err := strconv.Atoi(input)
|
||
if err != nil || port < 1 || port > 65535 {
|
||
fmt.Print("无效端口号,请输入 1-65535 之间的数字: ")
|
||
continue
|
||
}
|
||
|
||
fmt.Printf("\n已选择: 固定端口 %d\n", port)
|
||
return port
|
||
}
|
||
} |