feat(backend): unified response helpers, refresh token, security hardening
- new resp package (OK/Page/Fail/BadRequest/.../InternalErr): unified error
envelope {detail, code?}, sanitized 500s (full error logged server-side,
generic message to client - stops DB error leakage), pagination envelope
{items,total,page,page_size}; migrate 200+ inline c.JSON error sites
- refresh token: 7d rotation tokens (SHA-256 hashed at rest), public
POST /auth/refresh with single-use rotation, POST /auth/logout revokes
all user refresh tokens (endpoint previously missing - frontend 404)
- security: gh-ost command line password masked in task status payload;
Instance.MarshalJSON guarantees encrypted passwords never leave the API
(removes reliance on per-handler manual masking); production (DEBUG=false)
with default JWT secret now auto-generates and persists a random secret
- safe.Go recover wrapper for background goroutines (instance probe,
gh-ost async)
- delete dead service layer (zero callers)
- fix pre-existing go vet unkeyed bson.E literals
This commit is contained in:
1 parent
d5b8b8116c
commit
16f1f7d65f
26 files changed
+916
-963
No files matched your search
@@ -5,31 +5,34 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"sql-platform/internal/safe"
|
||||
)
|
||||
|
||||
// GhOstTask defines a gh-ost execution task
|
||||
type GhOstTask struct {
|
||||
Host string `json:"host"`
|
||||
Port int `json:"port,omitempty"`
|
||||
User string `json:"user"`
|
||||
Password string `json:"password"`
|
||||
Database string `json:"database"`
|
||||
Table string `json:"table"`
|
||||
Alter string `json:"alter"` // e.g. "ADD COLUMN status INT NOT NULL DEFAULT 0"
|
||||
ChunkSize int `json:"chunk_size,omitempty"`
|
||||
MaxLoad string `json:"max_load,omitempty"`
|
||||
CriticalLoad string `json:"critical_load,omitempty"`
|
||||
MaxLagMillis int `json:"max_lag_millis,omitempty"`
|
||||
AllowOnMaster bool `json:"allow_on_master"`
|
||||
DryRun bool `json:"dry_run"`
|
||||
Execute bool `json:"execute"`
|
||||
ExactRowCount bool `json:"exact_row_count"`
|
||||
PostponeCutOver bool `json:"postpone_cut_over"`
|
||||
AliyunRDS bool `json:"aliyun_rds,omitempty"` // --aliyun-rds
|
||||
AWSAurora bool `json:"aws_aurora,omitempty"` // --aws
|
||||
Host string `json:"host"`
|
||||
Port int `json:"port,omitempty"`
|
||||
User string `json:"user"`
|
||||
Password string `json:"password"`
|
||||
Database string `json:"database"`
|
||||
Table string `json:"table"`
|
||||
Alter string `json:"alter"` // e.g. "ADD COLUMN status INT NOT NULL DEFAULT 0"
|
||||
ChunkSize int `json:"chunk_size,omitempty"`
|
||||
MaxLoad string `json:"max_load,omitempty"`
|
||||
CriticalLoad string `json:"critical_load,omitempty"`
|
||||
MaxLagMillis int `json:"max_lag_millis,omitempty"`
|
||||
AllowOnMaster bool `json:"allow_on_master"`
|
||||
DryRun bool `json:"dry_run"`
|
||||
Execute bool `json:"execute"`
|
||||
ExactRowCount bool `json:"exact_row_count"`
|
||||
PostponeCutOver bool `json:"postpone_cut_over"`
|
||||
AliyunRDS bool `json:"aliyun_rds,omitempty"` // --aliyun-rds
|
||||
AWSAurora bool `json:"aws_aurora,omitempty"` // --aws
|
||||
}
|
||||
|
||||
// GhOstResult holds the execution result
|
||||
@@ -121,7 +124,7 @@ func RunGhOst(ctx context.Context, task GhOstTask, ghOstPath string) (*GhOstResu
|
||||
err := cmd.Run()
|
||||
|
||||
result := &GhOstResult{
|
||||
Command: "gh-ost " + strings.Join(args, " "),
|
||||
Command: "gh-ost " + maskSecrets(strings.Join(args, " ")),
|
||||
Duration: time.Since(start).String(),
|
||||
Output: stdout.String(),
|
||||
}
|
||||
@@ -145,7 +148,7 @@ func RunGhOstAsync(task GhOstTask, ghOstPath string, cb ProgressCallback) string
|
||||
Manager.tasks[taskID] = &GhOstResult{TaskID: taskID}
|
||||
Manager.mu.Unlock()
|
||||
|
||||
go func() {
|
||||
run := func() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Hour)
|
||||
defer cancel()
|
||||
|
||||
@@ -165,7 +168,7 @@ func RunGhOstAsync(task GhOstTask, ghOstPath string, cb ProgressCallback) string
|
||||
|
||||
result := &GhOstResult{
|
||||
TaskID: taskID,
|
||||
Command: "gh-ost " + strings.Join(args, " "),
|
||||
Command: "gh-ost " + maskSecrets(strings.Join(args, " ")),
|
||||
Duration: time.Since(start).String(),
|
||||
Output: stdout.String(),
|
||||
Success: err == nil,
|
||||
@@ -183,11 +186,17 @@ func RunGhOstAsync(task GhOstTask, ghOstPath string, cb ProgressCallback) string
|
||||
if cb != nil {
|
||||
cb(taskID, result.Output)
|
||||
}
|
||||
}()
|
||||
}
|
||||
safe.Go("ghost:"+taskID, run)
|
||||
|
||||
return taskID
|
||||
}
|
||||
|
||||
// maskSecrets 将命令行中的密码参数值替换为 ******,防止经任务状态接口回传前端
|
||||
func maskSecrets(cmdline string) string {
|
||||
return regexp.MustCompile(`(--password=)([^ ]+)`).ReplaceAllString(cmdline, `$1******`)
|
||||
}
|
||||
|
||||
// GetTaskStatus returns the status of an async task
|
||||
func (m *TaskManager) GetTaskStatus(taskID string) (string, *GhOstResult) {
|
||||
m.mu.RLock()
|
||||
|
||||
Reference in new issue
Block a user