-
Notifications
You must be signed in to change notification settings - Fork 2
/
main.go
186 lines (161 loc) · 3.97 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
package main
import (
"context"
"embed"
"errors"
"github.com/cloudflare/tableflip"
"github.com/gin-gonic/gin"
"html/template"
"io"
"io/fs"
"io/ioutil"
"log"
"net/http"
"os"
"os/signal"
"path"
"strconv"
"syscall"
"time"
"uranus/internal/config"
"uranus/internal/middlewares"
"uranus/internal/models"
"uranus/internal/routes"
"uranus/internal/services"
"uranus/internal/tools"
)
//go:embed web
var templates embed.FS
//go:embed web/public
var staticFS embed.FS
func init() {
// 生产模式写入日志
if gin.Mode() == gin.ReleaseMode {
logDir := path.Join(tools.GetPWD(), "logs")
if err := os.MkdirAll(logDir, 0755); err != nil && !os.IsExist(err) {
panic(err)
}
logFile := path.Join(logDir, "app.log")
file, err := os.OpenFile(logFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
if err != nil {
panic(err)
}
wrt := io.MultiWriter(os.Stdout, file)
log.SetOutput(wrt)
log.SetFlags(log.LstdFlags | log.Lshortfile)
}
}
func mustFS() http.FileSystem {
sub, err := fs.Sub(staticFS, "web/public")
if err != nil {
panic(err)
}
return http.FS(sub)
}
func initRouter() *gin.Engine {
app := gin.New()
// 解析模板
tmpl, err := template.ParseFS(templates, "web/includes/*.html", "web/*.html")
if err != nil {
panic(err)
}
app.SetHTMLTemplate(tmpl)
// 使用缓存中间件
app.Use(middlewares.CacheMiddleware())
// 设置静态文件路由
app.StaticFS("/public", mustFS())
// 处理 favicon.ico 请求
app.GET("/favicon.ico", func(c *gin.Context) {
file, err := staticFS.ReadFile("web/public/icon/favicon.ico")
if err != nil {
c.Status(http.StatusNotFound)
return
}
c.Data(http.StatusOK, "image/x-icon", file)
})
// 设置可信代理
err = app.SetTrustedProxies([]string{"127.0.0.1"})
if err != nil {
return nil
}
// 注册路由
routes.RegisterRoutes(app)
return app
}
func Graceful() {
pidFile := path.Join(tools.GetPWD(), "uranus.pid")
upg, err := tableflip.New(tableflip.Options{
PIDFile: pidFile,
})
if err != nil {
panic(err)
}
defer upg.Stop()
// 处理信号
go func() {
sig := make(chan os.Signal, 1)
signal.Notify(sig, syscall.SIGHUP, syscall.SIGUSR2, syscall.SIGTERM, syscall.SIGINT, syscall.SIGQUIT)
for s := range sig {
switch s {
case syscall.SIGHUP, syscall.SIGUSR2:
log.Printf("[PID][%d]: 收到升级信号, 升级开始", os.Getpid())
err := upg.Upgrade()
if err != nil {
log.Printf("[PID][%d]: 升级出错, %s", os.Getpid(), err)
continue
}
log.Printf("[PID][%d]: 升级完成", os.Getpid())
case syscall.SIGTERM, syscall.SIGINT, syscall.SIGQUIT:
log.Printf("[PID][%d]: 收到关闭信号, 准备关闭服务器", os.Getpid())
upg.Stop()
log.Printf("[PID][%d]: 服务器完全关闭", os.Getpid())
os.Exit(0)
}
}
}()
ln, err := upg.Fds.Listen("tcp", "0.0.0.0:7777")
if err != nil {
log.Fatalln("无法监听:", err)
}
server := &http.Server{
Handler: initRouter(),
}
go func() {
if err := server.Serve(ln); err != nil && !errors.Is(err, http.ErrServerClosed) {
log.Println("HTTP server:", err)
}
}()
log.Printf("[PID][%d]: 服务器启动成功并写入 PID 到文件", os.Getpid())
if err := ioutil.WriteFile(pidFile, []byte(strconv.Itoa(os.Getpid())), 0755); err != nil {
log.Println("写入 PID 文件出错:", err)
}
if err := upg.Ready(); err != nil {
panic(err)
}
<-upg.Exit()
time.AfterFunc(10*time.Second, func() {
log.Println("平滑关闭超时 ...")
os.Exit(1)
})
if err := server.Shutdown(context.Background()); err != nil {
log.Println("服务器关闭出错:", err)
}
log.Println("退出并删除pid文件")
if err := os.Remove(pidFile); err != nil {
log.Println("删除 PID 文件出错:", err)
}
}
func main() {
// 线上模式显示版本信息
if gin.Mode() == gin.ReleaseMode {
config.DisplayVersion()
}
// 初始化配置文件
config.InitAppConfig()
// 初始化 SQLite 数据库
go models.Init()
// 初始化 自动签名
go services.RenewSSL()
initRouter()
Graceful()
}