fix(db): convert expires_at to RFC3339 string format and simplify cleanup

This commit is contained in:
db123-test
2026-05-05 18:25:40 +03:30
parent f04f6192ff
commit 3202906153

View File

@@ -155,7 +155,7 @@ func IsPermanentWhitelisted(db *sql.DB, ip string) (bool, error) {
// AddTempEntry inserts or updates a temporary whitelist entry (upsert). // AddTempEntry inserts or updates a temporary whitelist entry (upsert).
func AddTempEntry(db *sql.DB, ip string, ttlSeconds int, reason, apiClientIP string) error { func AddTempEntry(db *sql.DB, ip string, ttlSeconds int, reason, apiClientIP string) error {
expiresAt := time.Now().Add(time.Duration(ttlSeconds) * time.Second) expiresAt := time.Now().Add(time.Duration(ttlSeconds) * time.Second).Format(time.RFC3339)
_, err := db.Exec(` _, err := db.Exec(`
INSERT INTO temp_whitelist(ip, expires_at, reason) INSERT INTO temp_whitelist(ip, expires_at, reason)
VALUES(?, ?, ?) VALUES(?, ?, ?)
@@ -173,10 +173,14 @@ func AddTempEntry(db *sql.DB, ip string, ttlSeconds int, reason, apiClientIP str
// DeleteTempEntry removes a temporary whitelist entry. // DeleteTempEntry removes a temporary whitelist entry.
func DeleteTempEntry(db *sql.DB, ip, apiClientIP string) error { func DeleteTempEntry(db *sql.DB, ip, apiClientIP string) error {
_, err := db.Exec(`DELETE FROM temp_whitelist WHERE ip = ?`, ip) result, err := db.Exec(`DELETE FROM temp_whitelist WHERE ip = ?`, ip)
if err != nil { if err != nil {
return err return err
} }
if rows, _ := result.RowsAffected(); rows == 0 {
slog.Warn("tried to delete non-existent temp ip", "ip", ip)
return nil
}
_, err = db.Exec(`INSERT INTO audit_log(action, ip, api_client_ip) VALUES('delete_temp', ?, ?)`, ip, apiClientIP) _, err = db.Exec(`INSERT INTO audit_log(action, ip, api_client_ip) VALUES('delete_temp', ?, ?)`, ip, apiClientIP)
if err != nil { if err != nil {
return err return err
@@ -186,7 +190,7 @@ func DeleteTempEntry(db *sql.DB, ip, apiClientIP string) error {
// IsTempWhitelisted checks whether the given IP has a valid temporary whitelist entry. // IsTempWhitelisted checks whether the given IP has a valid temporary whitelist entry.
func IsTempWhitelisted(db *sql.DB, ip string) (bool, error) { func IsTempWhitelisted(db *sql.DB, ip string) (bool, error) {
var expiresAt time.Time var expiresAt string
err := db.QueryRow(`SELECT expires_at FROM temp_whitelist WHERE ip = ?`, ip).Scan(&expiresAt) err := db.QueryRow(`SELECT expires_at FROM temp_whitelist WHERE ip = ?`, ip).Scan(&expiresAt)
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
return false, nil return false, nil
@@ -194,28 +198,35 @@ func IsTempWhitelisted(db *sql.DB, ip string) (bool, error) {
if err != nil { if err != nil {
return false, err return false, err
} }
return expiresAt.After(time.Now()), nil expires, parseErr := time.Parse(time.RFC3339, expiresAt)
if parseErr != nil {
slog.Error("failed to parse expires_at", "ip", ip, "error", parseErr)
return false, nil
}
return expires.After(time.Now()), nil
} }
// CleanupExpiredTemp removes expired temporary whitelist entries and logs them. // CleanupExpiredTemp removes expired temporary whitelist entries and logs them.
func CleanupExpiredTemp(db *sql.DB) error { func CleanupExpiredTemp(db *sql.DB) error {
_, err := db.Exec(`DELETE FROM temp_whitelist WHERE expires_at <= ? RETURNING ip`, time.Now()) rows, err := db.Query(`SELECT ip FROM temp_whitelist WHERE expires_at <= ?`, time.Now().Format(time.RFC3339))
if err != nil { if err != nil {
// SQLite may not support RETURNING; fall back to the two-step approach return err
var expiredIPs []string
rows, qErr := db.Query(`SELECT ip FROM temp_whitelist WHERE expires_at <= ?`, time.Now())
if qErr != nil {
return qErr
} }
var expiredIPs []string
for rows.Next() { for rows.Next() {
var ip string var ip string
if err := rows.Scan(&ip); err == nil { if err := rows.Scan(&ip); err == nil {
expiredIPs = append(expiredIPs, ip) expiredIPs = append(expiredIPs, ip)
} }
} }
if err := rows.Err(); err != nil {
rows.Close()
return err
}
rows.Close() rows.Close()
_, err = db.Exec(`DELETE FROM temp_whitelist WHERE expires_at <= ?`, time.Now()) _, err = db.Exec(`DELETE FROM temp_whitelist WHERE expires_at <= ?`, time.Now().Format(time.RFC3339))
if err != nil { if err != nil {
return err return err
} }
@@ -230,35 +241,6 @@ func CleanupExpiredTemp(db *sql.DB) error {
return nil return nil
} }
// The RETURNING path
return nil
}
// ListTempEntries returns all valid (non-expired) temporary whitelist entries.
func ListTempEntries(db *sql.DB) ([]map[string]interface{}, error) {
rows, err := db.Query(`
SELECT ip, expires_at, reason
FROM temp_whitelist
WHERE expires_at > ?
ORDER BY expires_at
`, time.Now())
if err != nil {
return nil, err
}
defer rows.Close()
return scanTempEntries(rows)
}
// ListPermEntries returns all permanent whitelist entries.
func ListPermEntries(db *sql.DB) ([]map[string]interface{}, error) {
rows, err := db.Query(`SELECT ip FROM perm_whitelist ORDER BY created_at`)
if err != nil {
return nil, err
}
defer rows.Close()
return scanPermEntries(rows)
}
// ListTempEntriesPaginated returns a page of valid temporary whitelist entries. // ListTempEntriesPaginated returns a page of valid temporary whitelist entries.
func ListTempEntriesPaginated(db *sql.DB, limit, offset int) ([]map[string]interface{}, error) { func ListTempEntriesPaginated(db *sql.DB, limit, offset int) ([]map[string]interface{}, error) {
rows, err := db.Query(` rows, err := db.Query(`
@@ -267,7 +249,7 @@ func ListTempEntriesPaginated(db *sql.DB, limit, offset int) ([]map[string]inter
WHERE expires_at > ? WHERE expires_at > ?
ORDER BY expires_at ORDER BY expires_at
LIMIT ? OFFSET ? LIMIT ? OFFSET ?
`, time.Now(), limit, offset) `, time.Now().Format(time.RFC3339), limit, offset)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -286,10 +268,10 @@ func ListPermEntriesPaginated(db *sql.DB, limit, offset int) ([]map[string]inter
} }
func scanTempEntries(rows *sql.Rows) ([]map[string]interface{}, error) { func scanTempEntries(rows *sql.Rows) ([]map[string]interface{}, error) {
var list []map[string]interface{} list := make([]map[string]interface{}, 0)
for rows.Next() { for rows.Next() {
var ip string var ip string
var expiresAt time.Time var expiresAt string
var reason sql.NullString var reason sql.NullString
if err := rows.Scan(&ip, &expiresAt, &reason); err != nil { if err := rows.Scan(&ip, &expiresAt, &reason); err != nil {
continue continue
@@ -300,11 +282,14 @@ func scanTempEntries(rows *sql.Rows) ([]map[string]interface{}, error) {
"reason": reason.String, "reason": reason.String,
}) })
} }
if err := rows.Err(); err != nil {
return nil, err
}
return list, nil return list, nil
} }
func scanPermEntries(rows *sql.Rows) ([]map[string]interface{}, error) { func scanPermEntries(rows *sql.Rows) ([]map[string]interface{}, error) {
var list []map[string]interface{} list := make([]map[string]interface{}, 0)
for rows.Next() { for rows.Next() {
var ip string var ip string
if err := rows.Scan(&ip); err != nil { if err := rows.Scan(&ip); err != nil {
@@ -312,6 +297,9 @@ func scanPermEntries(rows *sql.Rows) ([]map[string]interface{}, error) {
} }
list = append(list, map[string]interface{}{"ip": ip}) list = append(list, map[string]interface{}{"ip": ip})
} }
if err := rows.Err(); err != nil {
return nil, err
}
return list, nil return list, nil
} }