Data retention
This commit is contained in:
parent
2421536497
commit
74a4019dc5
|
|
@ -56,6 +56,7 @@ ENV AUTH_USERNAME admin
|
|||
ENV AUTH_PASSWORD password
|
||||
ENV AUTH_KEY password
|
||||
ENV AUTH_ENABLED true
|
||||
ENV DATA_RETENTION_IN_DAYS 0
|
||||
|
||||
EXPOSE ${PORT}
|
||||
VOLUME [ "/data" ]
|
||||
|
|
|
|||
|
|
@ -47,13 +47,19 @@ export const DashboardContextProvider = ({
|
|||
}) => {
|
||||
const viewport = useViewportSize()
|
||||
|
||||
const [dashboardId, setDashboardId] = useState(-1)
|
||||
const { getValues: getQuery, setValues: setQuery } = useQueryString()
|
||||
|
||||
const [dashboardId, setDashboardId] = useState(() => {
|
||||
const query = getQuery()
|
||||
const queryDashboardId = +(query['dashboard'] ?? '')
|
||||
|
||||
return !isNaN(queryDashboardId) ? queryDashboardId : -1
|
||||
})
|
||||
|
||||
const isDashboardSelected = !isNaN(dashboardId) && dashboardId >= 0
|
||||
|
||||
const dashboards = useQuery(['/dashboards'], getDashboards)
|
||||
|
||||
const { getValues: getQuery, setValues: setQuery } = useQueryString()
|
||||
|
||||
const dashboard = useQuery(
|
||||
['/dashboards', dashboardId],
|
||||
() => getDashboard(dashboardId),
|
||||
|
|
@ -105,9 +111,9 @@ export const DashboardContextProvider = ({
|
|||
|
||||
const [filter, setFilter] = useState<FilterValue>(() => {
|
||||
const query = getQuery()
|
||||
const queryFilter = query.get('interval')
|
||||
const queryFilterFrom = query.get('from')
|
||||
const queryFilterTo = query.get('to')
|
||||
const queryFilter = query['interval']
|
||||
const queryFilterFrom = query['from']
|
||||
const queryFilterTo = query['to']
|
||||
|
||||
const presetInterval =
|
||||
queryFilter &&
|
||||
|
|
@ -129,13 +135,15 @@ export const DashboardContextProvider = ({
|
|||
|
||||
useEffect(() => {
|
||||
setQuery({
|
||||
...getQuery(),
|
||||
dashboard: dashboardId.toString(),
|
||||
interval: filter.interval,
|
||||
...(filter.interval === 'custom' && {
|
||||
from: filter.customFrom.toISOString(),
|
||||
to: filter.customTo.toISOString(),
|
||||
}),
|
||||
})
|
||||
}, [filter])
|
||||
}, [filter, dashboardId])
|
||||
|
||||
const verticalMode = viewport.width < 800
|
||||
|
||||
|
|
|
|||
|
|
@ -21,8 +21,13 @@ export const useQueryString = () => {
|
|||
|
||||
const getValues = useCallback(() => {
|
||||
const [, searchParams] = valuesRef.current
|
||||
const result = {} as Record<string, string>
|
||||
|
||||
return searchParams
|
||||
searchParams.forEach((value, key) => {
|
||||
result[key] = value
|
||||
})
|
||||
|
||||
return result
|
||||
}, [])
|
||||
|
||||
const setValues = useCallback((values: Record<string, string>) => {
|
||||
|
|
|
|||
|
|
@ -6,3 +6,5 @@ AUTH_ENABLED=true
|
|||
AUTH_USERNAME=admin
|
||||
AUTH_PASSWORD=password
|
||||
AUTH_KEY=password
|
||||
# How long should the data be stored
|
||||
DATA_RETENTION_IN_DAYS=365
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package app
|
||||
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
|
@ -9,7 +10,19 @@ func (s *Server) StartCleaner() {
|
|||
|
||||
go func() {
|
||||
for {
|
||||
s.Services.Sessions.Cleanup()
|
||||
err := s.Services.Sessions.Cleanup()
|
||||
|
||||
if err != nil {
|
||||
log.Println("Error cleaning up sessions:", err)
|
||||
}
|
||||
|
||||
if s.Config.DataRetentionInDays > 0 {
|
||||
err := s.Services.SensorValues.Cleanup(s.Config.DataRetentionInDays)
|
||||
if err != nil {
|
||||
log.Println("Error cleaning up sensor values:", err)
|
||||
}
|
||||
}
|
||||
|
||||
<-ticker.C
|
||||
}
|
||||
}()
|
||||
|
|
|
|||
|
|
@ -1,37 +1,51 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Mode string
|
||||
DatabaseUrl string
|
||||
Port int
|
||||
Ip string
|
||||
AuthEnabled bool
|
||||
AuthUsername string
|
||||
AuthPassword string
|
||||
AuthKey string
|
||||
Mode string
|
||||
DatabaseUrl string
|
||||
Port int
|
||||
Ip string
|
||||
AuthEnabled bool
|
||||
AuthUsername string
|
||||
AuthPassword string
|
||||
AuthKey string
|
||||
DataRetentionInDays int64
|
||||
}
|
||||
|
||||
func LoadConfig() *Config {
|
||||
port, err := strconv.Atoi(os.Getenv("PORT"))
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
panic(fmt.Errorf("PORT must be an integer: %v", err))
|
||||
}
|
||||
|
||||
dataRetentionInDaysStr := os.Getenv("DATA_RETENTION_IN_DAYS")
|
||||
dataRetentionInDays := 0
|
||||
|
||||
if dataRetentionInDaysStr != "" {
|
||||
dataRetentionInDays, err = strconv.Atoi(dataRetentionInDaysStr)
|
||||
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("DATA_RETENTION_IN_DAYS must be an integer: %v", err))
|
||||
}
|
||||
}
|
||||
|
||||
config := Config{
|
||||
Mode: os.Getenv("GIN_MODE"),
|
||||
DatabaseUrl: os.Getenv("DATABASE_URL"),
|
||||
Port: port,
|
||||
Ip: os.Getenv("BIND_IP"),
|
||||
AuthEnabled: os.Getenv("AUTH_ENABLED") != "false",
|
||||
AuthUsername: os.Getenv("AUTH_USERNAME"),
|
||||
AuthPassword: os.Getenv("AUTH_PASSWORD"),
|
||||
AuthKey: os.Getenv("AUTH_KEY"),
|
||||
Mode: os.Getenv("GIN_MODE"),
|
||||
DatabaseUrl: os.Getenv("DATABASE_URL"),
|
||||
Port: port,
|
||||
Ip: os.Getenv("BIND_IP"),
|
||||
AuthEnabled: os.Getenv("AUTH_ENABLED") != "false",
|
||||
AuthUsername: os.Getenv("AUTH_USERNAME"),
|
||||
AuthPassword: os.Getenv("AUTH_PASSWORD"),
|
||||
AuthKey: os.Getenv("AUTH_KEY"),
|
||||
DataRetentionInDays: int64(dataRetentionInDays),
|
||||
}
|
||||
|
||||
// TODO: Crash when any auth* param is empty
|
||||
|
|
|
|||
|
|
@ -84,10 +84,9 @@ func (s *AlertsEvaluatorService) EvaluateAlert(alert *models.AlertItem) error {
|
|||
Value: condition["value"].(float64),
|
||||
}
|
||||
|
||||
value, err := s.ctx.Services.SensorValues.GetLatest(sensorValueCondition.SensorId, time.Now().Unix())
|
||||
lastValue = value.Value
|
||||
sensorId = int64(sensorValueCondition.SensorId)
|
||||
|
||||
value, err := s.ctx.Services.SensorValues.GetLatest(sensorValueCondition.SensorId, time.Now().Unix())
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
lastValue = float64(0)
|
||||
|
|
@ -96,6 +95,7 @@ func (s *AlertsEvaluatorService) EvaluateAlert(alert *models.AlertItem) error {
|
|||
return fmt.Errorf("error getting sensor value: %v", err)
|
||||
}
|
||||
} else {
|
||||
lastValue = value.Value
|
||||
conditionMet = evaluateSensorValueCondition(sensorValueCondition, value)
|
||||
}
|
||||
|
||||
|
|
@ -110,22 +110,23 @@ func (s *AlertsEvaluatorService) EvaluateAlert(alert *models.AlertItem) error {
|
|||
ValueUnit: condition["valueUnit"].(string),
|
||||
}
|
||||
|
||||
value, err := s.ctx.Services.SensorValues.GetLatest(sensorLastContactCondition.SensorId, time.Now().Unix())
|
||||
lastValue = float64(value.Timestamp)
|
||||
sensorId = sensorLastContactCondition.SensorId
|
||||
|
||||
value, err := s.ctx.Services.SensorValues.GetLatest(sensorLastContactCondition.SensorId, time.Now().Unix())
|
||||
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
lastValue = float64(0)
|
||||
} else {
|
||||
return fmt.Errorf("error getting sensor last contact value: %v", err)
|
||||
}
|
||||
} else {
|
||||
lastValue = float64(value.Timestamp)
|
||||
conditionInSec := int64(sensorLastContactCondition.Value * unitToSeconds(sensorLastContactCondition.ValueUnit))
|
||||
conditionMet = time.Now().Unix()-value.Timestamp > conditionInSec
|
||||
|
||||
}
|
||||
|
||||
conditionInSec := int64(sensorLastContactCondition.Value * unitToSeconds(sensorLastContactCondition.ValueUnit))
|
||||
|
||||
conditionMet = time.Now().Unix()-value.Timestamp > conditionInSec
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
package services
|
||||
|
|
@ -2,6 +2,7 @@ package services
|
|||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
|
@ -41,6 +42,19 @@ func (s *SensorValuesService) GetLatest(sensorId int64, to int64) (*sensorValue,
|
|||
return &value, nil
|
||||
}
|
||||
|
||||
func (s *SensorValuesService) Cleanup(retentionInDays int64) error {
|
||||
if retentionInDays <= 0 {
|
||||
return fmt.Errorf("retentionInDays must be greater than 0")
|
||||
}
|
||||
|
||||
_, err := s.ctx.DB.Exec("DELETE FROM sensor_values WHERE timestamp < ?", time.Now().Unix()-(retentionInDays*24*60*60))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SensorValuesService) getValueListQuery(sensorId int64, from int64, to int64, divide int64) (*sql.Rows, error) {
|
||||
if divide == 1 {
|
||||
return s.ctx.DB.Query("SELECT timestamp, value FROM sensor_values WHERE sensor_id = ? AND timestamp > ? AND timestamp < ? ORDER BY timestamp ASC", sensorId, from, to)
|
||||
|
|
|
|||
Loading…
Reference in New Issue