Compare commits
13 Commits
d952ddd4ea
...
dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3e8b7d150d | ||
|
|
fd5a0e8405 | ||
|
|
573c10ed1d | ||
|
|
3b8d8bd7a7 | ||
|
|
6338592d27 | ||
|
|
ef460ec891 | ||
|
|
62d9432a2d | ||
|
|
e1d79ed445 | ||
|
|
c5d7e317a4 | ||
|
|
06539dc086 | ||
|
|
22142db782 | ||
|
|
2c9c6ecfc0 | ||
|
|
c5db489d72 |
73
.gitea/workflows/build.yml
Normal file
73
.gitea/workflows/build.yml
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
name: Build
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
- dev
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
- dev
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Go
|
||||||
|
uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version: '1.25'
|
||||||
|
cache: false
|
||||||
|
|
||||||
|
- name: Download dependencies
|
||||||
|
run: go mod download
|
||||||
|
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
env:
|
||||||
|
GOOS: linux
|
||||||
|
GOARCH: amd64
|
||||||
|
CGO_ENABLED: 0
|
||||||
|
run: go build -v -o mcauth-linux-amd64 ./cmd/server
|
||||||
|
|
||||||
|
- name: Upload artifacts
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: mcauth-linux-amd64
|
||||||
|
path: mcauth-linux-amd64
|
||||||
|
|
||||||
|
build-docker:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: build
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
- name: Login to Gitea Container Registry
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: code.littlelan.cn
|
||||||
|
username: ${{ secrets.GIT_USERNAME }}
|
||||||
|
password: ${{ secrets.GIT_TOKEN }}
|
||||||
|
|
||||||
|
- name: Download artifact
|
||||||
|
uses: actions/download-artifact@v3
|
||||||
|
with:
|
||||||
|
name: mcauth-linux-amd64
|
||||||
|
|
||||||
|
- name: Build and push Docker image
|
||||||
|
uses: docker/build-push-action@v5
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
push: true
|
||||||
|
tags: |
|
||||||
|
code.littlelan.cn/carrotskin/mcauth:latest
|
||||||
|
code.littlelan.cn/carrotskin/mcauth:${{ github.sha }}
|
||||||
|
platforms: linux/amd64
|
||||||
35
Dockerfile
Normal file
35
Dockerfile
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
# 运行阶段
|
||||||
|
FROM alpine:latest
|
||||||
|
|
||||||
|
# 安装必要的运行时依赖
|
||||||
|
RUN apk add --no-cache ca-certificates tzdata wget
|
||||||
|
|
||||||
|
# 创建非 root 用户
|
||||||
|
RUN addgroup -g 1000 appuser && \
|
||||||
|
adduser -D -u 1000 -G appuser appuser
|
||||||
|
|
||||||
|
# 设置工作目录
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# 复制已经编译好的二进制文件
|
||||||
|
ARG BINARY_NAME=mcauth-linux-amd64
|
||||||
|
COPY ${BINARY_NAME} /app/server
|
||||||
|
|
||||||
|
# 复制配置文件(如果需要)
|
||||||
|
COPY configs/ /app/configs/
|
||||||
|
|
||||||
|
# 设置权限
|
||||||
|
RUN chown -R appuser:appuser /app
|
||||||
|
|
||||||
|
# 切换到非 root 用户
|
||||||
|
USER appuser
|
||||||
|
|
||||||
|
# 暴露端口
|
||||||
|
EXPOSE 8080
|
||||||
|
|
||||||
|
# 健康检查
|
||||||
|
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
||||||
|
CMD wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1
|
||||||
|
|
||||||
|
# 启动应用
|
||||||
|
ENTRYPOINT ["/app/server"]
|
||||||
@@ -32,7 +32,6 @@ import (
|
|||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
// _ "carrotskin/docs" // Swagger docs
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|||||||
@@ -62,6 +62,19 @@ func UserToUserInfo(user *model.User) *types.UserInfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UserToPublicUserInfo 将 User 模型转换为 PublicUserInfo 响应
|
||||||
|
func UserToPublicUserInfo(user *model.User) *types.PublicUserInfo {
|
||||||
|
return &types.PublicUserInfo{
|
||||||
|
ID: user.ID,
|
||||||
|
Username: user.Username,
|
||||||
|
Avatar: user.Avatar,
|
||||||
|
Points: user.Points,
|
||||||
|
Role: user.Role,
|
||||||
|
Status: user.Status,
|
||||||
|
CreatedAt: user.CreatedAt,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ProfileToProfileInfo 将 Profile 模型转换为 ProfileInfo 响应
|
// ProfileToProfileInfo 将 Profile 模型转换为 ProfileInfo 响应
|
||||||
func ProfileToProfileInfo(profile *model.Profile) *types.ProfileInfo {
|
func ProfileToProfileInfo(profile *model.Profile) *types.ProfileInfo {
|
||||||
return &types.ProfileInfo{
|
return &types.ProfileInfo{
|
||||||
@@ -87,22 +100,28 @@ func ProfilesToProfileInfos(profiles []*model.Profile) []*types.ProfileInfo {
|
|||||||
|
|
||||||
// TextureToTextureInfo 将 Texture 模型转换为 TextureInfo 响应
|
// TextureToTextureInfo 将 Texture 模型转换为 TextureInfo 响应
|
||||||
func TextureToTextureInfo(texture *model.Texture) *types.TextureInfo {
|
func TextureToTextureInfo(texture *model.Texture) *types.TextureInfo {
|
||||||
|
uploaderUsername := ""
|
||||||
|
if texture.Uploader != nil {
|
||||||
|
uploaderUsername = texture.Uploader.Username
|
||||||
|
}
|
||||||
|
|
||||||
return &types.TextureInfo{
|
return &types.TextureInfo{
|
||||||
ID: texture.ID,
|
ID: texture.ID,
|
||||||
UploaderID: texture.UploaderID,
|
UploaderID: texture.UploaderID,
|
||||||
Name: texture.Name,
|
UploaderUsername: uploaderUsername,
|
||||||
Description: texture.Description,
|
Name: texture.Name,
|
||||||
Type: types.TextureType(texture.Type),
|
Description: texture.Description,
|
||||||
URL: texture.URL,
|
Type: types.TextureType(texture.Type),
|
||||||
Hash: texture.Hash,
|
URL: texture.URL,
|
||||||
Size: texture.Size,
|
Hash: texture.Hash,
|
||||||
IsPublic: texture.IsPublic,
|
Size: texture.Size,
|
||||||
DownloadCount: texture.DownloadCount,
|
IsPublic: texture.IsPublic,
|
||||||
FavoriteCount: texture.FavoriteCount,
|
DownloadCount: texture.DownloadCount,
|
||||||
IsSlim: texture.IsSlim,
|
FavoriteCount: texture.FavoriteCount,
|
||||||
Status: texture.Status,
|
IsSlim: texture.IsSlim,
|
||||||
CreatedAt: texture.CreatedAt,
|
Status: texture.Status,
|
||||||
UpdatedAt: texture.UpdatedAt,
|
CreatedAt: texture.CreatedAt,
|
||||||
|
UpdatedAt: texture.UpdatedAt,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -93,6 +93,10 @@ func registerAuthRoutes(v1 *gin.RouterGroup, h *AuthHandler) {
|
|||||||
|
|
||||||
// registerUserRoutes 注册用户路由
|
// registerUserRoutes 注册用户路由
|
||||||
func registerUserRoutes(v1 *gin.RouterGroup, h *UserHandler, jwtService *auth.JWTService) {
|
func registerUserRoutes(v1 *gin.RouterGroup, h *UserHandler, jwtService *auth.JWTService) {
|
||||||
|
// 公开用户信息路由(无需认证)
|
||||||
|
v1.GET("/users/public", h.GetPublicInfo)
|
||||||
|
|
||||||
|
// 需要认证的用户路由
|
||||||
userGroup := v1.Group("/user")
|
userGroup := v1.Group("/user")
|
||||||
userGroup.Use(middleware.AuthMiddleware(jwtService))
|
userGroup.Use(middleware.AuthMiddleware(jwtService))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package handler
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"carrotskin/internal/container"
|
"carrotskin/internal/container"
|
||||||
|
"carrotskin/internal/model"
|
||||||
"carrotskin/internal/service"
|
"carrotskin/internal/service"
|
||||||
"carrotskin/internal/types"
|
"carrotskin/internal/types"
|
||||||
|
|
||||||
@@ -315,3 +316,55 @@ func (h *UserHandler) ResetYggdrasilPassword(c *gin.Context) {
|
|||||||
h.logger.Info("Yggdrasil密码重置成功", zap.Int64("userId", userID))
|
h.logger.Info("Yggdrasil密码重置成功", zap.Int64("userId", userID))
|
||||||
RespondSuccess(c, gin.H{"password": newPassword})
|
RespondSuccess(c, gin.H{"password": newPassword})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetPublicInfo 获取用户公开信息
|
||||||
|
// @Summary 获取用户公开信息
|
||||||
|
// @Description 根据用户名或用户ID获取用户的公开信息(不包含敏感信息如邮箱)
|
||||||
|
// @Tags user
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param username query string false "用户名"
|
||||||
|
// @Param id query int false "用户ID"
|
||||||
|
// @Success 200 {object} model.Response{data=types.PublicUserInfo} "获取成功"
|
||||||
|
// @Failure 400 {object} model.ErrorResponse "参数错误"
|
||||||
|
// @Failure 404 {object} model.ErrorResponse "用户不存在"
|
||||||
|
// @Router /api/v1/users/public [get]
|
||||||
|
func (h *UserHandler) GetPublicInfo(c *gin.Context) {
|
||||||
|
username := c.Query("username")
|
||||||
|
idStr := c.Query("id")
|
||||||
|
|
||||||
|
// 至少需要提供一个参数
|
||||||
|
if username == "" && idStr == "" {
|
||||||
|
RespondBadRequest(c, "必须提供用户名或用户ID", nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var user *model.User
|
||||||
|
var err error
|
||||||
|
|
||||||
|
// 优先使用用户名查询
|
||||||
|
if username != "" {
|
||||||
|
user, err = h.container.UserService.GetByUsername(c.Request.Context(), username)
|
||||||
|
} else {
|
||||||
|
// 使用用户ID查询
|
||||||
|
id := parseIntWithDefault(idStr, 0)
|
||||||
|
if id == 0 {
|
||||||
|
RespondBadRequest(c, "无效的用户ID", nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
user, err = h.container.UserService.GetByID(c.Request.Context(), int64(id))
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil || user == nil {
|
||||||
|
RespondNotFound(c, "用户不存在")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查用户状态
|
||||||
|
if user.Status != 1 {
|
||||||
|
RespondNotFound(c, "用户不可用")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
RespondSuccess(c, UserToPublicUserInfo(user))
|
||||||
|
}
|
||||||
|
|||||||
@@ -29,13 +29,13 @@ func (r *textureRepository) FindByID(ctx context.Context, id int64) (*model.Text
|
|||||||
|
|
||||||
func (r *textureRepository) FindByHash(ctx context.Context, hash string) (*model.Texture, error) {
|
func (r *textureRepository) FindByHash(ctx context.Context, hash string) (*model.Texture, error) {
|
||||||
var texture model.Texture
|
var texture model.Texture
|
||||||
err := r.db.WithContext(ctx).Where("hash = ?", hash).First(&texture).Error
|
err := r.db.WithContext(ctx).Preload("Uploader").Where("hash = ?", hash).First(&texture).Error
|
||||||
return handleNotFoundResult(&texture, err)
|
return handleNotFoundResult(&texture, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *textureRepository) FindByHashAndUploaderID(ctx context.Context, hash string, uploaderID int64) (*model.Texture, error) {
|
func (r *textureRepository) FindByHashAndUploaderID(ctx context.Context, hash string, uploaderID int64) (*model.Texture, error) {
|
||||||
var texture model.Texture
|
var texture model.Texture
|
||||||
err := r.db.WithContext(ctx).Where("hash = ? AND uploader_id = ?", hash, uploaderID).First(&texture).Error
|
err := r.db.WithContext(ctx).Preload("Uploader").Where("hash = ? AND uploader_id = ?", hash, uploaderID).First(&texture).Error
|
||||||
return handleNotFoundResult(&texture, err)
|
return handleNotFoundResult(&texture, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ type UserService interface {
|
|||||||
// 用户查询
|
// 用户查询
|
||||||
GetByID(ctx context.Context, id int64) (*model.User, error)
|
GetByID(ctx context.Context, id int64) (*model.User, error)
|
||||||
GetByEmail(ctx context.Context, email string) (*model.User, error)
|
GetByEmail(ctx context.Context, email string) (*model.User, error)
|
||||||
|
GetByUsername(ctx context.Context, username string) (*model.User, error)
|
||||||
|
|
||||||
// 用户更新
|
// 用户更新
|
||||||
UpdateInfo(ctx context.Context, user *model.User) error
|
UpdateInfo(ctx context.Context, user *model.User) error
|
||||||
|
|||||||
@@ -55,6 +55,22 @@ func (s *textureService) GetByID(ctx context.Context, id int64) (*model.Texture,
|
|||||||
if texture.Status == -1 {
|
if texture.Status == -1 {
|
||||||
return nil, errors.New("材质已删除")
|
return nil, errors.New("材质已删除")
|
||||||
}
|
}
|
||||||
|
// 如果缓存中没有 Uploader 信息,重新查询数据库
|
||||||
|
if texture.Uploader == nil {
|
||||||
|
texture2, err := s.textureRepo.FindByID(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if texture2 == nil {
|
||||||
|
return nil, ErrTextureNotFound
|
||||||
|
}
|
||||||
|
if texture2.Status == -1 {
|
||||||
|
return nil, errors.New("材质已删除")
|
||||||
|
}
|
||||||
|
// 更新缓存
|
||||||
|
s.cache.SetAsync(context.Background(), cacheKey, texture2, s.cache.Policy.TextureTTL)
|
||||||
|
return texture2, nil
|
||||||
|
}
|
||||||
return &texture, nil
|
return &texture, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,9 +87,7 @@ func (s *textureService) GetByID(ctx context.Context, id int64) (*model.Texture,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 存入缓存(异步)
|
// 存入缓存(异步)
|
||||||
if texture2 != nil {
|
s.cache.SetAsync(context.Background(), cacheKey, texture2, s.cache.Policy.TextureTTL)
|
||||||
s.cache.SetAsync(context.Background(), cacheKey, texture2, s.cache.Policy.TextureTTL)
|
|
||||||
}
|
|
||||||
|
|
||||||
return texture2, nil
|
return texture2, nil
|
||||||
}
|
}
|
||||||
@@ -365,7 +379,17 @@ func (s *textureService) UploadTexture(ctx context.Context, uploaderID int64, na
|
|||||||
// 清除用户的 texture 列表缓存(所有分页)
|
// 清除用户的 texture 列表缓存(所有分页)
|
||||||
s.cacheInv.BatchInvalidate(ctx, fmt.Sprintf("texture:user:%d:*", uploaderID))
|
s.cacheInv.BatchInvalidate(ctx, fmt.Sprintf("texture:user:%d:*", uploaderID))
|
||||||
|
|
||||||
return texture, nil
|
// 重新查询以预加载 Uploader 关联
|
||||||
|
textureWithUploader, err := s.textureRepo.FindByID(ctx, texture.ID)
|
||||||
|
if err != nil {
|
||||||
|
// 如果查询失败,返回原始创建的 texture 对象(虽然可能没有 Uploader 信息)
|
||||||
|
return texture, nil
|
||||||
|
}
|
||||||
|
if textureWithUploader == nil {
|
||||||
|
// 如果查询返回 nil(极端情况,如数据库复制延迟),返回原始创建的 texture 对象
|
||||||
|
return texture, nil
|
||||||
|
}
|
||||||
|
return textureWithUploader, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseTextureTypeInternal 解析材质类型
|
// parseTextureTypeInternal 解析材质类型
|
||||||
|
|||||||
@@ -199,6 +199,14 @@ func (s *userService) GetByEmail(ctx context.Context, email string) (*model.User
|
|||||||
}, s.cache.Policy.UserEmailTTL)
|
}, s.cache.Policy.UserEmailTTL)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *userService) GetByUsername(ctx context.Context, username string) (*model.User, error) {
|
||||||
|
// 使用 Cached 装饰器自动处理缓存
|
||||||
|
cacheKey := s.cacheKeys.UserByUsername(username)
|
||||||
|
return database.Cached(ctx, s.cache, cacheKey, func() (*model.User, error) {
|
||||||
|
return s.userRepo.FindByUsername(ctx, username)
|
||||||
|
}, s.cache.Policy.UserTTL)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *userService) UpdateInfo(ctx context.Context, user *model.User) error {
|
func (s *userService) UpdateInfo(ctx context.Context, user *model.User) error {
|
||||||
err := s.userRepo.Update(ctx, user)
|
err := s.userRepo.Update(ctx, user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -110,6 +110,18 @@ type UserInfo struct {
|
|||||||
UpdatedAt time.Time `json:"updated_at" example:"2025-10-01T10:00:00Z"`
|
UpdatedAt time.Time `json:"updated_at" example:"2025-10-01T10:00:00Z"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PublicUserInfo 用户公开信息
|
||||||
|
// @Description 用户公开信息(不包含敏感信息如邮箱)
|
||||||
|
type PublicUserInfo struct {
|
||||||
|
ID int64 `json:"id" example:"1"`
|
||||||
|
Username string `json:"username" example:"testuser"`
|
||||||
|
Avatar string `json:"avatar" example:"https://example.com/avatar.png"`
|
||||||
|
Points int `json:"points" example:"100"`
|
||||||
|
Role string `json:"role" example:"user"`
|
||||||
|
Status int16 `json:"status" example:"1"`
|
||||||
|
CreatedAt time.Time `json:"created_at" example:"2025-10-01T10:00:00Z"`
|
||||||
|
}
|
||||||
|
|
||||||
// TextureType 材质类型
|
// TextureType 材质类型
|
||||||
type TextureType string
|
type TextureType string
|
||||||
|
|
||||||
@@ -121,21 +133,22 @@ const (
|
|||||||
// TextureInfo 材质信息
|
// TextureInfo 材质信息
|
||||||
// @Description 材质详细信息
|
// @Description 材质详细信息
|
||||||
type TextureInfo struct {
|
type TextureInfo struct {
|
||||||
ID int64 `json:"id" example:"1"`
|
ID int64 `json:"id" example:"1"`
|
||||||
UploaderID int64 `json:"uploader_id" example:"1"`
|
UploaderID int64 `json:"uploader_id" example:"1"`
|
||||||
Name string `json:"name" example:"My Skin"`
|
UploaderUsername string `json:"uploader_username" example:"testuser"`
|
||||||
Description string `json:"description,omitempty" example:"A cool skin"`
|
Name string `json:"name" example:"My Skin"`
|
||||||
Type TextureType `json:"type" example:"SKIN"`
|
Description string `json:"description,omitempty" example:"A cool skin"`
|
||||||
URL string `json:"url" example:"https://rustfs.example.com/textures/xxx.png"`
|
Type TextureType `json:"type" example:"SKIN"`
|
||||||
Hash string `json:"hash" example:"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"`
|
URL string `json:"url" example:"https://rustfs.example.com/textures/xxx.png"`
|
||||||
Size int `json:"size" example:"2048"`
|
Hash string `json:"hash" example:"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"`
|
||||||
IsPublic bool `json:"is_public" example:"true"`
|
Size int `json:"size" example:"2048"`
|
||||||
DownloadCount int `json:"download_count" example:"100"`
|
IsPublic bool `json:"is_public" example:"true"`
|
||||||
FavoriteCount int `json:"favorite_count" example:"50"`
|
DownloadCount int `json:"download_count" example:"100"`
|
||||||
IsSlim bool `json:"is_slim" example:"false"`
|
FavoriteCount int `json:"favorite_count" example:"50"`
|
||||||
Status int16 `json:"status" example:"1"`
|
IsSlim bool `json:"is_slim" example:"false"`
|
||||||
CreatedAt time.Time `json:"created_at" example:"2025-10-01T10:00:00Z"`
|
Status int16 `json:"status" example:"1"`
|
||||||
UpdatedAt time.Time `json:"updated_at" example:"2025-10-01T10:00:00Z"`
|
CreatedAt time.Time `json:"created_at" example:"2025-10-01T10:00:00Z"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at" example:"2025-10-01T10:00:00Z"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ProfileInfo 角色信息
|
// ProfileInfo 角色信息
|
||||||
|
|||||||
@@ -140,8 +140,7 @@ func Load() (*Config, error) {
|
|||||||
// 设置默认值
|
// 设置默认值
|
||||||
setDefaults()
|
setDefaults()
|
||||||
|
|
||||||
// 设置环境变量前缀
|
// 自动读取环境变量(不设置前缀,因为 BindEnv 已经明确指定了变量名)
|
||||||
viper.SetEnvPrefix("CARROTSKIN")
|
|
||||||
viper.AutomaticEnv()
|
viper.AutomaticEnv()
|
||||||
|
|
||||||
// 手动设置环境变量映射
|
// 手动设置环境变量映射
|
||||||
@@ -320,6 +319,7 @@ func setupEnvMappings() {
|
|||||||
|
|
||||||
// overrideFromEnv 从环境变量中覆盖配置
|
// overrideFromEnv 从环境变量中覆盖配置
|
||||||
func overrideFromEnv(config *Config) {
|
func overrideFromEnv(config *Config) {
|
||||||
|
|
||||||
// 处理RustFS存储桶配置
|
// 处理RustFS存储桶配置
|
||||||
if texturesBucket := os.Getenv("RUSTFS_BUCKET_TEXTURES"); texturesBucket != "" {
|
if texturesBucket := os.Getenv("RUSTFS_BUCKET_TEXTURES"); texturesBucket != "" {
|
||||||
if config.RustFS.Buckets == nil {
|
if config.RustFS.Buckets == nil {
|
||||||
@@ -360,6 +360,24 @@ func overrideFromEnv(config *Config) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 处理Redis基本配置
|
||||||
|
if host := os.Getenv("REDIS_HOST"); host != "" {
|
||||||
|
config.Redis.Host = host
|
||||||
|
}
|
||||||
|
if port := os.Getenv("REDIS_PORT"); port != "" {
|
||||||
|
if val, err := strconv.Atoi(port); err == nil {
|
||||||
|
config.Redis.Port = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if password := os.Getenv("REDIS_PASSWORD"); password != "" {
|
||||||
|
config.Redis.Password = password
|
||||||
|
}
|
||||||
|
if database := os.Getenv("REDIS_DATABASE"); database != "" {
|
||||||
|
if val, err := strconv.Atoi(database); err == nil {
|
||||||
|
config.Redis.Database = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 处理Redis连接池配置
|
// 处理Redis连接池配置
|
||||||
if poolSize := os.Getenv("REDIS_POOL_SIZE"); poolSize != "" {
|
if poolSize := os.Getenv("REDIS_POOL_SIZE"); poolSize != "" {
|
||||||
if val, err := strconv.Atoi(poolSize); err == nil {
|
if val, err := strconv.Atoi(poolSize); err == nil {
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
package storage
|
package storage
|
||||||
|
|
||||||
import (
|
import (
|
||||||
// "context"
|
|
||||||
"testing"
|
"testing"
|
||||||
// "time"
|
|
||||||
|
|
||||||
"carrotskin/pkg/config"
|
"carrotskin/pkg/config"
|
||||||
|
|
||||||
@@ -41,4 +39,3 @@ func TestNewStorage_SkipConnectWhenNoCreds(t *testing.T) {
|
|||||||
t.Fatalf("NewStorage should not error when creds empty: %v", err)
|
t.Fatalf("NewStorage should not error when creds empty: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user