~sircmpwn/meta.sr.ht

4e9d20bc363fb4dbd173642ea3366c4cda673988 — Drew DeVault 2 months ago b5cb6e7
API: rig up audit log
A api/graph/model/auditlog.go => api/graph/model/auditlog.go +95 -0
@@ 0,0 1,95 @@
package model

import (
	"context"
	"database/sql"
	"time"
	"strconv"

	sq "github.com/Masterminds/squirrel"

	"git.sr.ht/~sircmpwn/gql.sr.ht/database"
	"git.sr.ht/~sircmpwn/gql.sr.ht/model"
)

type AuditLogEntry struct {
	ID        int       `json:"id"`
	Created   time.Time `json:"created"`
	IPAddress string    `json:"ipAddress"`
	EventType string    `json:"eventType"`
	Details   *string   `json:"details"`

	UserID int

	alias  string
}

func (ent *AuditLogEntry) As(alias string) *AuditLogEntry {
	ent.alias = alias
	return ent
}

func (ent *AuditLogEntry) Select(ctx context.Context) []string {
	cols := database.ColumnsFor(ctx, ent.alias, map[string]string{
		"id":        "id",
		"created":   "created",
		"ipAddress": "ip_address",
		"eventType": "event_type",
		"details":   "details",
	})
	return append(cols, "id", "user_id")
}

func (ent *AuditLogEntry) Fields(ctx context.Context) []interface{} {
	fields := database.FieldsFor(ctx, map[string]interface{}{
		"id":        &ent.ID,
		"created":   &ent.Created,
		"ipAddress": &ent.IPAddress,
		"eventType": &ent.EventType,
		"details":   &ent.Details,
	})
	return append(fields, &ent.ID, &ent.UserID)
}

func (ent *AuditLogEntry) QueryWithCursor(ctx context.Context, db *sql.DB,
	q sq.SelectBuilder, cur *model.Cursor) ([]*AuditLogEntry, *model.Cursor) {
	var (
		err  error
		rows *sql.Rows
	)

	if cur.Next != "" {
		next, _ := strconv.ParseInt(cur.Next, 10, 64)
		q = q.Where(database.WithAlias(ent.alias, "id") + "<= ?", next)
	}
	q = q.
		OrderBy(database.WithAlias(ent.alias, "id") + " DESC").
		Limit(uint64(cur.Count + 1))

	if rows, err = q.RunWith(db).QueryContext(ctx); err != nil {
		panic(err)
	}
	defer rows.Close()

	var ents []*AuditLogEntry
	for rows.Next() {
		var ent AuditLogEntry
		if err := rows.Scan(ent.Fields(ctx)...); err != nil {
			panic(err)
		}
		ents = append(ents, &ent)
	}

	if len(ents) > cur.Count {
		cur = &model.Cursor{
			Count:  cur.Count,
			Next:   strconv.Itoa(ents[len(ents)-1].ID),
			Search: cur.Search,
		}
		ents = ents[:cur.Count]
	} else {
		cur = nil
	}

	return ents, cur
}

M api/graph/model/models_gen.go => api/graph/model/models_gen.go +0 -8
@@ 17,14 17,6 @@ type AuditLogCursor struct {
	Cursor  *model.Cursor    `json:"cursor"`
}

type AuditLogEntry struct {
	ID        int       `json:"id"`
	Created   time.Time `json:"created"`
	IPAddress string    `json:"ipAddress"`
	EventType string    `json:"eventType"`
	Details   *string   `json:"details"`
}

type InvoiceCursor struct {
	Results []*Invoice    `json:"results"`
	Cursor  *model.Cursor `json:"cursor"`

M api/graph/model/sshkey.go => api/graph/model/sshkey.go +1 -1
@@ 46,7 46,7 @@ func (k *SSHKey) Fields(ctx context.Context) []interface{} {
	fields := database.FieldsFor(ctx, map[string]interface{}{
		"id":          &k.ID,
		"created":     &k.Created,
		"last_used":   &k.LastUsed,
		"lastUsed":    &k.LastUsed,
		"key":         &k.Key,
		"fingerprint": &k.Fingerprint,
		"comment":     &k.Comment,

M api/graph/schema.resolvers.go => api/graph/schema.resolvers.go +12 -1
@@ 98,7 98,18 @@ func (r *queryResolver) Invoices(ctx context.Context, cursor *gqlmodel.Cursor) (
}

func (r *queryResolver) AuditLog(ctx context.Context, cursor *gqlmodel.Cursor) (*model.AuditLogCursor, error) {
	panic(fmt.Errorf("not implemented"))
	if cursor == nil {
		cursor = gqlmodel.NewCursor(nil)
	}

	ent := (&model.AuditLogEntry{}).As(`ent`)
	query := database.
		Select(ctx, ent).
		From(`audit_log_entry ent`).
		Where(`ent.user_id = ?`, auth.ForContext(ctx).ID)

	ents, cursor := ent.QueryWithCursor(ctx, database.ForContext(ctx), query, cursor)
	return &model.AuditLogCursor{ents, cursor}, nil
}

func (r *sSHKeyResolver) User(ctx context.Context, obj *model.SSHKey) (*model.User, error) {