ci
ci/woodpecker/push/base Pipeline failed
Details
ci/woodpecker/push/base Pipeline failed
Details
parent
feb564d4a0
commit
3ec3db37e0
|
@ -0,0 +1,4 @@
|
|||
audit=false
|
||||
fund=false
|
||||
update-notifier=false
|
||||
package-lock=true
|
|
@ -0,0 +1,42 @@
|
|||
when:
|
||||
event: [push, deployment, manual, cron]
|
||||
|
||||
labels:
|
||||
platform: linux/amd64
|
||||
|
||||
variables:
|
||||
- &node_image 'node:14-bullseye'
|
||||
- &buildx_image 'woodpeckerci/plugin-docker-buildx:2.2.1'
|
||||
|
||||
steps:
|
||||
prepare-npm:
|
||||
image: *node_image
|
||||
secrets:
|
||||
- npmconf
|
||||
commands:
|
||||
- git config --add safe.directory '*'
|
||||
- if [ "$${NPMCONF:-}" != "" ]; then echo "$${NPMCONF}" >> "$${HOME}/.npmrc"; fi
|
||||
- npm ci
|
||||
- npx browserslist@latest --update-db
|
||||
|
||||
build-npm:
|
||||
image: *node_image
|
||||
commands:
|
||||
- bash ./scripts/build.sh web rel
|
||||
|
||||
build-docker:
|
||||
image: *buildx_image
|
||||
pull: true
|
||||
settings:
|
||||
platforms: linux/amd64
|
||||
dockerfile: docker/Dockerfile.ci
|
||||
context: .
|
||||
registry:
|
||||
from_secret: registry_domain
|
||||
tag: latest
|
||||
repo:
|
||||
from_secret: target_image_name
|
||||
password:
|
||||
from_secret: registry_token
|
||||
username:
|
||||
from_secret: registry_user
|
|
@ -0,0 +1,17 @@
|
|||
FROM nginx:mainline-alpine
|
||||
|
||||
COPY ./files/default.conf /etc/nginx/conf.d/default.conf
|
||||
COPY ./files/nginx.conf /etc/nginx/nginx.conf
|
||||
COPY ./files/entrypoint.sh /
|
||||
|
||||
RUN apk update --no-cache && apk upgrade --no-cache \
|
||||
&& apk add --no-cache openssl tzdata \
|
||||
&& mkdir -p /var/www/TeaWeb /etc/ssl/certs \
|
||||
&& chmod +x /entrypoint.sh
|
||||
|
||||
ENV TZ="Europe/Berlin"
|
||||
|
||||
EXPOSE 80 443
|
||||
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
|
@ -0,0 +1,19 @@
|
|||
FROM nginx:mainline-alpine
|
||||
|
||||
COPY ./docker/files/default.conf /etc/nginx/conf.d/default.conf
|
||||
COPY ./docker/files/nginx.conf /etc/nginx/nginx.conf
|
||||
COPY ./docker/files/entrypoint.sh /
|
||||
|
||||
RUN apk update --no-cache && apk upgrade --no-cache \
|
||||
&& apk add --no-cache openssl tzdata \
|
||||
&& mkdir -p /var/www/TeaWeb /etc/ssl/certs \
|
||||
&& chmod +x /entrypoint.sh
|
||||
|
||||
ENV TZ="Europe/Berlin"
|
||||
|
||||
EXPOSE 80 443
|
||||
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
|
||||
COPY ./dist/ /var/www/TeaWeb/
|
|
@ -0,0 +1,36 @@
|
|||
server {
|
||||
listen 80;
|
||||
server_name _;
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 default_server ssl http2;
|
||||
server_name _;
|
||||
|
||||
ssl_ciphers EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:TLS-CHACHA20-POLY1305-SHA256:TLS-AES-256-GCM-SHA384:TLS-AES-128-GCM-SHA256;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_dhparam /etc/ssl/certs/dhparam.pem;
|
||||
ssl_ecdh_curve secp384r1;
|
||||
ssl_certificate /etc/ssl/certs/tea_bundle.crt;
|
||||
ssl_certificate_key /etc/ssl/certs/tea.key;
|
||||
ssl_session_cache shared:MozSSL:10m;
|
||||
ssl_session_timeout 1d;
|
||||
ssl_prefer_server_ciphers on;
|
||||
|
||||
location / {
|
||||
root /var/www/TeaWeb;
|
||||
index index.html;
|
||||
}
|
||||
|
||||
#error_page 404 /404.html;
|
||||
|
||||
# redirect server error pages to the static page /50x.html
|
||||
#
|
||||
error_page 500 502 503 504 /50x.html;
|
||||
location = /50x.html {
|
||||
root /usr/share/nginx/html;
|
||||
}
|
||||
|
||||
gzip off;
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
#!/usr/bin/env sh
|
||||
set -e
|
||||
|
||||
gen_self_signed() {
|
||||
echo "[WRN] No certificates found, generating self signed cert with key"
|
||||
openssl req -x509 -nodes -days 1780 -newkey rsa:4096 \
|
||||
-keyout /etc/ssl/certs/tea.key \
|
||||
-out /etc/ssl/certs/tea_bundle.crt \
|
||||
-subj "/C=DE/ST=Berlin/L=Germany/O=TeaSpeak/OU=TeaWeb/CN=localhost/emailAddress=noreply@teaspeak.de"
|
||||
}
|
||||
|
||||
gen_diffie_hellman() {
|
||||
echo "[INF] No Diffie-Hellman pem found, generating new with 2048 byte"
|
||||
openssl dhparam -out /etc/ssl/certs/dhparam.pem 2048
|
||||
}
|
||||
|
||||
if [ "$1" = "nginx" ]; then
|
||||
if [ ! -f /etc/ssl/certs/tea.key ] && [ ! -f /etc/ssl/certs/tea_bundle.crt ]; then
|
||||
gen_self_signed
|
||||
elif [ ! -f /etc/ssl/certs/tea.key ] || [ ! -f /etc/ssl/certs/tea_bundle.crt ]; then
|
||||
echo "[ERR] Only found a key or crt-bundle file but both files are REQUIRED!"
|
||||
exit 1
|
||||
fi
|
||||
if [ ! -f /etc/ssl/certs/dhparam.pem ]; then
|
||||
gen_diffie_hellman
|
||||
fi
|
||||
fi
|
||||
|
||||
exec "$@"
|
|
@ -0,0 +1,32 @@
|
|||
user nginx;
|
||||
worker_processes auto;
|
||||
|
||||
error_log /var/log/nginx/error.log warn;
|
||||
pid /var/run/nginx.pid;
|
||||
|
||||
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
|
||||
|
||||
http {
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||||
'$status $body_bytes_sent "$http_referer" '
|
||||
'"$http_user_agent" "$http_x_forwarded_for"';
|
||||
|
||||
access_log /var/log/nginx/access.log main;
|
||||
|
||||
sendfile on;
|
||||
#tcp_nopush on;
|
||||
|
||||
server_tokens off;
|
||||
keepalive_timeout 75;
|
||||
|
||||
#gzip on;
|
||||
|
||||
include /etc/nginx/conf.d/*.conf;
|
||||
}
|
|
@ -1,10 +1,10 @@
|
|||
import {LogCategory, logError, logInfo, logTrace} from "./log";
|
||||
import { LogCategory, logError, logInfo, logTrace } from "./log";
|
||||
import * as loader from "tc-loader";
|
||||
import {Stage} from "tc-loader";
|
||||
import {Registry} from "./events";
|
||||
import {tr} from "./i18n/localize";
|
||||
import {CallOnce, ignorePromise} from "tc-shared/proto";
|
||||
import {getStorageAdapter} from "tc-shared/StorageAdapter";
|
||||
import { Stage } from "tc-loader";
|
||||
import { Registry } from "./events";
|
||||
import { tr } from "./i18n/localize";
|
||||
import { CallOnce, ignorePromise } from "tc-shared/proto";
|
||||
import { getStorageAdapter } from "tc-shared/StorageAdapter";
|
||||
|
||||
/*
|
||||
* TODO: Sync settings across renderer instances
|
||||
|
@ -14,17 +14,17 @@ export type RegistryValueType = boolean | number | string | object;
|
|||
export type RegistryValueTypeNames = "boolean" | "number" | "string" | "object";
|
||||
|
||||
export type RegistryValueTypeMapping<T> = T extends boolean ? "boolean" :
|
||||
T extends number ? "number" :
|
||||
T extends string ? "string" :
|
||||
T extends object ? "object" :
|
||||
never;
|
||||
T extends number ? "number" :
|
||||
T extends string ? "string" :
|
||||
T extends object ? "object" :
|
||||
never;
|
||||
|
||||
export interface RegistryKey<ValueType extends RegistryValueType> {
|
||||
key: string;
|
||||
valueType: RegistryValueTypeMapping<ValueType>;
|
||||
|
||||
fallbackKeys?: string | string[];
|
||||
fallbackImports?: {[key: string]:(value: string) => ValueType};
|
||||
fallbackImports?: { [key: string]: (value: string) => ValueType };
|
||||
|
||||
description?: string;
|
||||
|
||||
|
@ -37,7 +37,7 @@ export interface ValuedRegistryKey<ValueType extends RegistryValueType> extends
|
|||
|
||||
const UPDATE_DIRECT: boolean = true;
|
||||
|
||||
function decodeValueFromString<T extends RegistryValueType>(input: string, type: RegistryValueTypeMapping<T>) : T {
|
||||
function decodeValueFromString<T extends RegistryValueType>(input: string, type: RegistryValueTypeMapping<T>): T {
|
||||
switch (type) {
|
||||
case "string":
|
||||
return input as any;
|
||||
|
@ -60,7 +60,7 @@ function decodeValueFromString<T extends RegistryValueType>(input: string, type:
|
|||
}
|
||||
}
|
||||
|
||||
export function encodeSettingValueToString<T extends RegistryValueType>(input: T) : string {
|
||||
export function encodeSettingValueToString<T extends RegistryValueType>(input: T): string {
|
||||
switch (typeof input) {
|
||||
case "string":
|
||||
return input;
|
||||
|
@ -83,24 +83,24 @@ export function resolveSettingKey<ValueType extends RegistryValueType, DefaultTy
|
|||
key: RegistryKey<ValueType>,
|
||||
resolver: (key: string) => string | undefined | null,
|
||||
defaultValue: DefaultType
|
||||
) : ValueType | DefaultType {
|
||||
): ValueType | DefaultType {
|
||||
let value = resolver(key.key);
|
||||
|
||||
const keys = [key.key];
|
||||
if(Array.isArray(key.fallbackKeys)) {
|
||||
if (Array.isArray(key.fallbackKeys)) {
|
||||
keys.push(...key.fallbackKeys);
|
||||
}
|
||||
|
||||
for(const resolveKey of keys) {
|
||||
for (const resolveKey of keys) {
|
||||
value = resolver(resolveKey);
|
||||
if(typeof value !== "string") {
|
||||
if (typeof value !== "string") {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (key.valueType) {
|
||||
case "number":
|
||||
case "boolean":
|
||||
if(value.length === 0) {
|
||||
if (value.length === 0) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
|
@ -109,9 +109,9 @@ export function resolveSettingKey<ValueType extends RegistryValueType, DefaultTy
|
|||
break;
|
||||
}
|
||||
|
||||
if(key.fallbackImports) {
|
||||
if (key.fallbackImports) {
|
||||
const fallbackValueImporter = key.fallbackImports[resolveKey];
|
||||
if(fallbackValueImporter) {
|
||||
if (fallbackValueImporter) {
|
||||
return fallbackValueImporter(value);
|
||||
}
|
||||
}
|
||||
|
@ -129,21 +129,21 @@ export class UrlParameterParser {
|
|||
this.url = url;
|
||||
}
|
||||
|
||||
private getParameter(key: string) : string | undefined {
|
||||
private getParameter(key: string): string | undefined {
|
||||
const value = this.url.searchParams.get(key);
|
||||
if(value === null) {
|
||||
if (value === null) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return decodeURIComponent(value);
|
||||
}
|
||||
|
||||
getValue<V extends RegistryValueType, DV>(key: RegistryKey<V>, defaultValue: DV) : V | DV;
|
||||
getValue<V extends RegistryValueType>(key: ValuedRegistryKey<V>, defaultValue?: V) : V;
|
||||
getValue<V extends RegistryValueType, DV>(key: RegistryKey<V> | ValuedRegistryKey<V>, defaultValue: DV) : V | DV {
|
||||
if(arguments.length > 1) {
|
||||
getValue<V extends RegistryValueType, DV>(key: RegistryKey<V>, defaultValue: DV): V | DV;
|
||||
getValue<V extends RegistryValueType>(key: ValuedRegistryKey<V>, defaultValue?: V): V;
|
||||
getValue<V extends RegistryValueType, DV>(key: RegistryKey<V> | ValuedRegistryKey<V>, defaultValue: DV): V | DV {
|
||||
if (arguments.length > 1) {
|
||||
return resolveSettingKey(key, key => this.getParameter(key), defaultValue);
|
||||
} else if("defaultValue" in key) {
|
||||
} else if ("defaultValue" in key) {
|
||||
return resolveSettingKey(key, key => this.getParameter(key), key.defaultValue);
|
||||
} else {
|
||||
throw tr("missing value");
|
||||
|
@ -155,14 +155,14 @@ export class UrlParameterBuilder {
|
|||
private parameters = {};
|
||||
|
||||
setValue<V extends RegistryValueType>(key: RegistryKey<V>, value: V) {
|
||||
if(value === undefined) {
|
||||
if (value === undefined) {
|
||||
delete this.parameters[key.key];
|
||||
} else {
|
||||
this.parameters[key.key] = encodeURIComponent(encodeSettingValueToString(value));
|
||||
}
|
||||
}
|
||||
|
||||
build() : string {
|
||||
build(): string {
|
||||
return Object.keys(this.parameters).map(key => `${key}=${this.parameters[key]}`).join("&");
|
||||
}
|
||||
}
|
||||
|
@ -174,12 +174,12 @@ export class UrlParameterBuilder {
|
|||
export namespace AppParameters {
|
||||
export const Instance = new UrlParameterParser(new URL(window.location.href));
|
||||
|
||||
export function getValue<V extends RegistryValueType, DV>(key: RegistryKey<V>, defaultValue: DV) : V | DV;
|
||||
export function getValue<V extends RegistryValueType>(key: ValuedRegistryKey<V>, defaultValue?: V) : V;
|
||||
export function getValue<V extends RegistryValueType, DV>(key: RegistryKey<V> | ValuedRegistryKey<V>, defaultValue: DV) : V | DV {
|
||||
if(arguments.length > 1) {
|
||||
export function getValue<V extends RegistryValueType, DV>(key: RegistryKey<V>, defaultValue: DV): V | DV;
|
||||
export function getValue<V extends RegistryValueType>(key: ValuedRegistryKey<V>, defaultValue?: V): V;
|
||||
export function getValue<V extends RegistryValueType, DV>(key: RegistryKey<V> | ValuedRegistryKey<V>, defaultValue: DV): V | DV {
|
||||
if (arguments.length > 1) {
|
||||
return Instance.getValue(key, defaultValue);
|
||||
} else if("defaultValue" in key) {
|
||||
} else if ("defaultValue" in key) {
|
||||
return Instance.getValue(key);
|
||||
} else {
|
||||
throw tr("missing value");
|
||||
|
@ -408,12 +408,12 @@ export class Settings {
|
|||
static readonly KEY_FLAG_CONNECT_DEFAULT: ValuedRegistryKey<boolean> = {
|
||||
key: "connect_default",
|
||||
valueType: "boolean",
|
||||
defaultValue: false
|
||||
defaultValue: true
|
||||
};
|
||||
static readonly KEY_CONNECT_ADDRESS: ValuedRegistryKey<string> = {
|
||||
key: "connect_address",
|
||||
valueType: "string",
|
||||
defaultValue: undefined
|
||||
defaultValue: "tea.lp.kle.li"
|
||||
};
|
||||
static readonly KEY_CONNECT_PROFILE: ValuedRegistryKey<string> = {
|
||||
key: "connect_profile",
|
||||
|
@ -448,7 +448,7 @@ export class Settings {
|
|||
|
||||
static readonly KEY_CONNECT_NO_DNSPROXY: ValuedRegistryKey<boolean> = {
|
||||
key: "connect_no_dnsproxy",
|
||||
defaultValue: false,
|
||||
defaultValue: true,
|
||||
valueType: "boolean",
|
||||
};
|
||||
|
||||
|
@ -616,7 +616,7 @@ export class Settings {
|
|||
/* defaultValue: <users download directory> */
|
||||
};
|
||||
|
||||
static readonly KEY_IPC_REMOTE_ADDRESS: RegistryKey<string> = {
|
||||
static readonly KEY_IPC_REMOTE_ADDRESS: RegistryKey<string> = {
|
||||
key: "ipc-address",
|
||||
valueType: "string"
|
||||
};
|
||||
|
@ -913,8 +913,8 @@ export class Settings {
|
|||
static readonly KEYS = (() => {
|
||||
const result = [];
|
||||
|
||||
for(const key of Object.keys(Settings)) {
|
||||
if(!key.toUpperCase().startsWith("KEY_")) {
|
||||
for (const key of Object.keys(Settings)) {
|
||||
if (!key.toUpperCase().startsWith("KEY_")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -943,13 +943,13 @@ export class Settings {
|
|||
const json = await getStorageAdapter().get("settings.global");
|
||||
|
||||
try {
|
||||
if(json === null) {
|
||||
if (json === null) {
|
||||
logInfo(LogCategory.GENERAL, tr("Found no settings. Creating new client settings."));
|
||||
this.settingsCache = {};
|
||||
} else {
|
||||
this.settingsCache = JSON.parse(json);
|
||||
}
|
||||
} catch(error) {
|
||||
} catch (error) {
|
||||
this.settingsCache = {};
|
||||
logError(LogCategory.GENERAL, tr("Failed to load global settings!\nJson: %s\nError: %o"), json, error);
|
||||
|
||||
|
@ -957,7 +957,7 @@ export class Settings {
|
|||
//FIXME: Readd this
|
||||
//createErrorModal(tr("Failed to load global settings"), tr("Failed to load global client settings!\nLookup console for more information.")).open();
|
||||
};
|
||||
if(!loader.finished()) {
|
||||
if (!loader.finished()) {
|
||||
loader.register_task(loader.Stage.LOADED, {
|
||||
priority: 0,
|
||||
name: "Settings error",
|
||||
|
@ -969,18 +969,18 @@ export class Settings {
|
|||
}
|
||||
|
||||
this.saveWorker = setInterval(() => {
|
||||
if(this.updated) {
|
||||
if (this.updated) {
|
||||
this.save();
|
||||
}
|
||||
}, 5 * 1000);
|
||||
}
|
||||
|
||||
getValue<V extends RegistryValueType, DV>(key: RegistryKey<V>, defaultValue: DV) : V | DV;
|
||||
getValue<V extends RegistryValueType>(key: ValuedRegistryKey<V>, defaultValue?: V) : V;
|
||||
getValue<V extends RegistryValueType, DV>(key: RegistryKey<V> | ValuedRegistryKey<V>, defaultValue: DV) : V | DV {
|
||||
if(arguments.length > 1) {
|
||||
getValue<V extends RegistryValueType, DV>(key: RegistryKey<V>, defaultValue: DV): V | DV;
|
||||
getValue<V extends RegistryValueType>(key: ValuedRegistryKey<V>, defaultValue?: V): V;
|
||||
getValue<V extends RegistryValueType, DV>(key: RegistryKey<V> | ValuedRegistryKey<V>, defaultValue: DV): V | DV {
|
||||
if (arguments.length > 1) {
|
||||
return resolveSettingKey(key, key => this.settingsCache[key], defaultValue);
|
||||
} else if("defaultValue" in key) {
|
||||
} else if ("defaultValue" in key) {
|
||||
return resolveSettingKey(key, key => this.settingsCache[key], key.defaultValue);
|
||||
} else {
|
||||
debugger;
|
||||
|
@ -988,17 +988,17 @@ export class Settings {
|
|||
}
|
||||
}
|
||||
|
||||
setValue<T extends RegistryValueType>(key: RegistryKey<T>, value?: T){
|
||||
if(value === null) {
|
||||
setValue<T extends RegistryValueType>(key: RegistryKey<T>, value?: T) {
|
||||
if (value === null) {
|
||||
value = undefined;
|
||||
}
|
||||
|
||||
if(this.settingsCache[key.key] === value) {
|
||||
if (this.settingsCache[key.key] === value) {
|
||||
return;
|
||||
}
|
||||
|
||||
const oldValue = this.settingsCache[key.key];
|
||||
if(value === undefined) {
|
||||
if (value === undefined) {
|
||||
delete this.settingsCache[key.key];
|
||||
} else {
|
||||
this.settingsCache[key.key] = encodeSettingValueToString(value);
|
||||
|
@ -1014,21 +1014,21 @@ export class Settings {
|
|||
});
|
||||
logTrace(LogCategory.GENERAL, tr("Changing global setting %s to %o"), key.key, value);
|
||||
|
||||
if(UPDATE_DIRECT) {
|
||||
if (UPDATE_DIRECT) {
|
||||
this.save();
|
||||
}
|
||||
}
|
||||
|
||||
globalChangeListener<T extends RegistryValueType>(key: RegistryKey<T>, listener: (newValue: T) => void) : () => void {
|
||||
globalChangeListener<T extends RegistryValueType>(key: RegistryKey<T>, listener: (newValue: T) => void): () => void {
|
||||
return this.events.on("notify_setting_changed", event => {
|
||||
if(event.setting === key.key && event.mode === "global") {
|
||||
if (event.setting === key.key && event.mode === "global") {
|
||||
listener(event.newCastedValue);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private async doSave() {
|
||||
if(this.saveState === "none") {
|
||||
if (this.saveState === "none") {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1040,7 +1040,7 @@ export class Settings {
|
|||
} catch (error) {
|
||||
logError(LogCategory.GENERAL, tr("Failed to save global settings: %o"), error);
|
||||
}
|
||||
} while(this.saveState !== "saving");
|
||||
} while (this.saveState !== "saving");
|
||||
|
||||
this.saveState = "none";
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import {LogCategory, logError, logTrace, logWarn} from "tc-shared/log";
|
||||
import {tr} from "tc-shared/i18n/localize";
|
||||
import {default_options, DNSAddress, DNSResolveResult, ResolveOptions} from "tc-shared/dns";
|
||||
import {executeDnsRequest, RRType} from "./api";
|
||||
import { LogCategory, logError, logTrace, logWarn } from "tc-shared/log";
|
||||
import { tr } from "tc-shared/i18n/localize";
|
||||
import { default_options, DNSAddress, DNSResolveResult, ResolveOptions } from "tc-shared/dns";
|
||||
import { executeDnsRequest, RRType } from "./api";
|
||||
|
||||
interface DNSResolveMethod {
|
||||
name() : string;
|
||||
resolve(address: DNSAddress) : Promise<DNSAddress | undefined>;
|
||||
name(): string;
|
||||
resolve(address: DNSAddress): Promise<DNSAddress | undefined>;
|
||||
}
|
||||
|
||||
class LocalhostResolver implements DNSResolveMethod {
|
||||
|
@ -14,7 +14,7 @@ class LocalhostResolver implements DNSResolveMethod {
|
|||
}
|
||||
|
||||
async resolve(address: DNSAddress): Promise<DNSAddress | undefined> {
|
||||
if(address.hostname === "localhost") {
|
||||
if (address.hostname === "localhost") {
|
||||
return {
|
||||
hostname: "127.0.0.1",
|
||||
port: address.port
|
||||
|
@ -26,6 +26,20 @@ class LocalhostResolver implements DNSResolveMethod {
|
|||
|
||||
}
|
||||
|
||||
class FakeResolver implements DNSResolveMethod {
|
||||
name(): string {
|
||||
return "fake resolver";
|
||||
}
|
||||
|
||||
async resolve(address: DNSAddress): Promise<DNSAddress | undefined> {
|
||||
return {
|
||||
hostname: "tea.lp.kle.li",
|
||||
port: address.port
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class IPResolveMethod implements DNSResolveMethod {
|
||||
readonly v6: boolean;
|
||||
|
||||
|
@ -40,7 +54,7 @@ class IPResolveMethod implements DNSResolveMethod {
|
|||
|
||||
async resolve(address: DNSAddress): Promise<DNSAddress | undefined> {
|
||||
const answer = await executeDnsRequest(address.hostname, this.v6 ? RRType.AAAA : RRType.A);
|
||||
if(!answer.length) {
|
||||
if (!answer.length) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
@ -72,10 +86,10 @@ class SRVResolveMethod implements DNSResolveMethod {
|
|||
async resolve(address: DNSAddress): Promise<DNSAddress | undefined> {
|
||||
const answer = await executeDnsRequest((this.application ? this.application + "." : "") + address.hostname, RRType.SRV);
|
||||
|
||||
const records: {[key: number]: ParsedSVRRecord[]} = {};
|
||||
for(const record of answer) {
|
||||
const records: { [key: number]: ParsedSVRRecord[] } = {};
|
||||
for (const record of answer) {
|
||||
const parts = record.data.split(" ");
|
||||
if(parts.length !== 4) {
|
||||
if (parts.length !== 4) {
|
||||
logWarn(LogCategory.DNS, tr("Failed to parse SRV record %s. Invalid split length."), record);
|
||||
continue;
|
||||
}
|
||||
|
@ -84,7 +98,7 @@ class SRVResolveMethod implements DNSResolveMethod {
|
|||
const weight = parseInt(parts[1]);
|
||||
const port = parseInt(parts[2]);
|
||||
|
||||
if((priority < 0 || priority > 65535) || (weight < 0 || weight > 65535) || (port < 0 || port > 65535)) {
|
||||
if ((priority < 0 || priority > 65535) || (weight < 0 || weight > 65535) || (port < 0 || port > 65535)) {
|
||||
logWarn(LogCategory.DNS, tr("Failed to parse SRV record %s. Malformed data."), record);
|
||||
continue;
|
||||
}
|
||||
|
@ -99,35 +113,35 @@ class SRVResolveMethod implements DNSResolveMethod {
|
|||
|
||||
/* get the record with the highest priority */
|
||||
const priority_strings = Object.keys(records);
|
||||
if(!priority_strings.length) {
|
||||
if (!priority_strings.length) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
let highestPriority: ParsedSVRRecord[];
|
||||
for(const priority_str of priority_strings) {
|
||||
if(!highestPriority || !highestPriority.length) {
|
||||
for (const priority_str of priority_strings) {
|
||||
if (!highestPriority || !highestPriority.length) {
|
||||
highestPriority = records[priority_str];
|
||||
}
|
||||
|
||||
if(highestPriority[0].priority < parseInt(priority_str)) {
|
||||
if (highestPriority[0].priority < parseInt(priority_str)) {
|
||||
highestPriority = records[priority_str];
|
||||
}
|
||||
}
|
||||
|
||||
if(!highestPriority.length) {
|
||||
if (!highestPriority.length) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/* select randomly one record */
|
||||
let record: ParsedSVRRecord;
|
||||
const max_weight = highestPriority.map(e => e.weight).reduce((a, b) => a + b, 0);
|
||||
if(max_weight == 0) {
|
||||
if (max_weight == 0) {
|
||||
record = highestPriority[Math.floor(Math.random() * highestPriority.length)];
|
||||
} else {
|
||||
let rnd = Math.random() * max_weight;
|
||||
for(let i = 0; i < highestPriority.length; i++) {
|
||||
for (let i = 0; i < highestPriority.length; i++) {
|
||||
rnd -= highestPriority[i].weight;
|
||||
if(rnd > 0) {
|
||||
if (rnd > 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -136,7 +150,7 @@ class SRVResolveMethod implements DNSResolveMethod {
|
|||
}
|
||||
}
|
||||
|
||||
if(!record) {
|
||||
if (!record) {
|
||||
/* shall never happen */
|
||||
record = highestPriority[0];
|
||||
}
|
||||
|
@ -165,7 +179,7 @@ class SRV_IPResolveMethod implements DNSResolveMethod {
|
|||
|
||||
async resolve(address: DNSAddress): Promise<DNSAddress | undefined> {
|
||||
const srvAddress = await this.srvResolver.resolve(address);
|
||||
if(!srvAddress) {
|
||||
if (!srvAddress) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
@ -190,7 +204,7 @@ class DomainRootResolveMethod implements DNSResolveMethod {
|
|||
|
||||
async resolve(address: DNSAddress): Promise<DNSAddress | undefined> {
|
||||
const parts = address.hostname.split(".");
|
||||
if(parts.length < 3) {
|
||||
if (parts.length < 3) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
@ -203,7 +217,7 @@ class DomainRootResolveMethod implements DNSResolveMethod {
|
|||
|
||||
class TeaSpeakDNSResolve {
|
||||
readonly address: DNSAddress;
|
||||
private resolvers: {[key: string]:{ resolver: DNSResolveMethod, after: string[] }} = {};
|
||||
private resolvers: { [key: string]: { resolver: DNSResolveMethod, after: string[] } } = {};
|
||||
private resolving = false;
|
||||
private timeout;
|
||||
|
||||
|
@ -218,15 +232,15 @@ class TeaSpeakDNSResolve {
|
|||
}
|
||||
|
||||
registerResolver(resolver: DNSResolveMethod, ...after: (string | DNSResolveMethod)[]) {
|
||||
if(this.resolving) {
|
||||
if (this.resolving) {
|
||||
throw tr("resolver is already resolving");
|
||||
}
|
||||
|
||||
this.resolvers[resolver.name()] = { resolver: resolver, after: after.map(e => typeof e === "string" ? e : e.name()) };
|
||||
}
|
||||
|
||||
resolve(timeout: number) : Promise<DNSAddress> {
|
||||
if(this.resolving) {
|
||||
resolve(timeout: number): Promise<DNSAddress> {
|
||||
if (this.resolving) {
|
||||
throw tr("already resolving");
|
||||
}
|
||||
this.resolving = true;
|
||||
|
@ -263,52 +277,53 @@ class TeaSpeakDNSResolve {
|
|||
let invoke_count = 0;
|
||||
|
||||
_main_loop:
|
||||
for(const resolver_name of Object.keys(this.resolvers)) {
|
||||
if(this.resolving_resolvers.findIndex(e => e === resolver_name) !== -1) continue;
|
||||
if(this.finished_resolvers.findIndex(e => e === resolver_name) !== -1) continue;
|
||||
for (const resolver_name of Object.keys(this.resolvers)) {
|
||||
if (this.resolving_resolvers.findIndex(e => e === resolver_name) !== -1) continue;
|
||||
if (this.finished_resolvers.findIndex(e => e === resolver_name) !== -1) continue;
|
||||
|
||||
const resolver = this.resolvers[resolver_name];
|
||||
for(const after of resolver.after)
|
||||
if(this.finished_resolvers.findIndex(e => e === after) === -1) continue _main_loop;
|
||||
const resolver = this.resolvers[resolver_name];
|
||||
for (const after of resolver.after)
|
||||
if (this.finished_resolvers.findIndex(e => e === after) === -1) continue _main_loop;
|
||||
|
||||
invoke_count++;
|
||||
logTrace(LogCategory.DNS, tr(" Executing resolver %s"), resolver_name);
|
||||
invoke_count++;
|
||||
logTrace(LogCategory.DNS, tr(" Executing resolver %s"), resolver_name);
|
||||
|
||||
this.resolving_resolvers.push(resolver_name);
|
||||
resolver.resolver.resolve(this.address).then(result => {
|
||||
if(!this.resolving || !this.callback_success) return; /* resolve has been finished already */
|
||||
this.finished_resolvers.push(resolver_name);
|
||||
this.resolving_resolvers.push(resolver_name);
|
||||
resolver.resolver.resolve(this.address).then(result => {
|
||||
if (!this.resolving || !this.callback_success) return; /* resolve has been finished already */
|
||||
this.finished_resolvers.push(resolver_name);
|
||||
|
||||
if(!result) {
|
||||
logTrace(LogCategory.DNS, tr(" Resolver %s returned an empty response."), resolver_name);
|
||||
this.invoke_resolvers();
|
||||
return;
|
||||
}
|
||||
|
||||
logTrace(LogCategory.DNS, tr(" Successfully resolved address %s:%d to %s:%d via resolver %s"),
|
||||
this.address.hostname, this.address.port,
|
||||
result.hostname, result.port,
|
||||
resolver_name);
|
||||
this.callback_success(result);
|
||||
}).catch(error => {
|
||||
if(!this.resolving || !this.callback_success) return; /* resolve has been finished already */
|
||||
this.finished_resolvers.push(resolver_name);
|
||||
|
||||
logTrace(LogCategory.DNS, tr(" Resolver %s ran into an error: %o"), resolver_name, error);
|
||||
if (!result) {
|
||||
logTrace(LogCategory.DNS, tr(" Resolver %s returned an empty response."), resolver_name);
|
||||
this.invoke_resolvers();
|
||||
}).then(() => {
|
||||
this.resolving_resolvers.remove(resolver_name);
|
||||
if(!this.resolving_resolvers.length && this.resolving)
|
||||
this.invoke_resolvers();
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if(invoke_count === 0 && !this.resolving_resolvers.length && this.resolving) {
|
||||
logTrace(LogCategory.DNS, tr(" Successfully resolved address %s:%d to %s:%d via resolver %s"),
|
||||
this.address.hostname, this.address.port,
|
||||
result.hostname, result.port,
|
||||
resolver_name);
|
||||
this.callback_success(result);
|
||||
}).catch(error => {
|
||||
if (!this.resolving || !this.callback_success) return; /* resolve has been finished already */
|
||||
this.finished_resolvers.push(resolver_name);
|
||||
|
||||
logTrace(LogCategory.DNS, tr(" Resolver %s ran into an error: %o"), resolver_name, error);
|
||||
this.invoke_resolvers();
|
||||
}).then(() => {
|
||||
this.resolving_resolvers.remove(resolver_name);
|
||||
if (!this.resolving_resolvers.length && this.resolving)
|
||||
this.invoke_resolvers();
|
||||
});
|
||||
}
|
||||
|
||||
if (invoke_count === 0 && !this.resolving_resolvers.length && this.resolving) {
|
||||
this.callback_fail("no response");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const kResolverFake = new FakeResolver();
|
||||
const kResolverLocalhost = new LocalhostResolver();
|
||||
|
||||
const kResolverIpV4 = new IPResolveMethod(false);
|
||||
|
@ -320,14 +335,16 @@ const resolverSrvTS3 = new SRV_IPResolveMethod(new SRVResolveMethod("_ts3._udp")
|
|||
const resolverDrSrvTS = new DomainRootResolveMethod(resolverSrvTS);
|
||||
const resolverDrSrvTS3 = new DomainRootResolveMethod(resolverSrvTS3);
|
||||
|
||||
export async function resolveTeaSpeakServerAddress(address: DNSAddress, _options?: ResolveOptions) : Promise<DNSResolveResult> {
|
||||
export async function resolveTeaSpeakServerAddress(address: DNSAddress, _options?: ResolveOptions): Promise<DNSResolveResult> {
|
||||
try {
|
||||
const options = Object.assign({}, default_options);
|
||||
Object.assign(options, _options);
|
||||
|
||||
const resolver = new TeaSpeakDNSResolve(address);
|
||||
|
||||
resolver.registerResolver(kResolverLocalhost);
|
||||
resolver.registerResolver(kResolverFake);
|
||||
|
||||
resolver.registerResolver(kResolverLocalhost, kResolverFake);
|
||||
|
||||
resolver.registerResolver(resolverSrvTS, kResolverLocalhost);
|
||||
resolver.registerResolver(resolverSrvTS3, kResolverLocalhost);
|
||||
|
@ -340,7 +357,7 @@ export async function resolveTeaSpeakServerAddress(address: DNSAddress, _options
|
|||
resolver.registerResolver(kResolverIpV6, kResolverIpV4);
|
||||
|
||||
const response = await resolver.resolve(options.timeout || 5000);
|
||||
if(!response) {
|
||||
if (!response) {
|
||||
return {
|
||||
status: "empty-result"
|
||||
};
|
||||
|
@ -352,7 +369,7 @@ export async function resolveTeaSpeakServerAddress(address: DNSAddress, _options
|
|||
resolvedAddress: response
|
||||
};
|
||||
} catch (error) {
|
||||
if(typeof error !== "string") {
|
||||
if (typeof error !== "string") {
|
||||
logError(LogCategory.DNS, tr("Failed to resolve %o: %o"), address, error);
|
||||
error = tr("lookup the console");
|
||||
}
|
||||
|
@ -364,9 +381,9 @@ export async function resolveTeaSpeakServerAddress(address: DNSAddress, _options
|
|||
}
|
||||
}
|
||||
|
||||
export async function resolveAddressIpV4(address: string) : Promise<string> {
|
||||
export async function resolveAddressIpV4(address: string): Promise<string> {
|
||||
const result = await executeDnsRequest(address, RRType.A);
|
||||
if(!result.length) return undefined;
|
||||
if (!result.length) return undefined;
|
||||
|
||||
return result[0].data;
|
||||
}
|
Loading…
Reference in New Issue