feat: add page displaying all events
This commit is contained in:
parent
77ee068318
commit
481ef22b3a
18
lib/index.ts
18
lib/index.ts
|
|
@ -334,7 +334,11 @@ const registerListeners = (
|
|||
socket.data[key] = createProxy(data[key]);
|
||||
}
|
||||
|
||||
adminNamespace.emit("socket_connected", serialize(socket, nsp.name));
|
||||
adminNamespace.emit(
|
||||
"socket_connected",
|
||||
serialize(socket, nsp.name),
|
||||
new Date()
|
||||
);
|
||||
|
||||
socket.conn.on("upgrade", (transport: any) => {
|
||||
socket.data._admin.transport = transport.name;
|
||||
|
|
@ -346,17 +350,23 @@ const registerListeners = (
|
|||
});
|
||||
|
||||
socket.on("disconnect", (reason: string) => {
|
||||
adminNamespace.emit("socket_disconnected", nsp.name, socket.id, reason);
|
||||
adminNamespace.emit(
|
||||
"socket_disconnected",
|
||||
nsp.name,
|
||||
socket.id,
|
||||
reason,
|
||||
new Date()
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
nsp.adapter.on("join-room", (room: string, id: string) => {
|
||||
adminNamespace.emit("room_joined", nsp.name, room, id);
|
||||
adminNamespace.emit("room_joined", nsp.name, room, id, new Date());
|
||||
});
|
||||
|
||||
nsp.adapter.on("leave-room", (room: string, id: string) => {
|
||||
process.nextTick(() => {
|
||||
adminNamespace.emit("room_left", nsp.name, room, id);
|
||||
adminNamespace.emit("room_left", nsp.name, room, id, new Date());
|
||||
});
|
||||
});
|
||||
};
|
||||
|
|
|
|||
|
|
@ -36,11 +36,16 @@ export interface ServerEvents {
|
|||
config: (config: Config) => void;
|
||||
server_stats: (stats: ServerStats) => void;
|
||||
all_sockets: (sockets: SerializedSocket[]) => void;
|
||||
socket_connected: (socket: SerializedSocket) => void;
|
||||
socket_connected: (socket: SerializedSocket, timestamp: Date) => void;
|
||||
socket_updated: (socket: Partial<SerializedSocket>) => void;
|
||||
socket_disconnected: (nsp: string, id: string, reason: string) => void;
|
||||
room_joined: (nsp: string, room: string, id: string) => void;
|
||||
room_left: (nsp: string, room: string, id: string) => void;
|
||||
socket_disconnected: (
|
||||
nsp: string,
|
||||
id: string,
|
||||
reason: string,
|
||||
timestamp: Date
|
||||
) => void;
|
||||
room_joined: (nsp: string, room: string, id: string, timestamp: Date) => void;
|
||||
room_left: (nsp: string, room: string, id: string, timestamp: Date) => void;
|
||||
}
|
||||
|
||||
export interface ClientEvents {
|
||||
|
|
|
|||
|
|
@ -264,7 +264,7 @@ describe("Socket.IO Admin (server instrumentation)", () => {
|
|||
|
||||
// connect
|
||||
const serverSocket = await waitFor(io, "connection");
|
||||
const socket = await waitFor(adminSocket, "socket_connected");
|
||||
const [socket] = await waitFor(adminSocket, "socket_connected");
|
||||
|
||||
expect(socket.id).to.eql(serverSocket.id);
|
||||
expect(socket.nsp).to.eql("/");
|
||||
|
|
@ -329,7 +329,7 @@ describe("Socket.IO Admin (server instrumentation)", () => {
|
|||
|
||||
const serverSocket = await waitFor(io, "connection");
|
||||
|
||||
const socket = await waitFor(adminSocket, "socket_connected");
|
||||
const [socket] = await waitFor(adminSocket, "socket_connected");
|
||||
expect(socket.data).to.eql({ count: 1, array: [1] });
|
||||
|
||||
serverSocket.data.count++;
|
||||
|
|
@ -401,7 +401,7 @@ describe("Socket.IO Admin (server instrumentation)", () => {
|
|||
forceNew: true,
|
||||
});
|
||||
|
||||
const socket = await waitFor(adminSocket, "socket_connected");
|
||||
const [socket] = await waitFor(adminSocket, "socket_connected");
|
||||
|
||||
expect(socket.nsp).to.eql("/dynamic-101");
|
||||
clientSocket.disconnect();
|
||||
|
|
|
|||
|
|
@ -40,6 +40,11 @@ import {
|
|||
VSlideYReverseTransition,
|
||||
} from "vuetify/lib";
|
||||
|
||||
// created on the client side, for backward compatibility
|
||||
function defaultTimestamp() {
|
||||
return new Date().toISOString();
|
||||
}
|
||||
|
||||
export default {
|
||||
name: "App",
|
||||
|
||||
|
|
@ -153,25 +158,41 @@ export default {
|
|||
socket.on("all_sockets", (sockets) => {
|
||||
this.$store.commit("main/onAllSockets", sockets);
|
||||
});
|
||||
socket.on("socket_connected", (socket) => {
|
||||
this.$store.commit("main/onSocketConnected", socket);
|
||||
});
|
||||
socket.on(
|
||||
"socket_connected",
|
||||
(socket, timestamp = defaultTimestamp()) => {
|
||||
this.$store.commit("main/onSocketConnected", {
|
||||
timestamp,
|
||||
socket,
|
||||
});
|
||||
}
|
||||
);
|
||||
socket.on("socket_updated", (socket) => {
|
||||
this.$store.commit("main/onSocketUpdated", socket);
|
||||
});
|
||||
socket.on("socket_disconnected", (nsp, id, reason) => {
|
||||
this.$store.commit("main/onSocketDisconnected", {
|
||||
nsp,
|
||||
id,
|
||||
reason,
|
||||
});
|
||||
});
|
||||
socket.on("room_joined", (nsp, room, id) => {
|
||||
this.$store.commit("main/onRoomJoined", { nsp, room, id });
|
||||
});
|
||||
socket.on("room_left", (nsp, room, id) => {
|
||||
this.$store.commit("main/onRoomLeft", { nsp, room, id });
|
||||
});
|
||||
socket.on(
|
||||
"socket_disconnected",
|
||||
(nsp, id, reason, timestamp = defaultTimestamp()) => {
|
||||
this.$store.commit("main/onSocketDisconnected", {
|
||||
timestamp,
|
||||
nsp,
|
||||
id,
|
||||
reason,
|
||||
});
|
||||
}
|
||||
);
|
||||
socket.on(
|
||||
"room_joined",
|
||||
(nsp, room, id, timestamp = defaultTimestamp()) => {
|
||||
this.$store.commit("main/onRoomJoined", { timestamp, nsp, room, id });
|
||||
}
|
||||
);
|
||||
socket.on(
|
||||
"room_left",
|
||||
(nsp, room, id, timestamp = defaultTimestamp()) => {
|
||||
this.$store.commit("main/onRoomLeft", { timestamp, nsp, room, id });
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
onSubmit(form) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,33 @@
|
|||
<template>
|
||||
<v-chip :color="color" outlined>
|
||||
{{ $t("events.type." + type) }}
|
||||
</v-chip>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import colors from "vuetify/lib/util/colors";
|
||||
|
||||
export default {
|
||||
name: "EventType",
|
||||
|
||||
props: {
|
||||
type: String,
|
||||
},
|
||||
|
||||
computed: {
|
||||
color() {
|
||||
switch (this.type) {
|
||||
case "connection":
|
||||
return colors.green.base;
|
||||
case "room_joined":
|
||||
return colors.teal.base;
|
||||
case "room_left":
|
||||
return colors.amber.base;
|
||||
case "disconnection":
|
||||
return colors.red.base;
|
||||
}
|
||||
return colors.gray.base;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
@ -67,6 +67,11 @@ export default {
|
|||
icon: "mdi-account-circle-outline",
|
||||
to: { name: "clients" },
|
||||
},
|
||||
{
|
||||
title: this.$t("events.title"),
|
||||
icon: "mdi-calendar-text-outline",
|
||||
to: { name: "events" },
|
||||
},
|
||||
{
|
||||
title: this.$t("servers.title"),
|
||||
icon: "mdi-server",
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@
|
|||
"connected": "connected",
|
||||
"disconnected": "disconnected",
|
||||
"data": "Data",
|
||||
"timestamp": "Timestamp",
|
||||
"args": "Arguments",
|
||||
"connection": {
|
||||
"title": "Connection",
|
||||
"serverUrl": "Server URL",
|
||||
|
|
@ -84,5 +86,14 @@
|
|||
"language": "Language",
|
||||
"readonly": "Read-only?",
|
||||
"dark-theme": "Dark theme?"
|
||||
},
|
||||
"events": {
|
||||
"title": "Events",
|
||||
"type": {
|
||||
"connection": "Connection",
|
||||
"disconnection": "Disconnection",
|
||||
"room_joined": "Room joined",
|
||||
"room_left": "Room left"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,9 @@
|
|||
"status": "Statut",
|
||||
"connected": "connecté",
|
||||
"disconnected": "déconnecté",
|
||||
"data": "Données",
|
||||
"timestamp": "Horodatage",
|
||||
"args": "Arguments",
|
||||
"connection": {
|
||||
"title": "Connexion",
|
||||
"serverUrl": "URL du serveur",
|
||||
|
|
@ -80,5 +83,14 @@
|
|||
"language": "Langue",
|
||||
"readonly": "Lecture seule ?",
|
||||
"dark-theme": "Mode sombre ?"
|
||||
},
|
||||
"events": {
|
||||
"title": "Évènements",
|
||||
"type": {
|
||||
"connection": "Connexion",
|
||||
"disconnection": "Déconnexion",
|
||||
"room_joined": "Salle rejointe",
|
||||
"room_left": "Salle quittée"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import Clients from "../views/Clients";
|
|||
import Client from "../views/Client";
|
||||
import Servers from "../views/Servers";
|
||||
import Room from "../views/Room";
|
||||
import Events from "@/views/Events";
|
||||
|
||||
Vue.use(VueRouter);
|
||||
|
||||
|
|
@ -72,13 +73,22 @@ const routes = [
|
|||
topLevel: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/events/",
|
||||
name: "events",
|
||||
component: Events,
|
||||
meta: {
|
||||
topLevel: true,
|
||||
index: 4,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/servers/",
|
||||
name: "servers",
|
||||
component: Servers,
|
||||
meta: {
|
||||
topLevel: true,
|
||||
index: 4,
|
||||
index: 5,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ const getOrCreateNamespace = (namespaces, name) => {
|
|||
name,
|
||||
sockets: [],
|
||||
rooms: [],
|
||||
events: [],
|
||||
};
|
||||
namespaces.push(namespace);
|
||||
return namespace;
|
||||
|
|
@ -64,6 +65,17 @@ const addSocket = (state, socket) => {
|
|||
}
|
||||
};
|
||||
|
||||
const MAX_ARRAY_LENGTH = 1000;
|
||||
let EVENT_COUNTER = 0;
|
||||
|
||||
const pushEvents = (array, event) => {
|
||||
event.eventId = ++EVENT_COUNTER; // unique id
|
||||
array.push(event);
|
||||
if (array.length > MAX_ARRAY_LENGTH) {
|
||||
array.shift();
|
||||
}
|
||||
};
|
||||
|
||||
export default {
|
||||
namespaced: true,
|
||||
state: {
|
||||
|
|
@ -97,6 +109,9 @@ export default {
|
|||
rooms: (state) => {
|
||||
return state.selectedNamespace ? state.selectedNamespace.rooms : [];
|
||||
},
|
||||
events: (state) => {
|
||||
return state.selectedNamespace ? state.selectedNamespace.events : [];
|
||||
},
|
||||
},
|
||||
mutations: {
|
||||
selectNamespace(state, namespace) {
|
||||
|
|
@ -114,8 +129,14 @@ export default {
|
|||
find(state.namespaces, { name: "/" }) || state.namespaces[0];
|
||||
}
|
||||
},
|
||||
onSocketConnected(state, socket) {
|
||||
onSocketConnected(state, { timestamp, socket }) {
|
||||
addSocket(state, socket);
|
||||
const namespace = getOrCreateNamespace(state.namespaces, socket.nsp);
|
||||
pushEvents(namespace.events, {
|
||||
type: "connection",
|
||||
timestamp,
|
||||
id: socket.id,
|
||||
});
|
||||
},
|
||||
onSocketUpdated(state, socket) {
|
||||
const namespace = getOrCreateNamespace(state.namespaces, socket.nsp);
|
||||
|
|
@ -124,7 +145,7 @@ export default {
|
|||
merge(existingSocket, socket);
|
||||
}
|
||||
},
|
||||
onSocketDisconnected(state, { nsp, id }) {
|
||||
onSocketDisconnected(state, { timestamp, nsp, id, reason }) {
|
||||
const namespace = getOrCreateNamespace(state.namespaces, nsp);
|
||||
const [socket] = remove(namespace.sockets, { id });
|
||||
if (socket) {
|
||||
|
|
@ -137,8 +158,14 @@ export default {
|
|||
remove(state.clients, { id: socket.clientId });
|
||||
}
|
||||
}
|
||||
pushEvents(namespace.events, {
|
||||
type: "disconnection",
|
||||
timestamp,
|
||||
id,
|
||||
args: reason,
|
||||
});
|
||||
},
|
||||
onRoomJoined(state, { nsp, room, id }) {
|
||||
onRoomJoined(state, { nsp, room, id, timestamp }) {
|
||||
const namespace = getOrCreateNamespace(state.namespaces, nsp);
|
||||
const socket = find(namespace.sockets, { id });
|
||||
if (socket) {
|
||||
|
|
@ -146,8 +173,14 @@ export default {
|
|||
const _room = getOrCreateRoom(namespace, room);
|
||||
_room.sockets.push(socket);
|
||||
}
|
||||
pushEvents(namespace.events, {
|
||||
type: "room_joined",
|
||||
timestamp,
|
||||
id,
|
||||
args: room,
|
||||
});
|
||||
},
|
||||
onRoomLeft(state, { nsp, room, id }) {
|
||||
onRoomLeft(state, { timestamp, nsp, room, id }) {
|
||||
const namespace = getOrCreateNamespace(state.namespaces, nsp);
|
||||
const socket = find(namespace.sockets, { id });
|
||||
if (socket) {
|
||||
|
|
@ -159,6 +192,12 @@ export default {
|
|||
_room.active = false;
|
||||
remove(namespace.rooms, { name: room });
|
||||
}
|
||||
pushEvents(namespace.events, {
|
||||
type: "room_left",
|
||||
timestamp,
|
||||
id,
|
||||
args: room,
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -0,0 +1,102 @@
|
|||
<template>
|
||||
<div>
|
||||
<v-breadcrumbs :items="breadcrumbItems" />
|
||||
|
||||
<v-card>
|
||||
<v-card-text>
|
||||
<NamespaceSelector />
|
||||
</v-card-text>
|
||||
|
||||
<v-data-table
|
||||
:headers="headers"
|
||||
:items="events"
|
||||
:footer-props="footerProps"
|
||||
item-key="eventId"
|
||||
:sort-by="['timestamp', 'eventId']"
|
||||
:sort-desc="[true, true]"
|
||||
>
|
||||
<template #item.type="{ value }">
|
||||
<EventType :type="value" />
|
||||
</template>
|
||||
|
||||
<template #item.id="{ value }">
|
||||
<router-link class="link" :to="socketDetailsRoute(value)">{{
|
||||
value
|
||||
}}</router-link>
|
||||
</template>
|
||||
</v-data-table>
|
||||
</v-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters, mapState } from "vuex";
|
||||
import NamespaceSelector from "../components/NamespaceSelector";
|
||||
import EventType from "@/components/EventType";
|
||||
|
||||
export default {
|
||||
name: "Events",
|
||||
|
||||
components: { EventType, NamespaceSelector },
|
||||
|
||||
data() {
|
||||
return {
|
||||
footerProps: {
|
||||
"items-per-page-options": [20, 100, -1],
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
breadcrumbItems() {
|
||||
return [
|
||||
{
|
||||
text: this.$t("events.title"),
|
||||
disabled: true,
|
||||
},
|
||||
];
|
||||
},
|
||||
headers() {
|
||||
return [
|
||||
{
|
||||
text: this.$t("timestamp"),
|
||||
value: "timestamp",
|
||||
},
|
||||
{
|
||||
text: this.$t("sockets.socket"),
|
||||
value: "id",
|
||||
sortable: false,
|
||||
},
|
||||
{
|
||||
text: this.$t("type"),
|
||||
value: "type",
|
||||
sortable: false,
|
||||
},
|
||||
{
|
||||
text: this.$t("args"),
|
||||
value: "args",
|
||||
sortable: false,
|
||||
},
|
||||
];
|
||||
},
|
||||
...mapGetters("main", ["events"]),
|
||||
...mapState({
|
||||
selectedNamespace: (state) => state.main.selectedNamespace,
|
||||
}),
|
||||
},
|
||||
|
||||
methods: {
|
||||
socketDetailsRoute(sid) {
|
||||
return {
|
||||
name: "socket",
|
||||
params: { nsp: this.selectedNamespace.name, id: sid },
|
||||
};
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style scoped>
|
||||
.link {
|
||||
color: inherit;
|
||||
}
|
||||
</style>
|
||||
Loading…
Reference in New Issue