@ykurilov/remontnik-browser 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +134 -2
- package/dist/actions/index.d.ts +33 -0
- package/dist/actions/index.js +383 -0
- package/dist/actions/index.js.map +1 -0
- package/dist/attempts.d.ts +11 -0
- package/dist/attempts.js +26 -0
- package/dist/attempts.js.map +1 -0
- package/dist/cli/index.d.ts +8 -0
- package/dist/cli/index.js +210 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/main.d.ts +2 -0
- package/dist/cli/main.js +10 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/egress/index.d.ts +43 -0
- package/dist/egress/index.js +401 -0
- package/dist/egress/index.js.map +1 -0
- package/dist/guard/index.d.ts +101 -0
- package/dist/guard/index.js +298 -0
- package/dist/guard/index.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/live/control.d.ts +34 -0
- package/dist/live/control.js +111 -0
- package/dist/live/control.js.map +1 -0
- package/dist/live/page/index.d.ts +2 -0
- package/dist/live/page/index.js +200 -0
- package/dist/live/page/index.js.map +1 -0
- package/dist/live/server.d.ts +25 -0
- package/dist/live/server.js +267 -0
- package/dist/live/server.js.map +1 -0
- package/dist/live/stream.d.ts +39 -0
- package/dist/live/stream.js +82 -0
- package/dist/live/stream.js.map +1 -0
- package/dist/log.d.ts +4 -0
- package/dist/log.js +19 -0
- package/dist/log.js.map +1 -0
- package/dist/policy/index.d.ts +43 -0
- package/dist/policy/index.js +80 -0
- package/dist/policy/index.js.map +1 -0
- package/dist/profiles/blocked-journal.d.ts +8 -0
- package/dist/profiles/blocked-journal.js +32 -0
- package/dist/profiles/blocked-journal.js.map +1 -0
- package/dist/profiles/index.d.ts +31 -0
- package/dist/profiles/index.js +131 -0
- package/dist/profiles/index.js.map +1 -0
- package/dist/service/index.d.ts +15 -0
- package/dist/service/index.js +86 -0
- package/dist/service/index.js.map +1 -0
- package/dist/service/start.d.ts +31 -0
- package/dist/service/start.js +90 -0
- package/dist/service/start.js.map +1 -0
- package/dist/snapshot/index.d.ts +28 -0
- package/dist/snapshot/index.js +100 -0
- package/dist/snapshot/index.js.map +1 -0
- package/dist/snapshot/render.d.ts +17 -0
- package/dist/snapshot/render.js +82 -0
- package/dist/snapshot/render.js.map +1 -0
- package/dist/vault/index.d.ts +34 -0
- package/dist/vault/index.js +146 -0
- package/dist/vault/index.js.map +1 -0
- package/dist/vault/output-filter.d.ts +5 -0
- package/dist/vault/output-filter.js +89 -0
- package/dist/vault/output-filter.js.map +1 -0
- package/package.json +36 -3
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface AttemptLimiter {
|
|
2
|
+
blocked(client: string): boolean;
|
|
3
|
+
fail(client: string): void;
|
|
4
|
+
/** Сколько адресов сейчас помним — для проверки, что память не растёт. */
|
|
5
|
+
size(): number;
|
|
6
|
+
}
|
|
7
|
+
export declare function createAttemptLimiter(options: {
|
|
8
|
+
limit: number;
|
|
9
|
+
windowMs: number;
|
|
10
|
+
now?: () => number;
|
|
11
|
+
}): AttemptLimiter;
|
package/dist/attempts.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// Ограничение подбора ключа: неудачные попытки считаются отдельно по адресу клиента,
|
|
2
|
+
// поэтому чужие ошибки из той же сети не закрывают вход владельцу или врачу.
|
|
3
|
+
export function createAttemptLimiter(options) {
|
|
4
|
+
const { limit, windowMs } = options;
|
|
5
|
+
const now = options.now ?? Date.now;
|
|
6
|
+
const failures = new Map();
|
|
7
|
+
const recent = (client) => {
|
|
8
|
+
const since = now() - windowMs;
|
|
9
|
+
const times = (failures.get(client) ?? []).filter((time) => time > since);
|
|
10
|
+
if (times.length === 0)
|
|
11
|
+
failures.delete(client);
|
|
12
|
+
else
|
|
13
|
+
failures.set(client, times);
|
|
14
|
+
return times;
|
|
15
|
+
};
|
|
16
|
+
return {
|
|
17
|
+
blocked: (client) => recent(client).length >= limit,
|
|
18
|
+
fail(client) {
|
|
19
|
+
for (const known of [...failures.keys()])
|
|
20
|
+
recent(known);
|
|
21
|
+
failures.set(client, [...recent(client), now()]);
|
|
22
|
+
},
|
|
23
|
+
size: () => failures.size,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=attempts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attempts.js","sourceRoot":"","sources":["../src/attempts.ts"],"names":[],"mappings":"AAAA,qFAAqF;AACrF,6EAA6E;AAS7E,MAAM,UAAU,oBAAoB,CAAC,OAAgE;IACnG,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAA;IACnC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAA;IACnC,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAoB,CAAA;IAE5C,MAAM,MAAM,GAAG,CAAC,MAAc,EAAE,EAAE;QAChC,MAAM,KAAK,GAAG,GAAG,EAAE,GAAG,QAAQ,CAAA;QAC9B,MAAM,KAAK,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,GAAG,KAAK,CAAC,CAAA;QACzE,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;;YAC1C,QAAQ,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAA;QAChC,OAAO,KAAK,CAAA;IACd,CAAC,CAAA;IAED,OAAO;QACL,OAAO,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,IAAI,KAAK;QACnD,IAAI,CAAC,MAAM;YACT,KAAK,MAAM,KAAK,IAAI,CAAC,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC;gBAAE,MAAM,CAAC,KAAK,CAAC,CAAA;YACvD,QAAQ,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAA;QAClD,CAAC;QACD,IAAI,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,IAAI;KAC1B,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface CliIo {
|
|
2
|
+
env: Record<string, string | undefined>;
|
|
3
|
+
stdin: AsyncIterable<Uint8Array | string>;
|
|
4
|
+
stdout: (text: string) => void;
|
|
5
|
+
stderr: (text: string) => void;
|
|
6
|
+
fetch?: typeof fetch;
|
|
7
|
+
}
|
|
8
|
+
export declare function runBrowserCli(argv: readonly string[], io: CliIo): Promise<number>;
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
// Консольная `remontnik-browser`: те же команды страницы, что у HTTP-службы, плюс управление входами,
|
|
2
|
+
// которого по HTTP нет. Пароль — только из стандартного ввода (`--password-stdin`).
|
|
3
|
+
import { readFileSync } from 'node:fs';
|
|
4
|
+
import { BROWSER_COMMANDS } from '../actions/index.js';
|
|
5
|
+
import { browserPolicyProblems } from '../policy/index.js';
|
|
6
|
+
import { loginVaultPath, openLoginVault, parseVaultKey } from '../vault/index.js';
|
|
7
|
+
const USAGE = `remontnik-browser <команда> -p <профиль> [параметры]
|
|
8
|
+
|
|
9
|
+
Служба:
|
|
10
|
+
serve запустить службу (настройки из окружения)
|
|
11
|
+
check-policy [путь] проверить настройку браузера (по умолчанию REMONTNIK_BROWSER_POLICY)
|
|
12
|
+
|
|
13
|
+
Страница (через службу, REMONTNIK_BROWSER_URL и REMONTNIK_BROWSER_TOKEN):
|
|
14
|
+
open | read | back | shot | status -p <профиль>
|
|
15
|
+
url -p <профиль> <адрес>
|
|
16
|
+
click -p <профиль> --ref <e12> --danger <none|submit>
|
|
17
|
+
type -p <профиль> --ref <e12> --text <текст> [--submit] --danger <none|submit>
|
|
18
|
+
key -p <профиль> <клавиша>
|
|
19
|
+
scroll -p <профиль> --direction <up|down|left|right> [--px <число>]
|
|
20
|
+
fill-password -p <профиль> --entry <номер> --ref <e12>
|
|
21
|
+
|
|
22
|
+
Входы (на сервере браузера, REMONTNIK_BROWSER_DATA_DIR и REMONTNIK_BROWSER_VAULT_KEY):
|
|
23
|
+
logins list [--site <origin>]
|
|
24
|
+
logins add --site <origin> --login <логин> --profile <профиль> --password-stdin
|
|
25
|
+
logins remove <номер>
|
|
26
|
+
`;
|
|
27
|
+
const BOOLEAN_FLAGS = new Set(['submit', 'password-stdin', 'help']);
|
|
28
|
+
class UsageError extends Error {
|
|
29
|
+
}
|
|
30
|
+
function parseArguments(argv) {
|
|
31
|
+
const positional = [];
|
|
32
|
+
const flags = new Map();
|
|
33
|
+
for (let index = 0; index < argv.length; index++) {
|
|
34
|
+
const argument = argv[index];
|
|
35
|
+
if (argument === '-p' || argument.startsWith('--')) {
|
|
36
|
+
const name = argument === '-p' ? 'profile' : argument.slice(2);
|
|
37
|
+
if (name === 'password')
|
|
38
|
+
throw new UsageError('пароль в аргументах не принимается: передайте его в стандартный ввод с --password-stdin');
|
|
39
|
+
if (BOOLEAN_FLAGS.has(name)) {
|
|
40
|
+
flags.set(name, true);
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
const value = argv[index + 1];
|
|
44
|
+
if (value === undefined)
|
|
45
|
+
throw new UsageError(`${argument}: нужно значение`);
|
|
46
|
+
flags.set(name, value);
|
|
47
|
+
index++;
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
positional.push(argument);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return { positional, flags };
|
|
54
|
+
}
|
|
55
|
+
function vaultFrom(env) {
|
|
56
|
+
const dataDir = env.REMONTNIK_BROWSER_DATA_DIR;
|
|
57
|
+
if (!dataDir)
|
|
58
|
+
throw new UsageError('REMONTNIK_BROWSER_DATA_DIR не задан');
|
|
59
|
+
return openLoginVault({ file: loginVaultPath(dataDir), key: parseVaultKey(env.REMONTNIK_BROWSER_VAULT_KEY) });
|
|
60
|
+
}
|
|
61
|
+
async function logins(action, rest, flags, io) {
|
|
62
|
+
const vault = vaultFrom(io.env);
|
|
63
|
+
const text = (name) => {
|
|
64
|
+
const value = flags.get(name);
|
|
65
|
+
if (typeof value !== 'string' || value === '')
|
|
66
|
+
throw new UsageError(`нужен --${name}`);
|
|
67
|
+
return value;
|
|
68
|
+
};
|
|
69
|
+
switch (action) {
|
|
70
|
+
case 'list': {
|
|
71
|
+
const site = flags.get('site');
|
|
72
|
+
io.stdout(`${JSON.stringify({ logins: vault.list(typeof site === 'string' ? { site } : {}) })}\n`);
|
|
73
|
+
return 0;
|
|
74
|
+
}
|
|
75
|
+
case 'add': {
|
|
76
|
+
if (flags.get('password-stdin') !== true)
|
|
77
|
+
throw new UsageError('пароль читается только из стандартного ввода: добавьте --password-stdin');
|
|
78
|
+
const entry = await vault.add({ site: text('site'), login: text('login'), profile: text('profile') }, io.stdin);
|
|
79
|
+
io.stdout(`${JSON.stringify({ ok: true, entry })}\n`);
|
|
80
|
+
return 0;
|
|
81
|
+
}
|
|
82
|
+
case 'remove': {
|
|
83
|
+
const id = Number(rest[0]);
|
|
84
|
+
if (!Number.isInteger(id) || id <= 0)
|
|
85
|
+
throw new UsageError('logins remove <номер>');
|
|
86
|
+
if (!vault.remove(id)) {
|
|
87
|
+
io.stderr(`вход ${id} не найден\n`);
|
|
88
|
+
return 1;
|
|
89
|
+
}
|
|
90
|
+
io.stdout(`${JSON.stringify({ ok: true, removed: id })}\n`);
|
|
91
|
+
return 0;
|
|
92
|
+
}
|
|
93
|
+
default:
|
|
94
|
+
throw new UsageError('logins list | add | remove');
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/** Проверка файла настройки без запуска службы: её зовёт и `remontnik-doctor check-config`. */
|
|
98
|
+
function checkPolicy(path, io) {
|
|
99
|
+
if (!path)
|
|
100
|
+
throw new UsageError('нужен путь к настройке или REMONTNIK_BROWSER_POLICY');
|
|
101
|
+
let text;
|
|
102
|
+
try {
|
|
103
|
+
text = readFileSync(path, 'utf8');
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
io.stdout(`${JSON.stringify({ ok: false, problems: [`файл настройки не читается: ${error.code ?? error.message}`] })}\n`);
|
|
107
|
+
return 1;
|
|
108
|
+
}
|
|
109
|
+
let problems;
|
|
110
|
+
try {
|
|
111
|
+
problems = browserPolicyProblems(JSON.parse(text));
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
problems = [`файл не читается как JSON: ${error.message}`];
|
|
115
|
+
}
|
|
116
|
+
io.stdout(`${JSON.stringify({ ok: problems.length === 0, problems })}\n`);
|
|
117
|
+
return problems.length === 0 ? 0 : 1;
|
|
118
|
+
}
|
|
119
|
+
function commandInput(command, rest, flags) {
|
|
120
|
+
const profile = flags.get('profile');
|
|
121
|
+
if (typeof profile !== 'string' || profile === '')
|
|
122
|
+
throw new UsageError('профиль обязателен: -p <профиль>');
|
|
123
|
+
const input = { profile };
|
|
124
|
+
const copy = (flag, field = flag) => {
|
|
125
|
+
const value = flags.get(flag);
|
|
126
|
+
if (typeof value === 'string')
|
|
127
|
+
input[field] = value;
|
|
128
|
+
};
|
|
129
|
+
switch (command) {
|
|
130
|
+
case 'url':
|
|
131
|
+
input.url = rest[0] ?? flags.get('url');
|
|
132
|
+
break;
|
|
133
|
+
case 'key':
|
|
134
|
+
input.key = rest[0] ?? flags.get('key');
|
|
135
|
+
break;
|
|
136
|
+
case 'click':
|
|
137
|
+
copy('ref');
|
|
138
|
+
copy('danger');
|
|
139
|
+
break;
|
|
140
|
+
case 'type':
|
|
141
|
+
copy('ref');
|
|
142
|
+
copy('text');
|
|
143
|
+
if (flags.get('submit') === true)
|
|
144
|
+
input.submit = true;
|
|
145
|
+
copy('danger');
|
|
146
|
+
break;
|
|
147
|
+
case 'scroll':
|
|
148
|
+
copy('direction');
|
|
149
|
+
if (typeof flags.get('px') === 'string')
|
|
150
|
+
input.px = Number(flags.get('px'));
|
|
151
|
+
break;
|
|
152
|
+
case 'fill-password':
|
|
153
|
+
input.entryId = Number(flags.get('entry'));
|
|
154
|
+
copy('ref');
|
|
155
|
+
break;
|
|
156
|
+
}
|
|
157
|
+
return input;
|
|
158
|
+
}
|
|
159
|
+
async function callService(command, input, io) {
|
|
160
|
+
const token = io.env.REMONTNIK_BROWSER_TOKEN;
|
|
161
|
+
if (!token)
|
|
162
|
+
throw new UsageError('REMONTNIK_BROWSER_TOKEN не задан');
|
|
163
|
+
const base = (io.env.REMONTNIK_BROWSER_URL ?? 'http://127.0.0.1:7700').replace(/\/$/, '');
|
|
164
|
+
const response = await (io.fetch ?? fetch)(`${base}/v1/${command}`, {
|
|
165
|
+
method: 'POST',
|
|
166
|
+
headers: { authorization: `Bearer ${token}`, 'content-type': 'application/json' },
|
|
167
|
+
body: JSON.stringify(input),
|
|
168
|
+
});
|
|
169
|
+
const text = await response.text();
|
|
170
|
+
io.stdout(`${text}\n`);
|
|
171
|
+
try {
|
|
172
|
+
return JSON.parse(text).ok === true ? 0 : 1;
|
|
173
|
+
}
|
|
174
|
+
catch {
|
|
175
|
+
return 1;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
export async function runBrowserCli(argv, io) {
|
|
179
|
+
try {
|
|
180
|
+
const { positional, flags } = parseArguments(argv);
|
|
181
|
+
const [command, ...rest] = positional;
|
|
182
|
+
if (!command || flags.get('help') === true) {
|
|
183
|
+
io.stdout(USAGE);
|
|
184
|
+
return command ? 0 : 2;
|
|
185
|
+
}
|
|
186
|
+
if (command === 'serve') {
|
|
187
|
+
const { startBrowserServiceFromEnv } = await import('../service/start.js');
|
|
188
|
+
const service = await startBrowserServiceFromEnv(io.env);
|
|
189
|
+
io.stderr(`remontnik-browser: служба слушает ${service.url}${service.liveUrl ? `, живой экран ${service.liveUrl}` : ''}\n`);
|
|
190
|
+
for (const signal of ['SIGINT', 'SIGTERM'])
|
|
191
|
+
process.once(signal, () => void service.close().then(() => process.exit(0)));
|
|
192
|
+
return await new Promise(() => { });
|
|
193
|
+
}
|
|
194
|
+
if (command === 'check-policy')
|
|
195
|
+
return checkPolicy(rest[0] ?? io.env.REMONTNIK_BROWSER_POLICY, io);
|
|
196
|
+
if (command === 'logins') {
|
|
197
|
+
// Список входов есть и в службе; добавление и удаление — только здесь.
|
|
198
|
+
return await logins(rest[0], rest.slice(1), flags, io);
|
|
199
|
+
}
|
|
200
|
+
if (!BROWSER_COMMANDS.includes(command))
|
|
201
|
+
throw new UsageError(`нет команды ${command}`);
|
|
202
|
+
return await callService(command, commandInput(command, rest, flags), io);
|
|
203
|
+
}
|
|
204
|
+
catch (error) {
|
|
205
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
206
|
+
io.stderr(`remontnik-browser: ${message}\n${error instanceof UsageError ? `\n${USAGE}` : ''}`);
|
|
207
|
+
return error instanceof UsageError ? 2 : 1;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"}
|
package/dist/cli/main.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { runBrowserCli } from './index.js';
|
|
3
|
+
const code = await runBrowserCli(process.argv.slice(2), {
|
|
4
|
+
env: process.env,
|
|
5
|
+
stdin: process.stdin,
|
|
6
|
+
stdout: (text) => process.stdout.write(text),
|
|
7
|
+
stderr: (text) => process.stderr.write(text),
|
|
8
|
+
});
|
|
9
|
+
process.exitCode = code;
|
|
10
|
+
//# sourceMappingURL=main.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.js","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE1C,MAAM,IAAI,GAAG,MAAM,aAAa,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE;IACtD,GAAG,EAAE,OAAO,CAAC,GAAG;IAChB,KAAK,EAAE,OAAO,CAAC,KAAK;IACpB,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC;IAC5C,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC;CAC7C,CAAC,CAAA;AACF,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAA"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export interface EgressTarget {
|
|
2
|
+
/** Имя хоста без квадратных скобок IPv6, в нижнем регистре. */
|
|
3
|
+
host: string;
|
|
4
|
+
port: number;
|
|
5
|
+
/** Для запроса `http://` — его origin; у туннеля схемы не видно. */
|
|
6
|
+
origin?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface UpstreamProxy {
|
|
9
|
+
server: string;
|
|
10
|
+
username?: string;
|
|
11
|
+
password?: string;
|
|
12
|
+
}
|
|
13
|
+
export interface EgressProxy {
|
|
14
|
+
/** Адрес для `--proxy-server` Chromium. */
|
|
15
|
+
readonly url: string;
|
|
16
|
+
/** Пароль на прокси, свой на каждый запуск: без него — `407`. */
|
|
17
|
+
readonly username: string;
|
|
18
|
+
readonly password: string;
|
|
19
|
+
/** Рвёт открытые туннели и запросы, которые сейчас уже не разрешены (замок только что закрылся). */
|
|
20
|
+
dropRefused(): void;
|
|
21
|
+
close(): Promise<void>;
|
|
22
|
+
}
|
|
23
|
+
export type RefusalReason = 'locked' | 'internal';
|
|
24
|
+
export interface EgressOptions {
|
|
25
|
+
/** Можно ли соединиться с адресом сейчас: решение замка. */
|
|
26
|
+
allows(target: EgressTarget): boolean;
|
|
27
|
+
/** Разрешённый сайт: ему можно и на внутренний адрес. */
|
|
28
|
+
exempt(target: EgressTarget): boolean;
|
|
29
|
+
onRefused(target: EgressTarget, reason: RefusalReason): void;
|
|
30
|
+
upstream?: UpstreamProxy;
|
|
31
|
+
/** Внутренние `адрес:порт` (IPv6 — в скобках), куда можно и вне разрешённых сайтов: для тестов. */
|
|
32
|
+
reachableInternal?: readonly string[];
|
|
33
|
+
/** Разрешение имён; по умолчанию — системное. */
|
|
34
|
+
lookup?: (host: string) => Promise<string[]>;
|
|
35
|
+
/** Срок на разрешение имени, подключение и рукопожатие с прокси профиля. */
|
|
36
|
+
connectTimeoutMs?: number;
|
|
37
|
+
}
|
|
38
|
+
/** Адрес из разрешённых origin: для запроса http:// — точный origin, для туннеля — тот же хост и порт. */
|
|
39
|
+
export declare function isAllowedTarget(target: EgressTarget, allowedOrigins: readonly string[]): boolean;
|
|
40
|
+
export declare function describeTarget(target: EgressTarget): string;
|
|
41
|
+
/** Петля, частные сети, link-local, CGNAT, служебные и групповые адреса — и они же внутри IPv6. */
|
|
42
|
+
export declare function isInternalAddress(ip: string): boolean;
|
|
43
|
+
export declare function startEgressProxy(options: EgressOptions): Promise<EgressProxy>;
|