chore: establish local platform baseline
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
package webevents
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"data-terminal-cloud/internal/identity"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
)
|
||||
|
||||
var ErrCursorUnavailable = errors.New("web event cursor is unavailable")
|
||||
|
||||
type Event struct {
|
||||
ID int64 `json:"id"`
|
||||
Type string `json:"type"`
|
||||
ResourceType string `json:"resource_type"`
|
||||
ResourceID string `json:"resource_id"`
|
||||
SubjectID string `json:"subject_id,omitempty"`
|
||||
Status string `json:"status"`
|
||||
OccurredAt time.Time `json:"occurred_at"`
|
||||
}
|
||||
|
||||
type Service struct{ pool *pgxpool.Pool }
|
||||
|
||||
func NewService(pool *pgxpool.Pool) *Service { return &Service{pool: pool} }
|
||||
|
||||
func (s *Service) StartingCursor(ctx context.Context, principal identity.Principal) (int64, error) {
|
||||
var cursor int64
|
||||
err := s.pool.QueryRow(ctx, `
|
||||
SELECT COALESCE(max(we.id),0)
|
||||
FROM web_event we
|
||||
JOIN household_member hm ON hm.tenant_id=we.tenant_id AND hm.household_id=we.household_id AND hm.status='active'
|
||||
JOIN user_account ua ON ua.id=hm.user_id AND ua.tenant_id=we.tenant_id AND ua.status='active'
|
||||
WHERE ua.oidc_issuer=$1 AND ua.oidc_subject=$2 AND we.expires_at>now()`, principal.Issuer, principal.Subject,
|
||||
).Scan(&cursor)
|
||||
return cursor, err
|
||||
}
|
||||
|
||||
func (s *Service) ValidateCursor(ctx context.Context, principal identity.Principal, cursor int64) error {
|
||||
if cursor == 0 {
|
||||
return nil
|
||||
}
|
||||
var valid bool
|
||||
err := s.pool.QueryRow(ctx, `
|
||||
SELECT EXISTS(
|
||||
SELECT 1 FROM web_event we
|
||||
JOIN household_member hm ON hm.tenant_id=we.tenant_id AND hm.household_id=we.household_id AND hm.status='active'
|
||||
JOIN user_account ua ON ua.id=hm.user_id AND ua.tenant_id=we.tenant_id AND ua.status='active'
|
||||
WHERE we.id=$1 AND ua.oidc_issuer=$2 AND ua.oidc_subject=$3 AND we.expires_at>now()
|
||||
)`, cursor, principal.Issuer, principal.Subject,
|
||||
).Scan(&valid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !valid {
|
||||
return ErrCursorUnavailable
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) ListAfter(ctx context.Context, principal identity.Principal, cursor int64, limit int) ([]Event, error) {
|
||||
if limit < 1 || limit > 100 {
|
||||
limit = 100
|
||||
}
|
||||
rows, err := s.pool.Query(ctx, `
|
||||
SELECT we.id,we.event_type,we.resource_type,we.resource_public_id::text,
|
||||
COALESCE(sp.public_id::text,''),we.status,we.occurred_at
|
||||
FROM web_event we
|
||||
JOIN household_member hm ON hm.tenant_id=we.tenant_id AND hm.household_id=we.household_id AND hm.status='active'
|
||||
JOIN user_account ua ON ua.id=hm.user_id AND ua.tenant_id=we.tenant_id AND ua.status='active'
|
||||
LEFT JOIN subject_profile sp ON sp.id=we.subject_id AND sp.tenant_id=we.tenant_id
|
||||
WHERE we.id>$1 AND ua.oidc_issuer=$2 AND ua.oidc_subject=$3 AND we.expires_at>now()
|
||||
ORDER BY we.id LIMIT $4`, cursor, principal.Issuer, principal.Subject, limit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
items := make([]Event, 0)
|
||||
for rows.Next() {
|
||||
var item Event
|
||||
if err := rows.Scan(&item.ID, &item.Type, &item.ResourceType, &item.ResourceID, &item.SubjectID, &item.Status, &item.OccurredAt); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, item)
|
||||
}
|
||||
return items, rows.Err()
|
||||
}
|
||||
|
||||
func (s *Service) DeleteExpired(ctx context.Context, limit int) (int64, error) {
|
||||
if limit < 1 || limit > 10000 {
|
||||
limit = 1000
|
||||
}
|
||||
result, err := s.pool.Exec(ctx, `DELETE FROM web_event WHERE id IN (SELECT id FROM web_event WHERE expires_at<=now() ORDER BY id LIMIT $1)`, limit)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return result.RowsAffected(), nil
|
||||
}
|
||||
|
||||
func (s *Service) ExpireDevicePresence(ctx context.Context, staleBefore time.Time, limit int) (int, error) {
|
||||
if limit < 1 || limit > 10000 {
|
||||
limit = 1000
|
||||
}
|
||||
var count int
|
||||
err := s.pool.QueryRow(ctx, `SELECT expire_device_presence($1,$2)`, staleBefore.UTC(), limit).Scan(&count)
|
||||
return count, err
|
||||
}
|
||||
Reference in New Issue
Block a user