11 Commits

Author SHA1 Message Date
lafay
3e8b7d150d chore: Refactor Dockerfile and build workflow for improved efficiency
All checks were successful
Build / build (push) Successful in 4m4s
Build / build-docker (push) Successful in 1m16s
- Removed the build stage from the Dockerfile, simplifying the image creation process.
- Updated the Dockerfile to directly copy the pre-built binary instead of using a multi-stage build.
- Modified the build workflow to eliminate unnecessary build arguments, streamlining the configuration.
2026-01-10 05:21:45 +08:00
lafay
fd5a0e8405 chore: Update Docker image tags in build workflow
Some checks failed
Build / build (push) Successful in 3m57s
Build / build-docker (push) Has been cancelled
- Changed Docker image tags in the build workflow to reflect the new repository owner.
- Updated the image references to use 'carrotskin' instead of the previous owner for consistency.
2026-01-10 05:12:51 +08:00
lafay
573c10ed1d chore: Remove Swagger documentation generation from build workflow
Some checks failed
Build / build (push) Successful in 4m0s
Build / build-docker (push) Failing after 41s
- Eliminated the Swagger documentation generation step from the build process.
- Updated the main server file to remove the Swagger documentation import, streamlining the codebase.
2026-01-10 05:00:09 +08:00
lafay
3b8d8bd7a7 chore: Add Swagger documentation generation to build workflow
Some checks failed
Build / build (push) Failing after 55s
Build / build-docker (push) Has been skipped
- Included a step to generate Swagger documentation during the build process.
- This addition enhances API documentation and ensures it is up-to-date with the codebase.
2026-01-10 04:57:56 +08:00
lafay
6338592d27 chore: Remove Go proxy setup from build workflow
Some checks failed
Build / build (push) Failing after 3m8s
Build / build-docker (push) Has been skipped
- Eliminated the Go proxy configuration from the build workflow to streamline the setup process.
- This change simplifies the environment setup for dependency management.
2026-01-10 04:47:14 +08:00
lafay
ef460ec891 chore: Disable caching in Go setup for build workflow
Some checks failed
Build / build (push) Failing after 1m31s
Build / build-docker (push) Has been skipped
- Updated the build workflow to disable caching for the Go setup action.
- This change aims to ensure a clean build environment for better consistency.
2026-01-10 04:42:22 +08:00
lafay
62d9432a2d chore: Add Go proxy setup to build workflow
Some checks failed
Build / build-docker (push) Has been cancelled
Build / build (push) Has been cancelled
- Configured Go proxy settings for improved dependency management.
- Disabled Go checksum database for local development environments.
2026-01-10 04:39:11 +08:00
lafay
e1d79ed445 chore: Update build workflow to include 'dev' branch
Some checks failed
Build / build (push) Failing after 21m16s
Build / build-docker (push) Has been skipped
- Added 'dev' branch to the push and pull_request triggers in the build workflow configuration.
- Ensured that CI/CD processes are aligned for both master and dev branches.
2026-01-10 04:01:20 +08:00
lafay
c5d7e317a4 refactor: Streamline user information retrieval and validation
- Refactored the user information retrieval process to improve efficiency.
- Enhanced validation logic for input parameters in the user handler.
- Updated UserService interface to support new retrieval methods.
- Improved error handling for user status checks before responding.
2026-01-10 03:58:22 +08:00
lafay
06539dc086 feat: Add public user information retrieval endpoint
- Introduced a new endpoint to fetch public user information without authentication.
- Implemented UserToPublicUserInfo function to format user data for the response.
- Updated UserService interface and user service implementation to support fetching users by username.
- Enhanced user handler to validate input parameters and check user status before responding.
2026-01-10 03:52:35 +08:00
lafay
22142db782 fix: Improve texture upload handling and caching logic
- Simplified caching logic by removing unnecessary nil check before setting cache.
- Enhanced error handling in texture upload process to return the original texture object if fetching the uploader information fails or returns nil.
2026-01-10 03:23:26 +08:00
9 changed files with 210 additions and 4 deletions

View 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
View 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"]

View File

@@ -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{

View File

@@ -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))
{ {

View File

@@ -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))
}

View File

@@ -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

View File

@@ -87,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
} }
@@ -382,7 +380,16 @@ func (s *textureService) UploadTexture(ctx context.Context, uploaderID int64, na
s.cacheInv.BatchInvalidate(ctx, fmt.Sprintf("texture:user:%d:*", uploaderID)) s.cacheInv.BatchInvalidate(ctx, fmt.Sprintf("texture:user:%d:*", uploaderID))
// 重新查询以预加载 Uploader 关联 // 重新查询以预加载 Uploader 关联
return s.textureRepo.FindByID(ctx, texture.ID) 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 解析材质类型

View File

@@ -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 {

View File

@@ -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