fix(db): convert expires_at to RFC3339 string format and simplify cleanup
This commit is contained in:
76
src/db.go
76
src/db.go
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user