From 51bfc9d2af6c02f7425b35465aa253f59b56b0f7 Mon Sep 17 00:00:00 2001 From: mkelvers Date: Tue, 26 May 2026 16:14:14 +0200 Subject: [PATCH] feat: add audit log sqlc queries and generated code --- internal/db/db.go | 2 +- internal/db/models.go | 19 +++++- internal/db/querier.go | 5 +- internal/db/queries.sql | 12 ++++ internal/db/queries.sql.go | 123 ++++++++++++++++++++++++++++++++----- 5 files changed, 141 insertions(+), 20 deletions(-) diff --git a/internal/db/db.go b/internal/db/db.go index cd5bbb8..f43598b 100644 --- a/internal/db/db.go +++ b/internal/db/db.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.30.0 +// sqlc v1.31.1 package db diff --git a/internal/db/models.go b/internal/db/models.go index 7ac013e..1672321 100644 --- a/internal/db/models.go +++ b/internal/db/models.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.30.0 +// sqlc v1.31.1 package db @@ -47,6 +47,18 @@ type ApiToken struct { RevokedAt sql.NullTime `json:"revoked_at"` } +type AuditLog struct { + ID string `json:"id"` + OccurredAt time.Time `json:"occurred_at"` + UserID sql.NullString `json:"user_id"` + Action string `json:"action"` + ResourceType sql.NullString `json:"resource_type"` + ResourceID sql.NullString `json:"resource_id"` + Ip sql.NullString `json:"ip"` + UserAgent sql.NullString `json:"user_agent"` + MetadataJson sql.NullString `json:"metadata_json"` +} + type ContinueWatchingEntry struct { ID string `json:"id"` UserID string `json:"user_id"` @@ -58,6 +70,11 @@ type ContinueWatchingEntry struct { DurationSeconds sql.NullFloat64 `json:"duration_seconds"` } +type DataFix struct { + ID string `json:"id"` + AppliedAt time.Time `json:"applied_at"` +} + type EpisodeAvailabilityCache struct { AnimeID int64 `json:"anime_id"` Data string `json:"data"` diff --git a/internal/db/querier.go b/internal/db/querier.go index 27492be..378de6f 100644 --- a/internal/db/querier.go +++ b/internal/db/querier.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.30.0 +// sqlc v1.31.1 package db @@ -11,6 +11,7 @@ import ( type Querier interface { CountPendingAnimeFetchRetries(ctx context.Context) (int64, error) CreateAPIToken(ctx context.Context, arg CreateAPITokenParams) (ApiToken, error) + CreateAuditLog(ctx context.Context, arg CreateAuditLogParams) (AuditLog, error) CreateSession(ctx context.Context, arg CreateSessionParams) (Session, error) DeleteAnimeFetchRetry(ctx context.Context, animeID int64) error DeleteContinueWatchingEntry(ctx context.Context, arg DeleteContinueWatchingEntryParams) error @@ -22,6 +23,7 @@ type Querier interface { GetAllCachedAnime(ctx context.Context) ([]string, error) GetAnime(ctx context.Context, id int64) (Anime, error) GetAnimeNeedingRelationSync(ctx context.Context) ([]GetAnimeNeedingRelationSyncRow, error) + GetAuditLogsForUser(ctx context.Context, arg GetAuditLogsForUserParams) ([]AuditLog, error) GetContinueWatchingEntries(ctx context.Context, userID string) ([]GetContinueWatchingEntriesRow, error) GetContinueWatchingEntry(ctx context.Context, arg GetContinueWatchingEntryParams) (ContinueWatchingEntry, error) GetDueAnimeFetchRetries(ctx context.Context, limit int64) ([]AnimeFetchRetry, error) @@ -40,6 +42,7 @@ type Querier interface { MarkAnimeFetchRetryFailed(ctx context.Context, arg MarkAnimeFetchRetryFailedParams) error MarkEpisodeAvailabilityRefreshFailed(ctx context.Context, arg MarkEpisodeAvailabilityRefreshFailedParams) error MarkRelationsSynced(ctx context.Context, id int64) error + RefreshSession(ctx context.Context, arg RefreshSessionParams) error RevokeAllAPITokensForUser(ctx context.Context, userID string) error SaveWatchProgress(ctx context.Context, arg SaveWatchProgressParams) error SetJikanCache(ctx context.Context, arg SetJikanCacheParams) error diff --git a/internal/db/queries.sql b/internal/db/queries.sql index 07ed5f4..000f449 100644 --- a/internal/db/queries.sql +++ b/internal/db/queries.sql @@ -1,6 +1,18 @@ -- name: GetUser :one SELECT * FROM user WHERE id = ? LIMIT 1; +-- name: CreateAuditLog :one +INSERT INTO audit_log (id, user_id, action, resource_type, resource_id, ip, user_agent, metadata_json) +VALUES (?, ?, ?, ?, ?, ?, ?, ?) +RETURNING *; + +-- name: GetAuditLogsForUser :many +SELECT * +FROM audit_log +WHERE user_id = ? +ORDER BY occurred_at DESC +LIMIT ?; + -- name: GetUserByUsername :one SELECT * FROM user WHERE username = ? LIMIT 1; diff --git a/internal/db/queries.sql.go b/internal/db/queries.sql.go index bde28ea..edb6241 100644 --- a/internal/db/queries.sql.go +++ b/internal/db/queries.sql.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.30.0 +// sqlc v1.31.1 // source: queries.sql package db @@ -57,6 +57,49 @@ func (q *Queries) CreateAPIToken(ctx context.Context, arg CreateAPITokenParams) return i, err } +const createAuditLog = `-- name: CreateAuditLog :one +INSERT INTO audit_log (id, user_id, action, resource_type, resource_id, ip, user_agent, metadata_json) +VALUES (?, ?, ?, ?, ?, ?, ?, ?) +RETURNING id, occurred_at, user_id, "action", resource_type, resource_id, ip, user_agent, metadata_json +` + +type CreateAuditLogParams struct { + ID string `json:"id"` + UserID sql.NullString `json:"user_id"` + Action string `json:"action"` + ResourceType sql.NullString `json:"resource_type"` + ResourceID sql.NullString `json:"resource_id"` + Ip sql.NullString `json:"ip"` + UserAgent sql.NullString `json:"user_agent"` + MetadataJson sql.NullString `json:"metadata_json"` +} + +func (q *Queries) CreateAuditLog(ctx context.Context, arg CreateAuditLogParams) (AuditLog, error) { + row := q.db.QueryRowContext(ctx, createAuditLog, + arg.ID, + arg.UserID, + arg.Action, + arg.ResourceType, + arg.ResourceID, + arg.Ip, + arg.UserAgent, + arg.MetadataJson, + ) + var i AuditLog + err := row.Scan( + &i.ID, + &i.OccurredAt, + &i.UserID, + &i.Action, + &i.ResourceType, + &i.ResourceID, + &i.Ip, + &i.UserAgent, + &i.MetadataJson, + ) + return i, err +} + const createSession = `-- name: CreateSession :one INSERT INTO session (id, user_id, expires_at) VALUES (?, ?, ?) @@ -124,22 +167,6 @@ func (q *Queries) DeleteSession(ctx context.Context, id string) error { return err } -const refreshSession = `-- name: RefreshSession :exec -UPDATE session -SET expires_at = ? -WHERE id = ? -` - -type RefreshSessionParams struct { - ExpiresAt time.Time `json:"expires_at"` - ID string `json:"id"` -} - -func (q *Queries) RefreshSession(ctx context.Context, arg RefreshSessionParams) error { - _, err := q.db.ExecContext(ctx, refreshSession, arg.ExpiresAt, arg.ID) - return err -} - const deleteWatchListEntry = `-- name: DeleteWatchListEntry :exec DELETE FROM watch_list_entry WHERE user_id = ? AND anime_id = ? @@ -299,6 +326,52 @@ func (q *Queries) GetAnimeNeedingRelationSync(ctx context.Context) ([]GetAnimeNe return items, nil } +const getAuditLogsForUser = `-- name: GetAuditLogsForUser :many +SELECT id, occurred_at, user_id, "action", resource_type, resource_id, ip, user_agent, metadata_json +FROM audit_log +WHERE user_id = ? +ORDER BY occurred_at DESC +LIMIT ? +` + +type GetAuditLogsForUserParams struct { + UserID sql.NullString `json:"user_id"` + Limit int64 `json:"limit"` +} + +func (q *Queries) GetAuditLogsForUser(ctx context.Context, arg GetAuditLogsForUserParams) ([]AuditLog, error) { + rows, err := q.db.QueryContext(ctx, getAuditLogsForUser, arg.UserID, arg.Limit) + if err != nil { + return nil, err + } + defer rows.Close() + var items []AuditLog + for rows.Next() { + var i AuditLog + if err := rows.Scan( + &i.ID, + &i.OccurredAt, + &i.UserID, + &i.Action, + &i.ResourceType, + &i.ResourceID, + &i.Ip, + &i.UserAgent, + &i.MetadataJson, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + const getContinueWatchingEntries = `-- name: GetContinueWatchingEntries :many SELECT c.id, @@ -918,6 +991,22 @@ func (q *Queries) MarkRelationsSynced(ctx context.Context, id int64) error { return err } +const refreshSession = `-- name: RefreshSession :exec +UPDATE session +SET expires_at = ? +WHERE id = ? +` + +type RefreshSessionParams struct { + ExpiresAt time.Time `json:"expires_at"` + ID string `json:"id"` +} + +func (q *Queries) RefreshSession(ctx context.Context, arg RefreshSessionParams) error { + _, err := q.db.ExecContext(ctx, refreshSession, arg.ExpiresAt, arg.ID) + return err +} + const revokeAllAPITokensForUser = `-- name: RevokeAllAPITokensForUser :exec UPDATE api_token SET revoked_at = CURRENT_TIMESTAMP