Skip to content

Commit b9f398d

Browse files
committed
Add support for AuditEvents
1 parent 11c9bf8 commit b9f398d

File tree

3 files changed

+134
-1
lines changed

3 files changed

+134
-1
lines changed

MyApp/Configure.Db.Migrations.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public void Configure(IWebHostBuilder builder) => builder
4242
});
4343
AppTasks.Register("migrate.revert", args => migrator.Revert(args[0]));
4444
AppTasks.Register("migrate.rerun", args => migrator.Rerun(args[0]));
45-
AppTasks.Register("App.json", args => // Default App.db
45+
AppTasks.Register("App.json", args => // Default App.db
4646
appHost.VirtualFiles.WriteFile("App_Data/App.json", ClientConfig.ToSystemJson(
4747
migrator.DbFactory.GetTables(namedConnection:null, schema:null))));
4848

MyApp/wwwroot/mjs/app.mjs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { createApp, reactive, ref, computed } from "vue"
22
import { JsonServiceClient, $1, $$ } from "@servicestack/client"
33
import ServiceStackVue from "@servicestack/vue"
44
import GettingStarted from "./components/GettingStarted.mjs"
5+
import AuditEvents from "./components/AuditEvents.mjs"
56

67
let client = null, Apps = []
78
let AppData = {
@@ -12,6 +13,7 @@ export { client, Apps }
1213
/** Shared Global Components */
1314
const Components = {
1415
GettingStarted,
16+
AuditEvents,
1517
}
1618
const CustomElements = [
1719
'lite-youtube'
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
import { inject, onMounted, ref } from "vue"
2+
import { useClient, useFormatters } from "@servicestack/vue"
3+
import { QueryDb, QueryResponse } from "../dtos.mjs"
4+
5+
export class GetCrudEvents extends QueryDb
6+
{
7+
/** @param {{authSecret?:string,model?:string,modelId?:string}} [init] */
8+
constructor(init) { super(init); Object.assign(this, init) }
9+
/** @type {string} */
10+
authSecret;
11+
/** @type {string} */
12+
model;
13+
/** @type {string} */
14+
modelId;
15+
getTypeName() { return 'GetCrudEvents' }
16+
getMethod() { return 'GET' }
17+
createResponse() { return new QueryResponse() }
18+
}
19+
20+
export const AuditEvents = {
21+
template:/*html*/`
22+
<div v-if="events.length">
23+
<div class="flex justify-center">
24+
<button type="button" @click="open=!open"
25+
class="px-1 py-1.5 group text-gray-700 font-medium flex items-center" aria-expanded="false">
26+
<svg class="flex-none w-5 h-5 mr-2 text-gray-400 group-hover:text-gray-500" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
27+
<g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><path d="M3 3v5h5"/><path d="M3.05 13A9 9 0 1 0 6 5.3L3 8"/><path d="M12 7v5l4 2"/></g>
28+
</svg>
29+
<span class="mr-1">
30+
{{eventsCount}} Audit {{ eventsCount === 1 ? 'Event' : 'Events' }}
31+
</span>
32+
<svg v-if="!open"
33+
class="h-5 w-5 text-gray-400 group-hover:text-gray-500" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
34+
<path fill-rule="evenodd" d="M10 5a1 1 0 011 1v3h3a1 1 0 110 2h-3v3a1 1 0 11-2 0v-3H6a1 1 0 110-2h3V6a1 1 0 011-1z" clip-rule="evenodd" />
35+
</svg>
36+
<svg v-else
37+
class="h-5 w-5 text-gray-400 group-hover:text-gray-500" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
38+
<path fill-rule="evenodd" d="M5 10a1 1 0 011-1h8a1 1 0 110 2H6a1 1 0 01-1-1z" clip-rule="evenodd" />
39+
</svg>
40+
</button>
41+
</div>
42+
<div v-if="open" class="flex">
43+
<ul v-if="events.length" class="divide-y divide-gray-200 border-t w-full">
44+
<li v-for="x in events" :key="x.id" class="py-4 cursor-pointer" @click="toggle(x.id)">
45+
<div class="flex justify-between">
46+
<div class="pl-4 uppercase inline-block w-20">{{ x.eventType }}</div>
47+
<div>
48+
<span :title="'User ' + x.userAuthId">{{ x.userAuthName }}</span>
49+
</div>
50+
<div>
51+
<span :title="formatDate(x.eventDate)">{{ relativeTime(x.eventDate) }}</span>
52+
<div class="ml-3 inline-block w-5 align-middle">
53+
<svg v-if="!expanded(x.id)" class="h-5 w-5 flex-none text-gray-400" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
54+
<path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd"/>
55+
</svg>
56+
</div>
57+
</div>
58+
</div>
59+
<div class="flex space-x-3">
60+
<div class="flex-1 flex flex-col">
61+
<div v-if="expanded(x.id)" class="p-4">
62+
<table>
63+
<tr>
64+
<td class="font-medium">API</td>
65+
<td>{{ x.requestType }}</td>
66+
</tr>
67+
<tr>
68+
<td class="pr-4 font-medium">User Id</td>
69+
<td>{{ x.userAuthId }}</td>
70+
</tr>
71+
<tr>
72+
<td class="pr-4 font-medium">Date</td>
73+
<td>{{ formatDate(x.eventDate) }}</td>
74+
</tr>
75+
<tr>
76+
<td class="pr-4 font-medium">IP</td>
77+
<td>{{ x.remoteIp }}</td>
78+
</tr>
79+
</table>
80+
</div>
81+
</div>
82+
<div class="flex-1">
83+
<div v-if="expanded(x.id)" class="p-4">
84+
<PreviewFormat :value="JSON.parse(x.requestBody)" />
85+
</div>
86+
</div>
87+
</div>
88+
</li>
89+
</ul>
90+
</div>
91+
</div>
92+
`,
93+
props:['type','id'],
94+
setup(props) {
95+
const store = inject('store')
96+
const server = inject('server')
97+
const client = useClient()
98+
99+
const open = ref(false)
100+
const eventsCount = ref(0)
101+
const events = ref([])
102+
const expand = ref({})
103+
const { formatDate, relativeTime } = useFormatters()
104+
function expanded(id) {
105+
return !!expand.value[id]
106+
}
107+
function toggle(id) {
108+
expand.value[id] = !expand.value[id]
109+
}
110+
111+
onMounted(async () => {
112+
const request = new GetCrudEvents({
113+
modelId: props.id,
114+
model: props.type,
115+
include: 'total',
116+
orderBy: '-Id'
117+
})
118+
const api = await client.api(request)
119+
eventsCount.value = 0
120+
events.value = []
121+
if (api.succeeded) {
122+
eventsCount.value = api.response.total
123+
events.value = api.response.results
124+
}
125+
})
126+
127+
return { open, events, eventsCount, formatDate, relativeTime, expanded, toggle }
128+
}
129+
}
130+
131+
export default AuditEvents

0 commit comments

Comments
 (0)