From e8c2cf1ea46d92ab12ec6d72374e137579bc908c Mon Sep 17 00:00:00 2001 From: ShukeBta Date: Sat, 30 May 2026 03:45:01 +0800 Subject: [PATCH] fix: require password only for adult visibility changes --- README.md | 24 ++++++++--------- README_EN.md | 22 +++++++-------- docker-compose.yml | 2 +- internal/handler/profile.go | 22 ++++++++++++++- internal/handler/profile_test.go | 46 ++++++++++++++++++++++++++++++++ web/src/pages/ProfilePage.tsx | 12 ++++++--- 6 files changed, 99 insertions(+), 29 deletions(-) create mode 100644 internal/handler/profile_test.go diff --git a/README.md b/README.md index b491026..2f8733d 100644 --- a/README.md +++ b/README.md @@ -238,7 +238,7 @@ mkdir -p data cache media downloads ```bash cat > .env <<'EOF' # 固定版本;需要升级时改成新的 MediaStationGo-vX.Y.Z 后执行 docker compose pull && docker compose up -d -MEDIASTATION_IMAGE_TAG=MediaStationGo-v0.0.28 +MEDIASTATION_IMAGE_TAG=MediaStationGo-v0.0.29 MEDIASTATION_HTTP_PORT=18080 # 程序数据和缓存建议放在 MediaStationGo 部署目录下,便于备份和迁移。 @@ -307,7 +307,7 @@ vim docker-compose.yml # # 镜像版本: # 默认拉取 latest;如需固定版本,创建 .env 并写入: -# MEDIASTATION_IMAGE_TAG=MediaStationGo-v0.0.28 +# MEDIASTATION_IMAGE_TAG=MediaStationGo-v0.0.29 # # 路径映射总览: # /data 程序数据目录。保存 SQLite 数据库、JWT secret、系统配置等,必须持久化。 @@ -516,7 +516,7 @@ docker compose up -d ```bash cat > .env <<'EOF' -MEDIASTATION_IMAGE_TAG=MediaStationGo-v0.0.28 +MEDIASTATION_IMAGE_TAG=MediaStationGo-v0.0.29 MEDIASTATION_HTTP_PORT=18080 MEDIASTATION_DATA_DIR=./data MEDIASTATION_CACHE_DIR=./cache @@ -779,26 +779,26 @@ cd MediaStationGo | 平台 | 包名示例 | | --- | --- | -| Linux x86_64 | `MediaStationGo-v0.0.28-linux-amd64.tar.gz` | -| Linux ARM64 | `MediaStationGo-v0.0.28-linux-arm64.tar.gz` | -| Windows x86_64 | `MediaStationGo-v0.0.28-windows-amd64.zip` | -| macOS Intel | `MediaStationGo-v0.0.28-darwin-amd64.tar.gz` | -| macOS Apple Silicon | `MediaStationGo-v0.0.28-darwin-arm64.tar.gz` | +| Linux x86_64 | `MediaStationGo-v0.0.29-linux-amd64.tar.gz` | +| Linux ARM64 | `MediaStationGo-v0.0.29-linux-arm64.tar.gz` | +| Windows x86_64 | `MediaStationGo-v0.0.29-windows-amd64.zip` | +| macOS Intel | `MediaStationGo-v0.0.29-darwin-amd64.tar.gz` | +| macOS Apple Silicon | `MediaStationGo-v0.0.29-darwin-arm64.tar.gz` | 部署步骤: ```bash # Linux 示例 -tar -xzf MediaStationGo-v0.0.28-linux-amd64.tar.gz -cd MediaStationGo-v0.0.28-linux-amd64 +tar -xzf MediaStationGo-v0.0.29-linux-amd64.tar.gz +cd MediaStationGo-v0.0.29-linux-amd64 MEDIASTATION_APP_PORT=18080 ./mediastation-go ``` Windows: ```powershell -Expand-Archive .\MediaStationGo-v0.0.28-windows-amd64.zip -cd .\MediaStationGo-v0.0.28-windows-amd64 +Expand-Archive .\MediaStationGo-v0.0.29-windows-amd64.zip +cd .\MediaStationGo-v0.0.29-windows-amd64 $env:MEDIASTATION_APP_PORT = "18080" .\mediastation-go.exe ``` diff --git a/README_EN.md b/README_EN.md index d341f34..229c577 100644 --- a/README_EN.md +++ b/README_EN.md @@ -235,7 +235,7 @@ mkdir -p data cache media downloads ```bash cat > .env <<'EOF' -MEDIASTATION_IMAGE_TAG=MediaStationGo-v0.0.28 +MEDIASTATION_IMAGE_TAG=MediaStationGo-v0.0.29 MEDIASTATION_HTTP_PORT=18080 MEDIASTATION_DATA_DIR=./data MEDIASTATION_CACHE_DIR=./cache @@ -344,7 +344,7 @@ For production, pin a specific release tag instead of using `latest`. Recommende ```bash cat > .env <<'EOF' -MEDIASTATION_IMAGE_TAG=MediaStationGo-v0.0.28 +MEDIASTATION_IMAGE_TAG=MediaStationGo-v0.0.29 MEDIASTATION_HTTP_PORT=18080 MEDIASTATION_DATA_DIR=./data MEDIASTATION_CACHE_DIR=./cache @@ -593,25 +593,25 @@ Each release provides multi-platform archives: | Platform | Package example | | --- | --- | -| Linux x86_64 | `MediaStationGo-v0.0.28-linux-amd64.tar.gz` | -| Linux ARM64 | `MediaStationGo-v0.0.28-linux-arm64.tar.gz` | -| Windows x86_64 | `MediaStationGo-v0.0.28-windows-amd64.zip` | -| macOS Intel | `MediaStationGo-v0.0.28-darwin-amd64.tar.gz` | -| macOS Apple Silicon | `MediaStationGo-v0.0.28-darwin-arm64.tar.gz` | +| Linux x86_64 | `MediaStationGo-v0.0.29-linux-amd64.tar.gz` | +| Linux ARM64 | `MediaStationGo-v0.0.29-linux-arm64.tar.gz` | +| Windows x86_64 | `MediaStationGo-v0.0.29-windows-amd64.zip` | +| macOS Intel | `MediaStationGo-v0.0.29-darwin-amd64.tar.gz` | +| macOS Apple Silicon | `MediaStationGo-v0.0.29-darwin-arm64.tar.gz` | Linux example: ```bash -tar -xzf MediaStationGo-v0.0.28-linux-amd64.tar.gz -cd MediaStationGo-v0.0.28-linux-amd64 +tar -xzf MediaStationGo-v0.0.29-linux-amd64.tar.gz +cd MediaStationGo-v0.0.29-linux-amd64 MEDIASTATION_APP_PORT=18080 ./mediastation-go ``` Windows example: ```powershell -Expand-Archive .\MediaStationGo-v0.0.28-windows-amd64.zip -cd .\MediaStationGo-v0.0.28-windows-amd64 +Expand-Archive .\MediaStationGo-v0.0.29-windows-amd64.zip +cd .\MediaStationGo-v0.0.29-windows-amd64 $env:MEDIASTATION_APP_PORT = "18080" .\mediastation-go.exe ``` diff --git a/docker-compose.yml b/docker-compose.yml index 845fecf..f3583fc 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -17,7 +17,7 @@ # # 镜像版本: # 默认拉取 latest;如需固定版本,创建 .env 并写入: -# MEDIASTATION_IMAGE_TAG=MediaStationGo-v0.0.28 +# MEDIASTATION_IMAGE_TAG=MediaStationGo-v0.0.29 # # 路径映射总览: # /data 程序数据目录。保存 SQLite 数据库、JWT secret、系统配置等,必须持久化。 diff --git a/internal/handler/profile.go b/internal/handler/profile.go index 7385d40..659e771 100644 --- a/internal/handler/profile.go +++ b/internal/handler/profile.go @@ -2,6 +2,7 @@ package handler import ( + "context" "errors" "net/http" @@ -20,7 +21,12 @@ func updateProfileHandler(svc *service.Container) gin.HandlerFunc { } uid, _ := c.Get(middleware.CtxUserID) userID := uid.(string) - if patch.HideAdult != nil { + hideAdultChanged, err := profileHideAdultChanged(c.Request.Context(), svc, userID, patch) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + if hideAdultChanged { if err := svc.Auth.VerifyPassword(c.Request.Context(), userID, patch.Password); err != nil { c.JSON(http.StatusUnauthorized, gin.H{"error": "需要输入当前账号密码确认"}) return @@ -39,6 +45,20 @@ func updateProfileHandler(svc *service.Container) gin.HandlerFunc { } } +func profileHideAdultChanged(ctx context.Context, svc *service.Container, userID string, patch service.ProfileUpdate) (bool, error) { + if patch.HideAdult == nil { + return false, nil + } + user, err := svc.Repo.User.FindByID(ctx, userID) + if err != nil { + return false, err + } + if user == nil { + return false, errors.New("user not found") + } + return user.HideAdult != *patch.HideAdult, nil +} + type adminUpdateRoleReq struct { Role string `json:"role" binding:"required"` } diff --git a/internal/handler/profile_test.go b/internal/handler/profile_test.go new file mode 100644 index 0000000..448527a --- /dev/null +++ b/internal/handler/profile_test.go @@ -0,0 +1,46 @@ +package handler + +import ( + "testing" + + "github.com/glebarez/sqlite" + "gorm.io/gorm" + + "github.com/ShukeBta/MediaStationGo/internal/model" + "github.com/ShukeBta/MediaStationGo/internal/repository" + "github.com/ShukeBta/MediaStationGo/internal/service" +) + +func TestProfileHideAdultRequiresPasswordOnlyWhenChanged(t *testing.T) { + db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{}) + if err != nil { + t.Fatal(err) + } + if err := db.AutoMigrate(&model.User{}); err != nil { + t.Fatal(err) + } + repos := repository.New(db) + user := &model.User{Username: "viewer", PasswordHash: "hash", Role: "user", HideAdult: true} + if err := repos.User.Create(t.Context(), user); err != nil { + t.Fatal(err) + } + svc := &service.Container{Repo: repos} + + same := true + changed, err := profileHideAdultChanged(t.Context(), svc, user.ID, service.ProfileUpdate{HideAdult: &same}) + if err != nil { + t.Fatalf("same value returned error: %v", err) + } + if changed { + t.Fatal("same hide_adult value should not require password") + } + + next := false + changed, err = profileHideAdultChanged(t.Context(), svc, user.ID, service.ProfileUpdate{HideAdult: &next}) + if err != nil { + t.Fatalf("changed value returned error: %v", err) + } + if !changed { + t.Fatal("changed hide_adult value should require password") + } +} diff --git a/web/src/pages/ProfilePage.tsx b/web/src/pages/ProfilePage.tsx index 71a1874..87f893e 100644 --- a/web/src/pages/ProfilePage.tsx +++ b/web/src/pages/ProfilePage.tsx @@ -23,7 +23,8 @@ export function ProfilePage() { e.preventDefault() try { let password: string | undefined - if (hideAdult !== Boolean(user?.hide_adult)) { + const hideAdultChanged = hideAdult !== Boolean(user?.hide_adult) + if (hideAdultChanged) { const input = await requestPassword({ title: hideAdult ? '隐藏成人目录' : '取消隐藏成人目录', message: '此设置会同步影响 Web 与 Emby/Jellyfin/Infuse 等第三方客户端,请输入当前账号密码确认。', @@ -32,14 +33,17 @@ export function ProfilePage() { if (!input) return password = input } - const u = await profileAPI.update({ + const patch: Parameters[0] = { username, nickname, email, avatar_url: avatar, - hide_adult: hideAdult, password, - }) + } + if (hideAdultChanged) { + patch.hide_adult = hideAdult + } + const u = await profileAPI.update(patch) setUser(u) toast.success('资料已更新') } catch (err: unknown) {