Skip to content

Commit

Permalink
packetlogger UI
Browse files Browse the repository at this point in the history
  • Loading branch information
wardviaene committed Sep 5, 2024
1 parent 68f0ade commit 0f1732f
Show file tree
Hide file tree
Showing 5 changed files with 209 additions and 12 deletions.
35 changes: 30 additions & 5 deletions pkg/rest/stats.go
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,9 @@ func (c *Context) packetLogsHandler(w http.ResponseWriter, r *http.Request) {
for _, user := range users {
userMap[user.ID] = user.Login
}
// get filter
logTypeFilterQueryString := r.URL.Query().Get("logtype")
logTypeFilter := strings.Split(logTypeFilterQueryString, ",")
// logs
statsFiles := []string{
path.Join(wireguard.VPN_STATS_DIR, wireguard.VPN_PACKETLOGGER_DIR, userID+"-"+date.Format("2006-01-02")+".log"),
Expand Down Expand Up @@ -292,11 +295,14 @@ func (c *Context) packetLogsHandler(w http.ResponseWriter, r *http.Request) {
if err != nil {
continue // invalid record
}
row := LogRow{
Timestamp: timestamp.Add(time.Duration(offset) * time.Minute),
Data: inputSplit[1:],
if !filterLogRecord(logTypeFilter, inputSplit[1]) {
row := LogRow{
Timestamp: timestamp.Add(time.Duration(offset) * time.Minute),
Data: inputSplit[1:],
}
logData.Data = append(logData.Data, row)

}
logData.Data = append(logData.Data, row)
}

if err := scanner.Err(); err != nil {
Expand All @@ -312,7 +318,7 @@ func (c *Context) packetLogsHandler(w http.ResponseWriter, r *http.Request) {
}
}

out, err := json.Marshal(LogDataResponse{Enabled: true, LogData: logData, LogTypes: packetLogTypes})
out, err := json.Marshal(LogDataResponse{Enabled: true, LogData: logData, LogTypes: packetLogTypes, Users: userMap})
if err != nil {
c.returnError(w, fmt.Errorf("user stats response marshal error: %s", err), http.StatusBadRequest)
return
Expand Down Expand Up @@ -351,3 +357,22 @@ func dateEqual(date1, date2 time.Time) bool {
y2, m2, d2 := date2.Date()
return y1 == y2 && m1 == m2 && d1 == d2
}

func filterLogRecord(logTypeFilter []string, logType string) bool {
if len(logTypeFilter) > 0 && logTypeFilter[0] != "" {
for _, logTypeFilterItem := range logTypeFilter {
if logType == logTypeFilterItem {
return false
}

splitLogTypes := strings.Split(logTypeFilterItem, "+")
for _, splitLogType := range splitLogTypes {
if splitLogType == logType {
return false
}
}
}
return true
}
return false
}
11 changes: 11 additions & 0 deletions pkg/rest/stats_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,14 @@ func TestUserStatsHandler(t *testing.T) {
}

}

func TestFilterLogRecord(t *testing.T) {
logTypeFilter := []string{"tcp", "http+https"}
expected := []bool{false, false, true, false}
for k, v := range []string{"tcp", "http", "udp", "https"} {
res := filterLogRecord(logTypeFilter, v)
if res != expected[k] {
t.Fatalf("unexpected result: %v, got: %v", res, expected[k])
}
}
}
7 changes: 4 additions & 3 deletions pkg/rest/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -209,9 +209,10 @@ type NewUserRequest struct {
}

type LogDataResponse struct {
LogData LogData `json:"logData"`
Enabled bool `json:"enabled"`
LogTypes []string `json:"logTypes"`
LogData LogData `json:"logData"`
Enabled bool `json:"enabled"`
LogTypes []string `json:"logTypes"`
Users map[string]string `json:"users"`
}

type LogData struct {
Expand Down
8 changes: 4 additions & 4 deletions webapp/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { Users } from "./Routes/Users/Users";
import { Profile } from "./Routes/Profile/Profile";
import { Upgrade } from "./Routes/Upgrade/Upgrade";
import { GetMoreLicenses } from "./Routes/Licenses/GetMoreLicenses";
import { PacketLogs } from "./Routes/Logs/PacketLogs";
import { PacketLogs } from "./Routes/PacketLogs/PacketLogs";

const queryClient = new QueryClient()

Expand Down Expand Up @@ -47,14 +47,14 @@ export default function App() {
<Route path="/setup" element={<CheckRole role="admin"><Setup /></CheckRole>} />
<Route path="/setup/:page" element={<CheckRole role="admin"><Setup /></CheckRole>} />
<Route path="/auth-setup" element={<CheckRole role="admin"><AuthSetup /></CheckRole>} />
<Route path="/upgrade" element={<CheckRole role="admin"><Upgrade /></CheckRole>} />
<Route path="/licenses" element={<CheckRole role="admin"><GetMoreLicenses /></CheckRole>} />
<Route path="/packetlogs" element={<CheckRole role="admin"><PacketLogs /></CheckRole>} />
<Route path="/logout" element={<Logout />} />
<Route path="/login/:logintype/:id" element={<Navigate to={"/"} />} />
<Route path="/callback/:callbacktype/:id" element={<Navigate to={"/"} />} />
<Route path="/connection" element={<Connections />} />
<Route path="/profile" element={<Profile />} />
<Route path="/upgrade" element={<Upgrade />} />
<Route path="/licenses" element={<GetMoreLicenses />} />
<Route path="/packetlogs" element={<PacketLogs />} />
</Routes>
</AppShell.Main>
</AppShell>
Expand Down
160 changes: 160 additions & 0 deletions webapp/src/Routes/PacketLogs/PacketLogs.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
import { Card, Container, Text, Table, Title, Button, Grid, Select, MultiSelect, Popover} from "@mantine/core";
import { AppSettings } from "../../Constants/Constants";
import { useQuery } from "@tanstack/react-query";
import { useAuthContext } from "../../Auth/Auth";
import { Link, useParams, useSearchParams } from "react-router-dom";
import { TbSettings } from "react-icons/tb";
import { DatePickerInput } from "@mantine/dates";
import { useState } from "react";

type LogsDataResponse = {
enabled: boolean;
logData: LogData;
logTypes: string[];
users: UserMap;
}
type LogData = {
schema: LogDataSchema;
rows: LogRow[];
}
type LogDataSchema = {
columns: string[];
}
type LogRow = {
t: string;
d: string[];
}
type UserMap = {
[key: string]: string;
}

export function PacketLogs() {
const {authInfo} = useAuthContext();
const [currentQueryParameters, setSearchParams] = useSearchParams();
const dateParam = currentQueryParameters.get("date")
const userParam = currentQueryParameters.get("user")
const [logType, setLogType] = useState<string[]>([])
const [logsDate, setLogsDate] = useState<Date | null>(dateParam === null ? new Date() : new Date(dateParam));
const [user, setUser] = useState<string>(userParam === null ? "all" : userParam)
const { isPending, error, data } = useQuery<LogsDataResponse>({
queryKey: ['packetlogs', user, logsDate, logType],
queryFn: () =>
fetch(AppSettings.url + '/stats/packetlogs/'+(user === undefined ? "all" : user)+'/'+(logsDate == undefined ? new Date().toISOString().slice(0, 10) : logsDate.toISOString().slice(0, 10)) + "?logtype="+encodeURIComponent(logType.join(",")), {
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer " + authInfo.token
},
}).then((res) => {
return res.json()
}
),
})

if(isPending) return "Loading..."
if(error) return 'A backend error has occurred: ' + error.message

if(!data.enabled || data.logTypes.length == 0) { // show disabled page if not enabled
return (
<Container my={40}>
<Title ta="center" style={{marginBottom: 20}}>
Packet Logs
</Title>
<Card withBorder radius="md" padding="xl" bg="var(--mantine-color-body)">
<Text fz="xs" tt="uppercase" fw={700} c="dimmed">
{ !data.enabled ?
"Packet Logs are not activated. Activate packet logging in the VPN Settings."
:
data.logTypes.length == 0 ? "Packet logs are activated, but no packet logging types are selected. Select at least one packet log type." : null
}

</Text>
<Card.Section inheritPadding mt="sm" pb="md">
<Link to="/setup/vpn">
<Button leftSection={<TbSettings size={14} />} fz="sm" mt="md" radius="md" variant="default" size="sm">
VPN Settings
</Button>
</Link>
</Card.Section>
</Card>
</Container>
)
}

const rows = data.logData.rows.map((row, i) => (
<Table.Tr key={i}>
<Table.Td>{row.t}</Table.Td>
{row.d.map((element, y) => {
return (
<Table.Td key={i+"-"+y}>{element}</Table.Td>
)
})}
</Table.Tr>
));
return (
<Container my={40} size="80rem">
<Title ta="center" style={{marginBottom: 20}}>
Packet Logs
</Title>
<Grid>
<Grid.Col span={4}></Grid.Col>
<Grid.Col span={4}>
<DatePickerInput
value={logsDate}
onChange={setLogsDate}
size="xs"
/>
</Grid.Col>
<Grid.Col span={2}>
<Select
data={Object.keys(data.users).map((key) => {
return {
label: data.users[key],
value: key,
}
})}
size="xs"
withCheckIcon={false}
value={user}
onChange={(_value) => setUser(_value === null ? "" : _value)}
placeholder="User"
/>
</Grid.Col>
<Grid.Col span={2}>
<Popover width={300} position="bottom" withArrow shadow="md">
<Popover.Target>
<Button variant="default" size="xs">Filter</Button>
</Popover.Target>
<Popover.Dropdown>
<MultiSelect
label="Protocol"
searchable
hidePickedOptions
comboboxProps={{ offset: 0, withinPortal: false}}
data={data.logTypes}
value={logType}
onChange={setLogType}
size="xs"
placeholder="Log Type"
/>
</Popover.Dropdown>
</Popover>
</Grid.Col>
</Grid>
<Table>
<Table.Thead>
<Table.Tr key="heading">
<Table.Th>Timestamp</Table.Th>
<Table.Th>Protocol</Table.Th>
<Table.Th>Source IP</Table.Th>
<Table.Th>Dest. IP</Table.Th>
<Table.Th>Source Port</Table.Th>
<Table.Th>Dest. Port</Table.Th>
<Table.Th>Destination</Table.Th>
</Table.Tr>
</Table.Thead>
<Table.Tbody>{rows}</Table.Tbody>
</Table>
</Container>

)
}

0 comments on commit 0f1732f

Please sign in to comment.