{table, div, th, tr, td, thead, tbody, section, h3, time} = React.DOM {map, each, unique, is-type, join} = require 'prelude-ls' fields = <[ timestamp user acting_as action kind entity privilege ]> AuditTableHeader = React.createClass { display-name: \AuditTableHeader render: -> thead {}, (tr {}, (fields |> map -> th key: it, it.replace \_, ' ')) } Timestamp = React.createClass { display-name: \Timestamp render: -> ts = moment(@props.time) time datetime: ts.format!, title: ts.calendar!, [ ts.from-now! ] } wrap-array = -> if is-type \Array it it else [it] AuditEntry = React.createClass { display-name: \AuditEntry transformed-props: -> @props with entity: @props.resource || @props.role transform-field: (key, value) -> | not value => value # need this case to handle blank values that turn up sometimes | key in <[ user acting_as ]> => RoleLink {id: value} | key == 'entity' => ResourceLink {data: value} | key == 'timestamp' => Timestamp {time: value} | otherwise => value render: -> props = @transformed-props! tr class-name: @props.action, fields |> map ~> td key: it, [ @transform-field(it, props[it]) ] } # compare events by id to prevent duplicates # reverse order to push newest items on top new-event-set = -> evts = new SortedSet comparator: (a, b) -> a && b && (b.id - a.id) evts.contains-like = (item) -> existing = @find-iterator(item).value! @priv.comparator(existing, item) == 0 if existing? evts AuditTable = React.createClass { display-name: \AuditTable get-initial-state: -> events: new-event-set! render: -> section class-name: \audit, [ h3 {}, @props.caption table class-name: \audit-table, [ AuditTableHeader(key: \thead), tbody key: \tbody, @state.events.map -> new AuditEntry it with key: it.id ] ] component-did-mount: -> @props.src |> wrap-array |> each @add-source component-will-unmount: -> @sources |> each -> console.log "closing event source ", it it.close! add-event: ({data}) -> event = JSON.parse data if event.action == "check" && event.privilege == "read" && event.allowed # pass true else unless @state.events.contains-like event # console.log event @state.events.insert event @force-update! add-source: (url) -> console.log "opening eventsource to #url" evt-src = new EventSource url console.log evt-src evt-src.onmessage = @add-event evt-src.onerror = (a, b, c, d) -> console.log a, b, c, d @[]sources.push evt-src } export GlobalAudit = React.createClass { display-name: \GlobalAudit render: -> AuditTable src: '/api/audit/all', caption: 'All recent audit events' } url-of-role = (role) -> "/api/audit/roles/#{encodeURIComponent role}" url-of-resource = (resource) -> "/api/audit/resources/#{encodeURIComponent resource}" export AuditBox = React.createClass { display-name: \AuditBox render: -> roles = @props.roles || [] resources = @props.resources || [] role-srcs = roles |> map url-of-role res-srcs = resources |> map url-of-resource things = (roles ++ resources) |> unique |> join ', ' AuditTable { src: role-srcs ++ res-srcs caption: "Recent audit events for #things" } }