feat: update dockerfile

This commit is contained in:
2025-12-10 23:17:24 +08:00
parent 083142491f
commit 0bc77f61e2
18 changed files with 357 additions and 22 deletions

View File

@@ -1,5 +1,6 @@
# Build stage
FROM registry.cn-beijing.aliyuncs.com/bitwsd/golang AS builder
FROM golang:1.20-alpine AS builder
WORKDIR /app
@@ -7,10 +8,10 @@ WORKDIR /app
COPY . .
# Build binary
RUN CGO_ENABLED=0 GOOS=linux go build -mod=vendor -o main ./main.go
RUN CGO_ENABLED=0 GOOS=linux go build -mod=vendor -o doc_ai ./main.go
# Runtime stage
FROM registry.cn-beijing.aliyuncs.com/bitwsd/alpine
FROM alpine:latest
# Set timezone
RUN apk add --no-cache tzdata && \
@@ -21,7 +22,7 @@ RUN apk add --no-cache tzdata && \
WORKDIR /app
# Copy binary from builder
COPY --from=builder /app/main .
COPY --from=builder /app/doc_ai .
# Copy config files
COPY config/config_*.yaml ./config/
@@ -34,7 +35,7 @@ RUN mkdir -p /data/formula && \
EXPOSE 8024
# Set entrypoint
ENTRYPOINT ["./main"]
ENTRYPOINT ["./doc_ai"]
# Default command (can be overridden)
CMD ["-env", "prod"]

View File

@@ -3,7 +3,7 @@ package task
import (
"net/http"
"gitea.com/bitwsd/core/common/log"
"gitea.com/bitwsd/document_ai/pkg/log"
"gitea.com/bitwsd/document_ai/internal/model/task"
"gitea.com/bitwsd/document_ai/internal/service"
"gitea.com/bitwsd/document_ai/pkg/common"

View File

@@ -3,7 +3,7 @@ package user
import (
"net/http"
"gitea.com/bitwsd/core/common/log"
"gitea.com/bitwsd/document_ai/pkg/log"
"gitea.com/bitwsd/document_ai/config"
model "gitea.com/bitwsd/document_ai/internal/model/user"
"gitea.com/bitwsd/document_ai/internal/service"

View File

@@ -1,7 +1,7 @@
package config
import (
"gitea.com/bitwsd/core/common/log"
"gitea.com/bitwsd/document_ai/pkg/log"
"github.com/spf13/viper"
)

5
go.mod
View File

@@ -3,7 +3,6 @@ module gitea.com/bitwsd/document_ai
go 1.20
require (
gitea.com/bitwsd/core v0.0.0-20241128075635-8d72a929b914
github.com/alibabacloud-go/darabonba-openapi v0.2.1
github.com/alibabacloud-go/dysmsapi-20170525/v2 v2.0.18
github.com/alibabacloud-go/tea v1.1.19
@@ -13,7 +12,9 @@ require (
github.com/gin-gonic/gin v1.10.0
github.com/google/uuid v1.6.0
github.com/redis/go-redis/v9 v9.7.0
github.com/rs/zerolog v1.33.0
github.com/spf13/viper v1.19.0
gopkg.in/natefinch/lumberjack.v2 v2.2.1
gorm.io/driver/mysql v1.5.7
gorm.io/gorm v1.25.12
)
@@ -54,7 +55,6 @@ require (
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
github.com/rs/zerolog v1.33.0 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
@@ -76,6 +76,5 @@ require (
golang.org/x/time v0.5.0 // indirect
google.golang.org/protobuf v1.34.1 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

2
go.sum
View File

@@ -1,5 +1,3 @@
gitea.com/bitwsd/core v0.0.0-20241128075635-8d72a929b914 h1:3aRCeiuq/PWMr2yjEN9Y5NusfmpdMKiO4i/5tM5qc34=
gitea.com/bitwsd/core v0.0.0-20241128075635-8d72a929b914/go.mod h1:hbEUo3t/AFGCnQbxwdG4oiw2IHdlRgK02cqd0yicP1Y=
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4 h1:iC9YFYKDGEy3n/FtqJnOkZsene9olVspKmkX5A2YBEo=
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4/go.mod h1:sCavSAvdzOjul4cEqeVtvlSaSScfNsTQ+46HwlTL1hc=
github.com/alibabacloud-go/darabonba-openapi v0.1.18/go.mod h1:PB4HffMhJVmAgNKNq3wYbTUlFvPgxJpTzd1F5pTuUsc=

View File

@@ -11,11 +11,11 @@ import (
"strings"
"time"
"gitea.com/bitwsd/core/common/log"
"gitea.com/bitwsd/document_ai/config"
"gitea.com/bitwsd/document_ai/internal/model/formula"
"gitea.com/bitwsd/document_ai/internal/storage/cache"
"gitea.com/bitwsd/document_ai/internal/storage/dao"
"gitea.com/bitwsd/document_ai/pkg/log"
"gitea.com/bitwsd/document_ai/pkg/common"
"gitea.com/bitwsd/document_ai/pkg/constant"

View File

@@ -5,7 +5,7 @@ import (
"errors"
"strings"
"gitea.com/bitwsd/core/common/log"
"gitea.com/bitwsd/document_ai/pkg/log"
"gitea.com/bitwsd/document_ai/internal/model/task"
"gitea.com/bitwsd/document_ai/internal/storage/dao"
"gorm.io/gorm"

View File

@@ -6,7 +6,7 @@ import (
"fmt"
"math/rand"
"gitea.com/bitwsd/core/common/log"
"gitea.com/bitwsd/document_ai/pkg/log"
"gitea.com/bitwsd/document_ai/internal/storage/cache"
"gitea.com/bitwsd/document_ai/internal/storage/dao"
"gitea.com/bitwsd/document_ai/pkg/sms"

View File

@@ -10,9 +10,9 @@ import (
"syscall"
"time"
"gitea.com/bitwsd/core/common/cors"
"gitea.com/bitwsd/core/common/log"
"gitea.com/bitwsd/core/common/middleware"
"gitea.com/bitwsd/document_ai/pkg/cors"
"gitea.com/bitwsd/document_ai/pkg/log"
"gitea.com/bitwsd/document_ai/pkg/middleware"
"gitea.com/bitwsd/document_ai/api"
"gitea.com/bitwsd/document_ai/config"
"gitea.com/bitwsd/document_ai/internal/storage/cache"

61
pkg/cors/cors.go Normal file
View File

@@ -0,0 +1,61 @@
package cors
import (
"strconv"
"strings"
"github.com/gin-gonic/gin"
)
type Config struct {
AllowOrigins []string
AllowMethods []string
AllowHeaders []string
ExposeHeaders []string
AllowCredentials bool
MaxAge int
}
func DefaultConfig() Config {
return Config{
AllowOrigins: []string{"*"},
AllowMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
AllowHeaders: []string{"Origin", "Content-Type", "Accept"},
ExposeHeaders: []string{"Content-Length"},
AllowCredentials: true,
MaxAge: 86400, // 24 hours
}
}
func Cors(config Config) gin.HandlerFunc {
return func(c *gin.Context) {
origin := c.Request.Header.Get("Origin")
// 检查是否允许该来源
allowOrigin := "*"
for _, o := range config.AllowOrigins {
if o == origin {
allowOrigin = origin
break
}
}
c.Header("Access-Control-Allow-Origin", allowOrigin)
c.Header("Access-Control-Allow-Methods", strings.Join(config.AllowMethods, ","))
c.Header("Access-Control-Allow-Headers", strings.Join(config.AllowHeaders, ","))
c.Header("Access-Control-Expose-Headers", strings.Join(config.ExposeHeaders, ","))
c.Header("Access-Control-Max-Age", strconv.Itoa(config.MaxAge))
if config.AllowCredentials {
c.Header("Access-Control-Allow-Credentials", "true")
}
if c.Request.Method == "OPTIONS" {
c.AbortWithStatus(204)
return
}
c.Next()
}
}

View File

@@ -10,7 +10,7 @@ import (
"net/http"
"time"
"gitea.com/bitwsd/core/common/log"
"gitea.com/bitwsd/document_ai/pkg/log"
)
// RetryConfig 重试配置

30
pkg/log/log_config.go Normal file
View File

@@ -0,0 +1,30 @@
package log
var (
maxSize = 100 // MB
outputPath = "/app/logs/app.log"
)
type LogConfig struct {
AppName string `yaml:"appName"` // 应用名称
Level string `yaml:"level"` // debug, info, warn, error
Format string `yaml:"format"` // json, console
OutputPath string `yaml:"outputPath"` // 日志文件路径
MaxSize int `yaml:"maxSize"` // 单个日志文件最大尺寸单位MB
MaxAge int `yaml:"maxAge"` // 日志保留天数
MaxBackups int `yaml:"maxBackups"` // 保留的旧日志文件最大数量
Compress bool `yaml:"compress"` // 是否压缩旧日志
}
func DefaultLogConfig() *LogConfig {
return &LogConfig{
Level: "info",
Format: "json",
OutputPath: outputPath,
MaxSize: maxSize,
MaxAge: 7,
MaxBackups: 3,
Compress: true,
}
}

152
pkg/log/logger.go Normal file
View File

@@ -0,0 +1,152 @@
package log
import (
"context"
"fmt"
"os"
"path/filepath"
"runtime"
"time"
"github.com/rs/zerolog"
"gopkg.in/natefinch/lumberjack.v2"
)
type LogType string
const (
TypeAccess LogType = "access"
TypeBusiness LogType = "business"
TypeError LogType = "error"
)
var (
logger zerolog.Logger
)
// Setup 初始化日志配置
func Setup(conf LogConfig) error {
// 确保日志目录存在
if err := os.MkdirAll(filepath.Dir(conf.OutputPath), 0755); err != nil {
return fmt.Errorf("create log directory failed: %v", err)
}
// 配置日志轮转
writer := &lumberjack.Logger{
Filename: conf.OutputPath,
MaxSize: conf.MaxSize, // MB
MaxAge: conf.MaxAge, // days
MaxBackups: conf.MaxBackups,
Compress: conf.Compress,
}
// 设置日志级别
level, err := zerolog.ParseLevel(conf.Level)
if err != nil {
level = zerolog.InfoLevel
}
zerolog.SetGlobalLevel(level)
// 初始化logger 并添加 app_name
logger = zerolog.New(writer).With().
Timestamp().
Str("app_name", conf.AppName). // 添加 app_name
Logger()
return nil
}
// log 统一的日志记录函数
func log(ctx context.Context, level zerolog.Level, logType LogType, kv ...interface{}) {
if len(kv)%2 != 0 {
kv = append(kv, "MISSING")
}
event := logger.WithLevel(level)
// 添加日志类型
event.Str("type", string(logType))
// 添加请求ID
if reqID, exists := ctx.Value("request_id").(string); exists {
event.Str("request_id", reqID)
}
// 添加调用位置
if pc, file, line, ok := runtime.Caller(2); ok {
event.Str("caller", fmt.Sprintf("%s:%d %s", filepath.Base(file), line, runtime.FuncForPC(pc).Name()))
}
// 处理key-value对
for i := 0; i < len(kv); i += 2 {
key, ok := kv[i].(string)
if !ok {
continue
}
value := kv[i+1]
switch v := value.(type) {
case error:
event.AnErr(key, v)
case int:
event.Int(key, v)
case int64:
event.Int64(key, v)
case float64:
event.Float64(key, v)
case bool:
event.Bool(key, v)
case time.Duration:
event.Dur(key, v)
case time.Time:
event.Time(key, v)
case []byte:
event.Bytes(key, v)
case string:
event.Str(key, v)
default:
event.Interface(key, v)
}
}
event.Send()
}
// Debug 记录调试日志
func Debug(ctx context.Context, kv ...interface{}) {
log(ctx, zerolog.DebugLevel, TypeBusiness, kv...)
}
// Info 记录信息日志
func Info(ctx context.Context, kv ...interface{}) {
log(ctx, zerolog.InfoLevel, TypeBusiness, kv...)
}
// Warn 记录警告日志
func Warn(ctx context.Context, kv ...interface{}) {
log(ctx, zerolog.WarnLevel, TypeError, kv...)
}
// Error 记录错误日志
func Error(ctx context.Context, kv ...interface{}) {
log(ctx, zerolog.ErrorLevel, TypeError, kv...)
}
func Fatal(ctx context.Context, kv ...interface{}) {
// 获取错误堆栈
buf := make([]byte, 4096)
n := runtime.Stack(buf, false)
// 添加堆栈信息到kv
newKv := make([]interface{}, 0, len(kv)+2)
newKv = append(newKv, kv...)
newKv = append(newKv, "stack", string(buf[:n]))
log(ctx, zerolog.FatalLevel, TypeError, newKv...)
}
// Access 记录访问日志
func Access(ctx context.Context, kv ...interface{}) {
log(ctx, zerolog.InfoLevel, TypeAccess, kv...)
}

75
pkg/middleware/access.go Normal file
View File

@@ -0,0 +1,75 @@
package middleware
import (
"bytes"
"io"
"strings"
"time"
"gitea.com/bitwsd/document_ai/pkg/log"
"github.com/gin-gonic/gin"
)
const (
maxBodySize = 1024 * 500 // 500KB 限制
)
// 自定义 ResponseWriter 来捕获响应
type bodyWriter struct {
gin.ResponseWriter
body *bytes.Buffer
}
func (w *bodyWriter) Write(b []byte) (int, error) {
w.body.Write(b)
return w.ResponseWriter.Write(b)
}
func AccessLog() gin.HandlerFunc {
return func(c *gin.Context) {
start := time.Now()
path := c.Request.URL.Path
raw := c.Request.URL.RawQuery
// 处理请求体
var reqBody string
if c.Request.Body != nil && (c.Request.Method == "POST" || c.Request.Method == "PUT") {
// 读取并限制请求体大小
bodyBytes, _ := io.ReadAll(io.LimitReader(c.Request.Body, maxBodySize))
c.Request.Body = io.NopCloser(bytes.NewBuffer(bodyBytes)) // 重新设置 body
reqBody = string(bodyBytes)
}
// 设置自定义 ResponseWriter
var responseBody string
if !strings.Contains(c.GetHeader("Accept"), "text/event-stream") {
bw := &bodyWriter{body: &bytes.Buffer{}, ResponseWriter: c.Writer}
c.Writer = bw
}
c.Next()
// 获取响应体(非 SSE
if writer, ok := c.Writer.(*bodyWriter); ok {
responseBody = writer.body.String()
if len(responseBody) > maxBodySize {
responseBody = responseBody[:maxBodySize] + "... (truncated)"
}
}
// 记录访问日志
log.Access(c.Request.Context(),
"request_id", c.GetString("request_id"),
"method", c.Request.Method,
"path", path,
"query", raw,
"ip", c.ClientIP(),
"user_agent", c.Request.UserAgent(),
"status", c.Writer.Status(),
"duration", time.Since(start),
"request_body", reqBody,
"response_body", responseBody,
)
}
}

View File

@@ -0,0 +1,19 @@
package middleware
import (
"github.com/gin-gonic/gin"
"github.com/google/uuid"
)
func RequestID() gin.HandlerFunc {
return func(c *gin.Context) {
requestID := c.Request.Header.Get("X-Request-ID")
if requestID == "" {
requestID = uuid.New().String()
}
c.Request.Header.Set("X-Request-ID", requestID)
c.Set("request_id", requestID)
c.Next()
}
}

View File

@@ -12,8 +12,8 @@ import (
"strings"
"time"
"gitea.com/bitwsd/core/common/log"
"gitea.com/bitwsd/document_ai/config"
"gitea.com/bitwsd/document_ai/pkg/log"
"github.com/aliyun/aliyun-oss-go-sdk/oss"
)

View File

@@ -3,7 +3,7 @@ package utils
import (
"context"
"gitea.com/bitwsd/core/common/log"
"gitea.com/bitwsd/document_ai/pkg/log"
)
func SafeGo(fn func()) {