mirror of
https://github.com/AdguardTeam/AdGuardHome.git
synced 2026-10-25 04:28:06 +09:00
AGDNS-4277 generate client api using openapi specs
This commit is contained in:
1 parent
ef585d45cd
commit
c79ff5454e
166 files changed
+4908
-1062
No files matched your search
@@ -67,4 +67,13 @@ module.exports = {
|
||||
},
|
||||
],
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
// Orval-generated files — max-len is controlled by prettier
|
||||
files: ['src/api/generated.ts', 'src/api/model/**/*.ts'],
|
||||
rules: {
|
||||
'max-len': 'off',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
+29
-27
@@ -2,24 +2,24 @@
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [AGENTS.md — AdGuard Home client\_v2 (SolidJS Frontend)](#agentsmd--adguard-home-client_v2-solidjs-frontend)
|
||||
- [Table of Contents](#table-of-contents)
|
||||
- [AGENTS.md — AdGuard Home client_v2 (SolidJS Frontend)](#agentsmd--adguard-home-client_v2-solidjs-frontend)
|
||||
- [Table of Contents](#table-of-contents)
|
||||
- [Project Overview](#project-overview)
|
||||
- [Technical Context](#technical-context)
|
||||
- [Project Structure](#project-structure)
|
||||
- [Build And Test Commands](#build-and-test-commands)
|
||||
- [Contribution Instructions](#contribution-instructions)
|
||||
- [Code Guidelines](#code-guidelines)
|
||||
- [System Design](#system-design)
|
||||
- [Architecture](#architecture)
|
||||
- [Code Quality](#code-quality)
|
||||
- [Testing](#testing)
|
||||
- [Dependency Management](#dependency-management)
|
||||
- [Configuration \& Documentation](#configuration--documentation)
|
||||
- [Markdown Formatting](#markdown-formatting)
|
||||
- [Other](#other)
|
||||
- [Accessibility](#accessibility)
|
||||
- [Translations](#translations)
|
||||
- [System Design](#system-design)
|
||||
- [Architecture](#architecture)
|
||||
- [Code Quality](#code-quality)
|
||||
- [Testing](#testing)
|
||||
- [Dependency Management](#dependency-management)
|
||||
- [Configuration \& Documentation](#configuration--documentation)
|
||||
- [Markdown Formatting](#markdown-formatting)
|
||||
- [Other](#other)
|
||||
- [Accessibility](#accessibility)
|
||||
- [Translations](#translations)
|
||||
|
||||
# Project Overview
|
||||
|
||||
@@ -77,7 +77,7 @@ client_v2/
|
||||
│ ├── index.tsx # Main app entry (renders <App/>)
|
||||
│ ├── index.pcss # Global CSS (vars, reset)
|
||||
│ ├── initialState.ts # Domain model types + initial form state
|
||||
│ ├── api/ # Single Api class — all HTTP calls
|
||||
│ ├── api/ # Orval-generated typed client + customFetch mutator
|
||||
│ ├── common/
|
||||
│ │ ├── controls/ # Input primitives (Checkbox, Input, Select, Switch, …)
|
||||
│ │ ├── ui/ # Higher-level UI (Button, Dialog, Table, Tabs, Sidebar, …)
|
||||
@@ -166,9 +166,10 @@ no server process of its own. Design for the browser environment:
|
||||
- **Stateless client, stateful backend.** The UI holds no durable state; all
|
||||
configuration and data persist in the AdGuard Home backend via the `/control`
|
||||
HTTP API. Treat the browser as a thin view layer.
|
||||
- **Single source of HTTP.** All backend calls go through the singleton
|
||||
`apiClient` in `src/api/Api.ts`. Do not call `fetch` directly from components
|
||||
or stores — add a typed method to the `Api` class instead.
|
||||
- **Single source of HTTP.** All backend calls go through the orval-generated
|
||||
typed client in `src/api/generated.ts` (with the `customFetch` mutator in
|
||||
`src/api/customFetch.ts`). Do not call `fetch` directly from components
|
||||
or stores — import a generated function instead.
|
||||
- **No shared server memory.** Multiple browser tabs / reloads are independent;
|
||||
never rely on in-memory module state surviving a reload. Re-fetch data in
|
||||
component `onMount` rather than assuming a store is already populated.
|
||||
@@ -199,8 +200,9 @@ Universal design principles the codebase follows:
|
||||
→ api → backend. Stores never import components; api never imports stores.
|
||||
- **Explicit Boundaries** — modules interact through named exports; no
|
||||
reaching into another module's internals.
|
||||
- **Data Flow Clarity** — user action → store action function → `apiClient`
|
||||
→ `setState` → reactive UI update. Data moves in one predictable path.
|
||||
- **Data Flow Clarity** — user action → store action function → generated
|
||||
API function → `setState` → reactive UI update. Data moves in one
|
||||
predictable path.
|
||||
- **Minimize Coupling, Maximize Cohesion** — stores are self-contained and
|
||||
imported directly (no Context providers); components depend on narrow store
|
||||
exports.
|
||||
@@ -221,7 +223,7 @@ This project's layers, from top to bottom:
|
||||
| Components | Render UI, handle user interaction | `src/components/Dashboard/`, `src/components/Clients/` |
|
||||
| Common UI | Reusable controls and primitives | `src/common/controls/Select/`, `src/common/ui/Button/` |
|
||||
| Stores | Domain state + async actions | `src/stores/clients.ts`, `src/stores/queryLogs.ts` |
|
||||
| API | HTTP transport to backend | `src/api/Api.ts` |
|
||||
| API | HTTP transport to backend | `src/api/generated.ts`, `src/api/customFetch.ts` |
|
||||
| Helpers | Pure utilities, validators | `src/helpers/`, `src/lib/` |
|
||||
|
||||
```text
|
||||
@@ -229,7 +231,7 @@ Components (pages, controls)
|
||||
↓
|
||||
Stores (domain state + actions)
|
||||
↓
|
||||
API (apiClient → fetch /control)
|
||||
API (generated functions → customFetch → fetch /control)
|
||||
↓
|
||||
AdGuard Home backend (Go)
|
||||
```
|
||||
@@ -246,7 +248,7 @@ must not depend on stores or components. Helpers are pure and dependency-free.
|
||||
## Code Quality
|
||||
|
||||
- **Path aliases**: Use the `panel/*` alias for all `src/` imports
|
||||
(e.g. `import { apiClient } from 'panel/api/Api'`). Use `Twosky` for the
|
||||
(e.g. `import { status } from 'panel/api/generated'`). Use `Twosky` for the
|
||||
root `.twosky.json` config. Avoid deep relative paths (`../../..`).
|
||||
- **Component conventions**: PascalCase directories and component files.
|
||||
Each component lives in its own directory with an `index.tsx` and a
|
||||
@@ -284,12 +286,12 @@ must not depend on stores or components. Helpers are pure and dependency-free.
|
||||
`createMemo` (e.g., table columns), **access the prop in the memo body**
|
||||
so SolidJS tracks the dependency.
|
||||
- **Stores**: Module-scoped `createStore` singletons exported directly — no
|
||||
Context/Provider. Async actions set a `processing*` flag, call `apiClient`,
|
||||
then `setState`. Errors are reported via `addErrorToast`.
|
||||
Context/Provider. Async actions set a `processing*` flag, call generated
|
||||
API functions, then `setState`. Errors are reported via `addErrorToast`.
|
||||
- **Naming**: Files `PascalCase.tsx` for components, `camelCase.ts` for
|
||||
stores/helpers. CSS module files `*.module.pcss`.
|
||||
- **Error handling**: API errors throw from `Api.ts`; store actions catch and
|
||||
surface a toast. Do not swallow errors silently.
|
||||
- **Error handling**: API errors throw from `customFetch.ts`; store actions
|
||||
catch and surface a toast. Do not swallow errors silently.
|
||||
- **Logging**: `console.warn` and `console.error` are allowed; `console.log`
|
||||
is disallowed by ESLint.
|
||||
- **Static analysis gates**: ESLint, Prettier, and `tsc --noEmit` must pass.
|
||||
@@ -312,8 +314,8 @@ must not depend on stores or components. Helpers are pure and dependency-free.
|
||||
`@testing-library/jest-dom` matchers.
|
||||
- **Store/helper tests**: Call exported action/builder functions directly and
|
||||
assert on returned values or state (e.g.
|
||||
`src/__tests__/clientForm/buildClientConfig.test.ts`). Mock `apiClient` when
|
||||
a test would otherwise hit the network.
|
||||
`src/__tests__/clientForm/buildClientConfig.test.ts`). Mock generated API
|
||||
functions when a test would otherwise hit the network.
|
||||
- **Naming**: Mirror the source path under `__tests__/`
|
||||
(e.g. `stores/clients.ts` → `__tests__/stores/clients.test.ts`).
|
||||
- **E2E tests**: Playwright, configured in `playwright.config.ts`
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import { defineConfig } from 'orval';
|
||||
|
||||
export default defineConfig({
|
||||
adguardHome: {
|
||||
input: {
|
||||
target: '../openapi/openapi.yaml',
|
||||
},
|
||||
output: {
|
||||
target: './src/api/generated.ts',
|
||||
schemas: './src/api/model',
|
||||
client: 'fetch',
|
||||
baseUrl: 'control',
|
||||
prettier: true,
|
||||
override: {
|
||||
header: false,
|
||||
mutator: {
|
||||
path: './src/api/customFetch.ts',
|
||||
name: 'customFetch',
|
||||
},
|
||||
fetch: {
|
||||
includeHttpResponseReturnType: false,
|
||||
},
|
||||
enumGenerationType: 'union',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
Generated
+1526
File diff suppressed because it is too large.
Load diff
@@ -19,7 +19,8 @@
|
||||
"test:e2e:codegen": "npx playwright codegen",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"typecheck:watch": "tsc --noEmit --watch",
|
||||
"translations:check": "node ./scripts/check-translations.js"
|
||||
"translations:check": "node ./scripts/check-translations.js",
|
||||
"api:generate": "orval --config ./orval.config.ts && bash scripts/postgenerate.sh"
|
||||
},
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
@@ -69,6 +70,7 @@
|
||||
"html-webpack-plugin": "^5.6.0",
|
||||
"jsdom": "^26.1.0",
|
||||
"mini-css-extract-plugin": "^2.9.0",
|
||||
"orval": "^8.22.0",
|
||||
"playwright": "^1.61.0",
|
||||
"postcss": "^8.5.6",
|
||||
"postcss-import": "^16.1.1",
|
||||
|
||||
Executable
+5
@@ -0,0 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
npx prettier --write "src/api/generated.ts" "src/api/model/**/*.ts"
|
||||
npx eslint --quiet --fix "src/api/generated.ts" "src/api/model/**/*.ts" || true
|
||||
@@ -1,17 +1,15 @@
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
getAccessList: vi.fn(),
|
||||
setAccessList: vi.fn(),
|
||||
accessList: vi.fn(),
|
||||
accessSet: vi.fn(),
|
||||
addSuccessToast: vi.fn(),
|
||||
addErrorToast: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('panel/api/Api', () => ({
|
||||
apiClient: {
|
||||
getAccessList: mocks.getAccessList,
|
||||
setAccessList: mocks.setAccessList,
|
||||
},
|
||||
vi.mock('panel/api/generated', () => ({
|
||||
accessList: mocks.accessList,
|
||||
accessSet: mocks.accessSet,
|
||||
}));
|
||||
vi.mock('panel/stores/toasts', () => ({
|
||||
addSuccessToast: mocks.addSuccessToast,
|
||||
@@ -24,13 +22,13 @@ describe('toggleClientBlock', () => {
|
||||
beforeEach(() => vi.clearAllMocks());
|
||||
|
||||
it('not-disallowed + allowlist mode with >1 allowed → removes from allowed', async () => {
|
||||
mocks.getAccessList.mockResolvedValue({
|
||||
mocks.accessList.mockResolvedValue({
|
||||
allowed_clients: ['1.1.1.1', '2.2.2.2'],
|
||||
disallowed_clients: [],
|
||||
blocked_hosts: [],
|
||||
});
|
||||
await toggleClientBlock('1.1.1.1', false, '');
|
||||
expect(mocks.setAccessList).toHaveBeenCalledWith({
|
||||
expect(mocks.accessSet).toHaveBeenCalledWith({
|
||||
allowed_clients: ['2.2.2.2'],
|
||||
disallowed_clients: [],
|
||||
blocked_hosts: [],
|
||||
@@ -38,13 +36,13 @@ describe('toggleClientBlock', () => {
|
||||
});
|
||||
|
||||
it('not-disallowed, no allowlist → adds to disallowed', async () => {
|
||||
mocks.getAccessList.mockResolvedValue({
|
||||
mocks.accessList.mockResolvedValue({
|
||||
allowed_clients: [],
|
||||
disallowed_clients: [],
|
||||
blocked_hosts: [],
|
||||
});
|
||||
await toggleClientBlock('3.3.3.3', false, '');
|
||||
expect(mocks.setAccessList).toHaveBeenCalledWith({
|
||||
expect(mocks.accessSet).toHaveBeenCalledWith({
|
||||
allowed_clients: [],
|
||||
disallowed_clients: ['3.3.3.3'],
|
||||
blocked_hosts: [],
|
||||
@@ -52,13 +50,13 @@ describe('toggleClientBlock', () => {
|
||||
});
|
||||
|
||||
it('disallowed + allowlist mode → adds to allowed', async () => {
|
||||
mocks.getAccessList.mockResolvedValue({
|
||||
mocks.accessList.mockResolvedValue({
|
||||
allowed_clients: ['1.1.1.1'],
|
||||
disallowed_clients: ['2.2.2.2'],
|
||||
blocked_hosts: [],
|
||||
});
|
||||
await toggleClientBlock('2.2.2.2', true, '');
|
||||
expect(mocks.setAccessList).toHaveBeenCalledWith({
|
||||
expect(mocks.accessSet).toHaveBeenCalledWith({
|
||||
allowed_clients: ['1.1.1.1', '2.2.2.2'],
|
||||
disallowed_clients: ['2.2.2.2'],
|
||||
blocked_hosts: [],
|
||||
@@ -66,13 +64,13 @@ describe('toggleClientBlock', () => {
|
||||
});
|
||||
|
||||
it('disallowed, no allowlist → removes from disallowed (uses rule)', async () => {
|
||||
mocks.getAccessList.mockResolvedValue({
|
||||
mocks.accessList.mockResolvedValue({
|
||||
allowed_clients: [],
|
||||
disallowed_clients: ['client:X'],
|
||||
blocked_hosts: [],
|
||||
});
|
||||
await toggleClientBlock('1.2.3.4', true, 'client:X');
|
||||
expect(mocks.setAccessList).toHaveBeenCalledWith({
|
||||
expect(mocks.accessSet).toHaveBeenCalledWith({
|
||||
allowed_clients: [],
|
||||
disallowed_clients: [],
|
||||
blocked_hosts: [],
|
||||
|
||||
@@ -1,22 +1,20 @@
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
getTlsStatus: vi.fn(),
|
||||
getGlobalVersion: vi.fn(),
|
||||
tlsStatus: vi.fn(),
|
||||
getVersionJson: vi.fn(),
|
||||
getProfile: vi.fn(),
|
||||
addErrorToast: vi.fn(),
|
||||
addSuccessToast: vi.fn(),
|
||||
addNoticeToast: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('panel/api/Api', () => ({
|
||||
apiClient: {
|
||||
vi.mock('panel/api/generated', () => ({
|
||||
baseUrl: 'http://x',
|
||||
getGlobalVersion: mocks.getGlobalVersion,
|
||||
getVersionJson: mocks.getVersionJson,
|
||||
getProfile: mocks.getProfile,
|
||||
getUpdate: vi.fn(),
|
||||
getTlsStatus: mocks.getTlsStatus,
|
||||
},
|
||||
beginUpdate: vi.fn(),
|
||||
tlsStatus: mocks.tlsStatus,
|
||||
}));
|
||||
vi.mock('panel/stores/toasts', () => ({
|
||||
addErrorToast: mocks.addErrorToast,
|
||||
@@ -30,12 +28,12 @@ describe('getDnsStatus', () => {
|
||||
beforeEach(() => vi.clearAllMocks());
|
||||
|
||||
it('fetches TLS status when the core is running (FR-005)', async () => {
|
||||
mocks.getGlobalVersion.mockResolvedValue({
|
||||
mocks.getVersionJson.mockResolvedValue({
|
||||
disabled: true,
|
||||
new_version: 'x',
|
||||
});
|
||||
mocks.getProfile.mockResolvedValue({ name: 'n', theme: 't' });
|
||||
mocks.getTlsStatus.mockResolvedValue({ enabled: false });
|
||||
mocks.tlsStatus.mockResolvedValue({ enabled: false });
|
||||
|
||||
vi.spyOn(globalThis, 'fetch').mockResolvedValue(
|
||||
new Response(
|
||||
@@ -54,6 +52,6 @@ describe('getDnsStatus', () => {
|
||||
await getDnsStatus();
|
||||
// allow microtasks for chained getVersion/getTlsStatus/getProfile
|
||||
await new Promise((r) => setTimeout(r, 0));
|
||||
expect(mocks.getTlsStatus).toHaveBeenCalled();
|
||||
expect(mocks.tlsStatus).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@@ -1,22 +1,20 @@
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
findActiveDhcp: vi.fn(),
|
||||
getDhcpInterfaces: vi.fn(),
|
||||
getDhcpStatus: vi.fn(),
|
||||
setDhcpConfig: vi.fn(),
|
||||
checkActiveDhcp: vi.fn(),
|
||||
dhcpInterfaces: vi.fn(),
|
||||
dhcpStatus: vi.fn(),
|
||||
dhcpSetConfig: vi.fn(),
|
||||
addErrorToast: vi.fn(),
|
||||
addSuccessToast: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('panel/api/Api', () => ({
|
||||
apiClient: {
|
||||
findActiveDhcp: mocks.findActiveDhcp,
|
||||
getDhcpInterfaces: mocks.getDhcpInterfaces,
|
||||
getDhcpStatus: mocks.getDhcpStatus,
|
||||
getGlobalStatus: vi.fn(),
|
||||
setDhcpConfig: mocks.setDhcpConfig,
|
||||
},
|
||||
vi.mock('panel/api/generated', () => ({
|
||||
checkActiveDhcp: mocks.checkActiveDhcp,
|
||||
dhcpInterfaces: mocks.dhcpInterfaces,
|
||||
dhcpStatus: mocks.dhcpStatus,
|
||||
status: vi.fn(),
|
||||
dhcpSetConfig: mocks.dhcpSetConfig,
|
||||
}));
|
||||
vi.mock('panel/stores/toasts', () => ({
|
||||
addErrorToast: mocks.addErrorToast,
|
||||
@@ -29,19 +27,19 @@ describe('findActiveDhcp', () => {
|
||||
beforeEach(() => vi.clearAllMocks());
|
||||
|
||||
it('passes { interface } not a bare string', async () => {
|
||||
mocks.findActiveDhcp.mockResolvedValue({
|
||||
mocks.checkActiveDhcp.mockResolvedValue({
|
||||
v4: { other_server: { found: 'yes' }, static_ip: { static: 'yes' } },
|
||||
v6: { other_server: {} },
|
||||
});
|
||||
await findActiveDhcp('eth0');
|
||||
expect(mocks.findActiveDhcp).toHaveBeenCalledWith({ interface: 'eth0' });
|
||||
expect(mocks.checkActiveDhcp).toHaveBeenCalledWith({ interface: 'eth0' });
|
||||
});
|
||||
|
||||
it('shows dhcp_found error with retry action when another DHCP server detected', async () => {
|
||||
mocks.getDhcpInterfaces.mockResolvedValue({
|
||||
mocks.dhcpInterfaces.mockResolvedValue({
|
||||
eth0: { ipv4_addresses: ['1.1.1.1'], ipv6_addresses: [] },
|
||||
});
|
||||
mocks.findActiveDhcp.mockResolvedValue({
|
||||
mocks.checkActiveDhcp.mockResolvedValue({
|
||||
v4: {
|
||||
other_server: { found: 'yes' },
|
||||
static_ip: { static: 'yes', ip: 'x' },
|
||||
@@ -58,10 +56,10 @@ describe('findActiveDhcp', () => {
|
||||
});
|
||||
|
||||
it('shows dhcp_not_found success toast when clean', async () => {
|
||||
mocks.getDhcpInterfaces.mockResolvedValue({
|
||||
mocks.dhcpInterfaces.mockResolvedValue({
|
||||
eth0: { ipv4_addresses: ['1.1.1.1'], ipv6_addresses: [] },
|
||||
});
|
||||
mocks.findActiveDhcp.mockResolvedValue({
|
||||
mocks.checkActiveDhcp.mockResolvedValue({
|
||||
v4: {
|
||||
other_server: { found: 'no' },
|
||||
static_ip: { static: 'yes', ip: '1.1.1.1' },
|
||||
@@ -77,7 +75,7 @@ describe('findActiveDhcp', () => {
|
||||
describe('setDhcpConfig', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
mocks.setDhcpConfig.mockResolvedValue(undefined);
|
||||
mocks.dhcpSetConfig.mockResolvedValue(undefined);
|
||||
});
|
||||
|
||||
it('shows dhcp_config_saved toast (FR-010)', async () => {
|
||||
@@ -92,12 +90,12 @@ describe('setDhcpConfig', () => {
|
||||
describe('toggleDhcp', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
mocks.setDhcpConfig.mockResolvedValue(undefined);
|
||||
mocks.dhcpSetConfig.mockResolvedValue(undefined);
|
||||
});
|
||||
|
||||
it('computes enabled from passed config, not current state (FR-011)', async () => {
|
||||
await toggleDhcp({ enabled: false, interface_name: 'eth0' });
|
||||
expect(mocks.setDhcpConfig).toHaveBeenCalledWith(
|
||||
expect(mocks.dhcpSetConfig).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ enabled: true }),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
getDnsConfig: vi.fn(),
|
||||
setDnsConfig: vi.fn(),
|
||||
dnsInfo: vi.fn(),
|
||||
dnsConfig: vi.fn(),
|
||||
addErrorToast: vi.fn(),
|
||||
addSuccessToast: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('panel/api/Api', () => ({
|
||||
apiClient: {
|
||||
getDnsConfig: mocks.getDnsConfig,
|
||||
setDnsConfig: mocks.setDnsConfig,
|
||||
},
|
||||
vi.mock('panel/api/generated', () => ({
|
||||
dnsInfo: mocks.dnsInfo,
|
||||
dnsConfig: mocks.dnsConfig,
|
||||
}));
|
||||
vi.mock('panel/stores/toasts', () => ({
|
||||
addErrorToast: mocks.addErrorToast,
|
||||
@@ -27,7 +25,7 @@ describe('getDnsConfig', () => {
|
||||
beforeEach(() => vi.clearAllMocks());
|
||||
|
||||
it('defaults null blocking IPs and empty upstream_mode (FR-012)', async () => {
|
||||
mocks.getDnsConfig.mockResolvedValue({
|
||||
mocks.dnsInfo.mockResolvedValue({
|
||||
blocking_ipv4: null,
|
||||
blocking_ipv6: null,
|
||||
upstream_mode: '',
|
||||
@@ -43,7 +41,7 @@ describe('toggleResolveClients', () => {
|
||||
beforeEach(() => vi.clearAllMocks());
|
||||
|
||||
it('toggles resolve_clients and persists (FR-032)', async () => {
|
||||
mocks.setDnsConfig.mockResolvedValue({});
|
||||
mocks.dnsConfig.mockResolvedValue({});
|
||||
|
||||
const before = dnsConfigState.resolve_clients;
|
||||
await toggleResolveClients();
|
||||
@@ -54,12 +52,12 @@ describe('toggleResolveClients', () => {
|
||||
expect(dnsConfigState.resolve_clients).toBe(before);
|
||||
});
|
||||
|
||||
it('calls apiClient.setDnsConfig with inverted value (FR-032)', async () => {
|
||||
mocks.setDnsConfig.mockResolvedValue({});
|
||||
it('calls dnsConfig with inverted value (FR-032)', async () => {
|
||||
mocks.dnsConfig.mockResolvedValue({});
|
||||
|
||||
const before = dnsConfigState.resolve_clients;
|
||||
await toggleResolveClients();
|
||||
expect(mocks.setDnsConfig).toHaveBeenCalledWith({
|
||||
expect(mocks.dnsConfig).toHaveBeenCalledWith({
|
||||
resolve_clients: !before,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
setTlsConfig: vi.fn(),
|
||||
validateTlsConfig: vi.fn(),
|
||||
tlsConfigure: vi.fn(),
|
||||
tlsValidate: vi.fn(),
|
||||
addErrorToast: vi.fn(),
|
||||
addSuccessToast: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('panel/api/Api', () => ({
|
||||
apiClient: {
|
||||
setTlsConfig: mocks.setTlsConfig,
|
||||
validateTlsConfig: mocks.validateTlsConfig,
|
||||
getGlobalStatus: vi.fn(),
|
||||
},
|
||||
vi.mock('panel/api/generated', () => ({
|
||||
tlsConfigure: mocks.tlsConfigure,
|
||||
tlsValidate: mocks.tlsValidate,
|
||||
status: vi.fn(),
|
||||
}));
|
||||
vi.mock('panel/stores/toasts', () => ({
|
||||
addErrorToast: mocks.addErrorToast,
|
||||
@@ -31,7 +29,7 @@ describe('setTlsConfig', () => {
|
||||
beforeEach(() => vi.clearAllMocks());
|
||||
|
||||
it('defaults empty ports to 0 (FR-013)', async () => {
|
||||
mocks.setTlsConfig.mockImplementation(async (v: any) => ({
|
||||
mocks.tlsConfigure.mockImplementation(async (v: any) => ({
|
||||
...v,
|
||||
certificate_chain: '',
|
||||
private_key: '',
|
||||
@@ -43,14 +41,14 @@ describe('setTlsConfig', () => {
|
||||
port_dns_over_tls: '',
|
||||
port_dns_over_quic: '',
|
||||
});
|
||||
const sent = mocks.setTlsConfig.mock.calls[0][0];
|
||||
const sent = mocks.tlsConfigure.mock.calls[0][0];
|
||||
expect(sent.port_https).toBe(0);
|
||||
expect(sent.port_dns_over_tls).toBe(0);
|
||||
expect(sent.port_dns_over_quic).toBe(0);
|
||||
});
|
||||
|
||||
it('clears validation status fields when resetValidationStatus is called', async () => {
|
||||
mocks.validateTlsConfig.mockResolvedValue({
|
||||
mocks.tlsValidate.mockResolvedValue({
|
||||
valid_chain: true,
|
||||
valid_cert: true,
|
||||
valid_key: true,
|
||||
@@ -94,7 +92,7 @@ describe('setTlsConfig', () => {
|
||||
value: { protocol: 'http:', reload: reloadFn },
|
||||
writable: true,
|
||||
});
|
||||
mocks.setTlsConfig.mockImplementation(async (v: any) => ({
|
||||
mocks.tlsConfigure.mockImplementation(async (v: any) => ({
|
||||
...v,
|
||||
certificate_chain: '',
|
||||
private_key: '',
|
||||
|
||||
@@ -15,12 +15,10 @@ const mocks = vi.hoisted(() => ({
|
||||
addErrorToast: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('panel/api/Api', () => ({
|
||||
apiClient: {
|
||||
setRules: mocks.apiSetRules,
|
||||
addFilter: mocks.apiAddFilter,
|
||||
getFilteringStatus: mocks.apiGetFilteringStatus,
|
||||
},
|
||||
vi.mock('panel/api/generated', () => ({
|
||||
filteringSetRules: mocks.apiSetRules,
|
||||
filteringAddURL: mocks.apiAddFilter,
|
||||
filteringStatus: mocks.apiGetFilteringStatus,
|
||||
}));
|
||||
|
||||
vi.mock('panel/stores/toasts', () => ({
|
||||
|
||||
@@ -15,12 +15,10 @@ const mocks = vi.hoisted(() => ({
|
||||
addErrorToast: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('panel/api/Api', () => ({
|
||||
apiClient: {
|
||||
setRules: mocks.apiSetRules,
|
||||
addFilter: mocks.apiAddFilter,
|
||||
getFilteringStatus: mocks.apiGetFilteringStatus,
|
||||
},
|
||||
vi.mock('panel/api/generated', () => ({
|
||||
filteringSetRules: mocks.apiSetRules,
|
||||
filteringAddURL: mocks.apiAddFilter,
|
||||
filteringStatus: mocks.apiGetFilteringStatus,
|
||||
}));
|
||||
|
||||
vi.mock('panel/stores/toasts', () => ({
|
||||
|
||||
@@ -2,12 +2,10 @@ import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
|
||||
// Mock the API client.
|
||||
const mockCheckConfig = vi.fn();
|
||||
vi.mock('panel/api/Api', () => ({
|
||||
apiClient: {
|
||||
checkConfig: (...args: unknown[]) => mockCheckConfig(...args),
|
||||
getDefaultAddresses: vi.fn(),
|
||||
setAllSettings: vi.fn(),
|
||||
},
|
||||
vi.mock('panel/api/generated', () => ({
|
||||
installCheckConfig: (...args: unknown[]) => mockCheckConfig(...args),
|
||||
installGetAddresses: vi.fn(),
|
||||
installConfigure: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('panel/stores/toasts', () => ({ addErrorToast: vi.fn() }));
|
||||
|
||||
@@ -2,10 +2,8 @@ import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
|
||||
// Mock the API client before importing the store.
|
||||
const mockLogin = vi.fn().mockResolvedValue(undefined);
|
||||
vi.mock('panel/api/Api', () => ({
|
||||
apiClient: {
|
||||
vi.mock('panel/api/generated', () => ({
|
||||
login: (...args: unknown[]) => mockLogin(...args),
|
||||
},
|
||||
}));
|
||||
|
||||
// Mock addErrorToast so it does not depend on other stores.
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import { describe, expect, it, vi, beforeEach } from 'vitest';
|
||||
import { getAdditionalLogs, setFilteredLogs, queryLogsState } from 'panel/stores/queryLogs';
|
||||
import { apiClient } from 'panel/api/Api';
|
||||
import { queryLog } from 'panel/api/generated';
|
||||
|
||||
vi.mock('panel/api/Api', () => ({
|
||||
apiClient: {
|
||||
getQueryLog: vi.fn(),
|
||||
},
|
||||
vi.mock('panel/api/generated', () => ({
|
||||
queryLog: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('panel/stores/toasts', () => ({
|
||||
@@ -19,14 +17,14 @@ describe('queryLogs store', () => {
|
||||
});
|
||||
|
||||
it('getAdditionalLogs appends filtered rows using the oldest cursor and filter', async () => {
|
||||
(apiClient.getQueryLog as any).mockReset();
|
||||
(queryLog as any).mockReset();
|
||||
|
||||
// Seed state: a full first page (20 rewritten) with more behind it.
|
||||
const fullPage = Array.from({ length: 20 }, () => ({
|
||||
reason: 'Rewrite',
|
||||
question: {},
|
||||
}));
|
||||
(apiClient.getQueryLog as any).mockResolvedValueOnce({
|
||||
(queryLog as any).mockResolvedValueOnce({
|
||||
data: fullPage,
|
||||
oldest: 'cur',
|
||||
});
|
||||
@@ -35,7 +33,7 @@ describe('queryLogs store', () => {
|
||||
expect(queryLogsState.logs).toHaveLength(20);
|
||||
|
||||
// Load more: one extra rewritten entry, then end of log.
|
||||
(apiClient.getQueryLog as any).mockResolvedValueOnce({
|
||||
(queryLog as any).mockResolvedValueOnce({
|
||||
data: [{ reason: 'Rewrite', question: {} }],
|
||||
oldest: '',
|
||||
});
|
||||
@@ -43,7 +41,7 @@ describe('queryLogs store', () => {
|
||||
await getAdditionalLogs();
|
||||
|
||||
// The load-more request must carry the cursor and the reason filter.
|
||||
expect(apiClient.getQueryLog).toHaveBeenLastCalledWith(
|
||||
expect(queryLog).toHaveBeenLastCalledWith(
|
||||
expect.objectContaining({
|
||||
older_than: 'cur',
|
||||
reason: expect.arrayContaining([
|
||||
@@ -55,7 +53,7 @@ describe('queryLogs store', () => {
|
||||
}),
|
||||
);
|
||||
// Must NOT send the deprecated response_status
|
||||
const lastCall = (apiClient.getQueryLog as any).mock.calls.at(-1)[0];
|
||||
const lastCall = (queryLog as any).mock.calls.at(-1)[0];
|
||||
expect(lastCall).not.toHaveProperty('response_status');
|
||||
|
||||
expect(queryLogsState.logs).toHaveLength(21);
|
||||
@@ -64,16 +62,16 @@ describe('queryLogs store', () => {
|
||||
});
|
||||
|
||||
it('setFilteredLogs sends reason strings for blocked status', async () => {
|
||||
(apiClient.getQueryLog as any).mockReset();
|
||||
(apiClient.getQueryLog as any).mockResolvedValue({
|
||||
(queryLog as any).mockReset();
|
||||
(queryLog as any).mockResolvedValue({
|
||||
data: [{ reason: 'FilteredBlackList', question: {} }],
|
||||
oldest: '',
|
||||
});
|
||||
|
||||
await setFilteredLogs({ search: '', status: 'blocked', reason: 'all' });
|
||||
|
||||
const lastCall = (apiClient.getQueryLog as any).mock.calls.at(-1)[0];
|
||||
expect(apiClient.getQueryLog).toHaveBeenLastCalledWith(
|
||||
const lastCall = (queryLog as any).mock.calls.at(-1)[0];
|
||||
expect(queryLog).toHaveBeenLastCalledWith(
|
||||
expect.objectContaining({
|
||||
reason: expect.arrayContaining([
|
||||
'FilteredBlackList',
|
||||
@@ -87,7 +85,7 @@ describe('queryLogs store', () => {
|
||||
});
|
||||
|
||||
it('does not mark the log as complete when additional loading stops', async () => {
|
||||
(apiClient.getQueryLog as any).mockResolvedValue({
|
||||
(queryLog as any).mockResolvedValue({
|
||||
data: [],
|
||||
oldest: 'next-cursor',
|
||||
});
|
||||
@@ -99,7 +97,7 @@ describe('queryLogs store', () => {
|
||||
});
|
||||
|
||||
it('accumulates pages until oldest is empty (short-polling)', async () => {
|
||||
(apiClient.getQueryLog as any)
|
||||
(queryLog as any)
|
||||
.mockResolvedValueOnce({
|
||||
data: [{ reason: 'Rewrite' }],
|
||||
oldest: 'cur',
|
||||
@@ -112,7 +110,7 @@ describe('queryLogs store', () => {
|
||||
});
|
||||
|
||||
await setFilteredLogs({ search: '', status: 'rewritten', reason: 'all' });
|
||||
expect(apiClient.getQueryLog).toHaveBeenCalledTimes(2);
|
||||
expect(queryLog).toHaveBeenCalledTimes(2);
|
||||
expect(queryLogsState.processingGetLogs).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -1,16 +1,14 @@
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
getStats: vi.fn(),
|
||||
searchClients: vi.fn(),
|
||||
stats: vi.fn(),
|
||||
clientsSearch: vi.fn(),
|
||||
addErrorToast: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('panel/api/Api', () => ({
|
||||
apiClient: {
|
||||
getStats: mocks.getStats,
|
||||
searchClients: mocks.searchClients,
|
||||
},
|
||||
vi.mock('panel/api/generated', () => ({
|
||||
stats: mocks.stats,
|
||||
clientsSearch: mocks.clientsSearch,
|
||||
}));
|
||||
vi.mock('panel/stores/toasts', () => ({
|
||||
addErrorToast: mocks.addErrorToast,
|
||||
@@ -21,11 +19,11 @@ import { getStats, statsState } from 'panel/stores/stats';
|
||||
describe('getStats', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
mocks.searchClients.mockResolvedValue([]);
|
||||
mocks.clientsSearch.mockResolvedValue([]);
|
||||
});
|
||||
|
||||
it('enriches top clients and stores normalizedTopClients (FR-001/019)', async () => {
|
||||
mocks.getStats.mockResolvedValue({
|
||||
mocks.stats.mockResolvedValue({
|
||||
top_clients: [{ '1.2.3.4': 5 }],
|
||||
avg_processing_time: 0.012,
|
||||
top_blocked_domains: [],
|
||||
@@ -37,7 +35,7 @@ describe('getStats', () => {
|
||||
await getStats();
|
||||
|
||||
// searchClients was called with discovered client ids
|
||||
expect(mocks.searchClients).toHaveBeenCalledWith({
|
||||
expect(mocks.clientsSearch).toHaveBeenCalledWith({
|
||||
clients: [{ id: '1.2.3.4' }],
|
||||
});
|
||||
// normalizedTopClients is populated (configured bucket carries info name)
|
||||
@@ -45,11 +43,11 @@ describe('getStats', () => {
|
||||
});
|
||||
|
||||
it('converts avg_processing_time to milliseconds, falsy passthrough (FR-002)', async () => {
|
||||
mocks.getStats.mockResolvedValue({ avg_processing_time: 0.012 });
|
||||
mocks.stats.mockResolvedValue({ avg_processing_time: 0.012 });
|
||||
await getStats();
|
||||
expect(statsState.avgProcessingTime).toBe(12);
|
||||
|
||||
mocks.getStats.mockResolvedValue({ avg_processing_time: 0 });
|
||||
mocks.stats.mockResolvedValue({ avg_processing_time: 0 });
|
||||
await getStats();
|
||||
expect(statsState.avgProcessingTime).toBe(0); // not NaN
|
||||
});
|
||||
|
||||
@@ -22,11 +22,9 @@ const mocks = vi.hoisted(() => ({
|
||||
addErrorToast: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('panel/api/Api', () => ({
|
||||
apiClient: {
|
||||
setRules: mocks.apiSetRules,
|
||||
getFilteringStatus: mocks.apiGetFilteringStatus,
|
||||
},
|
||||
vi.mock('panel/api/generated', () => ({
|
||||
filteringSetRules: mocks.apiSetRules,
|
||||
filteringStatus: mocks.apiGetFilteringStatus,
|
||||
}));
|
||||
|
||||
vi.mock('panel/stores/toasts', () => ({
|
||||
|
||||
@@ -1,746 +0,0 @@
|
||||
import type { LocalesType } from 'panel/common/intl';
|
||||
|
||||
const BASE_URL = 'control';
|
||||
|
||||
import { getPathWithQueryString } from '../helpers/helpers';
|
||||
import { QUERY_LOGS_PAGE_LIMIT, HTML_PAGES, R_PATH_LAST_PART, THEMES } from '../helpers/constants';
|
||||
import intl from '../common/intl';
|
||||
|
||||
type Theme = (typeof THEMES)[keyof typeof THEMES];
|
||||
|
||||
class Api {
|
||||
baseUrl: string;
|
||||
|
||||
constructor(baseUrl: string) {
|
||||
this.baseUrl = baseUrl;
|
||||
}
|
||||
|
||||
async makeRequest(path: any, method = 'POST', config: any = {}) {
|
||||
const pathWithBase = `${this.baseUrl}/${path}`;
|
||||
const url = config.params
|
||||
? getPathWithQueryString(pathWithBase, config.params)
|
||||
: pathWithBase;
|
||||
|
||||
const fetchConfig: RequestInit = { method };
|
||||
const headers: Record<string, string> = {};
|
||||
|
||||
if (method !== 'GET' && config.data) {
|
||||
headers['Content-Type'] = config.headers?.['Content-Type'] || 'application/json';
|
||||
fetchConfig.body =
|
||||
typeof config.data === 'string' ? config.data : JSON.stringify(config.data);
|
||||
}
|
||||
|
||||
fetchConfig.headers = headers;
|
||||
|
||||
try {
|
||||
const response = await fetch(url, fetchConfig);
|
||||
const text = await response.text();
|
||||
const data = text
|
||||
? (() => {
|
||||
try {
|
||||
return JSON.parse(text);
|
||||
} catch {
|
||||
return text;
|
||||
}
|
||||
})()
|
||||
: '';
|
||||
|
||||
if (!response.ok) {
|
||||
const errorPath = url;
|
||||
const { pathname } = document.location;
|
||||
const shouldRedirect =
|
||||
pathname !== HTML_PAGES.LOGIN && pathname !== HTML_PAGES.INSTALL;
|
||||
|
||||
if (response.status === 403 && shouldRedirect) {
|
||||
const loginPageUrl = window.location.href.replace(
|
||||
R_PATH_LAST_PART,
|
||||
HTML_PAGES.LOGIN,
|
||||
);
|
||||
window.location.replace(loginPageUrl);
|
||||
return false;
|
||||
}
|
||||
|
||||
throw new Error(`${errorPath} | ${data} | ${response.status}`);
|
||||
}
|
||||
|
||||
return data;
|
||||
} catch (error) {
|
||||
const errorPath = url;
|
||||
|
||||
if (error instanceof Error && error.message.includes('|')) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
throw new Error(`${errorPath} | ${error.message || error}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Global methods
|
||||
GLOBAL_STATUS = { path: 'status', method: 'GET' };
|
||||
|
||||
GLOBAL_TEST_UPSTREAM_DNS = { path: 'test_upstream_dns', method: 'POST' };
|
||||
|
||||
GLOBAL_VERSION = { path: 'version.json', method: 'POST' };
|
||||
|
||||
GLOBAL_UPDATE = { path: 'update', method: 'POST' };
|
||||
|
||||
getGlobalStatus() {
|
||||
const { path, method } = this.GLOBAL_STATUS;
|
||||
|
||||
return this.makeRequest(path, method);
|
||||
}
|
||||
|
||||
testUpstream(servers: any) {
|
||||
const { path, method } = this.GLOBAL_TEST_UPSTREAM_DNS;
|
||||
const config = {
|
||||
data: servers,
|
||||
};
|
||||
return this.makeRequest(path, method, config);
|
||||
}
|
||||
|
||||
getGlobalVersion(data: any) {
|
||||
const { path, method } = this.GLOBAL_VERSION;
|
||||
const config = {
|
||||
data,
|
||||
};
|
||||
return this.makeRequest(path, method, config);
|
||||
}
|
||||
|
||||
getUpdate() {
|
||||
const { path, method } = this.GLOBAL_UPDATE;
|
||||
|
||||
return this.makeRequest(path, method);
|
||||
}
|
||||
|
||||
// Filtering
|
||||
FILTERING_STATUS = { path: 'filtering/status', method: 'GET' };
|
||||
|
||||
FILTERING_ADD_FILTER = { path: 'filtering/add_url', method: 'POST' };
|
||||
|
||||
FILTERING_REMOVE_FILTER = { path: 'filtering/remove_url', method: 'POST' };
|
||||
|
||||
FILTERING_SET_RULES = { path: 'filtering/set_rules', method: 'POST' };
|
||||
|
||||
FILTERING_REFRESH = { path: 'filtering/refresh', method: 'POST' };
|
||||
|
||||
FILTERING_SET_URL = { path: 'filtering/set_url', method: 'POST' };
|
||||
|
||||
FILTERING_CONFIG = { path: 'filtering/config', method: 'POST' };
|
||||
|
||||
FILTERING_CHECK_HOST = { path: 'filtering/check_host', method: 'GET' };
|
||||
|
||||
getFilteringStatus() {
|
||||
const { path, method } = this.FILTERING_STATUS;
|
||||
|
||||
return this.makeRequest(path, method);
|
||||
}
|
||||
|
||||
refreshFilters(config: any) {
|
||||
const { path, method } = this.FILTERING_REFRESH;
|
||||
const parameters = {
|
||||
data: config,
|
||||
};
|
||||
|
||||
return this.makeRequest(path, method, parameters);
|
||||
}
|
||||
|
||||
addFilter(config: any) {
|
||||
const { path, method } = this.FILTERING_ADD_FILTER;
|
||||
const parameters = {
|
||||
data: config,
|
||||
};
|
||||
|
||||
return this.makeRequest(path, method, parameters);
|
||||
}
|
||||
|
||||
removeFilter(config: any) {
|
||||
const { path, method } = this.FILTERING_REMOVE_FILTER;
|
||||
const parameters = {
|
||||
data: config,
|
||||
};
|
||||
|
||||
return this.makeRequest(path, method, parameters);
|
||||
}
|
||||
|
||||
setRules(rules: any) {
|
||||
const { path, method } = this.FILTERING_SET_RULES;
|
||||
const parameters = {
|
||||
data: rules,
|
||||
};
|
||||
return this.makeRequest(path, method, parameters);
|
||||
}
|
||||
|
||||
setFiltersConfig(config: any) {
|
||||
const { path, method } = this.FILTERING_CONFIG;
|
||||
const parameters = {
|
||||
data: config,
|
||||
};
|
||||
return this.makeRequest(path, method, parameters);
|
||||
}
|
||||
|
||||
setFilterUrl(config: any) {
|
||||
const { path, method } = this.FILTERING_SET_URL;
|
||||
const parameters = {
|
||||
data: config,
|
||||
};
|
||||
return this.makeRequest(path, method, parameters);
|
||||
}
|
||||
|
||||
checkHost(params: any) {
|
||||
const { path, method } = this.FILTERING_CHECK_HOST;
|
||||
const url = getPathWithQueryString(path, params);
|
||||
|
||||
return this.makeRequest(url, method);
|
||||
}
|
||||
|
||||
// Parental
|
||||
PARENTAL_STATUS = { path: 'parental/status', method: 'GET' };
|
||||
|
||||
PARENTAL_ENABLE = { path: 'parental/enable', method: 'POST' };
|
||||
|
||||
PARENTAL_DISABLE = { path: 'parental/disable', method: 'POST' };
|
||||
|
||||
getParentalStatus() {
|
||||
const { path, method } = this.PARENTAL_STATUS;
|
||||
|
||||
return this.makeRequest(path, method);
|
||||
}
|
||||
|
||||
enableParentalControl() {
|
||||
const { path, method } = this.PARENTAL_ENABLE;
|
||||
|
||||
return this.makeRequest(path, method);
|
||||
}
|
||||
|
||||
disableParentalControl() {
|
||||
const { path, method } = this.PARENTAL_DISABLE;
|
||||
|
||||
return this.makeRequest(path, method);
|
||||
}
|
||||
|
||||
// Safebrowsing
|
||||
SAFEBROWSING_STATUS = { path: 'safebrowsing/status', method: 'GET' };
|
||||
|
||||
SAFEBROWSING_ENABLE = { path: 'safebrowsing/enable', method: 'POST' };
|
||||
|
||||
SAFEBROWSING_DISABLE = { path: 'safebrowsing/disable', method: 'POST' };
|
||||
|
||||
getSafebrowsingStatus() {
|
||||
const { path, method } = this.SAFEBROWSING_STATUS;
|
||||
|
||||
return this.makeRequest(path, method);
|
||||
}
|
||||
|
||||
enableSafebrowsing() {
|
||||
const { path, method } = this.SAFEBROWSING_ENABLE;
|
||||
|
||||
return this.makeRequest(path, method);
|
||||
}
|
||||
|
||||
disableSafebrowsing() {
|
||||
const { path, method } = this.SAFEBROWSING_DISABLE;
|
||||
|
||||
return this.makeRequest(path, method);
|
||||
}
|
||||
|
||||
// Safesearch
|
||||
SAFESEARCH_STATUS = { path: 'safesearch/status', method: 'GET' };
|
||||
|
||||
SAFESEARCH_UPDATE = { path: 'safesearch/settings', method: 'PUT' };
|
||||
|
||||
getSafesearchStatus() {
|
||||
const { path, method } = this.SAFESEARCH_STATUS;
|
||||
|
||||
return this.makeRequest(path, method);
|
||||
}
|
||||
|
||||
/**
|
||||
* interface SafeSearchConfig {
|
||||
"enabled": boolean,
|
||||
"bing": boolean,
|
||||
"duckduckgo": boolean,
|
||||
"google": boolean,
|
||||
"pixabay": boolean,
|
||||
"yandex": boolean,
|
||||
"youtube": boolean
|
||||
* }
|
||||
* @param {*} data - SafeSearchConfig
|
||||
* @returns 200 ok
|
||||
*/
|
||||
updateSafesearch(data: any) {
|
||||
const { path, method } = this.SAFESEARCH_UPDATE;
|
||||
return this.makeRequest(path, method, { data });
|
||||
}
|
||||
|
||||
// enableSafesearch() {
|
||||
// const { path, method } = this.SAFESEARCH_ENABLE;
|
||||
// return this.makeRequest(path, method);
|
||||
// }
|
||||
|
||||
// disableSafesearch() {
|
||||
// const { path, method } = this.SAFESEARCH_DISABLE;
|
||||
// return this.makeRequest(path, method);
|
||||
// }
|
||||
|
||||
// Language
|
||||
async changeLanguage(config: { language: LocalesType }) {
|
||||
const profile = await this.getProfile();
|
||||
profile.language = config.language;
|
||||
|
||||
return this.setProfile(profile);
|
||||
}
|
||||
|
||||
// Theme
|
||||
async changeTheme(config: { theme: Theme }) {
|
||||
const profile = await this.getProfile();
|
||||
profile.theme = config.theme;
|
||||
|
||||
return this.setProfile(profile);
|
||||
}
|
||||
|
||||
// DHCP
|
||||
DHCP_STATUS = { path: 'dhcp/status', method: 'GET' };
|
||||
|
||||
DHCP_SET_CONFIG = { path: 'dhcp/set_config', method: 'POST' };
|
||||
|
||||
DHCP_FIND_ACTIVE = { path: 'dhcp/find_active_dhcp', method: 'POST' };
|
||||
|
||||
DHCP_INTERFACES = { path: 'dhcp/interfaces', method: 'GET' };
|
||||
|
||||
DHCP_ADD_STATIC_LEASE = { path: 'dhcp/add_static_lease', method: 'POST' };
|
||||
|
||||
DHCP_REMOVE_STATIC_LEASE = { path: 'dhcp/remove_static_lease', method: 'POST' };
|
||||
|
||||
DHCP_UPDATE_STATIC_LEASE = { path: 'dhcp/update_static_lease', method: 'POST' };
|
||||
|
||||
DHCP_RESET = { path: 'dhcp/reset', method: 'POST' };
|
||||
|
||||
DHCP_LEASES_RESET = { path: 'dhcp/reset_leases', method: 'POST' };
|
||||
|
||||
getDhcpStatus() {
|
||||
const { path, method } = this.DHCP_STATUS;
|
||||
|
||||
return this.makeRequest(path, method);
|
||||
}
|
||||
|
||||
getDhcpInterfaces() {
|
||||
const { path, method } = this.DHCP_INTERFACES;
|
||||
|
||||
return this.makeRequest(path, method);
|
||||
}
|
||||
|
||||
setDhcpConfig(config: any) {
|
||||
const { path, method } = this.DHCP_SET_CONFIG;
|
||||
const parameters = {
|
||||
data: config,
|
||||
};
|
||||
return this.makeRequest(path, method, parameters);
|
||||
}
|
||||
|
||||
findActiveDhcp(req: any) {
|
||||
const { path, method } = this.DHCP_FIND_ACTIVE;
|
||||
const parameters = {
|
||||
data: req,
|
||||
};
|
||||
return this.makeRequest(path, method, parameters);
|
||||
}
|
||||
|
||||
addStaticLease(config: any) {
|
||||
const { path, method } = this.DHCP_ADD_STATIC_LEASE;
|
||||
const parameters = {
|
||||
data: config,
|
||||
};
|
||||
return this.makeRequest(path, method, parameters);
|
||||
}
|
||||
|
||||
removeStaticLease(config: any) {
|
||||
const { path, method } = this.DHCP_REMOVE_STATIC_LEASE;
|
||||
const parameters = {
|
||||
data: config,
|
||||
};
|
||||
return this.makeRequest(path, method, parameters);
|
||||
}
|
||||
|
||||
updateStaticLease(config: any) {
|
||||
const { path, method } = this.DHCP_UPDATE_STATIC_LEASE;
|
||||
const parameters = {
|
||||
data: config,
|
||||
};
|
||||
return this.makeRequest(path, method, parameters);
|
||||
}
|
||||
|
||||
resetDhcp() {
|
||||
const { path, method } = this.DHCP_RESET;
|
||||
|
||||
return this.makeRequest(path, method);
|
||||
}
|
||||
|
||||
resetDhcpLeases() {
|
||||
const { path, method } = this.DHCP_LEASES_RESET;
|
||||
|
||||
return this.makeRequest(path, method);
|
||||
}
|
||||
|
||||
// Installation
|
||||
INSTALL_GET_ADDRESSES = { path: 'install/get_addresses', method: 'GET' };
|
||||
|
||||
INSTALL_CONFIGURE = { path: 'install/configure', method: 'POST' };
|
||||
|
||||
INSTALL_CHECK_CONFIG = { path: 'install/check_config', method: 'POST' };
|
||||
|
||||
getDefaultAddresses() {
|
||||
const { path, method } = this.INSTALL_GET_ADDRESSES;
|
||||
|
||||
return this.makeRequest(path, method);
|
||||
}
|
||||
|
||||
setAllSettings(config: any) {
|
||||
const { path, method } = this.INSTALL_CONFIGURE;
|
||||
const parameters = {
|
||||
data: config,
|
||||
};
|
||||
return this.makeRequest(path, method, parameters);
|
||||
}
|
||||
|
||||
checkConfig(config: any) {
|
||||
const { path, method } = this.INSTALL_CHECK_CONFIG;
|
||||
const parameters = {
|
||||
data: config,
|
||||
};
|
||||
return this.makeRequest(path, method, parameters);
|
||||
}
|
||||
|
||||
// DNS-over-HTTPS and DNS-over-TLS
|
||||
TLS_STATUS = { path: 'tls/status', method: 'GET' };
|
||||
|
||||
TLS_CONFIG = { path: 'tls/configure', method: 'POST' };
|
||||
|
||||
TLS_VALIDATE = { path: 'tls/validate', method: 'POST' };
|
||||
|
||||
getTlsStatus() {
|
||||
const { path, method } = this.TLS_STATUS;
|
||||
|
||||
return this.makeRequest(path, method);
|
||||
}
|
||||
|
||||
setTlsConfig(config: any) {
|
||||
const { path, method } = this.TLS_CONFIG;
|
||||
const parameters = {
|
||||
data: config,
|
||||
};
|
||||
return this.makeRequest(path, method, parameters);
|
||||
}
|
||||
|
||||
validateTlsConfig(config: any) {
|
||||
const { path, method } = this.TLS_VALIDATE;
|
||||
const parameters = {
|
||||
data: config,
|
||||
};
|
||||
return this.makeRequest(path, method, parameters);
|
||||
}
|
||||
|
||||
// Per-client settings
|
||||
GET_CLIENTS = { path: 'clients', method: 'GET' };
|
||||
|
||||
SEARCH_CLIENTS = { path: 'clients/search', method: 'POST' };
|
||||
|
||||
ADD_CLIENT = { path: 'clients/add', method: 'POST' };
|
||||
|
||||
DELETE_CLIENT = { path: 'clients/delete', method: 'POST' };
|
||||
|
||||
UPDATE_CLIENT = { path: 'clients/update', method: 'POST' };
|
||||
|
||||
getClients() {
|
||||
const { path, method } = this.GET_CLIENTS;
|
||||
|
||||
return this.makeRequest(path, method);
|
||||
}
|
||||
|
||||
addClient(config: any) {
|
||||
const { path, method } = this.ADD_CLIENT;
|
||||
const parameters = {
|
||||
data: config,
|
||||
};
|
||||
return this.makeRequest(path, method, parameters);
|
||||
}
|
||||
|
||||
deleteClient(config: any) {
|
||||
const { path, method } = this.DELETE_CLIENT;
|
||||
const parameters = {
|
||||
data: config,
|
||||
};
|
||||
return this.makeRequest(path, method, parameters);
|
||||
}
|
||||
|
||||
updateClient(config: any) {
|
||||
const { path, method } = this.UPDATE_CLIENT;
|
||||
const parameters = {
|
||||
data: config,
|
||||
};
|
||||
return this.makeRequest(path, method, parameters);
|
||||
}
|
||||
|
||||
searchClients(config: any) {
|
||||
const { path, method } = this.SEARCH_CLIENTS;
|
||||
const parameters = {
|
||||
data: config,
|
||||
};
|
||||
return this.makeRequest(path, method, parameters);
|
||||
}
|
||||
|
||||
// DNS access settings
|
||||
ACCESS_LIST = { path: 'access/list', method: 'GET' };
|
||||
|
||||
ACCESS_SET = { path: 'access/set', method: 'POST' };
|
||||
|
||||
getAccessList() {
|
||||
const { path, method } = this.ACCESS_LIST;
|
||||
|
||||
return this.makeRequest(path, method);
|
||||
}
|
||||
|
||||
setAccessList(config: any) {
|
||||
const { path, method } = this.ACCESS_SET;
|
||||
const parameters = {
|
||||
data: config,
|
||||
};
|
||||
return this.makeRequest(path, method, parameters);
|
||||
}
|
||||
|
||||
// DNS rewrites
|
||||
REWRITES_LIST = { path: 'rewrite/list', method: 'GET' };
|
||||
|
||||
REWRITE_ADD = { path: 'rewrite/add', method: 'POST' };
|
||||
|
||||
REWRITE_UPDATE = { path: 'rewrite/update', method: 'PUT' };
|
||||
|
||||
REWRITE_DELETE = { path: 'rewrite/delete', method: 'POST' };
|
||||
|
||||
getRewritesList() {
|
||||
const { path, method } = this.REWRITES_LIST;
|
||||
|
||||
return this.makeRequest(path, method);
|
||||
}
|
||||
|
||||
addRewrite(config: any) {
|
||||
const { path, method } = this.REWRITE_ADD;
|
||||
const parameters = {
|
||||
data: config,
|
||||
};
|
||||
return this.makeRequest(path, method, parameters);
|
||||
}
|
||||
|
||||
updateRewrite(config: any) {
|
||||
const { path, method } = this.REWRITE_UPDATE;
|
||||
const parameters = {
|
||||
data: config,
|
||||
};
|
||||
return this.makeRequest(path, method, parameters);
|
||||
}
|
||||
|
||||
deleteRewrite(config: any) {
|
||||
const { path, method } = this.REWRITE_DELETE;
|
||||
const parameters = {
|
||||
data: config,
|
||||
};
|
||||
return this.makeRequest(path, method, parameters);
|
||||
}
|
||||
|
||||
REWRITE_SETTINGS = { path: 'rewrite/settings', method: 'GET' };
|
||||
|
||||
REWRITE_SETTINGS_UPDATE = { path: 'rewrite/settings/update', method: 'PUT' };
|
||||
|
||||
getRewriteSettings() {
|
||||
const { path, method } = this.REWRITE_SETTINGS;
|
||||
return this.makeRequest(path, method);
|
||||
}
|
||||
|
||||
updateRewriteSettings(config: { enabled: boolean }) {
|
||||
const { path, method } = this.REWRITE_SETTINGS_UPDATE;
|
||||
const parameters = {
|
||||
data: config,
|
||||
};
|
||||
return this.makeRequest(path, method, parameters);
|
||||
}
|
||||
|
||||
// Blocked services
|
||||
BLOCKED_SERVICES_GET = { path: 'blocked_services/get', method: 'GET' };
|
||||
|
||||
BLOCKED_SERVICES_UPDATE = { path: 'blocked_services/update', method: 'PUT' };
|
||||
|
||||
BLOCKED_SERVICES_ALL = { path: 'blocked_services/all', method: 'GET' };
|
||||
|
||||
getAllBlockedServices() {
|
||||
const { path, method } = this.BLOCKED_SERVICES_ALL;
|
||||
|
||||
return this.makeRequest(path, method);
|
||||
}
|
||||
|
||||
getBlockedServices() {
|
||||
const { path, method } = this.BLOCKED_SERVICES_GET;
|
||||
|
||||
return this.makeRequest(path, method);
|
||||
}
|
||||
|
||||
updateBlockedServices(config: any) {
|
||||
const { path, method } = this.BLOCKED_SERVICES_UPDATE;
|
||||
const parameters = {
|
||||
data: config,
|
||||
};
|
||||
return this.makeRequest(path, method, parameters);
|
||||
}
|
||||
|
||||
// Settings for statistics
|
||||
GET_STATS = { path: 'stats', method: 'GET' };
|
||||
|
||||
GET_STATS_CONFIG = { path: 'stats/config', method: 'GET' };
|
||||
|
||||
UPDATE_STATS_CONFIG = { path: 'stats/config/update', method: 'PUT' };
|
||||
|
||||
STATS_RESET = { path: 'stats_reset', method: 'POST' };
|
||||
|
||||
getStats(recent?: number) {
|
||||
const { path, method } = this.GET_STATS;
|
||||
const config = recent ? { params: { recent } } : undefined;
|
||||
|
||||
return this.makeRequest(path, method, config);
|
||||
}
|
||||
|
||||
getStatsConfig() {
|
||||
const { path, method } = this.GET_STATS_CONFIG;
|
||||
|
||||
return this.makeRequest(path, method);
|
||||
}
|
||||
|
||||
setStatsConfig(data: any) {
|
||||
const { path, method } = this.UPDATE_STATS_CONFIG;
|
||||
const config = {
|
||||
data,
|
||||
};
|
||||
return this.makeRequest(path, method, config);
|
||||
}
|
||||
|
||||
resetStats() {
|
||||
const { path, method } = this.STATS_RESET;
|
||||
|
||||
return this.makeRequest(path, method);
|
||||
}
|
||||
|
||||
// Query log
|
||||
GET_QUERY_LOG = { path: 'querylog', method: 'GET' };
|
||||
|
||||
UPDATE_QUERY_LOG_CONFIG = { path: 'querylog/config/update', method: 'PUT' };
|
||||
|
||||
GET_QUERY_LOG_CONFIG = { path: 'querylog/config', method: 'GET' };
|
||||
|
||||
QUERY_LOG_CLEAR = { path: 'querylog_clear', method: 'POST' };
|
||||
|
||||
getQueryLog(params: any) {
|
||||
const { path, method } = this.GET_QUERY_LOG;
|
||||
const url = getPathWithQueryString(path, {
|
||||
...params,
|
||||
limit: QUERY_LOGS_PAGE_LIMIT,
|
||||
});
|
||||
|
||||
return this.makeRequest(url, method);
|
||||
}
|
||||
|
||||
getQueryLogConfig() {
|
||||
const { path, method } = this.GET_QUERY_LOG_CONFIG;
|
||||
|
||||
return this.makeRequest(path, method);
|
||||
}
|
||||
|
||||
setQueryLogConfig(data: any) {
|
||||
const { path, method } = this.UPDATE_QUERY_LOG_CONFIG;
|
||||
const config = {
|
||||
data,
|
||||
};
|
||||
return this.makeRequest(path, method, config);
|
||||
}
|
||||
|
||||
clearQueryLog() {
|
||||
const { path, method } = this.QUERY_LOG_CLEAR;
|
||||
|
||||
return this.makeRequest(path, method);
|
||||
}
|
||||
|
||||
// Login
|
||||
LOGIN = { path: 'login', method: 'POST' };
|
||||
|
||||
login(data: any) {
|
||||
const { path, method } = this.LOGIN;
|
||||
const config = {
|
||||
data,
|
||||
};
|
||||
return this.makeRequest(path, method, config);
|
||||
}
|
||||
|
||||
// Logout
|
||||
LOGOUT_PATH = 'logout';
|
||||
|
||||
getLogoutUrl() {
|
||||
return `${this.baseUrl}/${this.LOGOUT_PATH}`;
|
||||
}
|
||||
|
||||
// Profile
|
||||
GET_PROFILE = { path: 'profile', method: 'GET' };
|
||||
|
||||
UPDATE_PROFILE = { path: 'profile/update', method: 'PUT' };
|
||||
|
||||
getProfile() {
|
||||
const { path, method } = this.GET_PROFILE;
|
||||
|
||||
return this.makeRequest(path, method);
|
||||
}
|
||||
|
||||
setProfile(data: any) {
|
||||
const theme = data.theme ? data.theme : THEMES.auto;
|
||||
const defaultLanguage = intl.getUILanguage() ? intl.getUILanguage() : 'en';
|
||||
const language = data.language ? data.language : defaultLanguage;
|
||||
|
||||
const { path, method } = this.UPDATE_PROFILE;
|
||||
const config = { data: { theme, language } };
|
||||
|
||||
return this.makeRequest(path, method, config);
|
||||
}
|
||||
|
||||
// DNS config
|
||||
GET_DNS_CONFIG = { path: 'dns_info', method: 'GET' };
|
||||
|
||||
SET_DNS_CONFIG = { path: 'dns_config', method: 'POST' };
|
||||
|
||||
getDnsConfig() {
|
||||
const { path, method } = this.GET_DNS_CONFIG;
|
||||
|
||||
return this.makeRequest(path, method);
|
||||
}
|
||||
|
||||
setDnsConfig(data: any) {
|
||||
const { path, method } = this.SET_DNS_CONFIG;
|
||||
const config = {
|
||||
data,
|
||||
};
|
||||
return this.makeRequest(path, method, config);
|
||||
}
|
||||
|
||||
SET_PROTECTION = { path: 'protection', method: 'POST' };
|
||||
|
||||
setProtection(data: any) {
|
||||
const { enabled, duration } = data;
|
||||
const { path, method } = this.SET_PROTECTION;
|
||||
|
||||
return this.makeRequest(path, method, { data: { enabled, duration } });
|
||||
}
|
||||
|
||||
// Cache
|
||||
CLEAR_CACHE = { path: 'cache_clear', method: 'POST' };
|
||||
|
||||
clearCache() {
|
||||
const { path, method } = this.CLEAR_CACHE;
|
||||
|
||||
return this.makeRequest(path, method);
|
||||
}
|
||||
}
|
||||
|
||||
export const apiClient = new Api(BASE_URL);
|
||||
@@ -0,0 +1,78 @@
|
||||
import { HTML_PAGES, R_PATH_LAST_PART } from '../helpers/constants';
|
||||
|
||||
type CustomFetchOptions = RequestInit & {
|
||||
skipAuthRedirect?: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* Custom fetch mutator for orval-generated API client.
|
||||
*
|
||||
* Custom fetch wrapper providing AdGuard Home-specific behaviors:
|
||||
* - Sets `Content-Type: application/json` on requests with a body
|
||||
* - Parses response as JSON (falls back to raw text if JSON.parse fails)
|
||||
* - On 403 and not on login/install page → redirects browser to login
|
||||
* and returns `false` (does not throw)
|
||||
* - On 403 while on login/install page → throws Error normally
|
||||
* - Throws Error with format `${path} | ${data} | ${status}` for all
|
||||
* other non-ok responses
|
||||
* - On empty response body → returns empty string ''
|
||||
* - On 204 → returns empty string ''
|
||||
*/
|
||||
export const customFetch = async <T>(url: string, options?: CustomFetchOptions): Promise<T> => {
|
||||
const { skipAuthRedirect, ...fetchOptions } = options || {};
|
||||
|
||||
const fullUrl = url;
|
||||
const headers: Record<string, string> = {};
|
||||
|
||||
// Preserve any headers passed in options
|
||||
if (fetchOptions.headers) {
|
||||
const incomingHeaders = fetchOptions.headers as Record<string, string>;
|
||||
Object.assign(headers, incomingHeaders);
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(fullUrl, {
|
||||
...fetchOptions,
|
||||
headers,
|
||||
});
|
||||
|
||||
const text = await response.text();
|
||||
const data: T = text
|
||||
? (() => {
|
||||
try {
|
||||
return JSON.parse(text);
|
||||
} catch {
|
||||
return text;
|
||||
}
|
||||
})()
|
||||
: ('' as unknown as T);
|
||||
|
||||
if (!response.ok) {
|
||||
const { pathname } = document.location;
|
||||
const shouldRedirect = pathname !== HTML_PAGES.LOGIN && pathname !== HTML_PAGES.INSTALL;
|
||||
|
||||
if (response.status === 403 && shouldRedirect && !skipAuthRedirect) {
|
||||
const loginPageUrl = window.location.href.replace(
|
||||
R_PATH_LAST_PART,
|
||||
HTML_PAGES.LOGIN,
|
||||
);
|
||||
window.location.replace(loginPageUrl);
|
||||
return false as unknown as T;
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
`${fullUrl} | ${typeof data === 'string' ? data : JSON.stringify(data)} | ${response.status}`,
|
||||
);
|
||||
}
|
||||
|
||||
return data;
|
||||
} catch (error) {
|
||||
if (error instanceof Error && error.message.includes('|')) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
throw new Error(`${fullUrl} | ${error instanceof Error ? error.message : String(error)}`);
|
||||
}
|
||||
};
|
||||
|
||||
export default customFetch;
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* Client and host access list. Each of the lists should contain only unique elements. In addition, allowed and disallowed lists cannot contain the same elements.
|
||||
*/
|
||||
export interface AccessList {
|
||||
/** The allowlist of clients: IP addresses, CIDRs, or ClientIDs. */
|
||||
allowed_clients?: string[];
|
||||
/** The blocklist of clients: IP addresses, CIDRs, or ClientIDs. */
|
||||
disallowed_clients?: string[];
|
||||
/** The blocklist of hosts. */
|
||||
blocked_hosts?: string[];
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
import type { AccessList } from './accessList';
|
||||
|
||||
export type AccessListResponse = AccessList;
|
||||
@@ -0,0 +1,3 @@
|
||||
import type { AccessList } from './accessList';
|
||||
|
||||
export type AccessSetRequest = AccessList;
|
||||
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* /add_url request data
|
||||
*/
|
||||
export interface AddUrlRequest {
|
||||
name?: string;
|
||||
/** URL or an absolute path to the file containing filtering rules. */
|
||||
url?: string;
|
||||
whitelist?: boolean;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* Port information
|
||||
*/
|
||||
export interface AddressInfo {
|
||||
ip: string;
|
||||
port: number;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import type { NetInterfaces } from './netInterfaces';
|
||||
|
||||
/**
|
||||
* AdGuard Home addresses configuration
|
||||
*/
|
||||
export interface AddressesInfo {
|
||||
dns_port: number;
|
||||
interfaces: NetInterfaces;
|
||||
version: string;
|
||||
web_port: number;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
export interface BlockedService {
|
||||
/** The SVG icon as a Base64-encoded string to make it easier to embed it into a data URL. */
|
||||
icon_svg: string;
|
||||
/** The ID of this service. */
|
||||
id: string;
|
||||
/** The human-readable name of this service. */
|
||||
name: string;
|
||||
/** The array of the filtering rules. */
|
||||
rules: string[];
|
||||
/** The ID of the group, that the service belongs to. */
|
||||
group_id?: string;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import type { BlockedService } from './blockedService';
|
||||
import type { ServiceGroup } from './serviceGroup';
|
||||
|
||||
export interface BlockedServicesAll {
|
||||
blocked_services: BlockedService[];
|
||||
groups: ServiceGroup[];
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export type BlockedServicesArray = string[];
|
||||
@@ -0,0 +1,7 @@
|
||||
import type { Schedule } from './schedule';
|
||||
|
||||
export interface BlockedServicesSchedule {
|
||||
schedule?: Schedule;
|
||||
/** The names of the blocked services. */
|
||||
ids?: string[];
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import type { CheckConfigRequestInfo } from './checkConfigRequestInfo';
|
||||
import type { Lang } from './lang';
|
||||
|
||||
/**
|
||||
* Configuration to be checked
|
||||
*/
|
||||
export interface CheckConfigRequest {
|
||||
dns?: CheckConfigRequestInfo;
|
||||
language?: Lang;
|
||||
set_static_ip?: boolean;
|
||||
web?: CheckConfigRequestInfo;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
export interface CheckConfigRequestInfo {
|
||||
ip?: string;
|
||||
port?: number;
|
||||
autofix?: boolean;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import type { CheckConfigResponseInfo } from './checkConfigResponseInfo';
|
||||
import type { CheckConfigStaticIpInfo } from './checkConfigStaticIpInfo';
|
||||
|
||||
export interface CheckConfigResponse {
|
||||
dns: CheckConfigResponseInfo;
|
||||
language: CheckConfigResponseInfo;
|
||||
static_ip: CheckConfigStaticIpInfo;
|
||||
web: CheckConfigResponseInfo;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface CheckConfigResponseInfo {
|
||||
status: string;
|
||||
can_autofix: boolean;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import type { CheckConfigStaticIpInfoStatic } from './checkConfigStaticIpInfoStatic';
|
||||
|
||||
export interface CheckConfigStaticIpInfo {
|
||||
static?: CheckConfigStaticIpInfoStatic;
|
||||
/** Current dynamic IP address. Set if static=no */
|
||||
ip?: string;
|
||||
/** Error text. Set if static=error */
|
||||
error?: string;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Can be: yes, no, error
|
||||
*/
|
||||
export type CheckConfigStaticIpInfoStatic = 'yes' | 'no' | 'error';
|
||||
@@ -0,0 +1,63 @@
|
||||
import type { SafeSearchConfig } from './safeSearchConfig';
|
||||
import type { Schedule } from './schedule';
|
||||
|
||||
/**
|
||||
* Client information.
|
||||
*/
|
||||
export interface Client {
|
||||
/** Name */
|
||||
name?: string;
|
||||
/** IP, CIDR, MAC, or ClientID. */
|
||||
ids?: string[];
|
||||
use_global_settings?: boolean;
|
||||
filtering_enabled?: boolean;
|
||||
parental_enabled?: boolean;
|
||||
safebrowsing_enabled?: boolean;
|
||||
/** @deprecated */
|
||||
safesearch_enabled?: boolean;
|
||||
safe_search?: SafeSearchConfig;
|
||||
use_global_blocked_services?: boolean;
|
||||
blocked_services_schedule?: Schedule;
|
||||
blocked_services?: string[];
|
||||
upstreams?: string[];
|
||||
tags?: string[];
|
||||
/**
|
||||
* NOTE: If `ignore_querylog` is not set in HTTP API `GET /clients/add`
|
||||
* request then default value (false) will be used.
|
||||
*
|
||||
* If `ignore_querylog` is not set in HTTP API `GET /clients/update`
|
||||
* request then the existing value will not be changed.
|
||||
*
|
||||
* This behaviour can be changed in the future versions.
|
||||
*/
|
||||
ignore_querylog?: boolean;
|
||||
/**
|
||||
* NOTE: If `ignore_statistics` is not set in HTTP API `GET
|
||||
* /clients/add` request then default value (false) will be used.
|
||||
*
|
||||
* If `ignore_statistics` is not set in HTTP API `GET /clients/update`
|
||||
* request then the existing value will not be changed.
|
||||
*
|
||||
* This behaviour can be changed in the future versions.
|
||||
*/
|
||||
ignore_statistics?: boolean;
|
||||
/**
|
||||
* NOTE: If `upstreams_cache_enabled` is not set in HTTP API
|
||||
* `GET /clients/add` request then default value (false) will be used.
|
||||
*
|
||||
* If `upstreams_cache_enabled` is not set in HTTP API
|
||||
* `GET /clients/update` request then the existing value will not be
|
||||
* changed.
|
||||
*
|
||||
* This behaviour can be changed in the future versions.
|
||||
*/
|
||||
upstreams_cache_enabled?: boolean;
|
||||
/**
|
||||
* NOTE: If `upstreams_cache_enabled` is not set in HTTP API
|
||||
* `GET /clients/update` request then the existing value will not be
|
||||
* changed.
|
||||
*
|
||||
* This behaviour can be changed in the future versions.
|
||||
*/
|
||||
upstreams_cache_size?: number;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import type { WhoisInfo } from './whoisInfo';
|
||||
|
||||
/**
|
||||
* Auto-Client information
|
||||
*/
|
||||
export interface ClientAuto {
|
||||
/** IP address */
|
||||
ip?: string;
|
||||
/** Name */
|
||||
name?: string;
|
||||
/** The source of this information */
|
||||
source?: string;
|
||||
whois_info?: WhoisInfo;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Client delete request
|
||||
*/
|
||||
export interface ClientDelete {
|
||||
name?: string;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import type { SafeSearchConfig } from './safeSearchConfig';
|
||||
import type { WhoisInfo } from './whoisInfo';
|
||||
|
||||
/**
|
||||
* Client information.
|
||||
*/
|
||||
export interface ClientFindSubEntry {
|
||||
/** Name */
|
||||
name?: string;
|
||||
/** IP, CIDR, MAC, or ClientID. */
|
||||
ids?: string[];
|
||||
use_global_settings?: boolean;
|
||||
filtering_enabled?: boolean;
|
||||
parental_enabled?: boolean;
|
||||
safebrowsing_enabled?: boolean;
|
||||
/** @deprecated */
|
||||
safesearch_enabled?: boolean;
|
||||
safe_search?: SafeSearchConfig;
|
||||
use_global_blocked_services?: boolean;
|
||||
blocked_services?: string[];
|
||||
upstreams?: string[];
|
||||
whois_info?: WhoisInfo;
|
||||
/** Whether the client's IP is blocked or not. */
|
||||
disallowed?: boolean;
|
||||
/** The rule due to which the client is disallowed. If disallowed is set to true, and this string is empty, then the client IP is disallowed by the "allowed IP list", that is it is not included in the allowed list. */
|
||||
disallowed_rule?: string;
|
||||
ignore_querylog?: boolean;
|
||||
ignore_statistics?: boolean;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import type { Client } from './client';
|
||||
|
||||
/**
|
||||
* Client update request
|
||||
*/
|
||||
export interface ClientUpdate {
|
||||
name?: string;
|
||||
data?: Client;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import type { ClientsArray } from './clientsArray';
|
||||
import type { ClientsAutoArray } from './clientsAutoArray';
|
||||
|
||||
export interface Clients {
|
||||
clients?: ClientsArray;
|
||||
auto_clients?: ClientsAutoArray;
|
||||
supported_tags?: string[];
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import type { Client } from './client';
|
||||
|
||||
/**
|
||||
* Clients array
|
||||
*/
|
||||
export type ClientsArray = Client[];
|
||||
@@ -0,0 +1,6 @@
|
||||
import type { ClientAuto } from './clientAuto';
|
||||
|
||||
/**
|
||||
* Auto-Clients array
|
||||
*/
|
||||
export type ClientsAutoArray = ClientAuto[];
|
||||
@@ -0,0 +1,5 @@
|
||||
import type { ClientFindSubEntry } from './clientFindSubEntry';
|
||||
|
||||
export interface ClientsFindEntry {
|
||||
[key: string]: ClientFindSubEntry;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
export type ClientsFindParams = {
|
||||
/**
|
||||
* Filter by IP address or ClientIDs. Parameters with names `ip1`, `ip2`, and so on are also accepted and interpreted as "ip0 OR ip1 OR ip2".
|
||||
* TODO(a.garipov): Replace with a better query API.
|
||||
*/
|
||||
ip0?: string;
|
||||
};
|
||||
@@ -0,0 +1,6 @@
|
||||
import type { ClientsFindEntry } from './clientsFindEntry';
|
||||
|
||||
/**
|
||||
* Client search results.
|
||||
*/
|
||||
export type ClientsFindResponse = ClientsFindEntry[];
|
||||
@@ -0,0 +1,8 @@
|
||||
import type { ClientsSearchRequestItem } from './clientsSearchRequestItem';
|
||||
|
||||
/**
|
||||
* Client search request
|
||||
*/
|
||||
export interface ClientsSearchRequest {
|
||||
clients?: ClientsSearchRequestItem[];
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface ClientsSearchRequestItem {
|
||||
/** Client IP address, CIDR, MAC address, or ClientID */
|
||||
id?: string;
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
import type { DNSConfigBlockingMode } from './dNSConfigBlockingMode';
|
||||
import type { DNSConfigUpstreamMode } from './dNSConfigUpstreamMode';
|
||||
|
||||
/**
|
||||
* DNS server configuration
|
||||
*/
|
||||
export interface DNSConfig {
|
||||
/** Bootstrap servers, port is optional after colon. Empty value will reset it to default values. */
|
||||
bootstrap_dns?: string[];
|
||||
/** Upstream servers, port is optional after colon. Empty value will reset it to default values. */
|
||||
upstream_dns?: string[];
|
||||
/** List of fallback DNS servers used when upstream DNS servers are not responding. Empty value will clear the list. */
|
||||
fallback_dns?: string[];
|
||||
upstream_dns_file?: string;
|
||||
protection_enabled?: boolean;
|
||||
ratelimit?: number;
|
||||
/**
|
||||
* Length of the subnet mask for IPv4 addresses.
|
||||
* @minimum 0
|
||||
* @maximum 32
|
||||
*/
|
||||
ratelimit_subnet_subnet_len_ipv4?: number;
|
||||
/**
|
||||
* Length of the subnet mask for IPv6 addresses.
|
||||
* @minimum 0
|
||||
* @maximum 128
|
||||
*/
|
||||
ratelimit_subnet_subnet_len_ipv6?: number;
|
||||
/** List of IP addresses excluded from rate limiting. */
|
||||
ratelimit_whitelist?: string[];
|
||||
blocking_mode?: DNSConfigBlockingMode;
|
||||
blocking_ipv4?: string;
|
||||
blocking_ipv6?: string;
|
||||
/**
|
||||
* TTL for blocked responses.
|
||||
* @minimum 0
|
||||
*/
|
||||
blocked_response_ttl?: number;
|
||||
/** Protection is pause until this time. Nullable. */
|
||||
protection_disabled_until?: string;
|
||||
edns_cs_enabled?: boolean;
|
||||
edns_cs_use_custom?: boolean;
|
||||
edns_cs_custom_ip?: string;
|
||||
disable_ipv6?: boolean;
|
||||
dnssec_enabled?: boolean;
|
||||
cache_size?: number;
|
||||
cache_ttl_min?: number;
|
||||
cache_ttl_max?: number;
|
||||
/**
|
||||
* Enables or disables the DNS response cache.
|
||||
*
|
||||
* If `cache_enabled` is `true`, the companion field `cache_size` must
|
||||
* be present and greater than 0, or the `dns.cache_size` setting in
|
||||
* the configuration file must already be greater than 0.
|
||||
*/
|
||||
cache_enabled?: boolean;
|
||||
cache_optimistic?: boolean;
|
||||
/** Upstream modes enumeration. The empty string value is deprecated; use `load_balance` instead. */
|
||||
upstream_mode?: DNSConfigUpstreamMode;
|
||||
use_private_ptr_resolvers?: boolean;
|
||||
resolve_clients?: boolean;
|
||||
/** Upstream servers, port is optional after colon. Empty value will reset it to default values. */
|
||||
local_ptr_upstreams?: string[];
|
||||
/**
|
||||
* The number of seconds to wait for a response from the upstream server
|
||||
* @minimum 1
|
||||
*/
|
||||
upstream_timeout?: number;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export type DNSConfigBlockingMode = 'default' | 'refused' | 'nxdomain' | 'null_ip' | 'custom_ip';
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Upstream modes enumeration. The empty string value is deprecated; use `load_balance` instead.
|
||||
*/
|
||||
export type DNSConfigUpstreamMode = '' | 'fastest_addr' | 'load_balance' | 'parallel';
|
||||
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* The single interval within a day. It begins at the `start` and ends before the `end`.
|
||||
*/
|
||||
export interface DayRange {
|
||||
/**
|
||||
* The number of milliseconds elapsed from the start of a day. It must be less than `end` and is expected to be rounded to minutes. So the maximum value is `86340000` (23 hours and 59 minutes).
|
||||
* @minimum 0
|
||||
* @maximum 86340000
|
||||
*/
|
||||
start?: number;
|
||||
/**
|
||||
* The number of milliseconds elapsed from the start of a day. It is expected to be rounded to minutes. The maximum value is `86400000` (24 hours).
|
||||
* @minimum 0
|
||||
* @maximum 86400000
|
||||
*/
|
||||
end?: number;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import type { DhcpConfigV4 } from './dhcpConfigV4';
|
||||
import type { DhcpConfigV6 } from './dhcpConfigV6';
|
||||
|
||||
export interface DhcpConfig {
|
||||
enabled?: boolean;
|
||||
interface_name?: string;
|
||||
v4?: DhcpConfigV4;
|
||||
v6?: DhcpConfigV6;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
export interface DhcpConfigV4 {
|
||||
gateway_ip?: string;
|
||||
subnet_mask?: string;
|
||||
range_start?: string;
|
||||
range_end?: string;
|
||||
lease_duration?: number;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface DhcpConfigV6 {
|
||||
range_start?: string;
|
||||
lease_duration?: number;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* Request for checking for other DHCP servers in the network.
|
||||
*/
|
||||
export interface DhcpFindActiveReq {
|
||||
/** The name of the network interface */
|
||||
interface?: string;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* DHCP lease information
|
||||
*/
|
||||
export interface DhcpLease {
|
||||
mac: string;
|
||||
ip: string;
|
||||
hostname: string;
|
||||
expires: string;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import type { DhcpSearchV4 } from './dhcpSearchV4';
|
||||
import type { DhcpSearchV6 } from './dhcpSearchV6';
|
||||
|
||||
/**
|
||||
* Information about a DHCP server discovered in the current network.
|
||||
*/
|
||||
export interface DhcpSearchResult {
|
||||
v4?: DhcpSearchV4;
|
||||
v6?: DhcpSearchV6;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import type { DhcpSearchResultOtherServerFound } from './dhcpSearchResultOtherServerFound';
|
||||
|
||||
export interface DhcpSearchResultOtherServer {
|
||||
/** The result of searching the other DHCP server. */
|
||||
found?: DhcpSearchResultOtherServerFound;
|
||||
/** Set if found=error */
|
||||
error?: string;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* The result of searching the other DHCP server.
|
||||
*/
|
||||
export type DhcpSearchResultOtherServerFound = 'yes' | 'no' | 'error';
|
||||
@@ -0,0 +1,8 @@
|
||||
import type { DhcpSearchResultStaticIPStatic } from './dhcpSearchResultStaticIPStatic';
|
||||
|
||||
export interface DhcpSearchResultStaticIP {
|
||||
/** The result of determining static IP address. */
|
||||
static?: DhcpSearchResultStaticIPStatic;
|
||||
/** Set if static=no */
|
||||
ip?: string;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* The result of determining static IP address.
|
||||
*/
|
||||
export type DhcpSearchResultStaticIPStatic = 'yes' | 'no' | 'error';
|
||||
@@ -0,0 +1,7 @@
|
||||
import type { DhcpSearchResultOtherServer } from './dhcpSearchResultOtherServer';
|
||||
import type { DhcpSearchResultStaticIP } from './dhcpSearchResultStaticIP';
|
||||
|
||||
export interface DhcpSearchV4 {
|
||||
other_server?: DhcpSearchResultOtherServer;
|
||||
static_ip?: DhcpSearchResultStaticIP;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import type { DhcpSearchResultOtherServer } from './dhcpSearchResultOtherServer';
|
||||
|
||||
export interface DhcpSearchV6 {
|
||||
other_server?: DhcpSearchResultOtherServer;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* DHCP static lease information
|
||||
*/
|
||||
export interface DhcpStaticLease {
|
||||
mac: string;
|
||||
ip: string;
|
||||
hostname: string;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
import type { DhcpStaticLease } from './dhcpStaticLease';
|
||||
|
||||
export type DhcpStaticLeaseBody = DhcpStaticLease;
|
||||
@@ -0,0 +1,16 @@
|
||||
import type { DhcpConfigV4 } from './dhcpConfigV4';
|
||||
import type { DhcpConfigV6 } from './dhcpConfigV6';
|
||||
import type { DhcpLease } from './dhcpLease';
|
||||
import type { DhcpStaticLease } from './dhcpStaticLease';
|
||||
|
||||
/**
|
||||
* Built-in DHCP server configuration and status
|
||||
*/
|
||||
export interface DhcpStatus {
|
||||
enabled?: boolean;
|
||||
interface_name?: string;
|
||||
v4?: DhcpConfigV4;
|
||||
v6?: DhcpConfigV6;
|
||||
leases: DhcpLease[];
|
||||
static_leases?: DhcpStaticLease[];
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* DNS answer section
|
||||
*/
|
||||
export interface DnsAnswer {
|
||||
ttl?: number;
|
||||
type?: string;
|
||||
value?: string;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import type { DNSConfig } from './dNSConfig';
|
||||
|
||||
export type DnsInfo200 = DNSConfig & {
|
||||
default_local_ptr_upstreams?: string[];
|
||||
};
|
||||
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* DNS question section
|
||||
*/
|
||||
export interface DnsQuestion {
|
||||
class?: string;
|
||||
name?: string;
|
||||
unicode_name?: string;
|
||||
type?: string;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* A generic JSON error response.
|
||||
*/
|
||||
export interface Error {
|
||||
/** The error message, an opaque string. */
|
||||
message?: string;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* Filter subscription info
|
||||
*/
|
||||
export interface Filter {
|
||||
enabled: boolean;
|
||||
id: number;
|
||||
last_updated?: string;
|
||||
name: string;
|
||||
rules_count: number;
|
||||
url: string;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import type { FilteringReason } from './filteringReason';
|
||||
import type { ResultRule } from './resultRule';
|
||||
|
||||
/**
|
||||
* Check Host Result
|
||||
*/
|
||||
export interface FilterCheckHostResponse {
|
||||
reason?: FilteringReason;
|
||||
/**
|
||||
* In case if there's a rule applied to this DNS request, this is ID of the filter list that the rule belongs to.
|
||||
* Deprecated: use `rules[*].filter_list_id` instead.
|
||||
* @deprecated
|
||||
*/
|
||||
filter_id?: number;
|
||||
/**
|
||||
* Filtering rule applied to the request (if any).
|
||||
* Deprecated: use `rules[*].text` instead.
|
||||
* @deprecated
|
||||
*/
|
||||
rule?: string;
|
||||
/** Applied rules. */
|
||||
rules?: ResultRule[];
|
||||
/** Set if reason=FilteredBlockedService */
|
||||
service_name?: string;
|
||||
/** Set if reason=Rewrite */
|
||||
cname?: string;
|
||||
/** Set if reason=Rewrite */
|
||||
ip_addrs?: string[];
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* Filtering settings
|
||||
*/
|
||||
export interface FilterConfig {
|
||||
enabled?: boolean;
|
||||
interval?: number;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Refresh Filters request data
|
||||
*/
|
||||
export interface FilterRefreshRequest {
|
||||
whitelist?: boolean;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* /filtering/refresh response data
|
||||
*/
|
||||
export interface FilterRefreshResponse {
|
||||
updated?: number;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import type { FilterSetUrlData } from './filterSetUrlData';
|
||||
|
||||
/**
|
||||
* Filtering URL settings
|
||||
*/
|
||||
export interface FilterSetUrl {
|
||||
data?: FilterSetUrlData;
|
||||
url?: string;
|
||||
whitelist?: boolean;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* Filter update data
|
||||
*/
|
||||
export interface FilterSetUrlData {
|
||||
enabled: boolean;
|
||||
name: string;
|
||||
url: string;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import type { Filter } from './filter';
|
||||
|
||||
/**
|
||||
* Filtering settings
|
||||
*/
|
||||
export interface FilterStatus {
|
||||
enabled?: boolean;
|
||||
interval?: number;
|
||||
filters?: Filter[];
|
||||
whitelist_filters?: Filter[];
|
||||
user_rules?: string[];
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
export type FilteringCheckHostParams = {
|
||||
/**
|
||||
* Filter by host name
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
* Optional ClientID or client IP address
|
||||
*/
|
||||
client?: string;
|
||||
/**
|
||||
* Optional DNS type
|
||||
*/
|
||||
qtype?: string;
|
||||
};
|
||||
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* Request filtering status.
|
||||
*/
|
||||
export type FilteringReason =
|
||||
| 'NotFilteredNotFound'
|
||||
| 'NotFilteredWhiteList'
|
||||
| 'NotFilteredError'
|
||||
| 'FilteredBlackList'
|
||||
| 'FilteredSafeBrowsing'
|
||||
| 'FilteredParental'
|
||||
| 'FilteredInvalid'
|
||||
| 'FilteredSafeSearch'
|
||||
| 'FilteredBlockedService'
|
||||
| 'Rewrite'
|
||||
| 'RewriteEtcHosts'
|
||||
| 'RewriteRule';
|
||||
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* Query log configuration
|
||||
*/
|
||||
export interface GetQueryLogConfigResponse {
|
||||
/** Is query log enabled */
|
||||
enabled: boolean;
|
||||
/** Time period for query log rotation in milliseconds. */
|
||||
interval: number;
|
||||
/** Anonymize clients' IP addresses */
|
||||
anonymize_client_ip: boolean;
|
||||
/** List of host names, which should not be written to log */
|
||||
ignored: string[];
|
||||
/** If true, the host names in the `ignored` array are excluded from the query log. */
|
||||
ignored_enabled?: boolean;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* Statistics configuration
|
||||
*/
|
||||
export interface GetStatsConfigResponse {
|
||||
/** Are statistics enabled */
|
||||
enabled: boolean;
|
||||
/** Statistics rotation interval in milliseconds */
|
||||
interval: number;
|
||||
/** List of host names, which should not be counted */
|
||||
ignored: string[];
|
||||
/** If true, the host names in the `ignored` array are excluded from the statistics. */
|
||||
ignored_enabled?: boolean;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* /version.json request data
|
||||
*/
|
||||
export interface GetVersionRequest {
|
||||
/** If false, server will check for a new version data only once in several hours. */
|
||||
recheck_now?: boolean;
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
export * from './accessList';
|
||||
export * from './accessListResponse';
|
||||
export * from './accessSetRequest';
|
||||
export * from './addressesInfo';
|
||||
export * from './addressInfo';
|
||||
export * from './addUrlRequest';
|
||||
export * from './blockedService';
|
||||
export * from './blockedServicesAll';
|
||||
export * from './blockedServicesArray';
|
||||
export * from './blockedServicesSchedule';
|
||||
export * from './checkConfigRequest';
|
||||
export * from './checkConfigRequestInfo';
|
||||
export * from './checkConfigResponse';
|
||||
export * from './checkConfigResponseInfo';
|
||||
export * from './checkConfigStaticIpInfo';
|
||||
export * from './checkConfigStaticIpInfoStatic';
|
||||
export * from './client';
|
||||
export * from './clientAuto';
|
||||
export * from './clientDelete';
|
||||
export * from './clientFindSubEntry';
|
||||
export * from './clients';
|
||||
export * from './clientsArray';
|
||||
export * from './clientsAutoArray';
|
||||
export * from './clientsFindEntry';
|
||||
export * from './clientsFindParams';
|
||||
export * from './clientsFindResponse';
|
||||
export * from './clientsSearchRequest';
|
||||
export * from './clientsSearchRequestItem';
|
||||
export * from './clientUpdate';
|
||||
export * from './dayRange';
|
||||
export * from './dhcpConfig';
|
||||
export * from './dhcpConfigV4';
|
||||
export * from './dhcpConfigV6';
|
||||
export * from './dhcpFindActiveReq';
|
||||
export * from './dhcpLease';
|
||||
export * from './dhcpSearchResult';
|
||||
export * from './dhcpSearchResultOtherServer';
|
||||
export * from './dhcpSearchResultOtherServerFound';
|
||||
export * from './dhcpSearchResultStaticIP';
|
||||
export * from './dhcpSearchResultStaticIPStatic';
|
||||
export * from './dhcpSearchV4';
|
||||
export * from './dhcpSearchV6';
|
||||
export * from './dhcpStaticLease';
|
||||
export * from './dhcpStaticLeaseBody';
|
||||
export * from './dhcpStatus';
|
||||
export * from './dnsAnswer';
|
||||
export * from './dNSConfig';
|
||||
export * from './dNSConfigBlockingMode';
|
||||
export * from './dNSConfigUpstreamMode';
|
||||
export * from './dnsInfo200';
|
||||
export * from './dnsQuestion';
|
||||
export * from './error';
|
||||
export * from './filter';
|
||||
export * from './filterCheckHostResponse';
|
||||
export * from './filterConfig';
|
||||
export * from './filteringCheckHostParams';
|
||||
export * from './filteringReason';
|
||||
export * from './filterRefreshRequest';
|
||||
export * from './filterRefreshResponse';
|
||||
export * from './filterSetUrl';
|
||||
export * from './filterSetUrlData';
|
||||
export * from './filterStatus';
|
||||
export * from './getQueryLogConfigResponse';
|
||||
export * from './getStatsConfigResponse';
|
||||
export * from './getVersionRequest';
|
||||
export * from './initialConfiguration';
|
||||
export * from './lang';
|
||||
export * from './languageSettings';
|
||||
export * from './login';
|
||||
export * from './mobileConfigDoHParams';
|
||||
export * from './mobileConfigDoTParams';
|
||||
export * from './netInterface';
|
||||
export * from './netInterfaces';
|
||||
export * from './parentalStatus200';
|
||||
export * from './profileInfo';
|
||||
export * from './profileInfoTheme';
|
||||
export * from './putQueryLogConfigUpdateRequest';
|
||||
export * from './putStatsConfigUpdateRequest';
|
||||
export * from './queryLog';
|
||||
export * from './queryLogConfig';
|
||||
export * from './queryLogConfigInterval';
|
||||
export * from './queryLogItem';
|
||||
export * from './queryLogItemClient';
|
||||
export * from './queryLogItemClientProto';
|
||||
export * from './queryLogItemClientWhois';
|
||||
export * from './queryLogParams';
|
||||
export * from './queryLogResponseStatus';
|
||||
export * from './removeUrlRequest';
|
||||
export * from './resultRule';
|
||||
export * from './rewriteEntry';
|
||||
export * from './rewriteEntryBody';
|
||||
export * from './rewriteList';
|
||||
export * from './rewriteSettings';
|
||||
export * from './rewriteSettingsBody';
|
||||
export * from './rewriteUpdate';
|
||||
export * from './rewriteUpdateBody';
|
||||
export * from './safebrowsingStatus200';
|
||||
export * from './safeSearchConfig';
|
||||
export * from './schedule';
|
||||
export * from './serverStatus';
|
||||
export * from './serviceGroup';
|
||||
export * from './setProtectionRequest';
|
||||
export * from './setRulesRequest';
|
||||
export * from './stats';
|
||||
export * from './statsConfig';
|
||||
export * from './statsConfigInterval';
|
||||
export * from './statsParams';
|
||||
export * from './statsTimeUnits';
|
||||
export * from './tlsConfig';
|
||||
export * from './tlsConfigBody';
|
||||
export * from './tlsConfigKeyType';
|
||||
export * from './topArrayEntry';
|
||||
export * from './upstreamsConfig';
|
||||
export * from './upstreamsConfigResponse';
|
||||
export * from './versionInfo';
|
||||
export * from './whoisInfo';
|
||||
@@ -0,0 +1,15 @@
|
||||
import type { AddressInfo } from './addressInfo';
|
||||
import type { Lang } from './lang';
|
||||
|
||||
/**
|
||||
* AdGuard Home initial configuration for the first-install wizard.
|
||||
*/
|
||||
export interface InitialConfiguration {
|
||||
dns: AddressInfo;
|
||||
web: AddressInfo;
|
||||
language?: Lang;
|
||||
/** Basic auth password */
|
||||
password: string;
|
||||
/** Basic auth username */
|
||||
username: string;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* Language code.
|
||||
*/
|
||||
export type Lang =
|
||||
| 'ar'
|
||||
| 'be'
|
||||
| 'bg'
|
||||
| 'cs'
|
||||
| 'da'
|
||||
| 'de'
|
||||
| 'en'
|
||||
| 'es'
|
||||
| 'fa'
|
||||
| 'fi'
|
||||
| 'fr'
|
||||
| 'hr'
|
||||
| 'hu'
|
||||
| 'id'
|
||||
| 'it'
|
||||
| 'ja'
|
||||
| 'ko'
|
||||
| 'nl'
|
||||
| 'no'
|
||||
| 'pl'
|
||||
| 'pt-br'
|
||||
| 'pt-pt'
|
||||
| 'ro'
|
||||
| 'ru'
|
||||
| 'si-lk'
|
||||
| 'sk'
|
||||
| 'sl'
|
||||
| 'sr-cs'
|
||||
| 'sv'
|
||||
| 'th'
|
||||
| 'tr'
|
||||
| 'uk'
|
||||
| 'vi'
|
||||
| 'zh-cn'
|
||||
| 'zh-hk'
|
||||
| 'zh-tw';
|
||||
@@ -0,0 +1,8 @@
|
||||
import type { Lang } from './lang';
|
||||
|
||||
/**
|
||||
* Language settings object.
|
||||
*/
|
||||
export interface LanguageSettings {
|
||||
language: Lang;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* Login request data
|
||||
*/
|
||||
export interface Login {
|
||||
/** User name */
|
||||
name?: string;
|
||||
/** Password */
|
||||
password?: string;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
export type MobileConfigDoHParams = {
|
||||
/**
|
||||
* Host for which the config is generated. If no host is provided, `tls.server_name` from the configuration file is used. If `tls.server_name` is not set, the API returns an error with a 500 status.
|
||||
*/
|
||||
host: string;
|
||||
/**
|
||||
* ClientID.
|
||||
*/
|
||||
client_id?: string;
|
||||
};
|
||||
@@ -0,0 +1,10 @@
|
||||
export type MobileConfigDoTParams = {
|
||||
/**
|
||||
* Host for which the config is generated. If no host is provided, `tls.server_name` from the configuration file is used. If `tls.server_name` is not set, the API returns an error with a 500 status.
|
||||
*/
|
||||
host: string;
|
||||
/**
|
||||
* ClientID.
|
||||
*/
|
||||
client_id?: string;
|
||||
};
|
||||
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* Network interface info
|
||||
*/
|
||||
export interface NetInterface {
|
||||
/** Flags could be any combination of the following values, divided by the "|" character: "up", "broadcast", "loopback", "pointtopoint" and "multicast". */
|
||||
flags: string;
|
||||
/** The IP address of the gateway. */
|
||||
gateway_ip: string;
|
||||
hardware_address: string;
|
||||
/** The addresses of the interface of v4 family. */
|
||||
ipv4_addresses: string[];
|
||||
/** The addresses of the interface of v6 family. */
|
||||
ipv6_addresses: string[];
|
||||
name: string;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import type { NetInterface } from './netInterface';
|
||||
|
||||
/**
|
||||
* Network interfaces dictionary, keys are interface names.
|
||||
*/
|
||||
export interface NetInterfaces {
|
||||
[key: string]: NetInterface;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export type ParentalStatus200 = {
|
||||
enable?: boolean;
|
||||
sensitivity?: number;
|
||||
};
|
||||
@@ -0,0 +1,12 @@
|
||||
import type { Lang } from './lang';
|
||||
import type { ProfileInfoTheme } from './profileInfoTheme';
|
||||
|
||||
/**
|
||||
* Information about the current user
|
||||
*/
|
||||
export interface ProfileInfo {
|
||||
name: string;
|
||||
language: Lang;
|
||||
/** Interface theme */
|
||||
theme: ProfileInfoTheme;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Interface theme
|
||||
*/
|
||||
export type ProfileInfoTheme = 'auto' | 'dark' | 'light';
|
||||
@@ -0,0 +1,3 @@
|
||||
import type { GetQueryLogConfigResponse } from './getQueryLogConfigResponse';
|
||||
|
||||
export type PutQueryLogConfigUpdateRequest = GetQueryLogConfigResponse;
|
||||
@@ -0,0 +1,3 @@
|
||||
import type { GetStatsConfigResponse } from './getStatsConfigResponse';
|
||||
|
||||
export type PutStatsConfigUpdateRequest = GetStatsConfigResponse;
|
||||
Loaded 100 of 166 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user