Compare commits

..

10 Commits

Author SHA1 Message Date
maguodong 342d39a1a0 修改日志可以按照日期进行输出,并自动清理超过30天的旧日志文件 2026-03-28 17:52:52 +08:00
black1552 8add85cea7 refactor(utils): 替换日志库实现
- 导入自定义日志包 git.magicany.cc/black1552/gf-common/log
- 将 g.Log().Infof 替换为 log.Info 方法调用
- 将 g.Log().Errorf 替换为 log.Error 方法调用
- 统一所有 HTTP 请求方法的日志记录方式
- 移除上下文参数 ctx 在日志记录中的传递
2026-03-10 10:04:26 +08:00
black1552 95623f3802 refactor(gateway): 重构反向代理实现以支持 WebSocket 和 HTTP 请求
- 添加 gclient 和 gstr 依赖包用于 HTTP 客户端操作
- 实现新的 BuildRequest 函数,使用 gclient 处理普通 HTTP 请求
- 分离 WebSocket 请求处理到独立的 proxyWebSocket 函数
- 移除旧的 hasProtocol 函数和相关逻辑
- 添加完整的请求头和响应头复制机制
- 实现响应状态码和响应体的正确传递
- 简化 WebSocket 代理逻辑,使用标准反向代理处理
2026-03-06 17:00:37 +08:00
black1552 45262d8f88 fix(gateway): 修复代理响应头传递和JSON写入问题
- 添加 ModifyResponse 函数以正确传递原始响应头
- 实现响应头从代理服务器到客户端的完整传递
- 修复 WebSocket 升级处理中的响应写入逻辑
- 确保 JSON 数据通过 RawWriter 正确写入响应
2026-03-06 13:54:20 +08:00
black1552 f958579981 fix(gateway): 解决 WebSocket 升级和 HTTP 请求处理问题
- 使用 RawWriter() 获取原始的 http.ResponseWriter,避免 gf 框架的封装影响
- 确保代理能够正确处理 WebSocket 连接升级
- 修复了因框架封装导致的响应写入问题
2026-03-06 11:35:25 +08:00
black1552 737fd041a0 feat(gateway): 添加协议自动检测和补全功能
- 实现 hasProtocol 函数检查字符串是否包含协议前缀
- 支持 http、https、ws、wss 协议检测
- 自动为没有协议的主机地址添加相应协议前缀
- 根据请求类型判断协议:WebSocket 使用 ws,HTTP 使用 http
- 优化反向代理请求构建逻辑
2026-03-06 11:14:10 +08:00
black1552 969322a912 refactor(server): 重构网关请求代理实现
- 使用标准库 httputil.NewSingleHostReverseProxy 替代自定义 HTTP 客户端
- 移除 gclient、gcache 和相关依赖包导入
- 添加自定义 Director 函数处理目标 URL 设置
- 实现错误处理机制和 Bad Gateway 响应
- 简化 WebSocket 连接支持逻辑
- 优化请求头和响应头处理流程
2026-03-06 10:52:26 +08:00
black1552 f9fae10c76 feat(server): 添加网关代理功能
- 实现了 HTTP 请求转发功能,支持请求头和响应头的完整复制
- 添加了目标 URL 构建逻辑,处理路径和查询参数的正确拼接
- 集成了响应体读取和状态码设置功能
- 实现了 WebSocket 连接的反向代理处理
- 添加了缓存机制用于临时存储主机信息
- 集成了日志记录功能用于错误跟踪和调试
2026-03-06 10:31:06 +08:00
black1552 f130e18b8f chore(deps): 更新项目依赖包到最新版本
- 升级 github.com/gogf/gf 相关包从 v2.9.3 到 v2.10.0
- 升级 github.com/emirpasic/gods 从 v1.18.1 到 v2.0.0-alpha
- 升级 github.com/olekukonko/tablewriter 从 v1.0.9 到 v1.1.0
- 升级 go.opentelemetry.io/otel 相关包从 v1.37.0 到 v1.38.0
- 更新 github.com/stretchr/testify 从 v1.10.0 到 v1.11.1
- 调整 github.com/rogpeppe/go-internal 版本从 v1.14.1 到 v1.13.1
2026-03-06 10:24:02 +08:00
black1552 5a37aea56f feat(log): 实现日志系统彩色输出和文件分离功能
- 添加 ANSI 颜色常量定义支持彩色输出
- 实现 stripAnsiColors 函数去除 ANSI 颜色码
- 创建 logWriter 结构体实现控制台带颜色文件无颜色的输出
- 修改初始化逻辑使用自定义 writer 替代 MultiWriter
- 更新 Info、Error、Warn、Debug 函数添加时间戳和颜色前缀
- 将日志文件大小限制从 1MB 调整为 2MB
- 移除默认日志标志位,使用自定义格式化输出
2026-03-06 10:20:55 +08:00
6 changed files with 300 additions and 70 deletions

22
go.mod
View File

@ -6,10 +6,10 @@ require (
github.com/duke-git/lancet/v2 v2.3.7
github.com/eclipse/paho.mqtt.golang v1.5.1
github.com/glebarez/sqlite v1.11.0
github.com/gogf/gf/contrib/drivers/mysql/v2 v2.9.3
github.com/gogf/gf/contrib/registry/etcd/v2 v2.9.3
github.com/gogf/gf/contrib/rpc/grpcx/v2 v2.9.3
github.com/gogf/gf/v2 v2.9.3
github.com/gogf/gf/contrib/drivers/mysql/v2 v2.10.0
github.com/gogf/gf/contrib/registry/etcd/v2 v2.10.0
github.com/gogf/gf/contrib/rpc/grpcx/v2 v2.10.0
github.com/gogf/gf/v2 v2.10.0
github.com/gorilla/websocket v1.5.3
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
github.com/xuri/excelize/v2 v2.9.1
@ -26,14 +26,14 @@ require (
github.com/coreos/go-semver v0.3.0 // indirect
github.com/coreos/go-systemd/v22 v22.3.2 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/emirpasic/gods/v2 v2.0.0-alpha // indirect
github.com/fatih/color v1.18.0 // indirect
github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/glebarez/go-sqlite v1.22.0 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-sql-driver/mysql v1.9.2 // indirect
github.com/gogf/gf/contrib/registry/file/v2 v2.9.3 // indirect
github.com/gogf/gf/contrib/registry/file/v2 v2.10.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/uuid v1.6.0 // indirect
@ -47,7 +47,7 @@ require (
github.com/ncruces/go-strftime v1.0.0 // indirect
github.com/olekukonko/errors v1.1.0 // indirect
github.com/olekukonko/ll v0.0.9 // indirect
github.com/olekukonko/tablewriter v1.0.9 // indirect
github.com/olekukonko/tablewriter v1.1.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/richardlehane/mscfb v1.0.4 // indirect
@ -60,10 +60,10 @@ require (
go.etcd.io/etcd/client/pkg/v3 v3.5.17 // indirect
go.etcd.io/etcd/client/v3 v3.5.17 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/otel v1.37.0 // indirect
go.opentelemetry.io/otel/metric v1.37.0 // indirect
go.opentelemetry.io/otel/sdk v1.37.0 // indirect
go.opentelemetry.io/otel/trace v1.37.0 // indirect
go.opentelemetry.io/otel v1.38.0 // indirect
go.opentelemetry.io/otel/metric v1.38.0 // indirect
go.opentelemetry.io/otel/sdk v1.38.0 // indirect
go.opentelemetry.io/otel/trace v1.38.0 // indirect
go.uber.org/atomic v1.7.0 // indirect
go.uber.org/multierr v1.6.0 // indirect
go.uber.org/zap v1.21.0 // indirect

56
go.sum
View File

@ -19,8 +19,8 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/eclipse/paho.mqtt.golang v1.5.1 h1:/VSOv3oDLlpqR2Epjn1Q7b2bSTplJIeV2ISgCl2W7nE=
github.com/eclipse/paho.mqtt.golang v1.5.1/go.mod h1:1/yJCneuyOoCOzKSsOTUc0AJfpsItBGWvYpBLimhArU=
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
github.com/emirpasic/gods/v2 v2.0.0-alpha h1:dwFlh8pBg1VMOXWGipNMRt8v96dKAIvBehtCt6OtunU=
github.com/emirpasic/gods/v2 v2.0.0-alpha/go.mod h1:W0y4M2dtBB9U5z3YlghmpuUhiaZT2h6yoeE+C1sCp6A=
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
@ -37,16 +37,16 @@ github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre
github.com/go-sql-driver/mysql v1.9.2 h1:4cNKDYQ1I84SXslGddlsrMhc8k4LeDVj6Ad6WRjiHuU=
github.com/go-sql-driver/mysql v1.9.2/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gogf/gf/contrib/drivers/mysql/v2 v2.9.3 h1:P4jrnp+Vmh3kDeaH/kyHPI6rfoMmQD+sPJa716aMbS0=
github.com/gogf/gf/contrib/drivers/mysql/v2 v2.9.3/go.mod h1:yEhfx78wgpxUJhH9C9bWJ7I3JLcVCzUg11A4ORYTKeg=
github.com/gogf/gf/contrib/registry/etcd/v2 v2.9.3 h1:4ztKAHfwtddPRwxlVRRfLdJMzp42Z+9K5tFHrPPZl1Q=
github.com/gogf/gf/contrib/registry/etcd/v2 v2.9.3/go.mod h1:ey99pcs/hSwShOLWjd4mUUmq4S9fLy7elf7SslUAs20=
github.com/gogf/gf/contrib/registry/file/v2 v2.9.3 h1:7f+55KmwJzW0Kmam+N3VrLMlMkCTc5LTPDaZeElCLd0=
github.com/gogf/gf/contrib/registry/file/v2 v2.9.3/go.mod h1:kOYsqBlbf6pElgfLyIEFN6FpXnSSdgkV4w+7+w/78do=
github.com/gogf/gf/contrib/rpc/grpcx/v2 v2.9.3 h1:HoUu3a7t0iLI0/GbVP46rPHbrfBlKa6rfHsnCvry4+4=
github.com/gogf/gf/contrib/rpc/grpcx/v2 v2.9.3/go.mod h1:F5v+4GumRPIFZsAiz6H5QNoOiPM7cluMCvGF0zbzsGE=
github.com/gogf/gf/v2 v2.9.3 h1:qjN4s55FfUzxZ1AE8vUHNDX3V0eIOUGXhF2DjRTVZQ4=
github.com/gogf/gf/v2 v2.9.3/go.mod h1:w6rcfD13SmO7FKI80k9LSLiSMGqpMYp50Nfkrrc2sEE=
github.com/gogf/gf/contrib/drivers/mysql/v2 v2.10.0 h1:UvqxwinkelKxwdwnKUfdy51/ls4RL7MCeJqAZOVAy0I=
github.com/gogf/gf/contrib/drivers/mysql/v2 v2.10.0/go.mod h1:6v7oGBF9wv59WERJIOJxXmLhkUcxwON3tPYW3AZ7wbY=
github.com/gogf/gf/contrib/registry/etcd/v2 v2.10.0 h1:yOKc0YroBwCwyBHezJ1M6ISFfrLl7ZnRKsCS1dhF1DA=
github.com/gogf/gf/contrib/registry/etcd/v2 v2.10.0/go.mod h1:ezkHf1r7YxkFYis7Y1807D1tr+3nXolfL8IsY816Cuw=
github.com/gogf/gf/contrib/registry/file/v2 v2.10.0 h1:yy93PGhz2CE3PpF2JSGa2MXJWd4JNHYC5l5NEFCFRV4=
github.com/gogf/gf/contrib/registry/file/v2 v2.10.0/go.mod h1:ZzGWiTbQ9nAmDynrmzaRJlqYHD/F3MeMwCvHrkv/LP4=
github.com/gogf/gf/contrib/rpc/grpcx/v2 v2.10.0 h1:MWHgX/h/62+Oo1Jbeq8DrXbEWSOZxskpBBm3cFAV4/Q=
github.com/gogf/gf/contrib/rpc/grpcx/v2 v2.10.0/go.mod h1:zGZdjS08IqiSGhKfy69/qgcYKUYUL/VsOx5fuT++TVE=
github.com/gogf/gf/v2 v2.10.0 h1:rzDROlyqGMe/eM6dCalSR8dZOuMIdLhmxKSH1DGhbFs=
github.com/gogf/gf/v2 v2.10.0/go.mod h1:Svl1N+E8G/QshU2DUbh/3J/AJauqCgUnxHurXWR4Qx0=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
@ -92,8 +92,8 @@ github.com/olekukonko/errors v1.1.0 h1:RNuGIh15QdDenh+hNvKrJkmxxjV4hcS50Db478Ou5
github.com/olekukonko/errors v1.1.0/go.mod h1:ppzxA5jBKcO1vIpCXQ9ZqgDh8iwODz6OXIGKU8r5m4Y=
github.com/olekukonko/ll v0.0.9 h1:Y+1YqDfVkqMWuEQMclsF9HUR5+a82+dxJuL1HHSRpxI=
github.com/olekukonko/ll v0.0.9/go.mod h1:En+sEW0JNETl26+K8eZ6/W4UQ7CYSrrgg/EdIYT2H8g=
github.com/olekukonko/tablewriter v1.0.9 h1:XGwRsYLC2bY7bNd93Dk51bcPZksWZmLYuaTHR0FqfL8=
github.com/olekukonko/tablewriter v1.0.9/go.mod h1:5c+EBPeSqvXnLLgkm9isDdzR3wjfBkHR9Nhfp3NWrzo=
github.com/olekukonko/tablewriter v1.1.0 h1:N0LHrshF4T39KvI96fn6GT8HEjXRXYNDrDjKFDB7RIY=
github.com/olekukonko/tablewriter v1.1.0/go.mod h1:5c+EBPeSqvXnLLgkm9isDdzR3wjfBkHR9Nhfp3NWrzo=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@ -109,13 +109,13 @@ github.com/richardlehane/msoleps v1.0.4/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTK
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/tiendc/go-deepcopy v1.6.0 h1:0UtfV/imoCwlLxVsyfUd4hNHnB3drXsfle+wzSCA5Wo=
github.com/tiendc/go-deepcopy v1.6.0/go.mod h1:toXoeQoUqXOOS/X4sKuiAoSk6elIdqc0pN7MTgOOo2I=
github.com/xuri/efp v0.0.1 h1:fws5Rv3myXyYni8uwj2qKjVaRP30PdjeYe2Y6FDsCL8=
@ -135,16 +135,16 @@ go.etcd.io/etcd/client/v3 v3.5.17 h1:o48sINNeWz5+pjy/Z0+HKpj/xSnBkuVhVvXkjEXbqZY
go.etcd.io/etcd/client/v3 v3.5.17/go.mod h1:j2d4eXTHWkT2ClBgnnEPm/Wuu7jsqku41v9DZ3OtjQo=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ=
go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I=
go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE=
go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E=
go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI=
go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg=
go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFhbjxHHspCPc=
go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps=
go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4=
go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0=
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA=
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI=
go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E=
go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg=
go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM=
go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA=
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=

View File

@ -1 +0,0 @@
package log

View File

@ -5,8 +5,11 @@ import (
"io"
"log"
"os"
"path/filepath"
"regexp"
"runtime/debug"
"strings"
"time"
"github.com/gogf/gf/v2/os/gfile"
"github.com/gogf/gf/v2/os/gtime"
@ -14,46 +17,184 @@ import (
)
var (
logPath string
sysLog *log.Logger
logPath string
sysLog *log.Logger
filePath string
currentDate string // 当前日志文件对应的日期
fileLogger *lumberjack.Logger
)
func init() {
const (
Reset = "\033[0m"
Red = "\033[31m"
Green = "\033[32m"
Yellow = "\033[33m"
Blue = "\033[34m"
Purple = "\033[35m"
Cyan = "\033[36m"
)
// 正则表达式匹配 ANSI 颜色码
var ansiColorRegex = regexp.MustCompile(`\x1b\[[0-9;]*m`)
// stripAnsiColors 去除字符串中的 ANSI 颜色码
func stripAnsiColors(s string) string {
return ansiColorRegex.ReplaceAllString(s, "")
}
// logWriter 自定义 writer用于分别处理控制台和文件输出
type logWriter struct {
console io.Writer
file io.Writer
}
func (w *logWriter) Write(p []byte) (n int, err error) {
// 控制台输出保留颜色
_, err = w.console.Write(p)
if err != nil {
return 0, err
}
// 文件输出去除颜色码
colorless := stripAnsiColors(string(p))
_, err = w.file.Write([]byte(colorless))
if err != nil {
return 0, err
}
return len(p), nil
}
// cleanOldLogs 删除指定天数之前的日志文件(包括主文件和备份文件)
func cleanOldLogs(days int) {
if !gfile.Exists(logPath) {
return
}
// 获取所有日志文件
files, err := gfile.DirNames(logPath)
if err != nil {
return
}
now := time.Now()
for _, file := range files {
path := filepath.Join(logPath, file)
if gfile.IsDir(path) {
continue
}
var dateStr string
var matched bool
// 匹配主日志文件格式log-YYYY-MM-DD.log
if strings.HasPrefix(file, "log-") && strings.HasSuffix(file, ".log") {
// 检查是否是主文件(没有备份时间戳)
// 主文件格式log-2026-04-25.log
// 备份文件格式log-2026-04-25-2026-04-25T10-30-45.123.log
parts := strings.Split(strings.TrimSuffix(file, ".log"), "-")
if len(parts) == 4 {
// 主文件log-YYYY-MM-DD
dateStr = parts[1] + "-" + parts[2] + "-" + parts[3]
matched = true
} else if len(parts) > 4 {
// 备份文件log-YYYY-MM-DD-YYYY-MM-DDTHH-MM-SS.mmm
// 提取主日期部分(第一个日期)
dateStr = parts[1] + "-" + parts[2] + "-" + parts[3]
matched = true
}
}
if !matched {
continue
}
// 解析日期
fileTime, err := time.Parse("2006-01-02", dateStr)
if err != nil {
continue // 日期格式不正确,跳过
}
// 计算文件年龄
tage := now.Sub(fileTime)
if tage.Hours() > float64(days*24) {
// 超过指定天数,删除文件
err = os.Remove(path)
if err == nil {
Info(fmt.Sprintf("已删除过期日志文件:%s", file))
}
}
}
}
// checkAndRotateLogFile 检查是否需要切换日志文件(跨天时)
func checkAndRotateLogFile() {
date := gtime.Date()
if currentDate != date {
// 日期变化,需要重新初始化
currentDate = date
filePath = gfile.Join(logPath, fmt.Sprintf("log-%s.log", currentDate))
fileLogger = &lumberjack.Logger{
Filename: filePath,
MaxSize: 2, // 单个文件最大 10MB
MaxBackups: 5, // 最多保留 5 个备份
MaxAge: 30, // 保留 30 天
Compress: false, // 启用压缩
}
// 创建新的 writer
multiWriter := &logWriter{
console: os.Stdout,
file: fileLogger,
}
sysLog = log.New(multiWriter, "", 0)
// 清理 30 天前的旧日志
cleanOldLogs(30)
}
}
func Init() {
if sysLog != nil {
checkAndRotateLogFile() // 检查是否需要切换文件
return
}
logPath = gfile.Join(gfile.Pwd(), "logs")
filePath := gfile.Join(logPath, fmt.Sprintf("log-%s.log", gtime.Date()))
fileLogger := &lumberjack.Logger{
currentDate = gtime.Date()
filePath = gfile.Join(logPath, fmt.Sprintf("log-%s.log", currentDate))
fileLogger = &lumberjack.Logger{
Filename: filePath,
MaxSize: 1, // 单个文件最大 10MB
MaxSize: 2, // 单个文件最大 10MB
MaxBackups: 5, // 最多保留 5 个备份
MaxAge: 30, // 保留 30 天
Compress: false, // 启用压缩
}
// 创建 MultiWriter 实现同时输出到文件和终端
multiWriter := io.MultiWriter(fileLogger, os.Stdout)
sysLog = log.New(multiWriter, "", log.LstdFlags)
// 使用自定义 writer 实现控制台带颜色、文件无颜色的输出
multiWriter := &logWriter{
console: os.Stdout,
file: fileLogger,
}
sysLog = log.New(multiWriter, "", 0)
// 启动时清理 30 天前的旧日志
cleanOldLogs(30)
}
// Info 打印普通信息日志
func Info(v ...any) {
sysLog.SetPrefix("[INFO] ")
Init()
sysLog.SetPrefix(fmt.Sprintf("[%s] %s[INFO]%s ", time.Now().Format("2006-01-02 15:04:05"), Green, Reset))
sysLog.Println(fmt.Sprint(v...))
}
// Error 打印错误日志
func Error(v ...any) {
sysLog.SetPrefix("[ERROR] ")
Init()
sysLog.SetPrefix(fmt.Sprintf("[%s] %s[ERROR]%s ", time.Now().Format("2006-01-02 15:04:05"), Red, Reset))
msg := fmt.Sprint(v...)
sysLog.Println(msg, strings.TrimSpace(string(debug.Stack())))
}
// Warn 打印警告日志
func Warn(v ...any) {
sysLog.SetPrefix("[WARN] ")
Init()
sysLog.SetPrefix(fmt.Sprintf("[%s] %s[WARN]%s ", time.Now().Format("2006-01-02 15:04:05"), Yellow, Reset))
sysLog.Println(fmt.Sprint(v...))
}
// Debug 打印调试日志
func Debug(v ...any) {
sysLog.SetPrefix("[DEBUG] ")
Init()
sysLog.SetPrefix(fmt.Sprintf("[%s] %s[DEBUG]%s ", time.Now().Format("2006-01-02 15:04:05"), Blue, Reset))
sysLog.Println(fmt.Sprint(v...))
}

89
server/gateway.go Normal file
View File

@ -0,0 +1,89 @@
package server
import (
"fmt"
"io"
"net/http"
"net/http/httputil"
"net/url"
"strings"
"git.magicany.cc/black1552/gf-common/log"
"github.com/gogf/gf/v2/net/gclient"
"github.com/gogf/gf/v2/net/ghttp"
"github.com/gogf/gf/v2/os/gctx"
"github.com/gogf/gf/v2/text/gstr"
)
// hasProtocol 检查字符串是否包含协议前缀
func hasProtocol(s string) bool {
return strings.HasPrefix(s, "http://") ||
strings.HasPrefix(s, "https://") ||
strings.HasPrefix(s, "ws://") ||
strings.HasPrefix(s, "wss://")
}
// BuildRequest 反向代理请求到指定主机
// 自动支持所有 HTTP 方法及 WebSocket 连接
func BuildRequest(r *ghttp.Request, host string) {
if gstr.Contains(r.RequestURI, "/ws") {
proxyWebSocket(r, host)
return
}
client := gclient.New()
// 构建目标URL而不是直接复制RequestURI
targetURL := host + r.URL.Path
if r.URL.RawQuery != "" {
targetURL += "?" + r.URL.RawQuery
}
// 复制请求头
for key, values := range r.Header {
for _, value := range values {
client.SetHeader(key, value)
}
}
response, err := client.DoRequest(gctx.New(), r.Method, targetURL, r.GetBody())
if err != nil {
log.Error(gctx.New(), "request error:", err)
panic(fmt.Sprintf("request error: %v", err))
}
defer response.Body.Close()
// 读取响应体
respBody, err := io.ReadAll(response.Body)
if err != nil {
log.Error(gctx.New(), "read response body error:", err)
panic(fmt.Sprintf("read response body error: %v", err))
}
// 复制响应头
for key, values := range response.Header {
for _, value := range values {
r.Response.Header().Add(key, value)
}
}
// 设置响应状态码并写入响应体
r.Response.Status = response.StatusCode
r.Response.Write(respBody)
}
// proxyWebSocket 处理 WebSocket 连接的代理
func proxyWebSocket(r *ghttp.Request, targetHost string) {
// 解析目标主机 URL
targetURL, err := url.Parse(targetHost)
if err != nil {
log.Error(gctx.New(), "parse target host error:", err)
r.Response.WriteStatus(http.StatusInternalServerError)
r.Response.Write([]byte("Invalid target host"))
return
}
// 创建反向代理
proxy := httputil.NewSingleHostReverseProxy(targetURL)
// 修改请求 URL保留原始路径和查询参数
r.URL.Scheme = targetURL.Scheme
r.URL.Host = targetURL.Host
log.Info(gctx.New(), r.GetBodyString())
// 处理 WebSocket 连接
proxy.ServeHTTP(r.Response.Writer, r.Request)
}

View File

@ -3,6 +3,7 @@ package utils
import (
"context"
"git.magicany.cc/black1552/gf-common/log"
_ "github.com/gogf/gf/contrib/drivers/mysql/v2"
"github.com/gogf/gf/v2/crypto/gmd5"
"github.com/gogf/gf/v2/database/gdb"
@ -51,51 +52,51 @@ func NewClient[R any](request any, url string, header map[string]string) *SClien
return s
}
func (w *SClient[R]) Post(ctx context.Context) (res *R, err error) {
g.Log().Infof(ctx, "请求Url:%s,请求头:%v,请求方法:%s,请求内容:%s", w.url, w.header, "post", w.request)
log.Info("请求Url:%s,请求头:%v,请求方法:%s,请求内容:%s", w.url, w.header, "post", w.request)
resp := w.client.PostVar(ctx, w.url, w.request)
err = gconv.Struct(resp, &res)
if err != nil {
g.Log().Errorf(ctx, "解析响应体异常:%s", err)
log.Error("请求异常:", err)
return nil, err
}
return
}
func (w *SClient[R]) Get(ctx context.Context) (res *R, err error) {
g.Log().Infof(ctx, "请求Url:%s,请求头:%v,请求方法:%s,请求内容:%s", w.url, w.header, "get", w.request)
log.Info("请求Url:%s,请求头:%v,请求方法:%s,请求内容:%s", w.url, w.header, "get", w.request)
resp := w.client.GetVar(ctx, w.url, w.request)
err = gconv.Struct(resp, &res)
if err != nil {
g.Log().Errorf(ctx, "解析响应体异常:%s", err)
log.Error("解析响应体异常:", err)
return nil, err
}
return
}
func (w *SClient[R]) Put(ctx context.Context) (res *R, err error) {
g.Log().Infof(ctx, "请求Url:%s,请求头:%v,请求方法:%s,请求内容:%s", w.url, w.header, "put", w.request)
log.Info("请求Url:%s,请求头:%v,请求方法:%s,请求内容:%s", w.url, w.header, "put", w.request)
resp := w.client.PutVar(ctx, w.url, w.request)
err = gconv.Struct(resp, &res)
if err != nil {
g.Log().Errorf(ctx, "解析响应体异常:%s", err)
log.Error("解析响应体异常:", err)
return nil, err
}
return
}
func (w *SClient[R]) Delete(ctx context.Context) (res *R, err error) {
g.Log().Infof(ctx, "请求Url:%s,请求头:%v,请求方法:%s,请求内容:%s", w.url, w.header, "delete", w.request)
log.Info("请求Url:%s,请求头:%v,请求方法:%s,请求内容:%s", w.url, w.header, "delete", w.request)
resp := w.client.DeleteVar(ctx, w.url, w.request)
err = gconv.Struct(resp, &res)
if err != nil {
g.Log().Errorf(ctx, "解析响应体异常:%s", err)
log.Error("解析响应体异常:", err)
return nil, err
}
return
}
func (w *SClient[R]) Patch(ctx context.Context) (res *R, err error) {
g.Log().Infof(ctx, "请求Url:%s,请求头:%v,请求方法:%s,请求内容:%s", w.url, w.header, "patch", w.request)
log.Info("请求Url:%s,请求头:%v,请求方法:%s,请求内容:%s", w.url, w.header, "patch", w.request)
resp := w.client.PatchVar(ctx, w.url, w.request)
err = gconv.Struct(resp, &res)
if err != nil {
g.Log().Errorf(ctx, "解析响应体异常:%s", err)
log.Error("解析响应体异常:", err)
return nil, err
}
return