可靠性: 完善 R2 备份恢复与对象生命周期

This commit is contained in:
tamakyi
2026-11-05 08:41:55 +08:00
parent 3e7719f200
commit 937e688cfa
32 changed files with 4151 additions and 231 deletions
+32 -1
View File
@@ -277,7 +277,8 @@ func SendMessage(ctx context.Context, cache cache.Cache) {
}
// 上传图片(如有)
var imageURL string
var imageURL, imageKey string
imageAttached := false
if imageFile != nil {
uploadedURL, err := storage.UploadPictureToS3(imageFile, imageHeader)
if err != nil {
@@ -291,6 +292,31 @@ func SendMessage(ctx context.Context, cache cache.Cache) {
return
}
imageURL = uploadedURL
defer func() {
if imageAttached || imageKey != "" {
return
}
if err := db.R2DeleteTasks.EnqueueValues(stdctx.Background(), conf.Upload.ImageBucket, []string{imageURL}); err != nil {
logrus.WithError(err).Error("Failed to enqueue unresolved uploaded message image compensation")
}
}()
parsedKey, owned, err := storage.ObjectKeyFromCDNURL(imageURL, conf.Upload.ImageBucketCDNHost)
if err != nil || !owned || parsedKey == "" {
logrus.WithError(err).WithField("owned", owned).Error("Failed to resolve uploaded message image key")
ctx.SetInternalErrorFlash()
ctx.Redirect(fmt.Sprintf("/_/%s/chat/%s", domain, token))
return
}
imageKey = parsedKey
// 只有消息图片元数据成功落库才标记 attached;此前任意失败出口都补偿新对象。
defer func() {
if imageAttached {
return
}
if err := db.R2DeleteTasks.Enqueue(stdctx.Background(), conf.Upload.ImageBucket, []string{imageKey}); err != nil {
logrus.WithError(err).WithField("key", imageKey).Error("Failed to enqueue uploaded message image compensation")
}
}()
}
// 内容安全检测(仅文本)
@@ -341,11 +367,16 @@ func SendMessage(ctx context.Context, cache cache.Cache) {
if _, err := db.MessageImages.Create(ctx.Request().Context(), db.CreateMessageImageOptions{
MessageID: msg.ID,
URL: imageURL,
ObjectKey: imageKey,
FileName: imageHeader.Filename,
FileSize: imageHeader.Size,
}); err != nil {
logrus.WithError(err).Error("Failed to save message image")
ctx.SetInternalErrorFlash()
ctx.Redirect(fmt.Sprintf("/_/%s/chat/%s", domain, token))
return
}
imageAttached = true
}
// 达到上限时关闭对话(幂等,多实例同时执行也安全)