Files
team/server/public/model/post_deletion_report.go
zeekay 8feb24ea1f One name for this fork, and it is ours
The server module named github.com/hanzoai/team/server/v8 — a path that
resolves to the retired workspace's repository, not this one — while
server/public still published github.com/mattermost/mattermost/server/public.
Both are github.com/hanzoteam/server/... now, which is where this code
actually lives.

That second half is what made the binary unbootable. Upstream's public path
stayed claimed by upstream, mattermost-plugin-ai imported it, and the build
linked public/model twice; public/plugin's gob.Register then ran twice under
one name and panicked at init, before main. Reverting the rename traded the
brand for a working binary. Rebranding the plugin alongside it needs no such
trade: the agents fork is github.com/hanzoteam/agents/v2 against our public
module, so no module named mattermost/mattermost is in the graph at all.

Measured: `go list -deps ./cmd/mattermost` names exactly one public/model, and
it is ours.
2026-08-28 23:35:36 -07:00

246 lines
6.8 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package model
import (
"fmt"
"strings"
"time"
"github.com/hanzoteam/server/server/public/shared/i18n"
)
type DeletionStepStatus int
const (
StepSuccess DeletionStepStatus = iota
StepFailed
StepPartial
StepNotApplicable
)
func (s DeletionStepStatus) Icon() string {
switch s {
case StepSuccess:
return "✅"
case StepFailed:
return "❌"
case StepPartial:
return "⚠️"
case StepNotApplicable:
return "➖"
default:
return "❓"
}
}
func (s DeletionStepStatus) Label(T i18n.TranslateFunc) string {
switch s {
case StepSuccess:
return T("app.data_spillage.report.status.removed")
case StepFailed:
return T("app.data_spillage.report.status.failed")
case StepPartial:
return T("app.data_spillage.report.status.partial")
case StepNotApplicable:
return T("app.data_spillage.report.status.not_applicable")
default:
return T("app.data_spillage.report.status.unknown")
}
}
type DeletionSubStep struct {
Name string
Status DeletionStepStatus
Detail string
DetailParams map[string]any
Errors []string
}
type DeletionStepResult struct {
Name string
Status DeletionStepStatus
Detail string
DetailParams map[string]any
Errors []string
SubSteps []DeletionSubStep
}
type PostDeletionReport struct {
PostID string
Timestamp time.Time
Steps []DeletionStepResult
}
func (r *PostDeletionReport) AddStep(name string, status DeletionStepStatus, detail string, errs []string) {
r.Steps = append(r.Steps, DeletionStepResult{
Name: name,
Status: status,
Detail: detail,
Errors: errs,
})
}
func (r *PostDeletionReport) AddStepWithParams(name string, status DeletionStepStatus, detail string, detailParams map[string]any, errs []string) {
r.Steps = append(r.Steps, DeletionStepResult{
Name: name,
Status: status,
Detail: detail,
DetailParams: detailParams,
Errors: errs,
})
}
func (r *PostDeletionReport) translateDetail(T i18n.TranslateFunc, detail string, detailParams map[string]any) string {
if detail == "" {
return ""
}
if len(detailParams) > 0 {
if count, ok := detailParams["Count"]; ok {
return T(detail, count, detailParams)
}
return T(detail, detailParams)
}
return T(detail)
}
func (r *PostDeletionReport) Render(T i18n.TranslateFunc) string {
var b strings.Builder
successCount, failedCount, partialCount, notApplicableCount := r.CountStatuses()
totalSteps := len(r.Steps)
b.WriteString(fmt.Sprintf("### %s\n\n", T("app.data_spillage.report.title")))
b.WriteString(fmt.Sprintf("**%s** %s\n", T("app.data_spillage.report.generated"), r.Timestamp.Format("2006-01-02 at 15:04:05 UTC")))
b.WriteString(fmt.Sprintf("**%s** `%s`\n", T("app.data_spillage.report.post_id"), r.PostID))
b.WriteString(fmt.Sprintf("**%s** %d  |  ✅ %s: %d  |  ➖ %s: %d  |  ⚠️ %s: %d  |  ❌ %s: %d\n",
T("app.data_spillage.report.total_steps"), totalSteps,
T("app.data_spillage.report.status.removed"), successCount,
T("app.data_spillage.report.status.not_applicable"), notApplicableCount,
T("app.data_spillage.report.status.partial"), partialCount,
T("app.data_spillage.report.status.failed"), failedCount))
b.WriteString("\n---\n\n")
for i, step := range r.Steps {
stepNum := i + 1
r.renderStep(T, &b, stepNum, step)
}
b.WriteString("---\n\n")
r.renderSummaryTable(T, &b)
if failedCount > 0 || partialCount > 0 {
b.WriteString(fmt.Sprintf("\n> ⚠️ **%s**\n", T("app.data_spillage.report.incomplete_warning")))
}
return b.String()
}
func (r *PostDeletionReport) RenderSummary(T i18n.TranslateFunc) string {
var b strings.Builder
_, failedCount, partialCount, _ := r.CountStatuses()
r.renderSummaryTable(T, &b)
if failedCount > 0 || partialCount > 0 {
b.WriteString(fmt.Sprintf("\n> ⚠️ **%s**\n", T("app.data_spillage.report.incomplete_warning")))
}
return b.String()
}
func (r *PostDeletionReport) renderStep(T i18n.TranslateFunc, b *strings.Builder, num int, step DeletionStepResult) {
translatedName := T(step.Name)
translatedDetail := r.translateDetail(T, step.Detail, step.DetailParams)
if len(step.SubSteps) > 0 {
b.WriteString(fmt.Sprintf("##### %d. %s\n\n", num, translatedName))
successCount := 0
failedCount := 0
for _, sub := range step.SubSteps {
if sub.Status == StepSuccess {
successCount++
} else {
failedCount++
}
}
b.WriteString(fmt.Sprintf("**%s** %d  |  ✅ %s: %d  |  ❌ %s: %d\n\n",
T("app.data_spillage.report.revisions_found"), len(step.SubSteps),
T("app.data_spillage.report.cleared"), successCount,
T("app.data_spillage.report.status.failed"), failedCount))
for j, sub := range step.SubSteps {
subDetail := r.translateDetail(T, sub.Detail, sub.DetailParams)
b.WriteString(fmt.Sprintf("###### %s %s %d — `%s`\n", sub.Status.Icon(), T("app.data_spillage.report.revision"), j+1, sub.Name))
if subDetail != "" {
b.WriteString(fmt.Sprintf("- %s\n", subDetail))
}
r.renderErrors(T, b, sub.Errors)
b.WriteString("\n")
}
} else {
b.WriteString(fmt.Sprintf("##### %d. %s %s\n", num, step.Status.Icon(), translatedName))
if translatedDetail != "" {
b.WriteString(fmt.Sprintf("%s\n", translatedDetail))
}
r.renderErrors(T, b, step.Errors)
}
b.WriteString("\n")
}
func (r *PostDeletionReport) renderErrors(T i18n.TranslateFunc, b *strings.Builder, errs []string) {
if len(errs) == 0 {
return
}
b.WriteString(fmt.Sprintf("\n> **%s**\n> ```\n", T("app.data_spillage.report.error_log")))
for _, e := range errs {
for line := range strings.SplitSeq(e, "\n") {
b.WriteString(fmt.Sprintf("> %s\n", line))
}
}
b.WriteString("> ```\n")
}
func (r *PostDeletionReport) renderSummaryTable(T i18n.TranslateFunc, b *strings.Builder) {
b.WriteString(fmt.Sprintf("##### 📊 %s\n\n", T("app.data_spillage.report.summary")))
b.WriteString(fmt.Sprintf("| # | %s | %s | %s |\n",
T("app.data_spillage.report.column.step"),
T("app.data_spillage.report.column.status"),
T("app.data_spillage.report.column.detail")))
b.WriteString("|---|---|---|---|\n")
for i, step := range r.Steps {
translatedName := T(step.Name)
translatedDetail := r.translateDetail(T, step.Detail, step.DetailParams)
b.WriteString(fmt.Sprintf("| %d | %s | %s | %s |\n",
i+1, translatedName, step.Status.Icon(), translatedDetail))
}
}
func (r *PostDeletionReport) CountStatuses() (success, failed, partial, notApplicable int) {
for _, step := range r.Steps {
switch step.Status {
case StepSuccess:
success++
case StepFailed:
failed++
case StepPartial:
partial++
case StepNotApplicable:
notApplicable++
}
}
return
}
func CountSubStepSuccesses(subSteps []DeletionSubStep) int {
count := 0
for _, s := range subSteps {
if s.Status == StepSuccess {
count++
}
}
return count
}