92 lines
2.7 KiB
Go
92 lines
2.7 KiB
Go
package database
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
// Vacuum runs VACUUM via a tracked job and returns the job ID. The job
|
|
// result includes "size_before" and "size_after" bytes; "reclaimed_bytes"
|
|
// is the difference (>= 0).
|
|
func (s *Service) Vacuum(ctx context.Context) string {
|
|
return s.jobs.Start(ctx, "vacuum", func(jobCtx context.Context) (map[string]interface{}, error) {
|
|
return s.runVacuum(jobCtx)
|
|
})
|
|
}
|
|
|
|
func (s *Service) runVacuum(_ context.Context) (map[string]interface{}, error) {
|
|
if s.pool == nil {
|
|
return nil, fmt.Errorf("vacuum: no database pool")
|
|
}
|
|
before, _ := readDBSize(s.pool.Reader())
|
|
if _, err := s.pool.Writer().Exec("VACUUM"); err != nil {
|
|
return nil, fmt.Errorf("vacuum: %w", err)
|
|
}
|
|
after, _ := readDBSize(s.pool.Reader())
|
|
reclaimed := before - after
|
|
if reclaimed < 0 {
|
|
reclaimed = 0
|
|
}
|
|
return map[string]interface{}{
|
|
"size_before": before,
|
|
"size_after": after,
|
|
"reclaimed_bytes": reclaimed,
|
|
}, nil
|
|
}
|
|
|
|
// Optimize runs PRAGMA optimize via a tracked job. PRAGMA optimize is
|
|
// cheap and idempotent; we still track it so the UI can show progress
|
|
// consistently with VACUUM.
|
|
func (s *Service) Optimize(ctx context.Context) string {
|
|
return s.jobs.Start(ctx, "optimize", func(jobCtx context.Context) (map[string]interface{}, error) {
|
|
return s.runOptimize(jobCtx)
|
|
})
|
|
}
|
|
|
|
func (s *Service) runOptimize(_ context.Context) (map[string]interface{}, error) {
|
|
if s.pool == nil {
|
|
return nil, fmt.Errorf("optimize: no database pool")
|
|
}
|
|
if _, err := s.pool.Writer().Exec("PRAGMA optimize"); err != nil {
|
|
return nil, fmt.Errorf("pragma optimize: %w", err)
|
|
}
|
|
return map[string]interface{}{"status": "ok"}, nil
|
|
}
|
|
|
|
// HandleVacuum returns a gin handler for POST /api/v1/system/database/vacuum.
|
|
func HandleVacuum(s *Service) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
respondAccepted(c, s.Vacuum(ctxOrBackground(c)))
|
|
}
|
|
}
|
|
|
|
// HandleOptimize returns a gin handler for POST /api/v1/system/database/optimize.
|
|
func HandleOptimize(s *Service) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
respondAccepted(c, s.Optimize(ctxOrBackground(c)))
|
|
}
|
|
}
|
|
|
|
// resetRequest is the JSON body for POST /api/v1/system/database/reset.
|
|
type resetRequest struct {
|
|
Confirm string `json:"confirm"`
|
|
}
|
|
|
|
// HandleReset returns a gin handler for POST /api/v1/system/database/reset.
|
|
// Validates the body's confirm == "RESET" before spawning the reset job.
|
|
func HandleReset(s *Service) gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
var req resetRequest
|
|
_ = c.ShouldBindJSON(&req)
|
|
jobID, err := s.FactoryReset(ctxOrBackground(c), req.Confirm)
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
respondAccepted(c, jobID)
|
|
}
|
|
}
|