fix:修复由于数据库唯一键导致软删除apikey后key没有被释放后续无法再自定义相同的key
This commit is contained in:
@@ -3,6 +3,7 @@ package repository
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
dbent "github.com/Wei-Shaw/sub2api/ent"
|
dbent "github.com/Wei-Shaw/sub2api/ent"
|
||||||
@@ -257,9 +258,12 @@ func (r *apiKeyRepository) Update(ctx context.Context, key *service.APIKey) erro
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *apiKeyRepository) Delete(ctx context.Context, id int64) error {
|
func (r *apiKeyRepository) Delete(ctx context.Context, id int64) error {
|
||||||
|
// 存在唯一键约束 生成tombstone key 用来释放原key,长度远小于 128,满足 schema 限制
|
||||||
|
tombstoneKey := fmt.Sprintf("__deleted__%d__%d", id, time.Now().UnixNano())
|
||||||
// 显式软删除:避免依赖 Hook 行为,确保 deleted_at 一定被设置。
|
// 显式软删除:避免依赖 Hook 行为,确保 deleted_at 一定被设置。
|
||||||
affected, err := r.client.APIKey.Update().
|
affected, err := r.client.APIKey.Update().
|
||||||
Where(apikey.IDEQ(id), apikey.DeletedAtIsNil()).
|
Where(apikey.IDEQ(id), apikey.DeletedAtIsNil()).
|
||||||
|
SetKey(tombstoneKey).
|
||||||
SetDeletedAt(time.Now()).
|
SetDeletedAt(time.Now()).
|
||||||
Save(ctx)
|
Save(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -151,6 +151,31 @@ func (s *APIKeyRepoSuite) TestDelete() {
|
|||||||
s.Require().Error(err, "expected error after delete")
|
s.Require().Error(err, "expected error after delete")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *APIKeyRepoSuite) TestCreate_AfterSoftDelete_AllowsSameKey() {
|
||||||
|
user := s.mustCreateUser("recreate-after-soft-delete@test.com")
|
||||||
|
const reusedKey = "sk-reuse-after-soft-delete"
|
||||||
|
|
||||||
|
first := &service.APIKey{
|
||||||
|
UserID: user.ID,
|
||||||
|
Key: reusedKey,
|
||||||
|
Name: "First Key",
|
||||||
|
Status: service.StatusActive,
|
||||||
|
}
|
||||||
|
s.Require().NoError(s.repo.Create(s.ctx, first), "create first key")
|
||||||
|
|
||||||
|
s.Require().NoError(s.repo.Delete(s.ctx, first.ID), "soft delete first key")
|
||||||
|
|
||||||
|
second := &service.APIKey{
|
||||||
|
UserID: user.ID,
|
||||||
|
Key: reusedKey,
|
||||||
|
Name: "Second Key",
|
||||||
|
Status: service.StatusActive,
|
||||||
|
}
|
||||||
|
s.Require().NoError(s.repo.Create(s.ctx, second), "create second key with same key")
|
||||||
|
s.Require().NotZero(second.ID)
|
||||||
|
s.Require().NotEqual(first.ID, second.ID, "recreated key should be a new row")
|
||||||
|
}
|
||||||
|
|
||||||
// --- ListByUserID / CountByUserID ---
|
// --- ListByUserID / CountByUserID ---
|
||||||
|
|
||||||
func (s *APIKeyRepoSuite) TestListByUserID() {
|
func (s *APIKeyRepoSuite) TestListByUserID() {
|
||||||
|
|||||||
Reference in New Issue
Block a user