Skip to content

Commit 4735d42

Browse files
committed
Add audit logs
Also allow team tabs to match sub routes. Cleanup duplicate component (time & humantime)
1 parent 6a892de commit 4735d42

File tree

12 files changed

+193
-78
lines changed

12 files changed

+193
-78
lines changed

src/lib/HumanTime.svelte

-42
This file was deleted.

src/lib/LogViewer.svelte

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<script lang="ts">
22
import { browser } from '$app/environment';
33
import { graphql } from '$houdini';
4-
import HumanTime from '$lib/HumanTime.svelte';
4+
import Time from '$lib/Time.svelte';
55
import { createEventDispatcher, onDestroy, tick } from 'svelte';
66
import { get } from 'svelte/store';
77
@@ -182,7 +182,7 @@
182182
<div class="logline {getLogLevel(log.message)}">
183183
{#if showTime}
184184
<span class="timestamp">
185-
<HumanTime time={log.time} dateFormat="yyyy-MM-dd HH:mm:ss" />
185+
<Time time={log.time} dateFormat="yyyy-MM-dd HH:mm:ss" />
186186
</span>
187187
{/if}
188188
{#if showLevel}

src/lib/Pagination.svelte

+2-2
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@
2020
</script>
2121

2222
{#if !pageInfo || pageInfo.totalCount == PendingValue}
23-
<div />
23+
<!-- nothing -->
2424
{:else if pageInfo.totalCount <= limit}
25-
<div />
25+
<!-- nothing -->
2626
{:else}
2727
<Pagination
2828
count={count(pageInfo.totalCount)}

src/routes/team/[team]/(teamTabs)/+layout.svelte

+36-13
Original file line numberDiff line numberDiff line change
@@ -14,41 +14,60 @@
1414
const nav = [
1515
{
1616
tab: 'Overview',
17-
routeId: '/team/[team]/(teamTabs)'
17+
routeId: '/team/[team]/(teamTabs)',
18+
withSubRoutes: false
1819
},
1920
{
2021
tab: 'Apps',
21-
routeId: '/team/[team]/(teamTabs)/applications'
22+
routeId: '/team/[team]/(teamTabs)/applications',
23+
withSubRoutes: true
2224
},
2325
{
2426
tab: 'Jobs',
25-
routeId: '/team/[team]/(teamTabs)/jobs'
27+
routeId: '/team/[team]/(teamTabs)/jobs',
28+
withSubRoutes: true
2629
},
2730
{
2831
tab: 'Members',
29-
routeId: '/team/[team]/(teamTabs)/members'
32+
routeId: '/team/[team]/(teamTabs)/members',
33+
withSubRoutes: true
3034
},
3135
{
3236
tab: 'Deploys',
33-
routeId: '/team/[team]/(teamTabs)/deploy'
37+
routeId: '/team/[team]/(teamTabs)/deploy',
38+
withSubRoutes: true
3439
},
3540
{
3641
tab: 'Cost',
37-
routeId: '/team/[team]/(teamTabs)/cost'
42+
routeId: '/team/[team]/(teamTabs)/cost',
43+
withSubRoutes: true
3844
},
3945
{
4046
tab: 'Utilization',
41-
routeId: '/team/[team]/(teamTabs)/utilization'
47+
routeId: '/team/[team]/(teamTabs)/utilization',
48+
withSubRoutes: true
4249
},
4350
{
4451
tab: 'Vulnerabilities',
45-
routeId: '/team/[team]/(teamTabs)/vulnerabilities'
52+
routeId: '/team/[team]/(teamTabs)/vulnerabilities',
53+
withSubRoutes: true
4654
},
4755
{
4856
tab: 'Repositories',
49-
routeId: '/team/[team]/(teamTabs)/repositories'
57+
routeId: '/team/[team]/(teamTabs)/repositories',
58+
withSubRoutes: true
5059
}
5160
];
61+
62+
const isActive = (current: string | null, routeID: string, allWithPrefix = false) => {
63+
if (current === routeID) {
64+
return true;
65+
}
66+
if (current && allWithPrefix) {
67+
return current.startsWith(routeID);
68+
}
69+
return false;
70+
};
5271
</script>
5372

5473
<svelte:head><title>{team} - Console</title></svelte:head>
@@ -57,19 +76,23 @@
5776
<h2>{team}</h2>
5877
</div>
5978
<Tabs>
60-
{#each nav as { tab, routeId }}
61-
<Tab href={replacer(routeId, { team })} active={currentRoute == routeId} title={tab} />
79+
{#each nav as { tab, routeId, withSubRoutes }}
80+
<Tab
81+
href={replacer(routeId, { team })}
82+
active={isActive(currentRoute, routeId, withSubRoutes)}
83+
title={tab}
84+
/>
6285
{/each}
6386
{#if $TeamRoles.data}
6487
{#if $TeamRoles.data.team !== PendingValue && ($TeamRoles.data.team.viewerIsMember || $TeamRoles.data.team.viewerIsOwner)}
6588
<Tab
6689
href={replacer('/team/[team]/(teamTabs)/secrets', { team })}
67-
active={currentRoute == '/team/[team]/(teamTabs)/secrets'}
90+
active={isActive(currentRoute, '/team/[team]/(teamTabs)/secrets', true)}
6891
title="Secrets"
6992
/>
7093
<Tab
7194
href={replacer('/team/[team]/(teamTabs)/settings', { team })}
72-
active={currentRoute == '/team/[team]/(teamTabs)/settings'}
95+
active={isActive(currentRoute, '/team/[team]/(teamTabs)/settings', true)}
7396
title="Settings"
7497
/>
7598
{/if}

src/routes/team/[team]/(teamTabs)/secrets/+page.svelte

+9-9
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
11
<script lang="ts">
2+
import { page } from '$app/stores';
23
import Card from '$lib/Card.svelte';
4+
import Time from '$lib/Time.svelte';
35
import {
6+
Alert,
7+
Button,
8+
Loader,
49
Table,
5-
Thead,
610
Tbody,
711
Td,
812
Th,
9-
Alert,
10-
Loader,
11-
Tr,
12-
Button
13+
Thead,
14+
Tr
1315
} from '@nais/ds-svelte-community';
16+
import { PlusIcon } from '@nais/ds-svelte-community/icons';
1417
import type { PageData } from './$houdini';
15-
import { page } from '$app/stores';
1618
import CreateSecret from './CreateSecret.svelte';
17-
import HumanTime from '$lib/HumanTime.svelte';
18-
import { PlusIcon } from '@nais/ds-svelte-community/icons';
1919
2020
export let data: PageData;
2121
@@ -69,7 +69,7 @@
6969
</Td>
7070
<Td align="right">
7171
{#if secret.lastModifiedAt}
72-
<HumanTime time={secret.lastModifiedAt} distance />
72+
<Time time={secret.lastModifiedAt} distance />
7373
{:else}
7474
<code>n/a</code>
7575
{/if}

src/routes/team/[team]/(teamTabs)/settings/+page.gql

+10
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,15 @@ query TeamSettings($team: Slug!) {
1313
gcpProjectID
1414
slackAlertsChannel
1515
}
16+
auditLogs(limit: 5) @loading {
17+
nodes @loading(count: 5) {
18+
actor
19+
message
20+
createdAt
21+
}
22+
pageInfo @loading {
23+
hasNextPage @loading
24+
}
25+
}
1626
}
1727
}

src/routes/team/[team]/(teamTabs)/settings/+page.svelte

+27
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import { slide } from 'svelte/transition';
1616
import type { PageData } from './$houdini';
1717
import EditText from './EditText.svelte';
18+
import LogLine from './LogLine.svelte';
1819
1920
export let data: PageData;
2021
@@ -397,6 +398,28 @@
397398
>
398399
</Modal>
399400
{/if}
401+
402+
<Card columns={12}>
403+
<h3>Logs</h3>
404+
405+
{#each teamSettings.auditLogs.nodes as log}
406+
{#if log !== PendingValue}
407+
<LogLine {log} />
408+
{:else}
409+
<Skeleton variant="text" />
410+
{/if}
411+
{:else}
412+
<p>No audit logs</p>
413+
{/each}
414+
415+
{#if teamSettings.auditLogs.pageInfo.hasNextPage !== PendingValue && teamSettings.auditLogs.pageInfo.hasNextPage}
416+
<div class="center">
417+
<Button variant="secondary" size="medium" as="a" href="/team/{team}/settings/audit_logs">
418+
Show more logs
419+
</Button>
420+
</div>
421+
{/if}
422+
</Card>
400423
</div>
401424
{/if}
402425

@@ -455,4 +478,8 @@
455478
justify-content: space-between;
456479
align-items: center;
457480
}
481+
482+
.center {
483+
text-align: center;
484+
}
458485
</style>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<script lang="ts">
2+
import Time from '$lib/Time.svelte';
3+
import { BodyShort } from '@nais/ds-svelte-community';
4+
5+
export let log: {
6+
message: string;
7+
createdAt: Date;
8+
actor: string | null;
9+
};
10+
</script>
11+
12+
<div class="log-line">
13+
<p>
14+
{log.message}
15+
</p>
16+
<BodyShort size="small" style="color: var(--a-text-subtle)">
17+
<Time time={log.createdAt} dateFormat="dd. MMM yyyy HH:mm:ss" /> -
18+
{log.actor || 'Unknown'}
19+
</BodyShort>
20+
</div>
21+
22+
<style>
23+
.log-line p {
24+
margin: 0;
25+
}
26+
27+
.log-line:is(:not(:last-child)) {
28+
border-bottom: 1px solid var(--a-border-divider);
29+
padding-bottom: 1rem;
30+
margin-bottom: 1rem;
31+
}
32+
</style>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
query TeamAuditLogs($team: Slug!, $limit: Int, $offset: Int) {
2+
team(slug: $team) @loading {
3+
auditLogs(limit: $limit, offset: $offset) @loading {
4+
nodes @loading(count: 15) {
5+
actor
6+
message
7+
createdAt
8+
}
9+
pageInfo @loading {
10+
hasNextPage @loading
11+
totalCount @loading
12+
}
13+
}
14+
}
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<script lang="ts">
2+
import { PendingValue } from '$houdini';
3+
import Card from '$lib/Card.svelte';
4+
import Pagination from '$lib/Pagination.svelte';
5+
import { changeParams, limitOffset } from '$lib/pagination';
6+
import { Skeleton } from '@nais/ds-svelte-community';
7+
import LogLine from '../LogLine.svelte';
8+
import type { PageData } from './$houdini';
9+
10+
export let data: PageData;
11+
12+
$: ({ TeamAuditLogs } = data);
13+
$: ({ limit, offset } = limitOffset($TeamAuditLogs.variables));
14+
</script>
15+
16+
{#if $TeamAuditLogs.data}
17+
<Card>
18+
<h3>Audit logs</h3>
19+
20+
{#each $TeamAuditLogs.data.team.auditLogs.nodes || [] as log}
21+
{#if log !== PendingValue}
22+
<LogLine {log} />
23+
{:else}
24+
<Skeleton variant="text" />
25+
{/if}
26+
{:else}
27+
<p>No audit logs</p>
28+
{/each}
29+
30+
<Pagination
31+
pageInfo={$TeamAuditLogs.data.team.auditLogs.pageInfo}
32+
{limit}
33+
{offset}
34+
changePage={(page) => {
35+
changeParams({ page: page.toString() });
36+
}}
37+
/>
38+
</Card>
39+
{/if}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { error } from '@sveltejs/kit';
2+
import type { TeamAuditLogsVariables } from './$houdini';
3+
export const _TeamAuditLogsVariables: TeamAuditLogsVariables = ({ params, url }) => {
4+
const page = parseInt(url.searchParams.get('page') || '1');
5+
if (!page || page < 1) {
6+
throw error(400, 'Bad pagenumber');
7+
}
8+
const limit = 50;
9+
const offset = (page - 1) * limit;
10+
11+
return { limit, offset, team: params.team };
12+
};

0 commit comments

Comments
 (0)