allinssl/cmd/main.go

441 lines
9.3 KiB
Go

package main
import (
_ "ALLinSSL/backend/migrations"
"ALLinSSL/backend/public"
"ALLinSSL/backend/scheduler"
"ALLinSSL/backend/server"
"crypto/md5"
"encoding/hex"
"fmt"
"github.com/joho/godotenv"
ps "github.com/mitchellh/go-ps"
"os"
"os/exec"
"os/signal"
"runtime"
"strconv"
"syscall"
)
var s = &scheduler.Scheduler{}
var pidPath = "data/pid"
var envPath = "data/.env"
var envVars = map[string]string{
"web": "start",
"scheduler": "start",
}
func main() {
if len(os.Args) < 2 {
fmt.Println(`请不要直接运行本程序`)
// start()
return
}
env, err := godotenv.Read(envPath)
if err == nil {
envVars = env
}
cmd := os.Args[1]
switch cmd {
case "start":
mainRun()
case "1":
fmt.Println("Starting ALLinSSL...")
if checkRun() {
_ = exec.Command("/bin/bash", "-c", fmt.Sprintf("nohup %s start > /dev/null 2>&1 &", os.Args[0])).Run()
fmt.Println("Started ALLinSSL")
return
}
fmt.Println("ALLinSSL is already running")
case "2":
fmt.Println("Stopping ALLinSSL...")
pid, err := os.ReadFile(pidPath)
if err != nil {
fmt.Println("Error reading pid file")
return
}
exec.Command("kill", "-9", string(pid)).Run()
os.Remove(pidPath)
fmt.Println("Stopped ALLinSSL")
case "3":
fmt.Println("Restarting ALLinSSL...")
pid, err := os.ReadFile(pidPath)
if err != nil {
fmt.Println("Error reading pid file")
}
exec.Command("kill", "-9", string(pid)).Run()
os.Remove(pidPath)
exec.Command("/bin/bash", "-c", fmt.Sprintf("nohup %s start > /dev/null 2>&1 &", os.Args[0])).Run()
fmt.Println("Restarted ALLinSSL")
case "4":
var secure string
if len(os.Args) > 2 {
secure = os.Args[2]
} else {
fmt.Print("请输入安全入口: ")
fmt.Scanln(&secure)
}
if len(secure) < 5 {
fmt.Println("安全入口至少需要5位")
return
}
if secure[0] != '/' {
secure = "/" + secure
}
if secure == "/login" {
fmt.Println("安全入口不能是/login")
return
}
err := public.UpdateSetting("secure", secure)
if err != nil {
fmt.Println("Error updating setting:", err)
return
}
envVars["web"] = "restart"
err = control()
fmt.Println("安全入口设置成功:", secure)
case "5":
var input string
if len(os.Args) > 2 {
input = os.Args[2]
} else {
fmt.Print("请输入用户名: ")
fmt.Scanln(&input)
}
if len(input) < 5 {
fmt.Println("用户名至少需要5位")
return
}
s, err := public.NewSqlite("data/data.db", "")
if err != nil {
fmt.Println(err)
return
}
defer s.Close()
s.TableName = "users"
_, err = s.Where("id=1", []interface{}{}).Update(map[string]interface{}{"username": input})
if err != nil {
fmt.Println(err)
return
}
envVars["web"] = "restart"
err = control()
fmt.Println("用户名设置成功:", input)
case "6":
var input string
if len(os.Args) > 2 {
input = os.Args[2]
} else {
fmt.Print("请输入密码: ")
fmt.Scanln(&input)
}
if len(input) < 8 {
fmt.Println("密码至少需要8位")
return
}
s, err := public.NewSqlite("data/data.db", "")
if err != nil {
fmt.Println(err)
return
}
defer s.Close()
s.TableName = "users"
user, err := s.Where("id=1", []interface{}{}).Select()
if err != nil {
fmt.Println("Error selecting user:", err)
return
}
if len(user) == 0 {
fmt.Println("No user")
return
}
salt := user[0]["salt"].(string)
passwd := input + "_bt_all_in_ssl"
keyMd5 := md5.Sum([]byte(passwd))
passwdMd5 := hex.EncodeToString(keyMd5[:])
passwdMd5 += salt
keyMd5 = md5.Sum([]byte(passwdMd5))
passwdMd5 = hex.EncodeToString(keyMd5[:])
_, err = s.Where("id=1", []interface{}{}).Update(map[string]interface{}{"password": passwdMd5})
if err != nil {
fmt.Println(err)
return
}
envVars["web"] = "restart"
err = control()
fmt.Println("密码设置成功:", input)
case "7":
var input string
if len(os.Args) > 2 {
input = os.Args[2]
} else {
fmt.Print("请输入端口号: ")
fmt.Scanln(&input)
}
port, err := strconv.Atoi(input) // 转换成整数
if err != nil {
fmt.Println("端口号必须是数字!")
return
}
if port < 1 || port > 65535 {
fmt.Println("端口号必须在 1 到 65535 之间!")
return
}
err = public.UpdateSetting("port", input)
if err != nil {
fmt.Println("Error updating setting:", err)
return
}
fmt.Println("端口号设置成功:", input)
envVars["web"] = "restart"
control()
case "8":
envVars["web"] = "stop"
if control() != nil {
return
}
fmt.Println("web服务已停止")
case "9":
envVars["web"] = "start"
if control() != nil {
return
}
fmt.Println("web服务已启动")
case "10":
envVars["web"] = "restart"
if control() != nil {
return
}
fmt.Println("已重启web服务")
case "11":
envVars["scheduler"] = "stop"
if control() != nil {
return
}
fmt.Println("后台调度服务已停止")
case "12":
envVars["scheduler"] = "start"
if control() != nil {
return
}
fmt.Println("后台调度服务已开启")
case "13":
envVars["scheduler"] = "restart"
if control() != nil {
return
}
fmt.Println("已重启后台调度服务")
case "14":
err := public.UpdateSetting("https", "0")
if err != nil {
fmt.Println("Error updating setting:", err)
return
}
envVars["web"] = "restart"
control()
case "15":
public.ReloadConfig()
http := "http"
if public.GetSettingIgnoreError("https") == "1" {
http = "https"
}
localIp, err := public.GetLocalIP()
if err != nil {
localIp = "0.0.0.0"
}
localAddr := fmt.Sprintf("%s://%s:%s%s", http, localIp, public.Port, public.Secure)
publicIp, err := public.GetPublicIP()
if err != nil {
publicIp = "0.0.0.0"
}
publicAddr := fmt.Sprintf("%s://%s:%s%s", http, publicIp, public.Port, public.Secure)
s, err := public.NewSqlite("data/data.db", "")
if err != nil {
fmt.Println(err)
return
}
defer s.Close()
s.TableName = "users"
user, err := s.Where("id=1", []interface{}{}).Select()
if err != nil {
fmt.Println("Error selecting user:", err)
return
}
if len(user) == 0 {
fmt.Println("No user")
return
}
username, ok := user[0]["username"].(string)
if !ok {
fmt.Println("Error getting username")
return
}
fmt.Printf(`
外网面板地址: %s
内网面板地址: %s
用户名: %s
密码: ********
`,
publicAddr, localAddr, username)
default:
fmt.Println("无效的命令")
}
}
func control() (err error) {
pidStr, err := os.ReadFile(pidPath)
if err != nil {
fmt.Println("Error reading pid file")
return
}
err = godotenv.Write(envVars, envPath)
if err != nil {
fmt.Println("Error writing to .env file")
return
}
pid, err := strconv.Atoi(string(pidStr))
if err != nil {
fmt.Println("Error converting pid to int:", err)
return
}
process, err := os.FindProcess(pid)
if err != nil {
fmt.Println("Error finding process:", err)
return
}
err = process.Signal(syscall.SIGHUP)
if err != nil {
fmt.Println("Error sending signal:", err)
return
}
return
}
func mainRun() {
if checkRun() {
pid := os.Getpid()
os.WriteFile(pidPath, []byte(fmt.Sprintf("%d", pid)), 0644)
defer os.Remove(pidPath)
go func() {
fmt.Println("web服务正在运行...")
err := server.Run()
if err != nil {
fmt.Println("web服务在运行时遇到错误:", err)
}
fmt.Println("web服务已停止")
}()
fmt.Println("正在启动调度器...")
go s.Start()
fmt.Println("调度器启动成功")
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGHUP)
for {
<-c
run()
}
} else {
fmt.Println("服务已经启动")
}
}
func checkRun() bool {
_, err := os.Stat(pidPath)
if err != nil {
return true
}
pid, err := os.ReadFile(pidPath)
if err != nil {
os.Remove(pidPath)
return true
}
pidInt, err := strconv.Atoi(string(pid))
if err != nil {
fmt.Println("Error converting pid to int")
os.Remove(pidPath)
return true
}
switch runtime.GOOS {
case "windows":
return isProcessAliveWindows(pidInt)
default:
return isProcessAliveUnix(pidInt)
}
}
// Unix/Linux/macOS 检测
func isProcessAliveUnix(pid int) bool {
proc, err := os.FindProcess(pid)
if err != nil {
return true
}
// 发送 0 信号,不会杀死,只是检测
err = proc.Signal(syscall.Signal(0))
return err != nil
}
// Windows 检测(遍历进程表)
func isProcessAliveWindows(pid int) bool {
processList, err := ps.Processes()
if err != nil {
return true
}
for _, p := range processList {
if p.Pid() == pid {
return false
}
}
return true
}
func run() {
envVars, err := godotenv.Read(envPath)
if err != nil {
fmt.Println("Error reading .env file")
}
switch envVars["web"] {
case "start":
go func() {
fmt.Println("web服务正在运行...")
err := server.Run()
if err != nil {
fmt.Println("web服务在运行时遇到错误:", err)
return
// errchan <- err
}
fmt.Println("web服务已停止")
}()
case "stop":
public.ShutdownFunc()
fmt.Println("web服务已停止")
case "restart":
public.ShutdownFunc()
go func() {
fmt.Println("web服务正在运行...")
err := server.Run()
if err != nil {
fmt.Println("web服务在运行时遇到错误:", err)
return
}
fmt.Println("web服务已停止")
}()
}
switch envVars["scheduler"] {
case "start":
go s.Start()
case "stop":
s.Stop()
case "restart":
s.Restart()
}
}