refactor: rename entry field to ip across whitelist API

Rename the `entry` field to `ip` throughout the whitelist API for
clarity and consistency. This affects API request/response bodies,
path parameters, OpenAPI spec, documentation, and database queries
in the audit log endpoint.
This commit is contained in:
db123-test
2026-05-05 17:44:52 +03:30
parent 0c1bbdcaf4
commit 31fc204ef2
6 changed files with 50 additions and 57 deletions

View File

@@ -79,7 +79,7 @@ Add a permanent whitelisted IP or CIDR range.
```json ```json
{ {
"entry": "1.2.3.4" "ip": "1.2.3.4"
} }
``` ```
@@ -91,7 +91,7 @@ Add a permanent whitelisted IP or CIDR range.
curl -X POST http://127.0.0.1:8080/api/whitelist/perm \ curl -X POST http://127.0.0.1:8080/api/whitelist/perm \
-H "Authorization: Bearer CHANGE_ME" \ -H "Authorization: Bearer CHANGE_ME" \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
-d '{"entry":"203.0.113.10"}' -d '{"ip":"203.0.113.10"}'
``` ```
### GET /api/whitelist/perm/list ### GET /api/whitelist/perm/list
@@ -112,7 +112,7 @@ List all currently active permanent whitelisted IPs.
```json ```json
[ [
{ {
"entry": "203.0.113.10" "ip": "203.0.113.10"
} }
] ]
``` ```
@@ -124,7 +124,7 @@ curl -X GET "http://127.0.0.1:8080/api/whitelist/perm/list?limit=50&offset=0" \
-H "Authorization: Bearer CHANGE_ME" -H "Authorization: Bearer CHANGE_ME"
``` ```
### DELETE /api/whitelist/perm/{entry} ### DELETE /api/whitelist/perm/{ip}
Remove a permanent whitelisted IP or CIDR range. Remove a permanent whitelisted IP or CIDR range.

View File

@@ -41,7 +41,7 @@ All whitelist operations are logged to the `audit_log` table in SQLite.
Each entry records: Each entry records:
- `action` — what happened (`add_temp`, `delete_temp`, `add_perm`, `delete_perm`, `expire_temp`) - `action` — what happened (`add_temp`, `delete_temp`, `add_perm`, `delete_perm`, `expire_temp`)
- `ip` or `cidr` — the affected IP or CIDR - `ip` — the affected IP or CIDR range
- `ttl_seconds` — for temp entries - `ttl_seconds` — for temp entries
- `reason` — the reason provided - `reason` — the reason provided
- `api_client_ip` — the IP that made the API call (from NGINX `X-Real-IP`) - `api_client_ip` — the IP that made the API call (from NGINX `X-Real-IP`)

View File

@@ -191,9 +191,9 @@ paths:
schema: schema:
type: object type: object
required: required:
- entry - ip
properties: properties:
entry: ip:
type: string type: string
description: IP address or CIDR range to whitelist description: IP address or CIDR range to whitelist
example: "203.0.113.10" example: "203.0.113.10"
@@ -201,16 +201,16 @@ paths:
'204': '204':
description: Created description: Created
'400': '400':
description: Bad request — missing entry or invalid entry (must be valid IP or CIDR range) description: Bad request — missing ip or invalid ip (must be valid IP or CIDR range)
'401': '401':
description: Unauthorized — invalid API token description: Unauthorized — invalid API token
'409': '409':
description: Conflict — entry already exists description: Conflict — ip already exists
content: content:
text/plain: text/plain:
schema: schema:
type: string type: string
example: "entry already exists" example: "ip already exists"
'500': '500':
description: Internal error description: Internal error
@@ -247,7 +247,7 @@ paths:
items: items:
type: object type: object
properties: properties:
entry: ip:
type: string type: string
example: "203.0.113.10" example: "203.0.113.10"
'401': '401':
@@ -255,13 +255,13 @@ paths:
'500': '500':
description: Internal error — database failure description: Internal error — database failure
/api/whitelist/perm/{entry}: /api/whitelist/perm/{ip}:
delete: delete:
summary: Remove a permanent whitelisted IP or CIDR summary: Remove a permanent whitelisted IP or CIDR
description: Remove a permanent whitelisted IP address or CIDR range. description: Remove a permanent whitelisted IP address or CIDR range.
operationId: deletePermWhitelist operationId: deletePermWhitelist
parameters: parameters:
- name: entry - name: ip
in: path in: path
required: true required: true
schema: schema:
@@ -271,7 +271,7 @@ paths:
'204': '204':
description: Deleted description: Deleted
'400': '400':
description: Bad request — entry required description: Bad request — ip required
'401': '401':
description: Unauthorized — invalid API token description: Unauthorized — invalid API token
'500': '500':
@@ -337,10 +337,6 @@ paths:
type: string type: string
nullable: true nullable: true
example: "1.2.3.4" example: "1.2.3.4"
cidr:
type: string
nullable: true
example: "10.0.0.0/24"
ttl_seconds: ttl_seconds:
type: integer type: integer
nullable: true nullable: true

View File

@@ -169,7 +169,7 @@ func logsHandler(cfg Config, db *sql.DB) http.HandlerFunc {
return return
} }
query := `SELECT timestamp, action, ip, cidr, ttl_seconds, reason, api_client_ip FROM audit_log` query := `SELECT timestamp, action, entry, ttl_seconds, reason, api_client_ip FROM audit_log`
var args []any var args []any
filterAction := r.URL.Query().Get("action") filterAction := r.URL.Query().Get("action")
@@ -218,9 +218,9 @@ func logsHandler(cfg Config, db *sql.DB) http.HandlerFunc {
for rows.Next() { for rows.Next() {
var ts time.Time var ts time.Time
var action, apiClientIP string var action, apiClientIP string
var ip, cidr, reason sql.NullString var ip, reason sql.NullString
var ttlSeconds sql.NullInt64 var ttlSeconds sql.NullInt64
if err := rows.Scan(&ts, &action, &ip, &cidr, &ttlSeconds, &reason, &apiClientIP); err != nil { if err := rows.Scan(&ts, &action, &ip, &ttlSeconds, &reason, &apiClientIP); err != nil {
continue continue
} }
entry := map[string]interface{}{ entry := map[string]interface{}{
@@ -232,9 +232,6 @@ func logsHandler(cfg Config, db *sql.DB) http.HandlerFunc {
if ip.Valid { if ip.Valid {
entry["ip"] = ip.String entry["ip"] = ip.String
} }
if cidr.Valid {
entry["cidr"] = cidr.String
}
if ttlSeconds.Valid { if ttlSeconds.Valid {
entry["ttl_seconds"] = ttlSeconds.Int64 entry["ttl_seconds"] = ttlSeconds.Int64
} }
@@ -252,29 +249,29 @@ func permWhitelistAddHandler(cfg Config, db *sql.DB) http.HandlerFunc {
return return
} }
type request struct { type request struct {
Entry string `json:"entry"` IP string `json:"ip"`
} }
var req request var req request
if err := json.NewDecoder(r.Body).Decode(&req); err != nil { if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, "bad request", http.StatusBadRequest) http.Error(w, "bad request", http.StatusBadRequest)
return return
} }
if req.Entry == "" { if req.IP == "" {
http.Error(w, "entry required", http.StatusBadRequest) http.Error(w, "ip required", http.StatusBadRequest)
return return
} }
if !IsIP(req.Entry) && !IsCIDR(req.Entry) { if !IsIP(req.IP) && !IsCIDR(req.IP) {
http.Error(w, "invalid entry (must be valid IP or CIDR range)", http.StatusBadRequest) http.Error(w, "invalid ip (must be valid IP or CIDR range)", http.StatusBadRequest)
return return
} }
ok, err := AddPermanentEntry(db, req.Entry, getClientIP(r)) ok, err := AddPermanentEntry(db, req.IP, getClientIP(r))
if err != nil { if err != nil {
slog.Error("failed to add perm entry", "error", err) slog.Error("failed to add perm ip", "error", err)
http.Error(w, "internal error", http.StatusInternalServerError) http.Error(w, "internal error", http.StatusInternalServerError)
return return
} }
if !ok { if !ok {
http.Error(w, "entry already exists", http.StatusConflict) http.Error(w, "ip already exists", http.StatusConflict)
return return
} }
w.WriteHeader(http.StatusNoContent) w.WriteHeader(http.StatusNoContent)
@@ -287,13 +284,13 @@ func permWhitelistDeleteHandler(cfg Config, db *sql.DB) http.HandlerFunc {
http.Error(w, "unauthorized", http.StatusUnauthorized) http.Error(w, "unauthorized", http.StatusUnauthorized)
return return
} }
entry := r.PathValue("entry") ip := r.PathValue("ip")
if entry == "" { if ip == "" {
http.Error(w, "entry required", http.StatusBadRequest) http.Error(w, "ip required", http.StatusBadRequest)
return return
} }
if err := DeletePermanentEntry(db, entry, getClientIP(r)); err != nil { if err := DeletePermanentEntry(db, ip, getClientIP(r)); err != nil {
slog.Error("failed to delete perm entry", "error", err) slog.Error("failed to delete perm ip", "error", err)
http.Error(w, "internal error", http.StatusInternalServerError) http.Error(w, "internal error", http.StatusInternalServerError)
return return
} }

View File

@@ -24,7 +24,7 @@ func InitDB(dbPath string) (*sql.DB, error) {
schema := ` schema := `
CREATE TABLE IF NOT EXISTS perm_whitelist ( CREATE TABLE IF NOT EXISTS perm_whitelist (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
entry TEXT NOT NULL UNIQUE, ip TEXT NOT NULL UNIQUE,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
); );
@@ -41,7 +41,7 @@ func InitDB(dbPath string) (*sql.DB, error) {
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
timestamp DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, timestamp DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
action TEXT NOT NULL, action TEXT NOT NULL,
ip TEXT, entry TEXT,
ttl_seconds INTEGER, ttl_seconds INTEGER,
reason TEXT, reason TEXT,
api_client_ip TEXT api_client_ip TEXT
@@ -56,11 +56,11 @@ func InitDB(dbPath string) (*sql.DB, error) {
} }
func AddPermanentEntry(db *sql.DB, entry, apiClientIP string) (bool, error) { func AddPermanentEntry(db *sql.DB, entry, apiClientIP string) (bool, error) {
_, err := db.Exec(`INSERT INTO perm_whitelist(entry) VALUES(?)`, entry) _, err := db.Exec(`INSERT INTO perm_whitelist(ip) VALUES(?)`, entry)
if err != nil { if err != nil {
if isUniqueConstraintError(err) { if isUniqueConstraintError(err) {
_, err = db.Exec(` _, err = db.Exec(`
INSERT INTO audit_log(action, ip, api_client_ip) INSERT INTO audit_log(action, entry, api_client_ip)
VALUES('add_perm_duplicate', ?, ?) VALUES('add_perm_duplicate', ?, ?)
`, entry, apiClientIP) `, entry, apiClientIP)
if err != nil { if err != nil {
@@ -71,7 +71,7 @@ func AddPermanentEntry(db *sql.DB, entry, apiClientIP string) (bool, error) {
return false, err return false, err
} }
_, err = db.Exec(` _, err = db.Exec(`
INSERT INTO audit_log(action, ip, api_client_ip) INSERT INTO audit_log(action, entry, api_client_ip)
VALUES('add_perm', ?, ?) VALUES('add_perm', ?, ?)
`, entry, apiClientIP) `, entry, apiClientIP)
if err != nil { if err != nil {
@@ -83,13 +83,13 @@ func AddPermanentEntry(db *sql.DB, entry, apiClientIP string) (bool, error) {
func DeletePermanentEntry(db *sql.DB, entry, apiClientIP string) error { func DeletePermanentEntry(db *sql.DB, entry, apiClientIP string) error {
_, err := db.Exec(` _, err := db.Exec(`
DELETE FROM perm_whitelist DELETE FROM perm_whitelist
WHERE entry = ? WHERE ip = ?
`, entry) `, entry)
if err != nil { if err != nil {
return err return err
} }
_, err = db.Exec(` _, err = db.Exec(`
INSERT INTO audit_log(action, ip, api_client_ip) INSERT INTO audit_log(action, entry, api_client_ip)
VALUES('delete_perm', ?, ?) VALUES('delete_perm', ?, ?)
`, entry, apiClientIP) `, entry, apiClientIP)
if err != nil { if err != nil {
@@ -100,7 +100,7 @@ func DeletePermanentEntry(db *sql.DB, entry, apiClientIP string) error {
func IsPermanentWhitelisted(db *sql.DB, ip string) (bool, error) { func IsPermanentWhitelisted(db *sql.DB, ip string) (bool, error) {
rows, err := db.Query(` rows, err := db.Query(`
SELECT entry SELECT ip
FROM perm_whitelist FROM perm_whitelist
`) `)
if err != nil { if err != nil {
@@ -135,7 +135,7 @@ func AddTempEntry(db *sql.DB, ip string, ttlSeconds int, reason, apiClientIP str
return err return err
} }
_, err = db.Exec(` _, err = db.Exec(`
INSERT INTO audit_log(action, ip, ttl_seconds, reason, api_client_ip) INSERT INTO audit_log(action, entry, ttl_seconds, reason, api_client_ip)
VALUES('add_temp', ?, ?, ?, ?) VALUES('add_temp', ?, ?, ?, ?)
`, ip, ttlSeconds, reason, apiClientIP) `, ip, ttlSeconds, reason, apiClientIP)
if err != nil { if err != nil {
@@ -153,7 +153,7 @@ func DeleteTempEntry(db *sql.DB, ip, apiClientIP string) error {
return err return err
} }
_, err = db.Exec(` _, err = db.Exec(`
INSERT INTO audit_log(action, ip, api_client_ip) INSERT INTO audit_log(action, entry, api_client_ip)
VALUES('delete_temp', ?, ?) VALUES('delete_temp', ?, ?)
`, ip, apiClientIP) `, ip, apiClientIP)
if err != nil { if err != nil {
@@ -209,7 +209,7 @@ func CleanupExpiredTemp(db *sql.DB) error {
for _, ip := range expiredIPs { for _, ip := range expiredIPs {
_, err = db.Exec(` _, err = db.Exec(`
INSERT INTO audit_log(action, ip, reason) INSERT INTO audit_log(action, entry, reason)
VALUES('expire_temp', ?, ?) VALUES('expire_temp', ?, ?)
`, ip, reason) `, ip, reason)
if err != nil { if err != nil {
@@ -250,7 +250,7 @@ func ListTempEntries(db *sql.DB) ([]map[string]interface{}, error) {
func ListPermEntries(db *sql.DB) ([]map[string]interface{}, error) { func ListPermEntries(db *sql.DB) ([]map[string]interface{}, error) {
rows, err := db.Query(` rows, err := db.Query(`
SELECT entry SELECT ip
FROM perm_whitelist FROM perm_whitelist
ORDER BY created_at ORDER BY created_at
`) `)
@@ -260,12 +260,12 @@ func ListPermEntries(db *sql.DB) ([]map[string]interface{}, error) {
defer rows.Close() defer rows.Close()
var list []map[string]interface{} = make([]map[string]interface{}, 0) var list []map[string]interface{} = make([]map[string]interface{}, 0)
for rows.Next() { for rows.Next() {
var entry string var ip string
if err := rows.Scan(&entry); err != nil { if err := rows.Scan(&ip); err != nil {
continue continue
} }
list = append(list, map[string]interface{}{ list = append(list, map[string]interface{}{
"entry": entry, "ip": ip,
}) })
} }
return list, nil return list, nil
@@ -302,7 +302,7 @@ func ListTempEntriesPaginated(db *sql.DB, limit, offset int) ([]map[string]inter
func ListPermEntriesPaginated(db *sql.DB, limit, offset int) ([]map[string]interface{}, error) { func ListPermEntriesPaginated(db *sql.DB, limit, offset int) ([]map[string]interface{}, error) {
rows, err := db.Query(` rows, err := db.Query(`
SELECT entry SELECT ip
FROM perm_whitelist FROM perm_whitelist
ORDER BY created_at ORDER BY created_at
LIMIT ? OFFSET ? LIMIT ? OFFSET ?
@@ -313,12 +313,12 @@ func ListPermEntriesPaginated(db *sql.DB, limit, offset int) ([]map[string]inter
defer rows.Close() defer rows.Close()
var list []map[string]interface{} = make([]map[string]interface{}, 0) var list []map[string]interface{} = make([]map[string]interface{}, 0)
for rows.Next() { for rows.Next() {
var entry string var ip string
if err := rows.Scan(&entry); err != nil { if err := rows.Scan(&ip); err != nil {
continue continue
} }
list = append(list, map[string]interface{}{ list = append(list, map[string]interface{}{
"entry": entry, "ip": ip,
}) })
} }
return list, nil return list, nil

View File

@@ -38,7 +38,7 @@ func main() {
mux.HandleFunc("/api/whitelist/temp/{ip}", whitelistDeleteHandler(cfg, db)) mux.HandleFunc("/api/whitelist/temp/{ip}", whitelistDeleteHandler(cfg, db))
mux.HandleFunc("/api/whitelist/perm", permWhitelistAddHandler(cfg, db)) mux.HandleFunc("/api/whitelist/perm", permWhitelistAddHandler(cfg, db))
mux.HandleFunc("/api/whitelist/perm/list", permWhitelistListHandler(cfg, db)) mux.HandleFunc("/api/whitelist/perm/list", permWhitelistListHandler(cfg, db))
mux.HandleFunc("/api/whitelist/perm/{entry}", permWhitelistDeleteHandler(cfg, db)) mux.HandleFunc("/api/whitelist/perm/{ip}", permWhitelistDeleteHandler(cfg, db))
mux.HandleFunc("/api/logs", logsHandler(cfg, db)) mux.HandleFunc("/api/logs", logsHandler(cfg, db))
mux.HandleFunc("/status", statusHandler(db)) mux.HandleFunc("/status", statusHandler(db))