@things-factory/auth-ui 10.1.244 → 10.1.245

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,6 @@
1
1
  import '@material/web/icon/icon.js';
2
2
  import '@operato/data-grist';
3
+ import '@operato/input/ox-input-select-buttons.js';
3
4
  import type { CSSResultGroup } from 'lit';
4
5
  import { PageView } from '@operato/shell';
5
6
  import type { FetchOption } from '@operato/data-grist';
@@ -19,6 +20,8 @@ export declare class AuditPage extends AuditPageBase {
19
20
  private actor?;
20
21
  private door?;
21
22
  private outcome;
23
+ /** How many records each outcome chip would show under the other conditions, counted by the records' own door. */
24
+ private outcomeCounts;
22
25
  private health?;
23
26
  private grist;
24
27
  get context(): {
@@ -34,6 +37,7 @@ export declare class AuditPage extends AuditPageBase {
34
37
  private show;
35
38
  private lastDays;
36
39
  private pick;
40
+ private countOutcomes;
37
41
  private narrow;
38
42
  pageUpdated(_changes: any, _lifecycle: any): Promise<void>;
39
43
  private readHealth;
@@ -1,6 +1,7 @@
1
1
  import { __decorate, __metadata } from "tslib";
2
2
  import '@material/web/icon/icon.js';
3
3
  import '@operato/data-grist';
4
+ import '@operato/input/ox-input-select-buttons.js';
4
5
  import gql from 'graphql-tag';
5
6
  import { css, html, nothing } from 'lit';
6
7
  import { customElement, property, query, state } from 'lit/decorators.js';
@@ -31,6 +32,8 @@ let AuditPage = class AuditPage extends AuditPageBase {
31
32
  this.from = lastDays(30).from;
32
33
  this.to = new Date();
33
34
  this.outcome = '';
35
+ /** How many records each outcome chip would show under the other conditions, counted by the records' own door. */
36
+ this.outcomeCounts = new Map();
34
37
  }
35
38
  static { this.styles = [
36
39
  WorkSurfacePageStyles,
@@ -49,11 +52,6 @@ let AuditPage = class AuditPage extends AuditPageBase {
49
52
  gap: var(--spacing-tiny, 2px);
50
53
  }
51
54
 
52
- button[aria-pressed='true'] {
53
- background-color: var(--md-sys-color-secondary-container);
54
- color: var(--md-sys-color-on-secondary-container);
55
- }
56
-
57
55
  input[type='date'] {
58
56
  height: var(--context-action-height, 28px);
59
57
  box-sizing: border-box;
@@ -99,23 +97,41 @@ let AuditPage = class AuditPage extends AuditPageBase {
99
97
  }
100
98
  renderControls() {
101
99
  const t = i18next.t.bind(i18next);
102
- const pressed = (on) => (on ? 'true' : 'false');
100
+ /* One of each is always chosen: a press on the chosen chip leaves it chosen. */
101
+ const choose = (e, chosen, pick) => {
102
+ if (e.detail)
103
+ pick(e.detail);
104
+ else
105
+ e.target.value = chosen;
106
+ };
103
107
  return html `
104
- <div group>
105
- ${VIEWS.map(view => html `<button quiet aria-pressed=${pressed(this.view === view)} @click=${() => this.show(view)}>${t(`audit.view.${view}`)}</button>`)}
106
- </div>
108
+ <ox-input-select-buttons
109
+ .options=${VIEWS.map(view => ({ value: view, display: t(`audit.view.${view}`) }))}
110
+ .value=${this.view}
111
+ @change=${(e) => choose(e, this.view, view => this.show(view))}
112
+ ></ox-input-select-buttons>
107
113
 
108
114
  <div group>
109
- ${PERIODS.map(days => html `<button quiet aria-pressed=${pressed(this.days === days)} @click=${() => this.lastDays(days)}>${t('audit.period.days', { count: days })}</button>`)}
115
+ <ox-input-select-buttons
116
+ .options=${PERIODS.map(days => ({ value: String(days), display: t('audit.period.days', { count: days }) }))}
117
+ .value=${this.days ? String(this.days) : undefined}
118
+ @change=${(e) => choose(e, this.days ? String(this.days) : undefined, days => this.lastDays(Number(days)))}
119
+ ></ox-input-select-buttons>
110
120
  <input type="date" aria-label=${t('audit.period.from')} .value=${dateInputValue(this.from)} @change=${(e) => this.pick('from', e)} />
111
121
  <span>~</span>
112
122
  <input type="date" aria-label=${t('audit.period.to')} .value=${dateInputValue(this.to)} @change=${(e) => this.pick('to', e)} />
113
123
  </div>
114
124
 
115
125
  ${this.view === 'records'
116
- ? html `<div group>
117
- ${OUTCOMES.map(outcome => html `<button quiet aria-pressed=${pressed(this.outcome === outcome)} @click=${() => this.narrow({ outcome })}>${t(outcome ? `audit.outcome.${outcome}` : 'audit.outcome.all')}</button>`)}
118
- </div>`
126
+ ? html `<ox-input-select-buttons
127
+ .options=${OUTCOMES.map(outcome => ({
128
+ value: outcome || 'all',
129
+ display: t(outcome ? `audit.outcome.${outcome}` : 'audit.outcome.all'),
130
+ count: this.outcomeCounts.get(outcome || 'all') ?? 0
131
+ }))}
132
+ .value=${this.outcome || 'all'}
133
+ @change=${(e) => choose(e, this.outcome || 'all', outcome => this.narrow({ outcome: (outcome === 'all' ? '' : outcome) }))}
134
+ ></ox-input-select-buttons>`
119
135
  : nothing}
120
136
  ${this.view === 'records' && this.actor
121
137
  ? html `<span narrowed>${this.actor.name}<button quiet small @click=${() => this.narrow({ actor: undefined })}>${t('audit.button.all-people')}</button></span>`
@@ -154,6 +170,24 @@ let AuditPage = class AuditPage extends AuditPageBase {
154
170
  this.days = null;
155
171
  this.grist?.fetch();
156
172
  }
173
+ /*
174
+ * The outcome chips' numbers - the records' own door asked for its total once per outcome, under the period, the
175
+ * person and the privilege narrowed to, in one request.
176
+ */
177
+ async countOutcomes(filter) {
178
+ const response = await client.query({
179
+ query: gql `
180
+ query (${OUTCOMES.map((_, at) => `$f${at}: AuditRecordFilter!`).join(', ')}) {
181
+ ${OUTCOMES.map((_, at) => `c${at}: auditRecords(filter: $f${at}) { total }`).join('\n')}
182
+ }
183
+ `,
184
+ variables: Object.fromEntries(OUTCOMES.map((outcome, at) => [`f${at}`, { ...filter, outcome: outcome || undefined, page: 1, limit: 1 }])),
185
+ fetchPolicy: 'no-cache'
186
+ });
187
+ if (response.errors?.length)
188
+ return console.error(response.errors);
189
+ this.outcomeCounts = new Map(OUTCOMES.map((outcome, at) => [outcome || 'all', response.data?.[`c${at}`]?.total ?? 0]));
190
+ }
157
191
  narrow(change) {
158
192
  Object.assign(this, change);
159
193
  if (this.view !== 'records')
@@ -308,6 +342,7 @@ let AuditPage = class AuditPage extends AuditPageBase {
308
342
  .sort((a, b) => used(b) - used(a));
309
343
  return { total: records.length, records };
310
344
  }
345
+ this.countOutcomes({ ...period, actorId: this.actor?.id, door: this.door });
311
346
  const response = await client.query({
312
347
  query: gql `
313
348
  query ($filter: AuditRecordFilter!) {
@@ -403,6 +438,10 @@ __decorate([
403
438
  state(),
404
439
  __metadata("design:type", String)
405
440
  ], AuditPage.prototype, "outcome", void 0);
441
+ __decorate([
442
+ state(),
443
+ __metadata("design:type", Object)
444
+ ], AuditPage.prototype, "outcomeCounts", void 0);
406
445
  __decorate([
407
446
  state(),
408
447
  __metadata("design:type", Object)
@@ -1 +1 @@
1
- {"version":3,"file":"audit-page.js","sourceRoot":"","sources":["../../../client/pages/audit/audit-page.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/icon/icon.js'\nimport '@operato/data-grist'\n\nimport gql from 'graphql-tag'\nimport { css, html, nothing } from 'lit'\nimport type { CSSResultGroup } from 'lit'\nimport { customElement, property, query, state } from 'lit/decorators.js'\nimport { keyed } from 'lit/directives/keyed.js'\n\nimport { client } from '@operato/graphql'\nimport { i18next, localize } from '@operato/i18n'\nimport { formatWhen } from '@operato/utils/when.js'\nimport { p13n } from '@operato/p13n'\nimport { PageView } from '@operato/shell'\nimport { ButtonStyles, CommonGristStyles, CommonHeaderStyles, ScrollbarStyles, WorkSurfacePageStyles } from '@operato/styles'\nimport { isMobileDevice } from '@operato/utils'\n\nimport type { DataGrist, FetchOption } from '@operato/data-grist'\n\nimport { statusRenderer } from '../../components/user-status-renderer.js'\nimport { doorWords, lastDays, recordWords } from '../../utils/audit-words.js'\n\n/**\n * 감사 기록 (ADR-0141 §6.1) - who did what in this domain, read three ways: by person, by privilege,\n * and the requests one by one. A person's or a privilege's numbers open the requests behind them.\n *\n * Reading only. The audit has no door that changes a row (§4-6), so this screen has no action.\n */\n\ntype View = 'people' | 'doors' | 'records'\ntype Outcome = '' | 'denied' | 'error' | 'failed'\n\nconst PERIODS = [7, 30, 90]\nconst VIEWS: View[] = ['people', 'doors', 'records']\nconst OUTCOMES: Outcome[] = ['', 'denied', 'error', 'failed']\n\nconst AuditPageBase = p13n(localize(i18next)(PageView))\n\nfunction dateInputValue(date: Date) {\n const pad = (n: number) => String(n).padStart(2, '0')\n return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`\n}\n\n@customElement('audit-page')\nexport class AuditPage extends AuditPageBase {\n static styles: CSSResultGroup = [\n WorkSurfacePageStyles,\n ScrollbarStyles,\n CommonGristStyles,\n CommonHeaderStyles,\n ButtonStyles,\n css`\n .filters {\n flex-wrap: wrap;\n }\n\n [group] {\n display: flex;\n align-items: center;\n gap: var(--spacing-tiny, 2px);\n }\n\n button[aria-pressed='true'] {\n background-color: var(--md-sys-color-secondary-container);\n color: var(--md-sys-color-on-secondary-container);\n }\n\n input[type='date'] {\n height: var(--context-action-height, 28px);\n box-sizing: border-box;\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: var(--md-sys-shape-corner-small, 4px);\n background: var(--md-sys-color-surface-container-lowest);\n color: var(--md-sys-color-on-surface);\n font: inherit;\n font-size: var(--ui-t-body, 13px);\n padding: 0 var(--spacing-small, 4px);\n }\n\n [narrowed] {\n display: flex;\n align-items: center;\n gap: var(--spacing-small, 4px);\n font-size: var(--ui-t-body, 13px);\n color: var(--md-sys-color-on-surface);\n }\n\n [health] {\n margin-left: auto;\n font-size: var(--ui-t-body, 13px);\n color: var(--ui-crit, var(--md-sys-color-error));\n }\n `\n ]\n\n @property({ type: String }) mode: 'CARD' | 'GRID' | 'LIST' = isMobileDevice() ? 'LIST' : 'GRID'\n\n @state() private view: View = 'people'\n @state() private days: number | null = 30\n @state() private from: Date = lastDays(30).from\n @state() private to: Date = new Date()\n @state() private actor?: { id: string; name: string }\n @state() private door?: string\n @state() private outcome: Outcome = ''\n @state() private health?: { failures: number; since: string } | 'unknown'\n\n @query('ox-grist') private grist!: DataGrist\n\n get context() {\n return {\n title: i18next.t('audit.title'),\n help: 'auth/audit',\n filter: {\n handler: () => this.grist.toggleHeadroom()\n }\n }\n }\n\n render() {\n return html`${keyed(\n this.view,\n html`<ox-grist class=\"page-fill\" .mode=${this.mode} .config=${this.configOf(this.view)} .fetchHandler=${this.fetchHandler.bind(this)}>\n <div slot=\"headroom\" class=\"header\">\n <div class=\"filters\">${this.renderControls()}</div>\n </div>\n </ox-grist>`\n )}`\n }\n\n private renderControls() {\n const t = i18next.t.bind(i18next)\n const pressed = (on: boolean) => (on ? 'true' : 'false')\n\n return html`\n <div group>\n ${VIEWS.map(view => html`<button quiet aria-pressed=${pressed(this.view === view)} @click=${() => this.show(view)}>${t(`audit.view.${view}`)}</button>`)}\n </div>\n\n <div group>\n ${PERIODS.map(days => html`<button quiet aria-pressed=${pressed(this.days === days)} @click=${() => this.lastDays(days)}>${t('audit.period.days', { count: days })}</button>`)}\n <input type=\"date\" aria-label=${t('audit.period.from')} .value=${dateInputValue(this.from)} @change=${(e: Event) => this.pick('from', e)} />\n <span>~</span>\n <input type=\"date\" aria-label=${t('audit.period.to')} .value=${dateInputValue(this.to)} @change=${(e: Event) => this.pick('to', e)} />\n </div>\n\n ${this.view === 'records'\n ? html`<div group>\n ${OUTCOMES.map(outcome => html`<button quiet aria-pressed=${pressed(this.outcome === outcome)} @click=${() => this.narrow({ outcome })}>${t(outcome ? `audit.outcome.${outcome}` : 'audit.outcome.all')}</button>`)}\n </div>`\n : nothing}\n ${this.view === 'records' && this.actor\n ? html`<span narrowed>${this.actor.name}<button quiet small @click=${() => this.narrow({ actor: undefined })}>${t('audit.button.all-people')}</button></span>`\n : nothing}\n ${this.view === 'records' && this.door\n ? html`<span narrowed>${doorWords(t, this.door)}<button quiet small @click=${() => this.narrow({ door: undefined })}>${t('audit.button.all-privileges')}</button></span>`\n : nothing}\n ${this.renderHealth()}\n `\n }\n\n /* Only what is wrong is said: failures, or that they could not be counted. Never a 0 for \"unknown\". */\n private renderHealth() {\n const health = this.health\n if (health === 'unknown') return html`<span health>${i18next.t('audit.health.unknown')}</span>`\n if (!health?.failures) return nothing\n return html`<span health>${i18next.t('audit.health.failures', { count: health.failures, since: formatWhen(health.since, { lang: i18next.language, style: 'datetime' }) ?? '' })}</span>`\n }\n\n private show(view: View) {\n this.view = view\n }\n\n private lastDays(days: number) {\n const { from, to } = lastDays(days)\n this.days = days\n this.from = from\n this.to = to\n this.grist?.fetch()\n }\n\n private pick(which: 'from' | 'to', event: Event) {\n const value = (event.target as HTMLInputElement).value\n if (!value) return\n const [year, month, day] = value.split('-').map(Number)\n this[which] = which === 'from' ? new Date(year, month - 1, day, 0, 0, 0) : new Date(year, month - 1, day, 23, 59, 59, 999)\n this.days = null\n this.grist?.fetch()\n }\n\n private narrow(change: { actor?: { id: string; name: string }; door?: string; outcome?: Outcome }) {\n Object.assign(this, change)\n if (this.view !== 'records') this.view = 'records'\n else this.grist?.fetch()\n }\n\n async pageUpdated(_changes: any, _lifecycle: any) {\n if (this.active) {\n await this.updateComplete\n this.grist?.fetch()\n this.readHealth()\n }\n }\n\n private async readHealth() {\n try {\n const response = await client.query({ query: gql`{ auditHealth { failures since } }`, fetchPolicy: 'no-cache' })\n this.health = response.errors?.length || !response.data?.auditHealth ? 'unknown' : response.data.auditHealth\n } catch {\n this.health = 'unknown'\n }\n }\n\n private configOf(view: View) {\n const t = i18next.t.bind(i18next)\n const number = (name: string, header: string, click?: (record: any) => void) => ({\n type: 'integer',\n name,\n header,\n record: { align: 'right', editable: false },\n width: 90,\n ...(click ? { handlers: { click: (_c: any, _d: any, _col: any, record: any) => record && click(record) } } : {})\n })\n\n if (view === 'people') {\n const person = (record: any) => ({ id: record.id, name: record.name || record.email })\n return {\n rows: { appendable: false, selectable: false },\n pagination: { infinite: true },\n columns: [\n { type: 'gutter', gutterName: 'sequence' },\n {\n type: 'string',\n name: 'name',\n header: t('field.name'),\n record: { editable: false },\n width: 140,\n handlers: { click: (_c: any, _d: any, _col: any, record: any) => record && this.narrow({ actor: person(record), door: undefined, outcome: '' }) }\n },\n { type: 'string', name: 'email', header: t('field.email'), record: { editable: false }, width: 200 },\n { type: 'string', name: 'roles', header: t('field.roles'), record: { editable: false }, width: 200 },\n { type: 'string', name: 'status', header: t('field.status'), record: { editable: false, align: 'center', renderer: statusRenderer(() => 0) }, width: 80 },\n { type: 'datetime', name: 'lastCheckinAt', header: t('audit.field.last-checkin'), record: { editable: false }, width: 160 },\n number('checkins', t('audit.field.checkins')),\n number('failedSignins', t('audit.field.failed-signins'), record => this.narrow({ actor: person(record), door: undefined, outcome: 'failed' })),\n number('granted', t('audit.field.granted'), record => this.narrow({ actor: person(record), door: undefined, outcome: '' })),\n number('denied', t('audit.field.denied'), record => this.narrow({ actor: person(record), door: undefined, outcome: 'denied' }))\n ]\n }\n }\n\n if (view === 'doors') {\n return {\n rows: { appendable: false, selectable: false },\n pagination: { infinite: true },\n columns: [\n { type: 'gutter', gutterName: 'sequence' },\n {\n type: 'string',\n name: 'doorWords',\n header: t('audit.field.privilege'),\n record: { editable: false },\n width: 240,\n handlers: { click: (_c: any, _d: any, _col: any, record: any) => record && this.narrow({ door: record.door, actor: undefined, outcome: '' }) }\n },\n number('people', t('audit.field.people')),\n number('granted', t('audit.field.granted'), record => this.narrow({ door: record.door, actor: undefined, outcome: '' })),\n number('denied', t('audit.field.denied'), record => this.narrow({ door: record.door, actor: undefined, outcome: 'denied' }))\n ]\n }\n }\n\n return {\n rows: { appendable: false, selectable: false },\n pagination: { pages: [50, 100, 200] },\n columns: [\n { type: 'gutter', gutterName: 'sequence' },\n { type: 'datetime', name: 'createdAt', header: t('audit.field.time'), record: { editable: false }, width: 160 },\n { type: 'string', name: 'actorName', header: t('audit.field.actor'), record: { editable: false }, width: 120 },\n { type: 'string', name: 'operation', header: t('audit.field.request'), record: { editable: false }, width: 200 },\n { type: 'string', name: 'what', header: t('audit.field.actions'), record: { editable: false }, width: 360 },\n { type: 'string', name: 'ip', header: t('audit.field.address'), record: { editable: false }, width: 120 },\n { type: 'integer', name: 'durationMs', header: t('audit.field.duration'), record: { editable: false, align: 'right' }, width: 80 }\n ]\n }\n }\n\n async fetchHandler({ page, limit }: FetchOption) {\n const t = i18next.t.bind(i18next)\n const period = { from: this.from.toISOString(), to: this.to.toISOString() }\n\n if (this.view === 'people') {\n const response = await client.query({\n query: gql`\n query ($from: DateTimeISO!, $to: DateTimeISO!) {\n auditPeople(from: $from, to: $to) {\n user {\n id\n name\n email\n status\n }\n roles\n lastCheckinAt\n checkins\n failedSignins\n granted\n denied\n }\n }\n `,\n variables: period,\n fetchPolicy: 'no-cache'\n })\n const people = response.data?.auditPeople || []\n const records = people\n .map((one: any) => ({\n ...one,\n id: one.user.id,\n name: one.user.name,\n email: one.user.email,\n roles: one.roles.join(', '),\n status: one.user.status\n }))\n .sort((a: any, b: any) => String(b.lastCheckinAt || '').localeCompare(String(a.lastCheckinAt || '')))\n return { total: records.length, records }\n }\n\n if (this.view === 'doors') {\n const response = await client.query({\n query: gql`\n query ($from: DateTimeISO!, $to: DateTimeISO!) {\n auditDoors(from: $from, to: $to) {\n door\n people\n granted\n denied\n }\n }\n `,\n variables: period,\n fetchPolicy: 'no-cache'\n })\n /* The doors used come first; the rest keep the server's order. */\n const used = (one: any) => one.granted + one.denied\n const records = (response.data?.auditDoors || [])\n .map((one: any) => ({ ...one, doorWords: doorWords(t, one.door) }))\n .sort((a: any, b: any) => used(b) - used(a))\n return { total: records.length, records }\n }\n\n const response = await client.query({\n query: gql`\n query ($filter: AuditRecordFilter!) {\n auditRecords(filter: $filter) {\n items {\n request {\n id\n createdAt\n actor {\n id\n name\n email\n }\n ip\n channel\n operationType\n operationName\n fields\n route\n durationMs\n }\n actions {\n kind\n door\n decision\n outcome\n code\n count\n objectId\n detail\n }\n }\n total\n }\n }\n `,\n variables: {\n filter: {\n ...period,\n actorId: this.actor?.id,\n door: this.door,\n outcome: this.outcome || undefined,\n page,\n limit\n }\n },\n fetchPolicy: 'no-cache'\n })\n\n const list = response.data?.auditRecords\n return {\n total: list?.total || 0,\n records: (list?.items || []).map(({ request, actions }: any) => ({\n id: request.id,\n createdAt: request.createdAt,\n actorName: request.actor?.name || request.actor?.email || '',\n what: recordWords(t, actions),\n operation: request.channel === 'graphql' ? [request.operationType, request.fields || request.operationName].filter(Boolean).join(' ') : request.route || '',\n ip: request.ip,\n durationMs: request.durationMs\n }))\n }\n }\n}\n"]}
1
+ {"version":3,"file":"audit-page.js","sourceRoot":"","sources":["../../../client/pages/audit/audit-page.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/icon/icon.js'\nimport '@operato/data-grist'\nimport '@operato/input/ox-input-select-buttons.js'\n\nimport gql from 'graphql-tag'\nimport { css, html, nothing } from 'lit'\nimport type { CSSResultGroup } from 'lit'\nimport { customElement, property, query, state } from 'lit/decorators.js'\nimport { keyed } from 'lit/directives/keyed.js'\n\nimport { client } from '@operato/graphql'\nimport { i18next, localize } from '@operato/i18n'\nimport { formatWhen } from '@operato/utils/when.js'\nimport { p13n } from '@operato/p13n'\nimport { PageView } from '@operato/shell'\nimport { ButtonStyles, CommonGristStyles, CommonHeaderStyles, ScrollbarStyles, WorkSurfacePageStyles } from '@operato/styles'\nimport { isMobileDevice } from '@operato/utils'\n\nimport type { DataGrist, FetchOption } from '@operato/data-grist'\n\nimport { statusRenderer } from '../../components/user-status-renderer.js'\nimport { doorWords, lastDays, recordWords } from '../../utils/audit-words.js'\n\n/**\n * 감사 기록 (ADR-0141 §6.1) - who did what in this domain, read three ways: by person, by privilege,\n * and the requests one by one. A person's or a privilege's numbers open the requests behind them.\n *\n * Reading only. The audit has no door that changes a row (§4-6), so this screen has no action.\n */\n\ntype View = 'people' | 'doors' | 'records'\ntype Outcome = '' | 'denied' | 'error' | 'failed'\n\nconst PERIODS = [7, 30, 90]\nconst VIEWS: View[] = ['people', 'doors', 'records']\nconst OUTCOMES: Outcome[] = ['', 'denied', 'error', 'failed']\n\nconst AuditPageBase = p13n(localize(i18next)(PageView))\n\nfunction dateInputValue(date: Date) {\n const pad = (n: number) => String(n).padStart(2, '0')\n return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`\n}\n\n@customElement('audit-page')\nexport class AuditPage extends AuditPageBase {\n static styles: CSSResultGroup = [\n WorkSurfacePageStyles,\n ScrollbarStyles,\n CommonGristStyles,\n CommonHeaderStyles,\n ButtonStyles,\n css`\n .filters {\n flex-wrap: wrap;\n }\n\n [group] {\n display: flex;\n align-items: center;\n gap: var(--spacing-tiny, 2px);\n }\n\n input[type='date'] {\n height: var(--context-action-height, 28px);\n box-sizing: border-box;\n border: 1px solid var(--md-sys-color-outline-variant);\n border-radius: var(--md-sys-shape-corner-small, 4px);\n background: var(--md-sys-color-surface-container-lowest);\n color: var(--md-sys-color-on-surface);\n font: inherit;\n font-size: var(--ui-t-body, 13px);\n padding: 0 var(--spacing-small, 4px);\n }\n\n [narrowed] {\n display: flex;\n align-items: center;\n gap: var(--spacing-small, 4px);\n font-size: var(--ui-t-body, 13px);\n color: var(--md-sys-color-on-surface);\n }\n\n [health] {\n margin-left: auto;\n font-size: var(--ui-t-body, 13px);\n color: var(--ui-crit, var(--md-sys-color-error));\n }\n `\n ]\n\n @property({ type: String }) mode: 'CARD' | 'GRID' | 'LIST' = isMobileDevice() ? 'LIST' : 'GRID'\n\n @state() private view: View = 'people'\n @state() private days: number | null = 30\n @state() private from: Date = lastDays(30).from\n @state() private to: Date = new Date()\n @state() private actor?: { id: string; name: string }\n @state() private door?: string\n @state() private outcome: Outcome = ''\n /** How many records each outcome chip would show under the other conditions, counted by the records' own door. */\n @state() private outcomeCounts = new Map<string, number>()\n @state() private health?: { failures: number; since: string } | 'unknown'\n\n @query('ox-grist') private grist!: DataGrist\n\n get context() {\n return {\n title: i18next.t('audit.title'),\n help: 'auth/audit',\n filter: {\n handler: () => this.grist.toggleHeadroom()\n }\n }\n }\n\n render() {\n return html`${keyed(\n this.view,\n html`<ox-grist class=\"page-fill\" .mode=${this.mode} .config=${this.configOf(this.view)} .fetchHandler=${this.fetchHandler.bind(this)}>\n <div slot=\"headroom\" class=\"header\">\n <div class=\"filters\">${this.renderControls()}</div>\n </div>\n </ox-grist>`\n )}`\n }\n\n private renderControls() {\n const t = i18next.t.bind(i18next)\n /* One of each is always chosen: a press on the chosen chip leaves it chosen. */\n const choose = (e: CustomEvent, chosen: string | undefined, pick: (value: string) => void) => {\n if (e.detail) pick(e.detail)\n else (e.target as any).value = chosen\n }\n\n return html`\n <ox-input-select-buttons\n .options=${VIEWS.map(view => ({ value: view, display: t(`audit.view.${view}`) }))}\n .value=${this.view}\n @change=${(e: CustomEvent) => choose(e, this.view, view => this.show(view as View))}\n ></ox-input-select-buttons>\n\n <div group>\n <ox-input-select-buttons\n .options=${PERIODS.map(days => ({ value: String(days), display: t('audit.period.days', { count: days }) }))}\n .value=${this.days ? String(this.days) : undefined}\n @change=${(e: CustomEvent) => choose(e, this.days ? String(this.days) : undefined, days => this.lastDays(Number(days)))}\n ></ox-input-select-buttons>\n <input type=\"date\" aria-label=${t('audit.period.from')} .value=${dateInputValue(this.from)} @change=${(e: Event) => this.pick('from', e)} />\n <span>~</span>\n <input type=\"date\" aria-label=${t('audit.period.to')} .value=${dateInputValue(this.to)} @change=${(e: Event) => this.pick('to', e)} />\n </div>\n\n ${this.view === 'records'\n ? html`<ox-input-select-buttons\n .options=${OUTCOMES.map(outcome => ({\n value: outcome || 'all',\n display: t(outcome ? `audit.outcome.${outcome}` : 'audit.outcome.all'),\n count: this.outcomeCounts.get(outcome || 'all') ?? 0\n }))}\n .value=${this.outcome || 'all'}\n @change=${(e: CustomEvent) => choose(e, this.outcome || 'all', outcome => this.narrow({ outcome: (outcome === 'all' ? '' : outcome) as Outcome }))}\n ></ox-input-select-buttons>`\n : nothing}\n ${this.view === 'records' && this.actor\n ? html`<span narrowed>${this.actor.name}<button quiet small @click=${() => this.narrow({ actor: undefined })}>${t('audit.button.all-people')}</button></span>`\n : nothing}\n ${this.view === 'records' && this.door\n ? html`<span narrowed>${doorWords(t, this.door)}<button quiet small @click=${() => this.narrow({ door: undefined })}>${t('audit.button.all-privileges')}</button></span>`\n : nothing}\n ${this.renderHealth()}\n `\n }\n\n /* Only what is wrong is said: failures, or that they could not be counted. Never a 0 for \"unknown\". */\n private renderHealth() {\n const health = this.health\n if (health === 'unknown') return html`<span health>${i18next.t('audit.health.unknown')}</span>`\n if (!health?.failures) return nothing\n return html`<span health>${i18next.t('audit.health.failures', { count: health.failures, since: formatWhen(health.since, { lang: i18next.language, style: 'datetime' }) ?? '' })}</span>`\n }\n\n private show(view: View) {\n this.view = view\n }\n\n private lastDays(days: number) {\n const { from, to } = lastDays(days)\n this.days = days\n this.from = from\n this.to = to\n this.grist?.fetch()\n }\n\n private pick(which: 'from' | 'to', event: Event) {\n const value = (event.target as HTMLInputElement).value\n if (!value) return\n const [year, month, day] = value.split('-').map(Number)\n this[which] = which === 'from' ? new Date(year, month - 1, day, 0, 0, 0) : new Date(year, month - 1, day, 23, 59, 59, 999)\n this.days = null\n this.grist?.fetch()\n }\n\n /*\n * The outcome chips' numbers - the records' own door asked for its total once per outcome, under the period, the\n * person and the privilege narrowed to, in one request.\n */\n private async countOutcomes(filter: { from: string; to: string; actorId?: string; door?: string }) {\n const response = await client.query({\n query: gql`\n query (${OUTCOMES.map((_, at) => `$f${at}: AuditRecordFilter!`).join(', ')}) {\n ${OUTCOMES.map((_, at) => `c${at}: auditRecords(filter: $f${at}) { total }`).join('\\n')}\n }\n `,\n variables: Object.fromEntries(OUTCOMES.map((outcome, at) => [`f${at}`, { ...filter, outcome: outcome || undefined, page: 1, limit: 1 }])),\n fetchPolicy: 'no-cache'\n })\n if (response.errors?.length) return console.error(response.errors)\n this.outcomeCounts = new Map(OUTCOMES.map((outcome, at) => [outcome || 'all', response.data?.[`c${at}`]?.total ?? 0]))\n }\n\n private narrow(change: { actor?: { id: string; name: string }; door?: string; outcome?: Outcome }) {\n Object.assign(this, change)\n if (this.view !== 'records') this.view = 'records'\n else this.grist?.fetch()\n }\n\n async pageUpdated(_changes: any, _lifecycle: any) {\n if (this.active) {\n await this.updateComplete\n this.grist?.fetch()\n this.readHealth()\n }\n }\n\n private async readHealth() {\n try {\n const response = await client.query({ query: gql`{ auditHealth { failures since } }`, fetchPolicy: 'no-cache' })\n this.health = response.errors?.length || !response.data?.auditHealth ? 'unknown' : response.data.auditHealth\n } catch {\n this.health = 'unknown'\n }\n }\n\n private configOf(view: View) {\n const t = i18next.t.bind(i18next)\n const number = (name: string, header: string, click?: (record: any) => void) => ({\n type: 'integer',\n name,\n header,\n record: { align: 'right', editable: false },\n width: 90,\n ...(click ? { handlers: { click: (_c: any, _d: any, _col: any, record: any) => record && click(record) } } : {})\n })\n\n if (view === 'people') {\n const person = (record: any) => ({ id: record.id, name: record.name || record.email })\n return {\n rows: { appendable: false, selectable: false },\n pagination: { infinite: true },\n columns: [\n { type: 'gutter', gutterName: 'sequence' },\n {\n type: 'string',\n name: 'name',\n header: t('field.name'),\n record: { editable: false },\n width: 140,\n handlers: { click: (_c: any, _d: any, _col: any, record: any) => record && this.narrow({ actor: person(record), door: undefined, outcome: '' }) }\n },\n { type: 'string', name: 'email', header: t('field.email'), record: { editable: false }, width: 200 },\n { type: 'string', name: 'roles', header: t('field.roles'), record: { editable: false }, width: 200 },\n { type: 'string', name: 'status', header: t('field.status'), record: { editable: false, align: 'center', renderer: statusRenderer(() => 0) }, width: 80 },\n { type: 'datetime', name: 'lastCheckinAt', header: t('audit.field.last-checkin'), record: { editable: false }, width: 160 },\n number('checkins', t('audit.field.checkins')),\n number('failedSignins', t('audit.field.failed-signins'), record => this.narrow({ actor: person(record), door: undefined, outcome: 'failed' })),\n number('granted', t('audit.field.granted'), record => this.narrow({ actor: person(record), door: undefined, outcome: '' })),\n number('denied', t('audit.field.denied'), record => this.narrow({ actor: person(record), door: undefined, outcome: 'denied' }))\n ]\n }\n }\n\n if (view === 'doors') {\n return {\n rows: { appendable: false, selectable: false },\n pagination: { infinite: true },\n columns: [\n { type: 'gutter', gutterName: 'sequence' },\n {\n type: 'string',\n name: 'doorWords',\n header: t('audit.field.privilege'),\n record: { editable: false },\n width: 240,\n handlers: { click: (_c: any, _d: any, _col: any, record: any) => record && this.narrow({ door: record.door, actor: undefined, outcome: '' }) }\n },\n number('people', t('audit.field.people')),\n number('granted', t('audit.field.granted'), record => this.narrow({ door: record.door, actor: undefined, outcome: '' })),\n number('denied', t('audit.field.denied'), record => this.narrow({ door: record.door, actor: undefined, outcome: 'denied' }))\n ]\n }\n }\n\n return {\n rows: { appendable: false, selectable: false },\n pagination: { pages: [50, 100, 200] },\n columns: [\n { type: 'gutter', gutterName: 'sequence' },\n { type: 'datetime', name: 'createdAt', header: t('audit.field.time'), record: { editable: false }, width: 160 },\n { type: 'string', name: 'actorName', header: t('audit.field.actor'), record: { editable: false }, width: 120 },\n { type: 'string', name: 'operation', header: t('audit.field.request'), record: { editable: false }, width: 200 },\n { type: 'string', name: 'what', header: t('audit.field.actions'), record: { editable: false }, width: 360 },\n { type: 'string', name: 'ip', header: t('audit.field.address'), record: { editable: false }, width: 120 },\n { type: 'integer', name: 'durationMs', header: t('audit.field.duration'), record: { editable: false, align: 'right' }, width: 80 }\n ]\n }\n }\n\n async fetchHandler({ page, limit }: FetchOption) {\n const t = i18next.t.bind(i18next)\n const period = { from: this.from.toISOString(), to: this.to.toISOString() }\n\n if (this.view === 'people') {\n const response = await client.query({\n query: gql`\n query ($from: DateTimeISO!, $to: DateTimeISO!) {\n auditPeople(from: $from, to: $to) {\n user {\n id\n name\n email\n status\n }\n roles\n lastCheckinAt\n checkins\n failedSignins\n granted\n denied\n }\n }\n `,\n variables: period,\n fetchPolicy: 'no-cache'\n })\n const people = response.data?.auditPeople || []\n const records = people\n .map((one: any) => ({\n ...one,\n id: one.user.id,\n name: one.user.name,\n email: one.user.email,\n roles: one.roles.join(', '),\n status: one.user.status\n }))\n .sort((a: any, b: any) => String(b.lastCheckinAt || '').localeCompare(String(a.lastCheckinAt || '')))\n return { total: records.length, records }\n }\n\n if (this.view === 'doors') {\n const response = await client.query({\n query: gql`\n query ($from: DateTimeISO!, $to: DateTimeISO!) {\n auditDoors(from: $from, to: $to) {\n door\n people\n granted\n denied\n }\n }\n `,\n variables: period,\n fetchPolicy: 'no-cache'\n })\n /* The doors used come first; the rest keep the server's order. */\n const used = (one: any) => one.granted + one.denied\n const records = (response.data?.auditDoors || [])\n .map((one: any) => ({ ...one, doorWords: doorWords(t, one.door) }))\n .sort((a: any, b: any) => used(b) - used(a))\n return { total: records.length, records }\n }\n\n this.countOutcomes({ ...period, actorId: this.actor?.id, door: this.door })\n const response = await client.query({\n query: gql`\n query ($filter: AuditRecordFilter!) {\n auditRecords(filter: $filter) {\n items {\n request {\n id\n createdAt\n actor {\n id\n name\n email\n }\n ip\n channel\n operationType\n operationName\n fields\n route\n durationMs\n }\n actions {\n kind\n door\n decision\n outcome\n code\n count\n objectId\n detail\n }\n }\n total\n }\n }\n `,\n variables: {\n filter: {\n ...period,\n actorId: this.actor?.id,\n door: this.door,\n outcome: this.outcome || undefined,\n page,\n limit\n }\n },\n fetchPolicy: 'no-cache'\n })\n\n const list = response.data?.auditRecords\n return {\n total: list?.total || 0,\n records: (list?.items || []).map(({ request, actions }: any) => ({\n id: request.id,\n createdAt: request.createdAt,\n actorName: request.actor?.name || request.actor?.email || '',\n what: recordWords(t, actions),\n operation: request.channel === 'graphql' ? [request.operationType, request.fields || request.operationName].filter(Boolean).join(' ') : request.route || '',\n ip: request.ip,\n durationMs: request.durationMs\n }))\n }\n }\n}\n"]}