bugfix: pagination, URL rewriting
parent
79ff0c2cc1
commit
e47893481a
|
|
@ -0,0 +1,48 @@
|
|||
name: Lint, Build, Deploy
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 14 * * *'
|
||||
push:
|
||||
branches: [ trunk ]
|
||||
pull_request:
|
||||
branches: [ trunk ]
|
||||
jobs:
|
||||
gopherhole-verify:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v2
|
||||
with:
|
||||
# Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version
|
||||
version: latest
|
||||
# - name: Verify gopherhole
|
||||
# run: |
|
||||
# go get -u .
|
||||
# make lint
|
||||
|
||||
client-verify:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Verify client
|
||||
env:
|
||||
NODE_ENV: production
|
||||
run: |
|
||||
cd ./client;
|
||||
npm run verify
|
||||
|
||||
gopherhole-build:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [client-verify, gopherhole-verify]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Build gopherhole image
|
||||
env:
|
||||
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
||||
run: |
|
||||
docker build \
|
||||
--label="" \
|
||||
-t vdhsn/gopherhole .
|
||||
|
||||
|
|
@ -16,7 +16,7 @@ FROM golang:alpine as build-server
|
|||
|
||||
WORKDIR /build/
|
||||
|
||||
RUN apk add --no-cache --update make gcc musl-dev
|
||||
RUN apk add --no-cache --update make gcc musl-dev git
|
||||
|
||||
COPY --from=build-client /build /build
|
||||
RUN make .bin/gopherhole
|
||||
|
|
@ -42,6 +42,4 @@ EXPOSE 53/udp 53/tcp 80/tcp
|
|||
|
||||
VOLUME "/data"
|
||||
|
||||
ENTRYPOINT /opt/gopherhole
|
||||
|
||||
CMD [ "-http-address=:80", "-dns-address=:53", "-upstream=${GOPHERHOLE_UPSTREAM}", "-db-path=/data"]
|
||||
CMD /opt/gopherhole -http-address=0.0.0.0:80 -dns-address=0.0.0.0:53 -upstream ${GOPHERHOLE_UPSTREAM} -db-path /data
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -4,7 +4,7 @@
|
|||
"scripts": {
|
||||
"build": "NODE_ENV=production rollup -c",
|
||||
"dev": "NODE_ENV=development rollup -c -w",
|
||||
"start": "sirv public --single -G -D",
|
||||
"start": "sirv public --single -G -D --host",
|
||||
"validate": "svelte-check"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ export interface LogSearchOptions {
|
|||
start: Date
|
||||
end: Date
|
||||
page: number
|
||||
pageSize: number
|
||||
filter: string
|
||||
}
|
||||
|
||||
|
|
@ -40,12 +41,14 @@ export const getLogs = async({
|
|||
start = sub(new Date(), { hours: 24 }),
|
||||
end = new Date(),
|
||||
page = 0,
|
||||
pageSize = 25,
|
||||
filter = ""
|
||||
}: LogSearchOptions) => await apiCall<LogPayload>('metrics/log', 'GET', {
|
||||
filter,
|
||||
page,
|
||||
start: getUnixTime(start),
|
||||
end: getUnixTime(end),
|
||||
start: getUnixTime(start || sub(new Date(), { hours: 24 })),
|
||||
end: getUnixTime(end || new Date()),
|
||||
pageSize
|
||||
});
|
||||
|
||||
export interface StatsSearchOptions {
|
||||
|
|
@ -84,8 +87,8 @@ export const getStats = async ({
|
|||
key = StatSearchKey.Domain,
|
||||
interval = 30,
|
||||
}: StatsSearchOptions) => await apiCall<Stat>('metrics/stats', 'GET', {
|
||||
start: getUnixTime(start),
|
||||
end: getUnixTime(end),
|
||||
start: getUnixTime(start || sub(new Date(), { hours: 24 })),
|
||||
end: getUnixTime(end || new Date()),
|
||||
key,
|
||||
interval
|
||||
});
|
||||
|
|
|
|||
|
|
@ -29,9 +29,14 @@
|
|||
export let defaultValue: Date = new Date();
|
||||
export let value: Date = defaultValue;
|
||||
|
||||
$: dateTimeParts = toDateTimeParts(value);
|
||||
$: dateTimeParts = toDateTimeParts(value || defaultValue || new Date());
|
||||
|
||||
const update = ({ target: { value: v } }) => {
|
||||
if (!dateTimeParts) {
|
||||
dateTimeParts = toDateTimeParts(defaultValue);
|
||||
return;
|
||||
}
|
||||
|
||||
const { date, time } = dateTimeParts;
|
||||
|
||||
let dateTimePartsInput = isDate(v)
|
||||
|
|
|
|||
|
|
@ -6,18 +6,18 @@
|
|||
PaginationLink,
|
||||
} from "sveltestrap";
|
||||
|
||||
const pageSizes = [25, 50, 100, 250];
|
||||
const pagerElementsCount = 10;
|
||||
|
||||
export let page: number = 0;
|
||||
export let pages: number = 0;
|
||||
export let total: number = 0;
|
||||
export let pageSize: number = 50;
|
||||
export let onPageChange = (newPage: number, prevPage: number) =>
|
||||
console.log(`${newPage} - ${prevPage}`);
|
||||
|
||||
$: pageIndex = page + 1;
|
||||
$: pageCount = pages === 0 ? 1 : pages;
|
||||
$: pageSizeLabel = pageSize + "";
|
||||
|
||||
const pagerElementsCount = 10;
|
||||
let pagesList = [];
|
||||
$: {
|
||||
pagesList = [];
|
||||
|
|
@ -30,11 +30,14 @@
|
|||
}
|
||||
}
|
||||
|
||||
const handlePageSizeChange = ({ target: { value } }) => {
|
||||
pageSize = Number(value);
|
||||
};
|
||||
const handlePageSizeChange = ({ target: { value } }) =>
|
||||
(pageSize = Number(value));
|
||||
|
||||
const pageSizes = [25, 50, 100, 250];
|
||||
const handlePageChange = (p) => (e) => {
|
||||
e.preventDefault();
|
||||
page = p;
|
||||
console.debug(`changing to page ${page}`);
|
||||
};
|
||||
</script>
|
||||
|
||||
<section class="flex flex-row my-2 justify-between items-center">
|
||||
|
|
@ -42,21 +45,35 @@
|
|||
<div>
|
||||
<Pagination size="sm">
|
||||
<PaginationItem disabled={pageIndex === 1}>
|
||||
<PaginationLink first href="#" />
|
||||
<PaginationLink first href="#" on:click={handlePageChange(0)} />
|
||||
</PaginationItem>
|
||||
<PaginationItem disabled={pageIndex === 1}>
|
||||
<PaginationLink previous href="#" />
|
||||
<PaginationLink
|
||||
previous
|
||||
href="#"
|
||||
on:click={handlePageChange(page - 1)}
|
||||
/>
|
||||
</PaginationItem>
|
||||
{#each pagesList as p}
|
||||
<PaginationItem active={pageIndex - 1 === p}>
|
||||
<PaginationLink href="#">{p + 1}</PaginationLink>
|
||||
<PaginationLink on:click={handlePageChange(p)} href="#"
|
||||
>{p + 1}</PaginationLink
|
||||
>
|
||||
</PaginationItem>
|
||||
{/each}
|
||||
<PaginationItem disabled={pageIndex === pageCount}>
|
||||
<PaginationLink next href="#" />
|
||||
<PaginationLink
|
||||
next
|
||||
href="#"
|
||||
on:click={handlePageChange(page + 1)}
|
||||
/>
|
||||
</PaginationItem>
|
||||
<PaginationItem disabled={pageIndex === pageCount}>
|
||||
<PaginationLink last href="#" />
|
||||
<PaginationLink
|
||||
last
|
||||
href="#"
|
||||
on:click={handlePageChange(pageCount)}
|
||||
/>
|
||||
</PaginationItem>
|
||||
</Pagination>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -8,16 +8,14 @@
|
|||
export let pages: number = 0;
|
||||
export let pageSize: number = 0;
|
||||
export let total: number = 0;
|
||||
export let onPageChange = (newPage: number, prevPage: number) =>
|
||||
console.log(`${newPage} - ${prevPage}`);
|
||||
|
||||
$: pageSize = logs.length;
|
||||
// $: pageSize = logs.length;
|
||||
$: hasData = !!(logs && logs.length > 0);
|
||||
</script>
|
||||
|
||||
<section class="flex flex-column text-sm">
|
||||
{#if hasData}
|
||||
<LogPager {onPageChange} {page} {pages} {pageSize} {total} />
|
||||
<LogPager bind:page {pages} bind:pageSize {total} />
|
||||
<Table rows={logs} let:row hover bordered>
|
||||
<Column header="Started">
|
||||
{row.Started}
|
||||
|
|
@ -49,7 +47,7 @@
|
|||
{row.TotalTimeMs}
|
||||
</Column>
|
||||
</Table>
|
||||
<LogPager {onPageChange} {page} {pages} {pageSize} {total} />
|
||||
<LogPager bind:page {pages} bind:pageSize {total} />
|
||||
{:else}
|
||||
<p>No Logs yet!</p>
|
||||
<p>
|
||||
|
|
|
|||
|
|
@ -50,12 +50,20 @@
|
|||
</InputGroup>
|
||||
</FormGroup>
|
||||
<FormGroup class="flex flex-row">
|
||||
<DatetimePicker
|
||||
label="Start"
|
||||
defaultValue={start}
|
||||
bind:value={start}
|
||||
/>
|
||||
<DatetimePicker label="End" defaultValue={end} bind:value={end} />
|
||||
<section class="px-1">
|
||||
<DatetimePicker
|
||||
label="Start"
|
||||
defaultValue={start}
|
||||
bind:value={start}
|
||||
/>
|
||||
</section>
|
||||
<section class="px-1">
|
||||
<DatetimePicker
|
||||
label="End"
|
||||
defaultValue={end}
|
||||
bind:value={end}
|
||||
/>
|
||||
</section>
|
||||
</FormGroup>
|
||||
<FormGroup class="mx-2">
|
||||
<Dropdown>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
export let stats: Stat = null;
|
||||
export let column: string = null;
|
||||
|
||||
const generateChartOptions = (s: Stat, empty: Boolean = false) => {
|
||||
const generateChartOptions = (s: Stat = null, empty: Boolean = false) => {
|
||||
let labels = [];
|
||||
let datasets = [];
|
||||
|
||||
|
|
|
|||
|
|
@ -16,28 +16,28 @@
|
|||
const { search } = location;
|
||||
let params = new URLSearchParams(search.substring(1));
|
||||
|
||||
export let start: Date =
|
||||
fromUnixTimeSafe(params.get("start")) || sub(new Date(), { hours: 24 });
|
||||
export let end: Date = fromUnixTimeSafe(params.get("end")) || new Date();
|
||||
|
||||
export let filter: string = params.get("filter") || "";
|
||||
|
||||
const aggKey = params.get("key");
|
||||
const fixed = aggKey[0].toUpperCase() + aggKey.substr(1);
|
||||
const fixed = aggKey
|
||||
? aggKey[0].toUpperCase() + aggKey.substr(1)
|
||||
: "Domain";
|
||||
|
||||
export let chartKey: StatSearchKey =
|
||||
StatSearchKey[fixed] || StatSearchKey.Domain;
|
||||
export let chartInterval: number = 30;
|
||||
export let logPage: number = 0;
|
||||
let start: Date = fromUnixTimeSafe(params.get("start")) || null;
|
||||
let end: Date = fromUnixTimeSafe(params.get("end")) || null;
|
||||
|
||||
let logErrorMsg: string = null;
|
||||
let chartErrorMsg: string = null;
|
||||
let chartDataLoading: Boolean = false;
|
||||
let logDataLoading: Boolean = false;
|
||||
let filter: string = params.get("filter") || "";
|
||||
|
||||
let chartKey: StatSearchKey = StatSearchKey[fixed] || StatSearchKey.Domain;
|
||||
let chartInterval: number = 30;
|
||||
let page: number = Number(params.get("page") || 0);
|
||||
|
||||
let chartData: Stat = null;
|
||||
let chartErrorMsg: string = null;
|
||||
let chartDataLoading: Boolean = false;
|
||||
|
||||
let logs: Log[] = [];
|
||||
let pageSize: number = 50;
|
||||
let logErrorMsg: string = null;
|
||||
let logDataLoading: Boolean = false;
|
||||
let pageSize: number = Number(params.get("pageSize") || 25);
|
||||
let pageCount: number = 0;
|
||||
let logCount: number = 0;
|
||||
|
||||
|
|
@ -52,7 +52,8 @@
|
|||
const { error, payload } = await getLogs({
|
||||
start,
|
||||
end,
|
||||
page: logPage,
|
||||
page,
|
||||
pageSize,
|
||||
filter,
|
||||
});
|
||||
logDataLoading = false;
|
||||
|
|
@ -89,43 +90,56 @@
|
|||
return payload;
|
||||
};
|
||||
|
||||
let done = true;
|
||||
const updateData = async (evt) => {
|
||||
if (chartDataLoading || logDataLoading) {
|
||||
console.warn("SKIPPED DATA FETCH");
|
||||
if (chartDataLoading || logDataLoading || !done) {
|
||||
console.debug("SKIPPED DATA FETCH");
|
||||
return;
|
||||
}
|
||||
|
||||
console.groupCollapsed("Stats Data Update");
|
||||
const { filter: eFilter, start: eStart, end: eEnd, key: eKey } = evt;
|
||||
console.info("handled search, fetching new data:", evt);
|
||||
const {
|
||||
filter: eFilter,
|
||||
start: eStart,
|
||||
end: eEnd,
|
||||
key: eKey,
|
||||
page: ePage,
|
||||
pageSize: eps,
|
||||
} = evt;
|
||||
console.debug("handled search, fetching new data:", evt);
|
||||
console.groupEnd();
|
||||
|
||||
let truePage =
|
||||
logCount <= pageSize * page ? 0 : ePage === 0 ? null : ePage;
|
||||
if (logCount <= pageSize * page) {
|
||||
console.warn(
|
||||
"adjusting log page because logCount is too small for current settings"
|
||||
);
|
||||
|
||||
page = 0;
|
||||
}
|
||||
|
||||
navigate(
|
||||
`${location?.pathname}${buildQueryParams({
|
||||
start: getUnixTime(eStart),
|
||||
end: getUnixTime(eEnd),
|
||||
filter: eFilter,
|
||||
key: eKey,
|
||||
start: eStart ? getUnixTime(eStart) : null,
|
||||
end: eEnd ? getUnixTime(eEnd) : null,
|
||||
filter: eFilter === "" ? eFilter : null,
|
||||
key: eKey !== "domain" ? eKey : null,
|
||||
page: truePage,
|
||||
pageSize: eps && eps !== 25 ? eps : null,
|
||||
})}`,
|
||||
{ replace: true }
|
||||
);
|
||||
|
||||
const [logPayload, chartPayload] = await Promise.all([
|
||||
fetchLogs(),
|
||||
fetchStats(),
|
||||
]);
|
||||
chartData = chartPayload;
|
||||
[{ page, total: logCount, pageCount, pageSize, logs }, chartData] =
|
||||
await Promise.all([fetchLogs(), fetchStats()]);
|
||||
|
||||
({
|
||||
page: logPage,
|
||||
total: logCount,
|
||||
pageCount,
|
||||
pageSize,
|
||||
logs,
|
||||
} = logPayload);
|
||||
done = true;
|
||||
};
|
||||
|
||||
$: updateData({ start, end, key: chartKey, filter });
|
||||
// $: updateChart({ start, end, key: chartKey });
|
||||
// $: updateLog({ start, end, page, pageSize });
|
||||
$: updateData({ start, end, key: chartKey, filter, pageSize, page });
|
||||
</script>
|
||||
|
||||
<PageContainer
|
||||
|
|
@ -151,11 +165,10 @@
|
|||
{:else}
|
||||
<LogViewer
|
||||
pages={pageCount}
|
||||
page={logPage}
|
||||
total={logCount}
|
||||
{pageSize}
|
||||
onPageChange={console.log}
|
||||
{logs}
|
||||
bind:page
|
||||
bind:pageSize
|
||||
/>
|
||||
{/if}
|
||||
</section>
|
||||
|
|
|
|||
17
config.go
17
config.go
|
|
@ -1,10 +1,6 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/adamveld12/gopherhole/internal"
|
||||
)
|
||||
|
||||
|
|
@ -16,16 +12,3 @@ type StartupConfig struct {
|
|||
Recursors []string `json:"recursors"`
|
||||
Rules []internal.Rule `json:"rules"`
|
||||
}
|
||||
|
||||
func LoadStartupConfig(conf *StartupConfig, file string) error {
|
||||
data, err := os.Open(file)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not open file: %w", err)
|
||||
}
|
||||
|
||||
if err := json.NewDecoder(data).Decode(conf); err != nil {
|
||||
return fmt.Errorf("could not read json file: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
1
go.mod
1
go.mod
|
|
@ -9,5 +9,4 @@ require (
|
|||
github.com/gorilla/websocket v1.4.2
|
||||
github.com/mattn/go-sqlite3 v1.14.7
|
||||
github.com/miekg/dns v1.1.41
|
||||
github.com/nakabonne/tstorage v0.1.2 // indirect
|
||||
)
|
||||
|
|
|
|||
9
go.sum
9
go.sum
|
|
@ -1,4 +1,3 @@
|
|||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/go-chi/chi v1.5.4 h1:QHdzF2szwjqVV4wmByUnTcsbIg7UGaQ0tPF2t5GcAIs=
|
||||
github.com/go-chi/chi v1.5.4/go.mod h1:uaf8YgoFazUOkPBG7fxPftUylNumIev9awIWOENIuEg=
|
||||
github.com/go-chi/chi/v5 v5.0.2 h1:4xKeALZdMEsuI5s05PU2Bm89Uc5iM04qFubUCl5LfAQ=
|
||||
|
|
@ -11,13 +10,9 @@ github.com/mattn/go-sqlite3 v1.14.7 h1:fxWBnXkxfM6sRiuH3bqJ4CfzZojMOLVc0UTsTglEg
|
|||
github.com/mattn/go-sqlite3 v1.14.7/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||
github.com/miekg/dns v1.1.41 h1:WMszZWJG0XmzbK9FEmzH2TVcqYzFesusSIB41b8KHxY=
|
||||
github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
|
||||
github.com/nakabonne/tstorage v0.1.2 h1:kZcXXyO10DDUvDBRHhRo9+VwSTAuvIGt/WpEb+TXAbM=
|
||||
github.com/nakabonne/tstorage v0.1.2/go.mod h1:n1v68nvIeUguEaYuSqz1ycmiMkF02CJMKSJV0Of1h4w=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04 h1:cEhElsAv9LUt9ZUUocxzWe05oFLVd+AA2nstydTeI8g=
|
||||
|
|
@ -25,5 +20,3 @@ golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
|
|
|||
|
|
@ -99,6 +99,12 @@ func GetAggregateColumnHeader(ql QueryLog, h LogAggregateColumn) string {
|
|||
return string(ql.Status)
|
||||
case Protocol:
|
||||
return ql.Protocol
|
||||
case Domain:
|
||||
return ql.Domain
|
||||
case RecurseIP:
|
||||
return ql.RecurseUpstreamIP
|
||||
case LookupError:
|
||||
return ql.Error
|
||||
}
|
||||
|
||||
return ql.Domain
|
||||
|
|
|
|||
|
|
@ -53,7 +53,6 @@ func (a *adminHandler) getStats(r *http.Request) (*RestResponse, error) {
|
|||
}
|
||||
|
||||
return BasicResponse(true, la), nil
|
||||
|
||||
}
|
||||
|
||||
type LogFilter struct {
|
||||
|
|
@ -64,11 +63,13 @@ func (a *adminHandler) getLog(r *http.Request) (*RestResponse, error) {
|
|||
q := r.URL.Query()
|
||||
startFilter := q.Get("start")
|
||||
endFilter := q.Get("end")
|
||||
pageSizeStr := q.Get("pageSize")
|
||||
// filter := LogFilter{Expression: q.Get("filter")}
|
||||
pageStr := q.Get("page")
|
||||
|
||||
var err error
|
||||
var page int
|
||||
pageSize := 25
|
||||
startTime := time.Now().Add(time.Hour * -86400)
|
||||
endTime := time.Now()
|
||||
|
||||
|
|
@ -96,11 +97,17 @@ func (a *adminHandler) getLog(r *http.Request) (*RestResponse, error) {
|
|||
}
|
||||
}
|
||||
|
||||
if pageSizeStr != "" {
|
||||
if pageSize, err = strconv.Atoi(pageSizeStr); err != nil {
|
||||
return BasicResponse(false, "pageSize: must be a valid integer"), nil
|
||||
}
|
||||
}
|
||||
|
||||
gli := GetLogInput{
|
||||
// Filter: filter,
|
||||
Start: startTime,
|
||||
End: endTime,
|
||||
Limit: 250,
|
||||
Limit: pageSize,
|
||||
Page: page,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ type Resolved struct {
|
|||
|
||||
func (r Recursor) Resolve(request *dns.Msg) (Resolved, error) {
|
||||
var result Resolved
|
||||
errs := make([]error, len(r.Upstreams))
|
||||
errs := make([]error, 0, len(r.Upstreams))
|
||||
var err error
|
||||
var upstreamsTried int
|
||||
|
||||
|
|
|
|||
|
|
@ -50,6 +50,8 @@ func getType(rt RuleType) uint16 {
|
|||
return dns.TypeA
|
||||
case CNAME:
|
||||
return dns.TypeCNAME
|
||||
case Recurse:
|
||||
break
|
||||
}
|
||||
|
||||
return 0
|
||||
|
|
|
|||
|
|
@ -13,6 +13,11 @@ import (
|
|||
_ "github.com/mattn/go-sqlite3"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultSamples = 64
|
||||
maxSamples = 128
|
||||
)
|
||||
|
||||
type Sqlite struct {
|
||||
Path string
|
||||
*sql.DB
|
||||
|
|
@ -40,9 +45,6 @@ func (ss *Sqlite) Close() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
const defaultSamples = 64
|
||||
const maxSamples = 128
|
||||
|
||||
func (ss *Sqlite) GetLogAggregate(la LogAggregateInput) (LogAggregate, error) {
|
||||
if la.End.IsZero() || la.End.After(time.Now()) {
|
||||
la.End = time.Now().UTC()
|
||||
|
|
@ -71,7 +73,6 @@ func (ss *Sqlite) GetLogAggregate(la LogAggregateInput) (LogAggregate, error) {
|
|||
if sampleCount > maxSamples {
|
||||
sampleCount = maxSamples
|
||||
la.IntervalSeconds = timespanSecs / sampleCount
|
||||
log.Printf("got %v samples, capping to 256 for perf", sampleCount)
|
||||
}
|
||||
|
||||
log.Printf("%+v - samples: %v - timespan (seconds): %v", la, sampleCount, timespanSecs)
|
||||
|
|
@ -189,9 +190,12 @@ func (ss *Sqlite) GetRecursors() ([]RecursorRow, error) {
|
|||
if err != nil {
|
||||
return nil, fmt.Errorf("could not execute select for recursors: %w", err)
|
||||
}
|
||||
|
||||
defer rows.Close()
|
||||
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
results := []RecursorRow{}
|
||||
for rows.Next() {
|
||||
var row RecursorRow
|
||||
|
|
@ -355,7 +359,7 @@ func (ss *Sqlite) GetRules() ([]RuleRow, error) {
|
|||
|
||||
func (ss *Sqlite) GetLog(in GetLogInput) (GetLogResult, error) {
|
||||
if in.Limit <= 0 {
|
||||
in.Limit = 100
|
||||
in.Limit = 25
|
||||
}
|
||||
|
||||
if in.Start.IsZero() {
|
||||
|
|
@ -371,21 +375,37 @@ func (ss *Sqlite) GetLog(in GetLogInput) (GetLogResult, error) {
|
|||
Logs: []QueryLog{},
|
||||
}
|
||||
|
||||
lpi, err := ss.GetPagingInfo(in)
|
||||
if err != nil {
|
||||
return glr, err
|
||||
}
|
||||
|
||||
glr.TotalResults = lpi.Total
|
||||
glr.PageCount = lpi.PageCount + 1
|
||||
|
||||
sql := `
|
||||
SELECT
|
||||
started, clientIp, protocol, domain, totalTimeMs,
|
||||
error, recurseRoundTripTimeMs, recurseUpstreamIp, status
|
||||
FROM
|
||||
log
|
||||
WHERE
|
||||
id > ?
|
||||
AND strftime('%s', started) > strftime('%s', ?)
|
||||
AND strftime('%s', started) < strftime('%s', ?)
|
||||
ORDER BY started DESC
|
||||
LIMIT ?;
|
||||
started, clientIp, protocol, domain, totalTimeMs,
|
||||
error, recurseRoundTripTimeMs, recurseUpstreamIp, status
|
||||
FROM (
|
||||
SELECT id,
|
||||
started,
|
||||
clientIp,
|
||||
protocol,
|
||||
domain,
|
||||
totalTimeMs,
|
||||
error,
|
||||
recurseRoundTripTimeMs,
|
||||
recurseUpstreamIp,
|
||||
status
|
||||
FROM log
|
||||
WHERE strftime('%s', started) >= strftime('%s', ?)
|
||||
AND strftime('%s', started) <= strftime('%s', ?)
|
||||
ORDER BY started DESC
|
||||
) WHERE id <= ? ORDER BY id DESC LIMIT ?;
|
||||
`
|
||||
|
||||
rows, err := ss.DB.Query(sql, in.Page*in.Limit, in.Start.UTC().Format(ISO8601), in.End.UTC().Format(ISO8601), in.Limit)
|
||||
rows, err := ss.DB.Query(sql, in.Start.UTC().Format(ISO8601), in.End.UTC().Format(ISO8601), lpi.FirstItemID, in.Limit)
|
||||
if err != nil {
|
||||
return glr, fmt.Errorf("issue with GetLog sql query: %w", err)
|
||||
}
|
||||
|
|
@ -420,34 +440,39 @@ func (ss *Sqlite) GetLog(in GetLogInput) (GetLogResult, error) {
|
|||
glr.Logs = append(glr.Logs, q)
|
||||
}
|
||||
|
||||
total, pageCount, err := ss.GetPagingInfo(in)
|
||||
if err != nil {
|
||||
return glr, err
|
||||
}
|
||||
|
||||
glr.TotalResults = total
|
||||
glr.PageCount = pageCount
|
||||
|
||||
return glr, nil
|
||||
}
|
||||
|
||||
func (ss *Sqlite) GetPagingInfo(in GetLogInput) (totalItems, pageCount int, err error) {
|
||||
type LogPageInfo struct {
|
||||
Total int
|
||||
PageCount int
|
||||
FirstItemID int
|
||||
}
|
||||
|
||||
func (ss *Sqlite) GetPagingInfo(in GetLogInput) (lpi LogPageInfo, err error) {
|
||||
sql := `
|
||||
SELECT
|
||||
COUNT(*) as totalLogsEntries,
|
||||
COUNT(*) / ? as pageCount
|
||||
COUNT(*) / ? as pageCount,
|
||||
MAX(id) - ? as firstItemId
|
||||
FROM
|
||||
log
|
||||
WHERE
|
||||
strftime('%s', started) > strftime('%s', ?)
|
||||
strftime('%s', started) > strftime('%s', ?)
|
||||
AND strftime('%s', started) < strftime('%s', ?)
|
||||
ORDER BY id DESC
|
||||
`
|
||||
|
||||
row := ss.QueryRow(sql, in.Limit, in.Start.UTC().Format(ISO8601), in.End.UTC().Format(ISO8601))
|
||||
if err = row.Scan(&totalItems, &pageCount); err != nil {
|
||||
pageOffset := in.Limit * in.Page
|
||||
row := ss.QueryRow(sql, in.Limit, pageOffset, in.Start.UTC().Format(ISO8601), in.End.UTC().Format(ISO8601))
|
||||
if err = row.Scan(&lpi.Total, &lpi.PageCount, &lpi.FirstItemID); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if pageOffset > lpi.Total {
|
||||
err = errors.New("page number too high")
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
|||
4
main.go
4
main.go
|
|
@ -14,8 +14,8 @@ import (
|
|||
|
||||
var (
|
||||
dbPath = flag.String("db-path", ".", "Directory to write database files to")
|
||||
httpAddr = flag.String("http-address", ":80", "Bind address for http server")
|
||||
dnsAddr = flag.String("dns-address", ":53", "Bind address for dns server")
|
||||
httpAddr = flag.String("http-address", "0.0.0.0:80", "Bind address for http server")
|
||||
dnsAddr = flag.String("dns-address", "0.0.0.0:53", "Bind address for dns server")
|
||||
defaultUpstream = flag.String("upstream", "1.1.1.1:53", "default upstream DNS server when no others are specified")
|
||||
)
|
||||
|
||||
|
|
|
|||
51
makefile
51
makefile
|
|
@ -1,8 +1,21 @@
|
|||
LINTBIN := $(shell go env GOPATH)/bin/golangci-lint
|
||||
COMMIT_SHA := $(shell git rev-parse HEAD | cut -c 1-11)
|
||||
|
||||
build: clobber .bin/client/public .bin/gopherhole
|
||||
|
||||
dev: clean .bin/gopherhole
|
||||
cd .bin && ./gopherhole -dns-address=:5353 -http-address=:8000
|
||||
|
||||
client-dev:
|
||||
docker run -it --rm --name='client-dev' \
|
||||
--workdir /opt/client \
|
||||
-v $$PWD/client:/opt/client \
|
||||
--entrypoint ash \
|
||||
--user $${UID}:$${GID} \
|
||||
--memory=4g \
|
||||
-p 5000:5000 \
|
||||
node:lts-alpine
|
||||
|
||||
clean:
|
||||
@rm -rf .bin/gopherhole .bin/client
|
||||
|
||||
|
|
@ -10,7 +23,14 @@ clobber: clean
|
|||
@rm -rf .bin ./client/node_modules ./client/public/build
|
||||
|
||||
vdhsn/gopherhole:
|
||||
docker build -t vdhsn/gopherhole:latest .
|
||||
docker build \
|
||||
--label="org.opencontainers.image.created=$(shell date +'%FT%T%:z')" \
|
||||
--label="org.opencontainers.image.source=https://github.com/adamveld12/gopherhole.git" \
|
||||
--label="org.opencontainers.image.url=https://github.com/adamveld12/gopherhole" \
|
||||
--label="org.opencontainers.image.revision=$(COMMIT_SHA)" \
|
||||
--label="org.opencontainers.image.licenses=MIT" \
|
||||
--label="org.opencontainers.image.authors=Adam Veldhousen <adam@vdhsn.com>" \
|
||||
-t vdhsn/gopherhole:latest .
|
||||
|
||||
test:
|
||||
dig -p 5353 twitter.com @localhost
|
||||
|
|
@ -18,8 +38,26 @@ test:
|
|||
dig -p 5353 loki.veldhousen.ninja @localhost
|
||||
dig -p 5353 www.liveauctioneers.com @localhost
|
||||
|
||||
lint: tidy
|
||||
# lint: $(LINTBIN) tidy
|
||||
# golangci-lint run -p bugs \
|
||||
# -p performance \
|
||||
# -p unused \
|
||||
# -p complexity \
|
||||
# --sort-results \
|
||||
# --max-same-issues 5 \
|
||||
# --no-config \
|
||||
# --verbose \
|
||||
# --timeout 30s \
|
||||
# --concurrency 4 \
|
||||
# --issues-exit-code 1
|
||||
|
||||
tidy:
|
||||
go mod tidy
|
||||
|
||||
.PHONY: build clean clobber client-dev dev lint tidy test vdhsn/gopherhole
|
||||
|
||||
|
||||
.PHONY: build clean clobber client-dev dev test vdhsn/gopherhole
|
||||
|
||||
.bin:
|
||||
mkdir -p .bin
|
||||
|
|
@ -27,9 +65,6 @@ test:
|
|||
.bin/gopherhole: .bin
|
||||
@go build --tags "fts5" -v -o .bin/gopherhole .
|
||||
|
||||
client-dev: client/node_modules
|
||||
cd ./client && npm run dev
|
||||
|
||||
.bin/client/public: .bin client/public/build
|
||||
mkdir -p .bin/client/public
|
||||
cp -R ./client/public/ .bin/client/
|
||||
|
|
@ -39,3 +74,9 @@ client/public/build: client/node_modules
|
|||
|
||||
client/node_modules:
|
||||
cd ./client && npm install
|
||||
|
||||
$(LINTBIN):
|
||||
echo "installing golangci-lint to $(LINTBIN)..."
|
||||
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(shell go env GOPATH)/bin latest
|
||||
chmod +x $(LINTBIN)
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue