Add admin api for deleting invalid metrics (#32666)
This commit is contained in:
parent
5d1f1193bb
commit
5ab5cfc2ef
7 changed files with 589 additions and 12 deletions
|
|
@ -438,9 +438,11 @@ func (s *HTTPd) RegisterSystemHandler(site *core.Site, pub publisher, cache *uti
|
|||
api.Use(ensureDbAuth(auth))
|
||||
|
||||
routes := map[string]route{
|
||||
"backup": {"GET", "/backup", getBackup()},
|
||||
"restore": {"POST", "/restore", restoreDatabase(shutdown)},
|
||||
"reset": {"POST", "/reset", resetDatabase(shutdown)},
|
||||
"backup": {"GET", "/backup", getBackup()},
|
||||
"restore": {"POST", "/restore", restoreDatabase(shutdown)},
|
||||
"reset": {"POST", "/reset", resetDatabase(shutdown)},
|
||||
"deleteenergy": {"DELETE", "/metrics/energy", deleteEnergyHandler},
|
||||
"deletetariffs": {"DELETE", "/metrics/tariffs", deleteTariffsHandler},
|
||||
}
|
||||
|
||||
for _, r := range routes {
|
||||
|
|
|
|||
88
server/http_db_metrics_handler.go
Normal file
88
server/http_db_metrics_handler.go
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/evcc-io/evcc/core/metrics"
|
||||
"github.com/evcc-io/evcc/server/db"
|
||||
)
|
||||
|
||||
// deleteResult reports the number of affected rows
|
||||
type deleteResult struct {
|
||||
Deleted int64 `json:"deleted"`
|
||||
}
|
||||
|
||||
// timeRange parses the mandatory from/to query parameters
|
||||
func timeRange(r *http.Request) (time.Time, time.Time, error) {
|
||||
q := r.URL.Query()
|
||||
|
||||
from, err := time.Parse(time.RFC3339, q.Get("from"))
|
||||
if err != nil {
|
||||
return time.Time{}, time.Time{}, errors.New("invalid 'from' parameter")
|
||||
}
|
||||
|
||||
to, err := time.Parse(time.RFC3339, q.Get("to"))
|
||||
if err != nil {
|
||||
return time.Time{}, time.Time{}, errors.New("invalid 'to' parameter")
|
||||
}
|
||||
|
||||
return from, to, nil
|
||||
}
|
||||
|
||||
// deleteEnergyHandler removes energy metrics. Parameters match
|
||||
// /api/history/energy so the same request can be previewed before deleting.
|
||||
func deleteEnergyHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if db.Instance == nil {
|
||||
jsonError(w, http.StatusBadRequest, errors.New("database offline"))
|
||||
return
|
||||
}
|
||||
|
||||
from, to, err := timeRange(r)
|
||||
if err != nil {
|
||||
jsonError(w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
|
||||
q := r.URL.Query()
|
||||
filter := metrics.EnergyFilter{
|
||||
Group: q.Get("group"),
|
||||
Name: q.Get("name"),
|
||||
Title: q.Get("title"),
|
||||
}
|
||||
|
||||
rows, err := metrics.DeleteEnergy(from, to, filter)
|
||||
if err != nil {
|
||||
jsonError(w, http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
|
||||
jsonWrite(w, deleteResult{rows})
|
||||
}
|
||||
|
||||
// deleteTariffsHandler removes persisted tariff values
|
||||
func deleteTariffsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if db.Instance == nil {
|
||||
jsonError(w, http.StatusBadRequest, errors.New("database offline"))
|
||||
return
|
||||
}
|
||||
|
||||
from, to, err := timeRange(r)
|
||||
if err != nil {
|
||||
jsonError(w, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
|
||||
rows, err := metrics.DeleteTariffs(from, to, r.URL.Query().Get("usage"))
|
||||
if err != nil {
|
||||
status := http.StatusInternalServerError
|
||||
if errors.Is(err, metrics.ErrInvalidUsage) {
|
||||
status = http.StatusBadRequest
|
||||
}
|
||||
jsonError(w, status, err)
|
||||
return
|
||||
}
|
||||
|
||||
jsonWrite(w, deleteResult{rows})
|
||||
}
|
||||
181
server/mcp/openapi.json
generated
181
server/mcp/openapi.json
generated
|
|
@ -2244,6 +2244,171 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"/db/metrics/energy": {
|
||||
"delete": {
|
||||
"operationId": "deleteEnergyMetrics",
|
||||
"summary": "Delete energy metrics",
|
||||
"description": "Deletes recorded energy slots in the given time range, optionally narrowed by entity. Filter parameters match /history/energy, so the same request can be previewed there before deleting. Session users must supply the admin password in the X-Admin-Password header. API key holders via Bearer token are exempt.",
|
||||
"tags": [
|
||||
"db"
|
||||
],
|
||||
"security": [
|
||||
{
|
||||
"cookieAuth": []
|
||||
},
|
||||
{
|
||||
"bearerAuth": []
|
||||
}
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"$ref": "#/components/parameters/adminPassword"
|
||||
},
|
||||
{
|
||||
"name": "from",
|
||||
"in": "query",
|
||||
"required": true,
|
||||
"description": "Start time, inclusive (RFC3339)",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"example": "2026-07-01T00:00:00Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "to",
|
||||
"in": "query",
|
||||
"required": true,
|
||||
"description": "End time, exclusive (RFC3339)",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"example": "2026-07-02T00:00:00Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "group",
|
||||
"in": "query",
|
||||
"description": "Filter by entity group",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"example": "forecast",
|
||||
"enum": [
|
||||
"forecast",
|
||||
"battery",
|
||||
"grid",
|
||||
"pv",
|
||||
"home",
|
||||
"loadpoint",
|
||||
"meter",
|
||||
"consumer",
|
||||
"temperature"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "name",
|
||||
"in": "query",
|
||||
"description": "Filter by entity name",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"example": "db:8"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "title",
|
||||
"in": "query",
|
||||
"description": "Filter by entity title",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"example": "Battery"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"$ref": "#/components/responses/Deleted"
|
||||
},
|
||||
"400": {
|
||||
"description": "Invalid parameters or database offline"
|
||||
},
|
||||
"401": {
|
||||
"$ref": "#/components/responses/Unauthorized"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/db/metrics/tariffs": {
|
||||
"delete": {
|
||||
"operationId": "deleteTariffMetrics",
|
||||
"summary": "Delete tariff metrics",
|
||||
"description": "Deletes recorded tariff values in the given time range. Without usage the entire record is removed, otherwise only that usage is cleared and records left without any value are removed. Session users must supply the admin password in the X-Admin-Password header. API key holders via Bearer token are exempt.",
|
||||
"tags": [
|
||||
"db"
|
||||
],
|
||||
"security": [
|
||||
{
|
||||
"cookieAuth": []
|
||||
},
|
||||
{
|
||||
"bearerAuth": []
|
||||
}
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"$ref": "#/components/parameters/adminPassword"
|
||||
},
|
||||
{
|
||||
"name": "from",
|
||||
"in": "query",
|
||||
"required": true,
|
||||
"description": "Start time, inclusive (RFC3339)",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"example": "2026-07-01T00:00:00Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "to",
|
||||
"in": "query",
|
||||
"required": true,
|
||||
"description": "End time, exclusive (RFC3339)",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"example": "2026-07-02T00:00:00Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "usage",
|
||||
"in": "query",
|
||||
"description": "Limit deletion to a single tariff usage",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"example": "co2",
|
||||
"enum": [
|
||||
"grid",
|
||||
"feedin",
|
||||
"co2",
|
||||
"temperature"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"$ref": "#/components/responses/Deleted"
|
||||
},
|
||||
"400": {
|
||||
"description": "Invalid parameters or database offline"
|
||||
},
|
||||
"401": {
|
||||
"$ref": "#/components/responses/Unauthorized"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/history/energy": {
|
||||
"get": {
|
||||
"operationId": "getEnergyHistory",
|
||||
|
|
@ -5296,6 +5461,22 @@
|
|||
}
|
||||
},
|
||||
"responses": {
|
||||
"Deleted": {
|
||||
"description": "Success - number of deleted records",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"deleted": {
|
||||
"type": "integer",
|
||||
"example": 96
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"NanoSecondsResult": {
|
||||
"description": "Success - Number result - Unit is **nanoseconds**",
|
||||
"content": {
|
||||
|
|
|
|||
|
|
@ -1353,6 +1353,115 @@ paths:
|
|||
description: Reset successful, instance is restarting
|
||||
"401":
|
||||
$ref: "#/components/responses/Unauthorized"
|
||||
/db/metrics/energy:
|
||||
delete:
|
||||
operationId: deleteEnergyMetrics
|
||||
summary: Delete energy metrics
|
||||
description: "Deletes recorded energy slots in the given time range, optionally narrowed by entity. Filter parameters match /history/energy, so the same request can be previewed there before deleting. Session users must supply the admin password in the X-Admin-Password header. API key holders via Bearer token are exempt."
|
||||
tags:
|
||||
- db
|
||||
security:
|
||||
- cookieAuth: []
|
||||
- bearerAuth: []
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/adminPassword"
|
||||
- name: from
|
||||
in: query
|
||||
required: true
|
||||
description: Start time, inclusive (RFC3339)
|
||||
schema:
|
||||
type: string
|
||||
format: date-time
|
||||
example: "2026-07-01T00:00:00Z"
|
||||
- name: to
|
||||
in: query
|
||||
required: true
|
||||
description: End time, exclusive (RFC3339)
|
||||
schema:
|
||||
type: string
|
||||
format: date-time
|
||||
example: "2026-07-02T00:00:00Z"
|
||||
- name: group
|
||||
in: query
|
||||
description: Filter by entity group
|
||||
schema:
|
||||
type: string
|
||||
example: forecast
|
||||
enum:
|
||||
- forecast
|
||||
- battery
|
||||
- grid
|
||||
- pv
|
||||
- home
|
||||
- loadpoint
|
||||
- meter
|
||||
- consumer
|
||||
- temperature
|
||||
- name: name
|
||||
in: query
|
||||
description: Filter by entity name
|
||||
schema:
|
||||
type: string
|
||||
example: db:8
|
||||
- name: title
|
||||
in: query
|
||||
description: Filter by entity title
|
||||
schema:
|
||||
type: string
|
||||
example: Battery
|
||||
responses:
|
||||
"200":
|
||||
$ref: "#/components/responses/Deleted"
|
||||
"400":
|
||||
description: Invalid parameters or database offline
|
||||
"401":
|
||||
$ref: "#/components/responses/Unauthorized"
|
||||
/db/metrics/tariffs:
|
||||
delete:
|
||||
operationId: deleteTariffMetrics
|
||||
summary: Delete tariff metrics
|
||||
description: "Deletes recorded tariff values in the given time range. Without usage the entire record is removed, otherwise only that usage is cleared and records left without any value are removed. Session users must supply the admin password in the X-Admin-Password header. API key holders via Bearer token are exempt."
|
||||
tags:
|
||||
- db
|
||||
security:
|
||||
- cookieAuth: []
|
||||
- bearerAuth: []
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/adminPassword"
|
||||
- name: from
|
||||
in: query
|
||||
required: true
|
||||
description: Start time, inclusive (RFC3339)
|
||||
schema:
|
||||
type: string
|
||||
format: date-time
|
||||
example: "2026-07-01T00:00:00Z"
|
||||
- name: to
|
||||
in: query
|
||||
required: true
|
||||
description: End time, exclusive (RFC3339)
|
||||
schema:
|
||||
type: string
|
||||
format: date-time
|
||||
example: "2026-07-02T00:00:00Z"
|
||||
- name: usage
|
||||
in: query
|
||||
description: Limit deletion to a single tariff usage
|
||||
schema:
|
||||
type: string
|
||||
example: co2
|
||||
enum:
|
||||
- grid
|
||||
- feedin
|
||||
- co2
|
||||
- temperature
|
||||
responses:
|
||||
"200":
|
||||
$ref: "#/components/responses/Deleted"
|
||||
"400":
|
||||
description: Invalid parameters or database offline
|
||||
"401":
|
||||
$ref: "#/components/responses/Unauthorized"
|
||||
/history/energy:
|
||||
get:
|
||||
operationId: getEnergyHistory
|
||||
|
|
@ -1816,6 +1925,16 @@ components:
|
|||
- "1"
|
||||
- "0"
|
||||
responses:
|
||||
Deleted:
|
||||
description: Success - number of deleted records
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
deleted:
|
||||
type: integer
|
||||
example: 96
|
||||
NanoSecondsResult:
|
||||
description: Success - Number result - Unit is **nanoseconds**
|
||||
content:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue