Pull request: beta client squashed
Merge in DNS/adguard-home from beta-client-2 to master
Squashed commit of the following:
commit b2640cc49a6c5484d730b534dcf5a8013d7fa478
Merge: 659def862 aef4659e9
Author: Eugene Burkov <e.burkov@adguard.com>
Date: Tue Dec 29 19:23:09 2020 +0300
Merge branch 'master' into beta-client-2
commit 659def8626467949c35b7a6a0c99ffafb07b4385
Author: Eugene Burkov <e.burkov@adguard.com>
Date: Tue Dec 29 17:25:14 2020 +0300
all: upgrade github actions node version
commit b4b8cf8dd75672e9155da5d111ac66e8f5ba1535
Author: Vladislav Abdulmyanov <v.abdulmyanov@adguard.com>
Date: Tue Dec 29 16:57:14 2020 +0300
all: beta client squashed
This commit is contained in:
parent
aef4659e93
commit
5e20ac7ed5
|
@ -9,8 +9,10 @@
|
||||||
/AdGuardHome.log
|
/AdGuardHome.log
|
||||||
/data
|
/data
|
||||||
/build
|
/build
|
||||||
|
/build2
|
||||||
/dist
|
/dist
|
||||||
/client/node_modules
|
/client/node_modules
|
||||||
|
/client2/node_modules
|
||||||
/.gitattributes
|
/.gitattributes
|
||||||
/.gitignore
|
/.gitignore
|
||||||
/.goreleaser.yml
|
/.goreleaser.yml
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
'env':
|
'env':
|
||||||
'GO_VERSION': '1.14'
|
'GO_VERSION': '1.14'
|
||||||
'NODE_VERSION': '13'
|
'NODE_VERSION': '14'
|
||||||
|
|
||||||
'on':
|
'on':
|
||||||
'push':
|
'push':
|
||||||
|
|
|
@ -7,9 +7,11 @@
|
||||||
# Only build, run, and test outputs here. Sorted.
|
# Only build, run, and test outputs here. Sorted.
|
||||||
*-packr.go
|
*-packr.go
|
||||||
*.db
|
*.db
|
||||||
|
*.log
|
||||||
*.snap
|
*.snap
|
||||||
/bin/
|
/bin/
|
||||||
/build/
|
/build/
|
||||||
|
/build2/
|
||||||
/data/
|
/data/
|
||||||
/dist/
|
/dist/
|
||||||
/dnsfilter/tests/dnsfilter.TestLotsOfRules*.pprof
|
/dnsfilter/tests/dnsfilter.TestLotsOfRules*.pprof
|
||||||
|
@ -19,4 +21,5 @@
|
||||||
/snapcraft_login
|
/snapcraft_login
|
||||||
AdGuardHome*
|
AdGuardHome*
|
||||||
coverage.txt
|
coverage.txt
|
||||||
|
leases.db
|
||||||
node_modules/
|
node_modules/
|
||||||
|
|
|
@ -18,6 +18,7 @@ RUN apk --update --no-cache add \
|
||||||
gcc \
|
gcc \
|
||||||
git \
|
git \
|
||||||
npm \
|
npm \
|
||||||
|
yarn \
|
||||||
&& rm -rf /tmp/* /var/cache/apk/*
|
&& rm -rf /tmp/* /var/cache/apk/*
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
@ -26,6 +27,7 @@ COPY . ./
|
||||||
|
|
||||||
# Prepare the client code
|
# Prepare the client code
|
||||||
RUN npm --prefix client ci && npm --prefix client run build-prod
|
RUN npm --prefix client ci && npm --prefix client run build-prod
|
||||||
|
RUN yarn --cwd client2 build
|
||||||
|
|
||||||
# Download go dependencies
|
# Download go dependencies
|
||||||
RUN go mod download
|
RUN go mod download
|
||||||
|
|
5
Makefile
5
Makefile
|
@ -137,13 +137,16 @@ build:
|
||||||
|
|
||||||
client:
|
client:
|
||||||
npm --prefix client run build-prod
|
npm --prefix client run build-prod
|
||||||
|
yarn --cwd client2 build
|
||||||
|
|
||||||
client_with_deps:
|
client_with_deps:
|
||||||
npm --prefix client ci
|
npm --prefix client ci
|
||||||
npm --prefix client run build-prod
|
npm --prefix client run build-prod
|
||||||
|
yarn --cwd client2 build
|
||||||
|
|
||||||
client-watch:
|
client-watch:
|
||||||
npm --prefix client run watch
|
npm --prefix client run watch
|
||||||
|
yarn --cwd client2 start
|
||||||
|
|
||||||
docker:
|
docker:
|
||||||
DOCKER_CLI_EXPERIMENTAL=enabled \
|
DOCKER_CLI_EXPERIMENTAL=enabled \
|
||||||
|
@ -163,6 +166,7 @@ lint: js-lint go-lint
|
||||||
|
|
||||||
js-lint: dependencies
|
js-lint: dependencies
|
||||||
npm --prefix client run lint
|
npm --prefix client run lint
|
||||||
|
yarn --cwd client2 lint
|
||||||
|
|
||||||
go-install-tools:
|
go-install-tools:
|
||||||
env GO=$(GO) sh ./scripts/go-install-tools.sh
|
env GO=$(GO) sh ./scripts/go-install-tools.sh
|
||||||
|
@ -184,6 +188,7 @@ ci: client_with_deps
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
npm --prefix client ci
|
npm --prefix client ci
|
||||||
|
yarn --cwd client2 install
|
||||||
$(GO) mod download
|
$(GO) mod download
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
scripts
|
||||||
|
node_modules
|
||||||
|
postcss.config.js
|
||||||
|
src/lib/entities
|
||||||
|
src/lib/apis
|
||||||
|
openApi
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"extends": [
|
||||||
|
"./scripts/lint/dev.js"
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
declare module '*.pcss' {
|
||||||
|
const content: {[className: string]: string};
|
||||||
|
export default content;
|
||||||
|
}
|
||||||
|
declare module '*.css' {
|
||||||
|
const content: {[className: string]: string};
|
||||||
|
export default content;
|
||||||
|
}
|
||||||
|
declare module '*.png'
|
||||||
|
declare module '*.jpg'
|
||||||
|
declare let AUTH_TOKEN: string;
|
||||||
|
declare let MAIN_TOKEN: string | undefined;
|
||||||
|
declare let NO_CAPTCHA: boolean | undefined;
|
||||||
|
declare module 'dygraphs';
|
||||||
|
declare module '@novnc/novnc/core/rfb';
|
||||||
|
// cp - CloudPayments script
|
||||||
|
declare let cp: any;
|
||||||
|
declare const DEV: any;
|
|
@ -0,0 +1,92 @@
|
||||||
|
{
|
||||||
|
"author": "Performix",
|
||||||
|
"private": true,
|
||||||
|
"name": "adguard-home",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"scripts": {
|
||||||
|
"build": "rm -rf ../build2 && yarn install && webpack --config ./scripts/webpack/webpack.config.prod.js",
|
||||||
|
"start": "webpack serve --config ./scripts/webpack/webpack.config.dev.js",
|
||||||
|
"generate": "rm -rf ./src/lib/entities ./src/lib/apis && ts-node --compiler-options '{ \"module\": \"CommonJS\" }' ./scripts/generator/index.ts",
|
||||||
|
"translations:check": "ts-node --compiler-options '{ \"module\": \"CommonJS\" }' ./scripts/plugins/checkTranslations.ts",
|
||||||
|
"lint": "eslint -c ./scripts/lint/prod.js --ext .tsx --ext .ts ./",
|
||||||
|
"go:build": "cd .. && make REBUILD_CLIENT=0 build",
|
||||||
|
"go:run": "sudo ../AdguardHome"
|
||||||
|
},
|
||||||
|
"husky": {
|
||||||
|
"hooks": {
|
||||||
|
"pre-commit": "yarn lint"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"@sentry/react": "^5.27.0",
|
||||||
|
"antd": "^4.7.2",
|
||||||
|
"classnames": "^2.2.6",
|
||||||
|
"dayjs": "^1.9.3",
|
||||||
|
"formik": "^2.2.0",
|
||||||
|
"mobx": "^6.0.1",
|
||||||
|
"mobx-react-lite": "^3.0.1",
|
||||||
|
"qs": "^6.9.4",
|
||||||
|
"react": "^17.0.0",
|
||||||
|
"react-dom": "^17.0.0",
|
||||||
|
"react-router-dom": "^5.2.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/classnames": "^2.2.10",
|
||||||
|
"@types/qs": "^6.9.5",
|
||||||
|
"@types/react": "^16.9.53",
|
||||||
|
"@types/react-dom": "^16.9.8",
|
||||||
|
"@types/react-redux": "^7.1.9",
|
||||||
|
"@types/react-router-dom": "^5.1.6",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^4.5.0",
|
||||||
|
"@typescript-eslint/parser": "^4.5.0",
|
||||||
|
"antd-dayjs-webpack-plugin": "^1.0.1",
|
||||||
|
"autoprefixer": "^10.0.1",
|
||||||
|
"connect-history-api-fallback": "^1.6.0",
|
||||||
|
"copy-webpack-plugin": "^6.2.1",
|
||||||
|
"css-loader": "^5.0.0",
|
||||||
|
"eslint": "^7.11.0",
|
||||||
|
"eslint-config-airbnb-base": "^14.2.0",
|
||||||
|
"eslint-config-airbnb-typescript": "^12.0.0",
|
||||||
|
"eslint-import-resolver-typescript": "^2.3.0",
|
||||||
|
"eslint-loader": "^4.0.2",
|
||||||
|
"eslint-plugin-import": "^2.22.1",
|
||||||
|
"eslint-plugin-react": "^7.21.5",
|
||||||
|
"eslint-plugin-react-hooks": "^4.2.0",
|
||||||
|
"file-loader": "^6.1.1",
|
||||||
|
"html-webpack-plugin": "^4.5.0",
|
||||||
|
"http-proxy-middleware": "^1.0.6",
|
||||||
|
"husky": "^4.3.0",
|
||||||
|
"less": "^3.12.2",
|
||||||
|
"less-loader": "^5.0.0",
|
||||||
|
"mini-css-extract-plugin": "^1.1.1",
|
||||||
|
"optimize-css-assets-webpack-plugin": "^5.0.4",
|
||||||
|
"postcss": "^8.1.2",
|
||||||
|
"postcss-calc": "^7.0.5",
|
||||||
|
"postcss-css-variables": "^0.17.0",
|
||||||
|
"postcss-custom-media": "^7.0.8",
|
||||||
|
"postcss-import": "^13.0.0",
|
||||||
|
"postcss-inline-svg": "^4.1.0",
|
||||||
|
"postcss-loader": "^4.0.4",
|
||||||
|
"postcss-mixins": "^7.0.1",
|
||||||
|
"postcss-modules": "^3.2.2",
|
||||||
|
"postcss-nested": "^5.0.1",
|
||||||
|
"postcss-preset-env": "^6.7.0",
|
||||||
|
"postcss-reporter": "^7.0.1",
|
||||||
|
"postcss-variables": "^1.1.1",
|
||||||
|
"style-loader": "^2.0.0",
|
||||||
|
"stylelint": "^13.7.2",
|
||||||
|
"stylelint-webpack-plugin": "^2.1.1",
|
||||||
|
"terser-webpack-plugin": "^5.0.0",
|
||||||
|
"ts-loader": "^8.0.6",
|
||||||
|
"ts-morph": "^8.1.2",
|
||||||
|
"ts-node": "^9.0.0",
|
||||||
|
"typescript": "^4.0.3",
|
||||||
|
"url-loader": "^4.1.1",
|
||||||
|
"webpack": "^5.10.0",
|
||||||
|
"webpack-cli": "^4.2.0",
|
||||||
|
"webpack-dev-server": "^3.11.0",
|
||||||
|
"webpack-merge": "^5.2.0",
|
||||||
|
"yaml": "^1.10.0"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
module.exports = {
|
||||||
|
plugins: [
|
||||||
|
['postcss-import', {}],
|
||||||
|
['postcss-nested', {}],
|
||||||
|
['postcss-custom-media', {}],
|
||||||
|
['postcss-variables', {}],
|
||||||
|
['postcss-calc', {}],
|
||||||
|
['postcss-mixins', {}],
|
||||||
|
['postcss-preset-env', { stage: 3, features: { 'nesting-rules': true } }],
|
||||||
|
['postcss-reporter', { clearMessages: true }],
|
||||||
|
['postcss-inline-svg', {
|
||||||
|
paths: ['frontend/icons', 'vendor/adguard/utils-bundle/src/Resources/frontend/icons'],
|
||||||
|
svgo: { plugins: [{ cleanupAttrs: true }] }
|
||||||
|
}],
|
||||||
|
['autoprefixer'],
|
||||||
|
]
|
||||||
|
};
|
Binary file not shown.
After Width: | Height: | Size: 2.1 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.3 KiB |
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16pt" height="16pt"
|
||||||
|
viewBox="0 0 16 16" version="1.1">
|
||||||
|
<g id="surface1">
|
||||||
|
<path style=" stroke:none;fill-rule:evenodd;fill:rgb(0%,0%,0%);fill-opacity:1;"
|
||||||
|
d="M 8 0 C 10.5 0 13.515625 0.574219 16 1.835938 L 15.996094 2.542969 C 15.957031 5.605469 15.410156 11.71875 8 16 C 0.5 11.667969 0.03125 5.460938 0.00390625 2.433594 L 0 1.835938 C 2.484375 0.574219 5.5 0 8 0 Z M 11.769531 4.203125 L 11.761719 4.203125 L 7.890625 8.160156 L 6.433594 6.4375 C 5.738281 5.644531 4.792969 6.25 4.570312 6.40625 L 7.929688 10.285156 L 12.570312 4.136719 C 12.230469 3.867188 11.933594 4.054688 11.769531 4.203125 Z M 11.769531 4.203125 "/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 801 B |
|
@ -0,0 +1,23 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, shrink-to-fit=no">
|
||||||
|
<meta name="theme-color" content="#000000">
|
||||||
|
<meta name="google" content="notranslate">
|
||||||
|
<meta http-equiv="x-dns-prefetch-control" content="off">
|
||||||
|
<meta name="mobile-web-app-capable" content="yes" />
|
||||||
|
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||||
|
<meta name="apple-mobile-web-app-status-bar-style" content="default">
|
||||||
|
<link rel="apple-touch-icon" sizes="180x180" href="assets/apple-touch-icon-180x180.png" />
|
||||||
|
<link rel="mask-icon" href="assets/safari-pinned-tab.svg" color="#67B279">
|
||||||
|
<link rel="icon" type="image/png" href="assets/favicon.png" sizes="48x48">
|
||||||
|
<title>AdGuard Home</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<noscript>
|
||||||
|
You need to enable JavaScript to run this app.
|
||||||
|
</noscript>
|
||||||
|
<div id="app"></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,22 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, shrink-to-fit=no">
|
||||||
|
<meta name="theme-color" content="#000000">
|
||||||
|
<meta name="google" content="notranslate">
|
||||||
|
<meta name="mobile-web-app-capable" content="yes" />
|
||||||
|
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||||
|
<meta name="apple-mobile-web-app-status-bar-style" content="default">
|
||||||
|
<link rel="apple-touch-icon" sizes="180x180" href="assets/apple-touch-icon-180x180.png" />
|
||||||
|
<link rel="mask-icon" href="assets/safari-pinned-tab.svg" color="#67B279">
|
||||||
|
<link rel="icon" type="image/png" href="assets/favicon.png" sizes="48x48">
|
||||||
|
<title>Setup AdGuard Home</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<noscript>
|
||||||
|
You need to enable JavaScript to run this app.
|
||||||
|
</noscript>
|
||||||
|
<div id="app"></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,22 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, shrink-to-fit=no">
|
||||||
|
<meta name="theme-color" content="#000000">
|
||||||
|
<meta name="google" content="notranslate">
|
||||||
|
<link rel="apple-touch-icon" sizes="180x180" href="assets/apple-touch-icon-180x180.png" />
|
||||||
|
<meta name="mobile-web-app-capable" content="yes" />
|
||||||
|
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||||
|
<meta name="apple-mobile-web-app-status-bar-style" content="default">
|
||||||
|
<link rel="mask-icon" href="assets/safari-pinned-tab.svg" color="#67B279">
|
||||||
|
<link rel="icon" type="image/png" href="assets/favicon.png" sizes="48x48">
|
||||||
|
<title>Login</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<noscript>
|
||||||
|
You need to enable JavaScript to run this app.
|
||||||
|
</noscript>
|
||||||
|
<div id="app"></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,12 @@
|
||||||
|
export const OPEN_API_PATH = '../openapi/openapi.yaml';
|
||||||
|
export const ENT_DIR = './src/lib/entities';
|
||||||
|
export const API_DIR = './src/lib/apis';
|
||||||
|
export const LOCALE_FOLDER_PATH = './src/lib/intl/__locales';
|
||||||
|
export const TRANSLATOR_CLASS_NAME = 'Translator';
|
||||||
|
export const USE_INTL_NAME = 'useIntl';
|
||||||
|
|
||||||
|
export const trimQuotes = (str: string) => {
|
||||||
|
return str.replace(/\'|\"/g, '');
|
||||||
|
};
|
||||||
|
|
||||||
|
export const GENERATOR_ENTITY_ALLIAS = 'Entities/';
|
|
@ -0,0 +1,18 @@
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import * as YAML from 'yaml';
|
||||||
|
import { OPEN_API_PATH } from '../consts';
|
||||||
|
|
||||||
|
import EntitiesGenerator from './src/generateEntities';
|
||||||
|
import ApisGenerator from './src/generateApis';
|
||||||
|
|
||||||
|
|
||||||
|
const generateApi = (openApi: Record<string, any>) => {
|
||||||
|
const ent = new EntitiesGenerator(openApi);
|
||||||
|
ent.save();
|
||||||
|
|
||||||
|
const api = new ApisGenerator(openApi);
|
||||||
|
api.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
const openApiFile = fs.readFileSync(OPEN_API_PATH, 'utf8');
|
||||||
|
generateApi(YAML.parse(openApiFile));
|
|
@ -0,0 +1,317 @@
|
||||||
|
/* eslint-disable no-template-curly-in-string */
|
||||||
|
/* eslint-disable @typescript-eslint/no-unused-expressions */
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import * as path from 'path';
|
||||||
|
import { stringify } from 'qs';
|
||||||
|
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||||
|
import * as morph from 'ts-morph';
|
||||||
|
|
||||||
|
import {
|
||||||
|
API_DIR as API_DIR_CONST,
|
||||||
|
GENERATOR_ENTITY_ALLIAS,
|
||||||
|
} from '../../consts';
|
||||||
|
import { toCamel, capitalize, schemaParamParser } from './utils';
|
||||||
|
|
||||||
|
|
||||||
|
const API_DIR = path.resolve(API_DIR_CONST);
|
||||||
|
if (!fs.existsSync(API_DIR)) {
|
||||||
|
fs.mkdirSync(API_DIR);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { Project, QuoteKind } = morph;
|
||||||
|
|
||||||
|
|
||||||
|
class ApiGenerator {
|
||||||
|
project = new Project({
|
||||||
|
tsConfigFilePath: './tsconfig.json',
|
||||||
|
addFilesFromTsConfig: false,
|
||||||
|
manipulationSettings: {
|
||||||
|
quoteKind: QuoteKind.Single,
|
||||||
|
usePrefixAndSuffixTextForRename: false,
|
||||||
|
useTrailingCommas: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
openapi: Record<string, any>;
|
||||||
|
|
||||||
|
serverUrl: string;
|
||||||
|
|
||||||
|
paths: any;
|
||||||
|
|
||||||
|
/* interface Controllers {
|
||||||
|
[controller: string]: {
|
||||||
|
[operationId: string]: { parameters - from opneApi, responses - from opneApi, method }
|
||||||
|
}
|
||||||
|
} */
|
||||||
|
controllers: Record<string, any> = {};
|
||||||
|
|
||||||
|
apis: morph.SourceFile[] = [];
|
||||||
|
|
||||||
|
constructor(openapi: Record<string, any>) {
|
||||||
|
this.openapi = openapi;
|
||||||
|
this.paths = openapi.paths;
|
||||||
|
this.serverUrl = openapi.servers[0].url;
|
||||||
|
|
||||||
|
Object.keys(this.paths).forEach((pathKey) => {
|
||||||
|
Object.keys(this.paths[pathKey]).forEach((method) => {
|
||||||
|
const {
|
||||||
|
tags, operationId, parameters, responses, requestBody, security,
|
||||||
|
} = this.paths[pathKey][method];
|
||||||
|
const controller = toCamel((tags ? tags[0] : pathKey.split('/')[1]).replace('-controller', ''));
|
||||||
|
|
||||||
|
if (this.controllers[controller]) {
|
||||||
|
this.controllers[controller][operationId] = {
|
||||||
|
parameters,
|
||||||
|
responses,
|
||||||
|
method,
|
||||||
|
requestBody,
|
||||||
|
security,
|
||||||
|
pathKey: pathKey.replace(/{/g, '${'),
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
this.controllers[controller] = { [operationId]: {
|
||||||
|
parameters,
|
||||||
|
responses,
|
||||||
|
method,
|
||||||
|
requestBody,
|
||||||
|
security,
|
||||||
|
pathKey: pathKey.replace(/{/g, '${'),
|
||||||
|
} };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
this.generateApiFiles();
|
||||||
|
}
|
||||||
|
|
||||||
|
generateApiFiles = () => {
|
||||||
|
Object.keys(this.controllers).forEach(this.generateApiFile);
|
||||||
|
};
|
||||||
|
|
||||||
|
generateApiFile = (cName: string) => {
|
||||||
|
const apiFile = this.project.createSourceFile(`${API_DIR}/${cName}.ts`);
|
||||||
|
apiFile.addStatements([
|
||||||
|
'// This file was autogenerated. Please do not change.',
|
||||||
|
'// All changes will be overwrited on commit.',
|
||||||
|
'',
|
||||||
|
]);
|
||||||
|
|
||||||
|
// const schemaProperties = schemas[schemaName].properties;
|
||||||
|
const importEntities: any[] = [];
|
||||||
|
|
||||||
|
// add api class to file
|
||||||
|
const apiClass = apiFile.addClass({
|
||||||
|
name: `${capitalize(cName)}Api`,
|
||||||
|
isDefaultExport: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// get operations of controller
|
||||||
|
const controllerOperations = this.controllers[cName];
|
||||||
|
const operationList = Object.keys(controllerOperations).sort();
|
||||||
|
// for each operation add fetcher
|
||||||
|
operationList.forEach((operation) => {
|
||||||
|
const {
|
||||||
|
requestBody, responses, parameters, method, pathKey, security,
|
||||||
|
} = controllerOperations[operation];
|
||||||
|
|
||||||
|
const queryParams: any[] = []; // { name, type }
|
||||||
|
const bodyParam: any[] = []; // { name, type }
|
||||||
|
|
||||||
|
let hasResponseBodyType: /* boolean | ReturnType<schemaParamParser> */ false | [string, boolean, boolean, boolean, boolean] = false;
|
||||||
|
let contentType = '';
|
||||||
|
if (parameters) {
|
||||||
|
parameters.forEach((p: any) => {
|
||||||
|
const [
|
||||||
|
pType, isArray, isClass, isImport,
|
||||||
|
] = schemaParamParser(p.schema, this.openapi);
|
||||||
|
|
||||||
|
if (isImport) {
|
||||||
|
importEntities.push({ type: pType, isClass });
|
||||||
|
}
|
||||||
|
if (p.in === 'query') {
|
||||||
|
queryParams.push({
|
||||||
|
name: p.name, type: `${pType}${isArray ? '[]' : ''}`, hasQuestionToken: !p.required });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (queryParams.length > 0) {
|
||||||
|
const imp = apiFile.getImportDeclaration((i) => {
|
||||||
|
return i.getModuleSpecifierValue() === 'qs';
|
||||||
|
}); if (!imp) {
|
||||||
|
apiFile.addImportDeclaration({
|
||||||
|
moduleSpecifier: 'qs',
|
||||||
|
defaultImport: 'qs',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (requestBody) {
|
||||||
|
let content = requestBody.content;
|
||||||
|
const { $ref }: { $ref: string } = requestBody;
|
||||||
|
|
||||||
|
if (!content && $ref) {
|
||||||
|
const name = $ref.split('/').pop() as string;
|
||||||
|
content = this.openapi.components.requestBodies[name].content;
|
||||||
|
}
|
||||||
|
|
||||||
|
[contentType] = Object.keys(content);
|
||||||
|
const data = content[contentType];
|
||||||
|
|
||||||
|
const [
|
||||||
|
pType, isArray, isClass, isImport,
|
||||||
|
] = schemaParamParser(data.schema, this.openapi);
|
||||||
|
|
||||||
|
if (isImport) {
|
||||||
|
importEntities.push({ type: pType, isClass });
|
||||||
|
bodyParam.push({ name: pType.toLowerCase(), type: `${isClass ? 'I' : ''}${pType}${isArray ? '[]' : ''}`, isClass, pType });
|
||||||
|
} else {
|
||||||
|
bodyParam.push({ name: 'data', type: `${pType}${isArray ? '[]' : ''}` });
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (responses['200']) {
|
||||||
|
const { content, headers } = responses['200'];
|
||||||
|
if (content && (content['*/*'] || content['application/json'])) {
|
||||||
|
const { schema, examples } = content['*/*'] || content['application/json'];
|
||||||
|
|
||||||
|
if (!schema) {
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
const propType = schemaParamParser(schema, this.openapi);
|
||||||
|
const [pType, , isClass, isImport] = propType;
|
||||||
|
|
||||||
|
if (isImport) {
|
||||||
|
importEntities.push({ type: pType, isClass });
|
||||||
|
}
|
||||||
|
hasResponseBodyType = propType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let returnType = '';
|
||||||
|
if (hasResponseBodyType) {
|
||||||
|
const [pType, isArray, isClass] = hasResponseBodyType as any;
|
||||||
|
let data = `Promise<${isClass ? 'I' : ''}${pType}${isArray ? '[]' : ''}`;
|
||||||
|
returnType = data;
|
||||||
|
} else {
|
||||||
|
returnType = 'Promise<number';
|
||||||
|
}
|
||||||
|
const shouldValidate = bodyParam.filter(b => b.isClass);
|
||||||
|
if (shouldValidate.length > 0) {
|
||||||
|
returnType += ' | string[]';
|
||||||
|
}
|
||||||
|
// append Error to default type return;
|
||||||
|
returnType += ' | Error>';
|
||||||
|
|
||||||
|
const fetcher = apiClass.addMethod({
|
||||||
|
isAsync: true,
|
||||||
|
isStatic: true,
|
||||||
|
name: operation,
|
||||||
|
returnType,
|
||||||
|
});
|
||||||
|
const params = [...queryParams, ...bodyParam].sort((a, b) => (Number(!!a.hasQuestionToken) - Number(!!b.hasQuestionToken)));
|
||||||
|
fetcher.addParameters(params);
|
||||||
|
|
||||||
|
fetcher.setBodyText((w) => {
|
||||||
|
// Add data to URLSearchParams
|
||||||
|
if (contentType === 'text/plain') {
|
||||||
|
bodyParam.forEach((b) => {
|
||||||
|
w.writeLine(`const params = String(${b.name});`);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if (shouldValidate.length > 0) {
|
||||||
|
w.writeLine(`const haveError: string[] = [];`);
|
||||||
|
shouldValidate.forEach((b) => {
|
||||||
|
w.writeLine(`const ${b.name}Valid = new ${b.pType}(${b.name});`);
|
||||||
|
w.writeLine(`haveError.push(...${b.name}Valid.validate());`);
|
||||||
|
});
|
||||||
|
w.writeLine(`if (haveError.length > 0) {`);
|
||||||
|
w.writeLine(` return Promise.resolve(haveError);`)
|
||||||
|
w.writeLine(`}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Switch return of fetch in case on queryParams
|
||||||
|
if (queryParams.length > 0) {
|
||||||
|
w.writeLine('const queryParams = {');
|
||||||
|
queryParams.forEach((q) => {
|
||||||
|
w.writeLine(` ${q.name}: ${q.name},`);
|
||||||
|
});
|
||||||
|
w.writeLine('}');
|
||||||
|
w.writeLine(`return await fetch(\`${this.serverUrl}${pathKey}?\${qs.stringify(queryParams, { arrayFormat: 'comma' })}\`, {`);
|
||||||
|
} else {
|
||||||
|
w.writeLine(`return await fetch(\`${this.serverUrl}${pathKey}\`, {`);
|
||||||
|
}
|
||||||
|
// Add method
|
||||||
|
w.writeLine(` method: '${method.toUpperCase()}',`);
|
||||||
|
|
||||||
|
// add Fetch options
|
||||||
|
if (contentType && contentType !== 'multipart/form-data') {
|
||||||
|
w.writeLine(' headers: {');
|
||||||
|
w.writeLine(` 'Content-Type': '${contentType}',`);
|
||||||
|
w.writeLine(' },');
|
||||||
|
}
|
||||||
|
if (contentType) {
|
||||||
|
switch (contentType) {
|
||||||
|
case 'text/plain':
|
||||||
|
w.writeLine(' body: params,');
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
w.writeLine(` body: JSON.stringify(${bodyParam.map((b) => b.isClass ? `${b.name}Valid.serialize()` : b.name).join(', ')}),`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle response
|
||||||
|
if (hasResponseBodyType) {
|
||||||
|
w.writeLine('}).then(async (res) => {');
|
||||||
|
w.writeLine(' if (res.status === 200) {');
|
||||||
|
w.writeLine(' return res.json();');
|
||||||
|
} else {
|
||||||
|
w.writeLine('}).then(async (res) => {');
|
||||||
|
w.writeLine(' if (res.status === 200) {');
|
||||||
|
w.writeLine(' return res.status;');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle Error
|
||||||
|
w.writeLine(' } else {');
|
||||||
|
w.writeLine(' return new Error(String(res.status));');
|
||||||
|
w.writeLine(' }');
|
||||||
|
w.writeLine('})');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const imports: any[] = [];
|
||||||
|
const types: string[] = [];
|
||||||
|
importEntities.forEach((i) => {
|
||||||
|
const { type } = i;
|
||||||
|
if (!types.includes(type)) {
|
||||||
|
imports.push(i);
|
||||||
|
types.push(type);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
imports.sort((a,b) => a.type > b.type ? 1 : -1).forEach((ie) => {
|
||||||
|
const { type: pType, isClass } = ie;
|
||||||
|
if (isClass) {
|
||||||
|
apiFile.addImportDeclaration({
|
||||||
|
moduleSpecifier: `${GENERATOR_ENTITY_ALLIAS}${pType}`,
|
||||||
|
defaultImport: pType,
|
||||||
|
namedImports: [`I${pType}`],
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
apiFile.addImportDeclaration({
|
||||||
|
moduleSpecifier: `${GENERATOR_ENTITY_ALLIAS}${pType}`,
|
||||||
|
namedImports: [pType],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.apis.push(apiFile);
|
||||||
|
};
|
||||||
|
|
||||||
|
save = () => {
|
||||||
|
this.apis.forEach(async (e) => {
|
||||||
|
await e.saveSync();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export default ApiGenerator;
|
|
@ -0,0 +1,519 @@
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import * as path from 'path';
|
||||||
|
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||||
|
import * as morph from 'ts-morph';
|
||||||
|
|
||||||
|
import { ENT_DIR } from '../../consts';
|
||||||
|
import { TYPES, toCamel, schemaParamParser } from './utils';
|
||||||
|
|
||||||
|
const { Project, QuoteKind } = morph;
|
||||||
|
|
||||||
|
|
||||||
|
const EntDir = path.resolve(ENT_DIR);
|
||||||
|
if (!fs.existsSync(EntDir)) {
|
||||||
|
fs.mkdirSync(EntDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
class EntitiesGenerator {
|
||||||
|
project = new Project({
|
||||||
|
tsConfigFilePath: './tsconfig.json',
|
||||||
|
addFilesFromTsConfig: false,
|
||||||
|
manipulationSettings: {
|
||||||
|
quoteKind: QuoteKind.Single,
|
||||||
|
usePrefixAndSuffixTextForRename: false,
|
||||||
|
useTrailingCommas: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
openapi: Record<string, any>;
|
||||||
|
|
||||||
|
schemas: Record<string, any>;
|
||||||
|
|
||||||
|
schemaNames: string[];
|
||||||
|
|
||||||
|
entities: morph.SourceFile[] = [];
|
||||||
|
|
||||||
|
constructor(openapi: Record<string, any>) {
|
||||||
|
this.openapi = openapi;
|
||||||
|
this.schemas = openapi.components.schemas;
|
||||||
|
this.schemaNames = Object.keys(this.schemas);
|
||||||
|
this.generateEntities();
|
||||||
|
}
|
||||||
|
|
||||||
|
generateEntities = () => {
|
||||||
|
this.schemaNames.forEach(this.generateEntity);
|
||||||
|
};
|
||||||
|
|
||||||
|
generateEntity = (sName: string) => {
|
||||||
|
const { properties, type, oneOf } = this.schemas[sName];
|
||||||
|
const notAClass = !properties && TYPES[type as keyof typeof TYPES];
|
||||||
|
|
||||||
|
if (oneOf) {
|
||||||
|
this.generateOneOf(sName);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (notAClass) {
|
||||||
|
this.generateEnum(sName);
|
||||||
|
} else {
|
||||||
|
this.generateClass(sName);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
generateEnum = (sName: string) => {
|
||||||
|
const entityFile = this.project.createSourceFile(`${EntDir}/${sName}.ts`);
|
||||||
|
entityFile.addStatements([
|
||||||
|
'// This file was autogenerated. Please do not change.',
|
||||||
|
'// All changes will be overwrited on commit.',
|
||||||
|
'',
|
||||||
|
]);
|
||||||
|
|
||||||
|
const { enum: enumMembers } = this.schemas[sName];
|
||||||
|
entityFile.addEnum({
|
||||||
|
name: sName,
|
||||||
|
members: enumMembers.map((e: string) => ({ name: e.toUpperCase(), value: e })),
|
||||||
|
isExported: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.entities.push(entityFile);
|
||||||
|
};
|
||||||
|
|
||||||
|
generateOneOf = (sName: string) => {
|
||||||
|
const entityFile = this.project.createSourceFile(`${EntDir}/${sName}.ts`);
|
||||||
|
entityFile.addStatements([
|
||||||
|
'// This file was autogenerated. Please do not change.',
|
||||||
|
'// All changes will be overwrited on commit.',
|
||||||
|
'',
|
||||||
|
]);
|
||||||
|
const importEntities: { type: string, isClass: boolean }[] = [];
|
||||||
|
const entities = this.schemas[sName].oneOf.map((elem: any) => {
|
||||||
|
const [
|
||||||
|
pType, isArray, isClass, isImport,
|
||||||
|
] = schemaParamParser(elem, this.openapi);
|
||||||
|
importEntities.push({ type: pType, isClass });
|
||||||
|
return { type: pType, isArray };
|
||||||
|
});
|
||||||
|
entityFile.addTypeAlias({
|
||||||
|
name: sName,
|
||||||
|
isExported: true,
|
||||||
|
type: entities.map((e: any) => e.isArray ? `I${e.type}[]` : `I${e.type}`).join(' | '),
|
||||||
|
})
|
||||||
|
|
||||||
|
// add import
|
||||||
|
importEntities.sort((a, b) => a.type > b.type ? 1 : -1).forEach((ie) => {
|
||||||
|
const { type: pType, isClass } = ie;
|
||||||
|
if (isClass) {
|
||||||
|
entityFile.addImportDeclaration({
|
||||||
|
moduleSpecifier: `./${pType}`,
|
||||||
|
namedImports: [`I${pType}`],
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
entityFile.addImportDeclaration({
|
||||||
|
moduleSpecifier: `./${pType}`,
|
||||||
|
namedImports: [pType],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.entities.push(entityFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
generateClass = (sName: string) => {
|
||||||
|
const entityFile = this.project.createSourceFile(`${EntDir}/${sName}.ts`);
|
||||||
|
entityFile.addStatements([
|
||||||
|
'// This file was autogenerated. Please do not change.',
|
||||||
|
'// All changes will be overwrited on commit.',
|
||||||
|
'',
|
||||||
|
]);
|
||||||
|
|
||||||
|
const { properties: sProps, required } = this.schemas[sName];
|
||||||
|
|
||||||
|
const importEntities: { type: string, isClass: boolean }[] = [];
|
||||||
|
const entityInterface = entityFile.addInterface({
|
||||||
|
name: `I${sName}`,
|
||||||
|
isExported: true,
|
||||||
|
});
|
||||||
|
const sortedSProps = Object.keys(sProps || {}).sort();
|
||||||
|
// add server response interface to entityFile
|
||||||
|
sortedSProps.forEach((sPropName) => {
|
||||||
|
const [
|
||||||
|
pType, isArray, isClass, isImport, isAdditional
|
||||||
|
] = schemaParamParser(sProps[sPropName], this.openapi);
|
||||||
|
|
||||||
|
if (isImport) {
|
||||||
|
importEntities.push({ type: pType, isClass });
|
||||||
|
}
|
||||||
|
const propertyType = isAdditional
|
||||||
|
? `{ [key: string]: ${isClass ? 'I' : ''}${pType}${isArray ? '[]' : ''} }`
|
||||||
|
: `${isClass ? 'I' : ''}${pType}${isArray ? '[]' : ''}`;
|
||||||
|
entityInterface.addProperty({
|
||||||
|
name: sPropName,
|
||||||
|
type: propertyType,
|
||||||
|
hasQuestionToken: !(
|
||||||
|
(required && required.includes(sPropName)) || sProps[sPropName].required
|
||||||
|
),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// add import
|
||||||
|
const imports: { type: string, isClass: boolean }[] = [];
|
||||||
|
const types: string[] = [];
|
||||||
|
importEntities.forEach((i) => {
|
||||||
|
const { type } = i;
|
||||||
|
if (!types.includes(type)) {
|
||||||
|
imports.push(i);
|
||||||
|
types.push(type);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
imports.sort((a, b) => a.type > b.type ? 1 : -1).forEach((ie) => {
|
||||||
|
const { type: pType, isClass } = ie;
|
||||||
|
if (isClass) {
|
||||||
|
entityFile.addImportDeclaration({
|
||||||
|
defaultImport: pType,
|
||||||
|
moduleSpecifier: `./${pType}`,
|
||||||
|
namedImports: [`I${pType}`],
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
entityFile.addImportDeclaration({
|
||||||
|
moduleSpecifier: `./${pType}`,
|
||||||
|
namedImports: [pType],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const entityClass = entityFile.addClass({
|
||||||
|
name: sName,
|
||||||
|
isDefaultExport: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// addProperties to class;
|
||||||
|
sortedSProps.forEach((sPropName) => {
|
||||||
|
const [pType, isArray, isClass, isImport, isAdditional] = schemaParamParser(sProps[sPropName], this.openapi);
|
||||||
|
|
||||||
|
const isRequred = (required && required.includes(sPropName))
|
||||||
|
|| sProps[sPropName].required;
|
||||||
|
|
||||||
|
const propertyType = isAdditional
|
||||||
|
? `{ [key: string]: ${pType}${isArray ? '[]' : ''}${isRequred ? '' : ' | undefined'} }`
|
||||||
|
: `${pType}${isArray ? '[]' : ''}${isRequred ? '' : ' | undefined'}`;
|
||||||
|
|
||||||
|
entityClass.addProperty({
|
||||||
|
name: `_${sPropName}`,
|
||||||
|
isReadonly: true,
|
||||||
|
type: propertyType,
|
||||||
|
});
|
||||||
|
const getter = entityClass.addGetAccessor({
|
||||||
|
name: toCamel(sPropName),
|
||||||
|
returnType: propertyType,
|
||||||
|
statements: [`return this._${sPropName};`],
|
||||||
|
});
|
||||||
|
const { description, example, minItems, maxItems, maxLength, minLength, maximum, minimum } = sProps[sPropName];
|
||||||
|
if (description || example) {
|
||||||
|
getter.addJsDoc(`${example ? `Description: ${description}` : ''}${example ? `\nExample: ${example}` : ''}`);
|
||||||
|
}
|
||||||
|
if (minItems) {
|
||||||
|
entityClass.addGetAccessor({
|
||||||
|
isStatic: true,
|
||||||
|
name: `${toCamel(sPropName)}MinItems`,
|
||||||
|
statements: [`return ${minItems};`],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (maxItems) {
|
||||||
|
entityClass.addGetAccessor({
|
||||||
|
isStatic: true,
|
||||||
|
name: `${toCamel(sPropName)}MaxItems`,
|
||||||
|
statements: [`return ${maxItems};`],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (typeof minLength === 'number') {
|
||||||
|
entityClass.addGetAccessor({
|
||||||
|
isStatic: true,
|
||||||
|
name: `${toCamel(sPropName)}MinLength`,
|
||||||
|
statements: [`return ${minLength};`],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (maxLength) {
|
||||||
|
entityClass.addGetAccessor({
|
||||||
|
isStatic: true,
|
||||||
|
name: `${toCamel(sPropName)}MaxLength`,
|
||||||
|
statements: [`return ${maxLength};`],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (typeof minimum === 'number') {
|
||||||
|
entityClass.addGetAccessor({
|
||||||
|
isStatic: true,
|
||||||
|
name: `${toCamel(sPropName)}MinValue`,
|
||||||
|
statements: [`return ${minimum};`],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (maximum) {
|
||||||
|
entityClass.addGetAccessor({
|
||||||
|
isStatic: true,
|
||||||
|
name: `${toCamel(sPropName)}MaxValue`,
|
||||||
|
statements: [`return ${maximum};`],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(isArray && isClass) && !isClass) {
|
||||||
|
const isEnum = !isClass && isImport;
|
||||||
|
const isRequired = (required && required.includes(sPropName)) || sProps[sPropName].required;
|
||||||
|
const { maxLength, minLength, maximum, minimum } = sProps[sPropName];
|
||||||
|
const haveValidationFields = maxLength || typeof minLength === 'number' || maximum || typeof minimum === 'number';
|
||||||
|
if (isRequired || haveValidationFields) {
|
||||||
|
const prop = toCamel(sPropName);
|
||||||
|
const validateField = entityClass.addMethod({
|
||||||
|
isStatic: true,
|
||||||
|
name: `${prop}Validate`,
|
||||||
|
returnType: `boolean`,
|
||||||
|
parameters: [{
|
||||||
|
name: prop,
|
||||||
|
type: `${pType}${isArray ? '[]' : ''}${isRequred ? '' : ' | undefined'}`,
|
||||||
|
}],
|
||||||
|
})
|
||||||
|
|
||||||
|
validateField.setBodyText((w) => {
|
||||||
|
w.write('return ');
|
||||||
|
const nonRequiredCall = isRequired ? prop : `!${prop} ? true : ${prop}`;
|
||||||
|
if (pType === 'string') {
|
||||||
|
if (isArray) {
|
||||||
|
w.write(`${nonRequiredCall}.reduce<boolean>((result, p) => result && (typeof p === 'string' && !!p.trim()), true)`);
|
||||||
|
} else {
|
||||||
|
if (typeof minLength === 'number' && maxLength) {
|
||||||
|
w.write(`(${nonRequiredCall}.length >${minLength > 0 ? '=' : ''} ${minLength}) && (${nonRequiredCall}.length <= ${maxLength})`);
|
||||||
|
}
|
||||||
|
if (typeof minLength !== 'number' || !maxLength) {
|
||||||
|
w.write(`${isRequired ? `typeof ${prop} === 'string'` : `!${prop} ? true : typeof ${prop} === 'string'`} && !!${nonRequiredCall}.trim()`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (pType === 'number') {
|
||||||
|
if (isArray) {
|
||||||
|
w.write(`${nonRequiredCall}.reduce<boolean>((result, p) => result && typeof p === 'number', true)`);
|
||||||
|
} else {
|
||||||
|
if (typeof minimum === 'number' && maximum) {
|
||||||
|
w.write(`${isRequired ? `${prop} >= ${minimum} && ${prop} <= ${maximum}` : `!${prop} ? true : ((${prop} >= ${minimum}) && (${prop} <= ${maximum}))`}`);
|
||||||
|
}
|
||||||
|
if (typeof minimum !== 'number' || !maximum) {
|
||||||
|
w.write(`${isRequired ? `typeof ${prop} === 'number'` : `!${prop} ? true : typeof ${prop} === 'number'`}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (pType === 'boolean') {
|
||||||
|
w.write(`${isRequired ? `typeof ${prop} === 'boolean'` : `!${prop} ? true : typeof ${prop} === 'boolean'`}`);
|
||||||
|
} else if (isEnum) {
|
||||||
|
if (isArray){
|
||||||
|
w.write(`${nonRequiredCall}.reduce<boolean>((result, p) => result && Object.keys(${pType}).includes(${prop}), true)`);
|
||||||
|
} else {
|
||||||
|
w.write(`${isRequired ? `Object.keys(${pType}).includes(${prop})` : `!${prop} ? true : typeof ${prop} === 'boolean'`}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
w.write(';');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// add constructor;
|
||||||
|
const ctor = entityClass.addConstructor({
|
||||||
|
parameters: [{
|
||||||
|
name: 'props',
|
||||||
|
type: `I${sName}`,
|
||||||
|
}],
|
||||||
|
});
|
||||||
|
ctor.setBodyText((w) => {
|
||||||
|
sortedSProps.forEach((sPropName) => {
|
||||||
|
const [
|
||||||
|
pType, isArray, isClass, , isAdditional
|
||||||
|
] = schemaParamParser(sProps[sPropName], this.openapi);
|
||||||
|
const req = (required && required.includes(sPropName))
|
||||||
|
|| sProps[sPropName].required;
|
||||||
|
if (!req) {
|
||||||
|
if ((pType === 'boolean' || pType === 'number' || pType ==='string') && !isClass && !isArray) {
|
||||||
|
w.writeLine(`if (typeof props.${sPropName} === '${pType}') {`);
|
||||||
|
} else {
|
||||||
|
w.writeLine(`if (props.${sPropName}) {`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isAdditional) {
|
||||||
|
if (isArray && isClass) {
|
||||||
|
w.writeLine(`${!req ? ' ' : ''}this._${sPropName} = props.${sPropName}.map((p) => Object.keys(p).reduce((prev, key) => {
|
||||||
|
return { ...prev, [key]: new ${pType}(p[key])};
|
||||||
|
},{}))`);
|
||||||
|
} else if (isClass) {
|
||||||
|
w.writeLine(`${!req ? ' ' : ''}this._${sPropName} = Object.keys(props.${sPropName}).reduce((prev, key) => {
|
||||||
|
return { ...prev, [key]: new ${pType}(props.${sPropName}[key])};
|
||||||
|
},{})`);
|
||||||
|
} else {
|
||||||
|
if (pType === 'string' && !isArray) {
|
||||||
|
w.writeLine(`${!req ? ' ' : ''}this._${sPropName} = Object.keys(props.${sPropName}).reduce((prev, key) => {
|
||||||
|
return { ...prev, [key]: props.${sPropName}[key].trim()};
|
||||||
|
},{})`);
|
||||||
|
} else {
|
||||||
|
w.writeLine(`${!req ? ' ' : ''}this._${sPropName} = Object.keys(props.${sPropName}).reduce((prev, key) => {
|
||||||
|
return { ...prev, [key]: props.${sPropName}[key]};
|
||||||
|
},{})`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (isArray && isClass) {
|
||||||
|
w.writeLine(`${!req ? ' ' : ''}this._${sPropName} = props.${sPropName}.map((p) => new ${pType}(p));`);
|
||||||
|
} else if (isClass) {
|
||||||
|
w.writeLine(`${!req ? ' ' : ''}this._${sPropName} = new ${pType}(props.${sPropName});`);
|
||||||
|
} else {
|
||||||
|
if (pType === 'string' && !isArray) {
|
||||||
|
w.writeLine(`${!req ? ' ' : ''}this._${sPropName} = props.${sPropName}.trim();`);
|
||||||
|
} else {
|
||||||
|
w.writeLine(`${!req ? ' ' : ''}this._${sPropName} = props.${sPropName};`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!req) {
|
||||||
|
w.writeLine('}');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// add serialize method;
|
||||||
|
const serialize = entityClass.addMethod({
|
||||||
|
isStatic: false,
|
||||||
|
name: 'serialize',
|
||||||
|
returnType: `I${sName}`,
|
||||||
|
});
|
||||||
|
serialize.setBodyText((w) => {
|
||||||
|
w.writeLine(`const data: I${sName} = {`);
|
||||||
|
const unReqFields: string[] = [];
|
||||||
|
sortedSProps.forEach((sPropName) => {
|
||||||
|
const req = (required && required.includes(sPropName))
|
||||||
|
|| sProps[sPropName].required;
|
||||||
|
const [, isArray, isClass, , isAdditional] = schemaParamParser(sProps[sPropName], this.openapi);
|
||||||
|
if (!req) {
|
||||||
|
unReqFields.push(sPropName);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (isAdditional) {
|
||||||
|
if (isArray && isClass) {
|
||||||
|
w.writeLine(` ${sPropName}: this._${sPropName}.map((p) => Object.keys(p).reduce((prev, key) => ({ ...prev, [key]: p[key].serialize() }))),`);
|
||||||
|
} else if (isClass) {
|
||||||
|
w.writeLine(` ${sPropName}: Object.keys(this._${sPropName}).reduce<Record<string, any>>((prev, key) => ({ ...prev, [key]: this._${sPropName}[key].serialize() }), {}),`);
|
||||||
|
} else {
|
||||||
|
w.writeLine(` ${sPropName}: Object.keys(this._${sPropName}).reduce((prev, key) => ({ ...prev, [key]: this._${sPropName}[key] })),`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (isArray && isClass) {
|
||||||
|
w.writeLine(` ${sPropName}: this._${sPropName}.map((p) => p.serialize()),`);
|
||||||
|
} else if (isClass) {
|
||||||
|
w.writeLine(` ${sPropName}: this._${sPropName}.serialize(),`);
|
||||||
|
} else {
|
||||||
|
w.writeLine(` ${sPropName}: this._${sPropName},`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
w.writeLine('};');
|
||||||
|
unReqFields.forEach((sPropName) => {
|
||||||
|
const [, isArray, isClass, , isAdditional] = schemaParamParser(sProps[sPropName], this.openapi);
|
||||||
|
w.writeLine(`if (typeof this._${sPropName} !== 'undefined') {`);
|
||||||
|
if (isAdditional) {
|
||||||
|
if (isArray && isClass) {
|
||||||
|
w.writeLine(` data.${sPropName} = this._${sPropName}.map((p) => Object.keys(p).reduce((prev, key) => ({ ...prev, [key]: p[key].serialize() }), {}));`);
|
||||||
|
} else if (isClass) {
|
||||||
|
w.writeLine(` data.${sPropName} = Object.keys(this._${sPropName}).reduce((prev, key) => ({ ...prev, [key]: this._${sPropName}[key].serialize() }), {});`);
|
||||||
|
} else {
|
||||||
|
w.writeLine(` data.${sPropName} = Object.keys(this._${sPropName}).reduce((prev, key) => ({ ...prev, [key]: this._${sPropName}[key] }), {});`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (isArray && isClass) {
|
||||||
|
w.writeLine(` data.${sPropName} = this._${sPropName}.map((p) => p.serialize());`);
|
||||||
|
} else if (isClass) {
|
||||||
|
w.writeLine(` data.${sPropName} = this._${sPropName}.serialize();`);
|
||||||
|
} else {
|
||||||
|
w.writeLine(` data.${sPropName} = this._${sPropName};`);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
w.writeLine(`}`);
|
||||||
|
});
|
||||||
|
w.writeLine('return data;');
|
||||||
|
});
|
||||||
|
|
||||||
|
// add validate method
|
||||||
|
const validate = entityClass.addMethod({
|
||||||
|
isStatic: false,
|
||||||
|
name: 'validate',
|
||||||
|
returnType: `string[]`,
|
||||||
|
})
|
||||||
|
validate.setBodyText((w) => {
|
||||||
|
w.writeLine('const validate = {');
|
||||||
|
Object.keys(sProps || {}).forEach((sPropName) => {
|
||||||
|
const [pType, isArray, isClass, , isAdditional] = schemaParamParser(sProps[sPropName], this.openapi);
|
||||||
|
|
||||||
|
const { maxLength, minLength, maximum, minimum } = sProps[sPropName];
|
||||||
|
|
||||||
|
const isRequired = (required && required.includes(sPropName)) || sProps[sPropName].required;
|
||||||
|
const nonRequiredCall = isRequired ? `this._${sPropName}` : `!this._${sPropName} ? true : this._${sPropName}`;
|
||||||
|
|
||||||
|
if (isArray && isClass) {
|
||||||
|
w.writeLine(` ${sPropName}: ${nonRequiredCall}.reduce((result, p) => result && p.validate().length === 0, true),`);
|
||||||
|
} else if (isClass && !isAdditional) {
|
||||||
|
w.writeLine(` ${sPropName}: ${nonRequiredCall}.validate().length === 0,`);
|
||||||
|
} else {
|
||||||
|
if (pType === 'string') {
|
||||||
|
if (isArray) {
|
||||||
|
w.writeLine(` ${sPropName}: ${nonRequiredCall}.reduce((result, p) => result && typeof p === 'string', true),`);
|
||||||
|
} else {
|
||||||
|
if (typeof minLength === 'number' && maxLength) {
|
||||||
|
w.writeLine(` ${sPropName}: (${nonRequiredCall}.length >${minLength > 0 ? '=' : ''} ${minLength}) && (${nonRequiredCall}.length <= ${maxLength}),`);
|
||||||
|
}
|
||||||
|
if (typeof minLength !== 'number' || !maxLength) {
|
||||||
|
w.writeLine(` ${sPropName}: ${isRequired ? `typeof this._${sPropName} === 'string'` : `!this._${sPropName} ? true : typeof this._${sPropName} === 'string'`} && !this._${sPropName} ? true : this._${sPropName},`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (pType === 'number') {
|
||||||
|
if (isArray) {
|
||||||
|
w.writeLine(` ${sPropName}: ${nonRequiredCall}.reduce((result, p) => result && typeof p === 'number', true),`);
|
||||||
|
} else {
|
||||||
|
if (typeof minimum === 'number' && maximum) {
|
||||||
|
w.writeLine(` ${sPropName}: ${isRequired ? `this._${sPropName} >= ${minimum} && this._${sPropName} <= ${maximum}` : `!this._${sPropName} ? true : ((this._${sPropName} >= ${minimum}) && (this._${sPropName} <= ${maximum}))`},`);
|
||||||
|
}
|
||||||
|
if (typeof minimum !== 'number' || !maximum) {
|
||||||
|
w.writeLine(` ${sPropName}: ${isRequired ? `typeof this._${sPropName} === 'number'` : `!this._${sPropName} ? true : typeof this._${sPropName} === 'number'`},`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (pType === 'boolean') {
|
||||||
|
w.writeLine(` ${sPropName}: ${isRequired ? `typeof this._${sPropName} === 'boolean'` : `!this._${sPropName} ? true : typeof this._${sPropName} === 'boolean'`},`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
w.writeLine('};');
|
||||||
|
w.writeLine('const isError: string[] = [];')
|
||||||
|
w.writeLine('Object.keys(validate).forEach((key) => {');
|
||||||
|
w.writeLine(' if (!(validate as any)[key]) {');
|
||||||
|
w.writeLine(' isError.push(key);');
|
||||||
|
w.writeLine(' }');
|
||||||
|
w.writeLine('});');
|
||||||
|
w.writeLine('return isError;');
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
// add update method;
|
||||||
|
const update = entityClass.addMethod({
|
||||||
|
isStatic: false,
|
||||||
|
name: 'update',
|
||||||
|
returnType: `${sName}`,
|
||||||
|
});
|
||||||
|
update.addParameter({
|
||||||
|
name: 'props',
|
||||||
|
type: `Partial<I${sName}>`,
|
||||||
|
});
|
||||||
|
update.setBodyText((w) => { w.writeLine(`return new ${sName}({ ...this.serialize(), ...props });`); });
|
||||||
|
|
||||||
|
this.entities.push(entityFile);
|
||||||
|
};
|
||||||
|
|
||||||
|
save = () => {
|
||||||
|
this.entities.forEach(async (e) => {
|
||||||
|
await e.saveSync();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default EntitiesGenerator;
|
|
@ -0,0 +1,74 @@
|
||||||
|
const toCamel = (s: string) => {
|
||||||
|
return s.replace(/([-_][a-z])/ig, ($1) => {
|
||||||
|
return $1.toUpperCase()
|
||||||
|
.replace('-', '')
|
||||||
|
.replace('_', '');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const capitalize = (s: string) => {
|
||||||
|
return s[0].toUpperCase() + s.slice(1);
|
||||||
|
};
|
||||||
|
const TYPES = {
|
||||||
|
integer: 'number',
|
||||||
|
float: 'number',
|
||||||
|
number: 'number',
|
||||||
|
string: 'string',
|
||||||
|
boolean: 'boolean',
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param schemaProp: valueof shema.properties[key]
|
||||||
|
* @param openApi: openapi object
|
||||||
|
* @returns [propType - basicType or import one, isArray, isClass, isImport]
|
||||||
|
*/
|
||||||
|
const schemaParamParser = (schemaProp: any, openApi: any): [string, boolean, boolean, boolean, boolean] => {
|
||||||
|
let type = '';
|
||||||
|
let isImport = false;
|
||||||
|
let isClass = false;
|
||||||
|
let isArray = false;
|
||||||
|
let isAdditional = false;
|
||||||
|
|
||||||
|
if (schemaProp.$ref || schemaProp.additionalProperties?.$ref) {
|
||||||
|
const temp = (schemaProp.$ref || schemaProp.additionalProperties?.$ref).split('/');
|
||||||
|
|
||||||
|
if (schemaProp.additionalProperties) {
|
||||||
|
isAdditional = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
type = `${temp[temp.length - 1]}`;
|
||||||
|
|
||||||
|
const cl = openApi ? openApi.components.schemas[temp[temp.length - 1]] : {};
|
||||||
|
|
||||||
|
if (cl.type === 'string' && cl.enum) {
|
||||||
|
isImport = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cl.type === 'object' && !cl.oneOf) {
|
||||||
|
isClass = true;
|
||||||
|
isImport = true;
|
||||||
|
} else if (cl.type === 'array') {
|
||||||
|
const temp: any = schemaParamParser(cl.items, openApi);
|
||||||
|
type = `${temp[0]}`;
|
||||||
|
isArray = true;
|
||||||
|
isClass = isClass || temp[2];
|
||||||
|
isImport = isImport || temp[3];
|
||||||
|
}
|
||||||
|
} else if (schemaProp.type === 'array') {
|
||||||
|
const temp: any = schemaParamParser(schemaProp.items, openApi);
|
||||||
|
type = `${temp[0]}`;
|
||||||
|
isArray = true;
|
||||||
|
isClass = isClass || temp[2];
|
||||||
|
isImport = isImport || temp[3];
|
||||||
|
} else {
|
||||||
|
type = (TYPES as Record<any, string>)[schemaProp.type];
|
||||||
|
}
|
||||||
|
if (!type) {
|
||||||
|
// TODO: Fix bug with Error fields.
|
||||||
|
type = 'any';
|
||||||
|
// throw new Error('Failed to find entity type');
|
||||||
|
}
|
||||||
|
|
||||||
|
return [type, isArray, isClass, isImport, isAdditional];
|
||||||
|
};
|
||||||
|
|
||||||
|
export { TYPES, toCamel, capitalize, schemaParamParser };
|
|
@ -0,0 +1,226 @@
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import {
|
||||||
|
Project,
|
||||||
|
VariableStatement,
|
||||||
|
SyntaxKind,
|
||||||
|
Node,
|
||||||
|
Statement,
|
||||||
|
ts,
|
||||||
|
Identifier,
|
||||||
|
SourceFile,
|
||||||
|
} from 'ts-morph';
|
||||||
|
import {
|
||||||
|
LOCALE_FOLDER_PATH,
|
||||||
|
TRANSLATOR_CLASS_NAME,
|
||||||
|
USE_INTL_NAME,
|
||||||
|
trimQuotes,
|
||||||
|
} from '../consts';
|
||||||
|
import { checkForms, AvailableLocales } from '../../src/localization/Translator';
|
||||||
|
|
||||||
|
const project = new Project({
|
||||||
|
tsConfigFilePath: './tsconfig.json',
|
||||||
|
});
|
||||||
|
|
||||||
|
let lang = 'ru';
|
||||||
|
let option = '';
|
||||||
|
|
||||||
|
if (process.argv.length > 2) {
|
||||||
|
lang = process.argv[2];
|
||||||
|
option = process.argv[3];
|
||||||
|
}
|
||||||
|
|
||||||
|
const usedTranslations: string[] = [];
|
||||||
|
const usedPluralTranslations: string[] = [];
|
||||||
|
|
||||||
|
const problemFiles: string[] = [];
|
||||||
|
const sourceFiles = project.getSourceFiles();
|
||||||
|
const sourceFilesWithIntl = sourceFiles.filter((sf) => {
|
||||||
|
return !!sf.getImportDeclarations().find((id) => {
|
||||||
|
return !!id.getNamedImports().find((ni) => ni.getName() === USE_INTL_NAME)
|
||||||
|
})
|
||||||
|
});
|
||||||
|
const getFileUsedIntl = (statements: Statement<ts.Statement>[]) => {
|
||||||
|
statements.forEach((s) => {
|
||||||
|
if (s instanceof VariableStatement) {
|
||||||
|
s.forEachDescendant((node) => {
|
||||||
|
let intVariableDeclaration: Identifier = null;
|
||||||
|
switch (node.getKind()) {
|
||||||
|
case SyntaxKind.VariableDeclaration:
|
||||||
|
if (node.getSymbol()) {
|
||||||
|
const name = node.getSymbol().getName();
|
||||||
|
const callExp = node.getChildren().find((n) => n.getKind() === SyntaxKind.CallExpression);
|
||||||
|
if (callExp) {
|
||||||
|
const callExpIden = callExp.getChildren().find(n => n.getKind() === SyntaxKind.Identifier);
|
||||||
|
if (callExpIden && callExpIden.getSymbol().getName() === USE_INTL_NAME) {
|
||||||
|
intVariableDeclaration = node as Identifier;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (intVariableDeclaration) {
|
||||||
|
intVariableDeclaration.findReferencesAsNodes().forEach((fr) => {
|
||||||
|
if (fr instanceof Node) {
|
||||||
|
const parent = fr.getParentIfKind(SyntaxKind.PropertyAccessExpression);
|
||||||
|
if (parent && (parent.getName() === 'getMessage' || parent.getName() === 'getPlural')) {
|
||||||
|
const syntaxList = parent.getNextSiblings().find((n) => n.getKind() === SyntaxKind.SyntaxList);
|
||||||
|
if (syntaxList) {
|
||||||
|
const id = syntaxList.getChildren()[0];
|
||||||
|
if (id && id.getKind() !== SyntaxKind.StringLiteral) {
|
||||||
|
problemFiles.push(fr.getSourceFile().getFilePath());
|
||||||
|
}
|
||||||
|
if (id) {
|
||||||
|
usedTranslations.push(trimQuotes(id.getText()));
|
||||||
|
if (parent.getName() === 'getPlural') {
|
||||||
|
usedPluralTranslations.push(trimQuotes(id.getText()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const getFileUsedTranslations = (file: SourceFile) => {
|
||||||
|
const namedImport = file.getImportDeclarations().find((id) => !!id.getNamedImports().find((ni) => ni.getName() === TRANSLATOR_CLASS_NAME));
|
||||||
|
if (namedImport) {
|
||||||
|
const identifier = namedImport.getImportClause().getNamedImports().find((iden) => iden.getName() === TRANSLATOR_CLASS_NAME);
|
||||||
|
const translateReferences = identifier.getNodeProperty('name').findReferencesAsNodes();
|
||||||
|
if (translateReferences.length > 0) {
|
||||||
|
translateReferences.forEach((identifierNode) => {
|
||||||
|
if (identifierNode.getParentIfKind(SyntaxKind.TypeReference)) {
|
||||||
|
const translatorVariable = identifierNode.getParent().getPreviousSibling().getPreviousSiblingIfKind(SyntaxKind.Identifier);
|
||||||
|
if (translatorVariable) {
|
||||||
|
translatorVariable.findReferencesAsNodes().forEach((node) => {
|
||||||
|
const parent = node.getParentIfKind(SyntaxKind.PropertyAccessExpression);
|
||||||
|
if (parent && (parent.getName() === 'getMessage' || parent.getName() === 'getPlural')) {
|
||||||
|
|
||||||
|
const syntaxList = parent.getNextSiblings().find((n) => n.getKind() === SyntaxKind.SyntaxList);
|
||||||
|
if (syntaxList) {
|
||||||
|
const id = syntaxList.getChildren()[0];
|
||||||
|
if (id && id.getKind() !== SyntaxKind.StringLiteral) {
|
||||||
|
problemFiles.push(parent.getSourceFile().getFilePath());
|
||||||
|
}
|
||||||
|
if (id) {
|
||||||
|
usedTranslations.push(trimQuotes(id.getText()));
|
||||||
|
if (parent.getName() === 'getPlural') {
|
||||||
|
usedPluralTranslations.push(trimQuotes(id.getText()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sourceFilesWithIntl.forEach((file) => {
|
||||||
|
getFileUsedIntl(file.getStatements());
|
||||||
|
})
|
||||||
|
|
||||||
|
const sourceFilesWithTranslator = project.getSourceFiles().filter((sf) => {
|
||||||
|
return !!sf.getImportDeclarations().find((id) => {
|
||||||
|
return !!id.getNamedImports().find((ni) => ni.getName() === TRANSLATOR_CLASS_NAME)
|
||||||
|
})
|
||||||
|
});
|
||||||
|
sourceFilesWithTranslator.forEach((file) => {
|
||||||
|
getFileUsedTranslations(file);
|
||||||
|
})
|
||||||
|
const filteredUsedTranslations = Array.from(new Set(usedTranslations));
|
||||||
|
const filteredUsedPluralTranslations = Array.from(new Set(usedPluralTranslations));
|
||||||
|
|
||||||
|
if (problemFiles.length) {
|
||||||
|
console.warn(`\n============== Files where translation id provided not as string ==============\n`);
|
||||||
|
console.log(problemFiles.join('\n'));
|
||||||
|
process.exit(255);
|
||||||
|
}
|
||||||
|
|
||||||
|
const allFiles = fs.readdirSync(LOCALE_FOLDER_PATH);
|
||||||
|
// Use ru or needed language
|
||||||
|
const translationFile = allFiles.find((file) => file.includes(`${lang}.json`));
|
||||||
|
|
||||||
|
if (!translationFile) {
|
||||||
|
console.error('File not found');
|
||||||
|
process.exit(255);
|
||||||
|
}
|
||||||
|
|
||||||
|
const translationsObject = JSON.parse(fs.readFileSync(`./src/lib/intl/__locales/${translationFile}`, { flag: 'r+' }) as unknown as string);
|
||||||
|
const translations = {
|
||||||
|
locale: translationFile,
|
||||||
|
messages: Object.keys(translationsObject),
|
||||||
|
};
|
||||||
|
|
||||||
|
const someMessagesNotFound: string[] = [];
|
||||||
|
const notUsed: string[] = [];
|
||||||
|
const notFound: string[] = [];
|
||||||
|
const checkLocaleMessages = (locale: string, messages: string[]) => {
|
||||||
|
filteredUsedTranslations.forEach(f => {
|
||||||
|
if (!messages.includes(f)) {
|
||||||
|
notFound.push(f);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
messages.forEach(t => {
|
||||||
|
if (!filteredUsedTranslations.includes(t)) {
|
||||||
|
notUsed.push(t);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (notFound.length > 0) {
|
||||||
|
someMessagesNotFound.push(locale);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const render = (data: string[], title: string) => {
|
||||||
|
console.log(`============ ${title} ============`);
|
||||||
|
console.table(data);
|
||||||
|
console.log(`============ ${title} ============`);
|
||||||
|
}
|
||||||
|
|
||||||
|
checkLocaleMessages(translations.locale, translations.messages);
|
||||||
|
|
||||||
|
const checkPluralForm = () => {
|
||||||
|
const pluralFormWrong: string[] = [];
|
||||||
|
filteredUsedPluralTranslations.forEach((id) => {
|
||||||
|
const message = translationsObject[id];
|
||||||
|
if (!checkForms(message, lang as AvailableLocales, id)) {
|
||||||
|
pluralFormWrong.push(id)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return pluralFormWrong;
|
||||||
|
}
|
||||||
|
|
||||||
|
const plural = checkPluralForm();
|
||||||
|
if (!option && (someMessagesNotFound.length || plural.length > 0 )) {
|
||||||
|
someMessagesNotFound.forEach(locale => console.error(`\nSome translatins for ${locale} was not found!\n`));
|
||||||
|
plural.forEach(id => console.error(`\nTranslation with id: "${id}" - have wrong number of plural forms!\n`));
|
||||||
|
process.exit(255);
|
||||||
|
}
|
||||||
|
if (option) {
|
||||||
|
switch (option) {
|
||||||
|
case '--show-missing': {
|
||||||
|
render(notFound, 'NotFound')
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case '--show-unused': {
|
||||||
|
render(notUsed, 'notUsed')
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case '--check-plurals': {
|
||||||
|
render(plural, 'Wrong Plural Form')
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
if (someMessagesNotFound.length) {
|
||||||
|
someMessagesNotFound.forEach(locale => console.error(`\nSome translatins for ${locale} was not found!\n\n`));
|
||||||
|
process.exit(255);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,79 @@
|
||||||
|
module.exports = {
|
||||||
|
parser: '@typescript-eslint/parser',
|
||||||
|
parserOptions: {
|
||||||
|
project: './tsconfig.json',
|
||||||
|
ecmaFeatures: {
|
||||||
|
jsx: true
|
||||||
|
},
|
||||||
|
extraFileExtensions: ['mjs', 'tsx', 'ts'],
|
||||||
|
ecmaVersion: 2020,
|
||||||
|
sourceType: 'module'
|
||||||
|
},
|
||||||
|
plugins: ['react', '@typescript-eslint', 'import'],
|
||||||
|
env: {
|
||||||
|
browser: true,
|
||||||
|
commonjs: true,
|
||||||
|
es6: true,
|
||||||
|
es2020: true,
|
||||||
|
jest: true,
|
||||||
|
},
|
||||||
|
settings: {
|
||||||
|
react: {
|
||||||
|
pragma: 'React',
|
||||||
|
version: 'detect',
|
||||||
|
},
|
||||||
|
'import/resolver': {
|
||||||
|
typescript: {
|
||||||
|
alwaysTryTypes: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'import/parsers': {
|
||||||
|
'@typescript-eslint/parser': ['.ts', '.tsx'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
'@typescript-eslint/explicit-module-boundary-types': 0,
|
||||||
|
'@typescript-eslint/explicit-function-return-type': [0, { allowExpressions: true }],
|
||||||
|
'@typescript-eslint/indent': ['error', 4],
|
||||||
|
'@typescript-eslint/interface-name-prefix': [0, { prefixWithI: 'never' }],
|
||||||
|
'@typescript-eslint/no-explicit-any': [0],
|
||||||
|
'@typescript-eslint/naming-convention': [2, {
|
||||||
|
selector: 'enum', format: ['UPPER_CASE', 'PascalCase'],
|
||||||
|
}],
|
||||||
|
'@typescript-eslint/no-non-null-assertion': 0,
|
||||||
|
'arrow-body-style': 'off',
|
||||||
|
'consistent-return': 0,
|
||||||
|
curly: [2, 'all'],
|
||||||
|
'default-case': 0,
|
||||||
|
'import/no-cycle': 0,
|
||||||
|
'import/prefer-default-export': 'off',
|
||||||
|
'import/no-named-as-default': 0,
|
||||||
|
indent: [0, 4],
|
||||||
|
'no-alert': 2,
|
||||||
|
'no-console': 2,
|
||||||
|
'no-debugger': 2,
|
||||||
|
'no-underscore-dangle': 'off',
|
||||||
|
'no-useless-escape': 'off',
|
||||||
|
'object-curly-newline': 'off',
|
||||||
|
'react-hooks/exhaustive-deps': 0,
|
||||||
|
'react/display-name': 0,
|
||||||
|
'react/jsx-indent-props': ['error', 4],
|
||||||
|
'react/jsx-indent': ['error', 4],
|
||||||
|
'react/jsx-one-expression-per-line': 'off',
|
||||||
|
'react/jsx-props-no-spreading': 0,
|
||||||
|
'react/prop-types': 'off',
|
||||||
|
'react/state-in-constructor': 'off',
|
||||||
|
},
|
||||||
|
extends: [
|
||||||
|
'airbnb-base',
|
||||||
|
'airbnb-typescript/base',
|
||||||
|
'airbnb/hooks',
|
||||||
|
'plugin:react/recommended',
|
||||||
|
'plugin:@typescript-eslint/eslint-recommended',
|
||||||
|
'plugin:@typescript-eslint/recommended',
|
||||||
|
'plugin:import/errors',
|
||||||
|
'plugin:import/warnings',
|
||||||
|
'plugin:import/typescript',
|
||||||
|
],
|
||||||
|
globals: {},
|
||||||
|
};
|
|
@ -0,0 +1,10 @@
|
||||||
|
module.exports = {
|
||||||
|
rules: {
|
||||||
|
'no-alert': 0,
|
||||||
|
'no-debugger': 0,
|
||||||
|
'no-console': 0,
|
||||||
|
},
|
||||||
|
extends: [
|
||||||
|
'./common',
|
||||||
|
],
|
||||||
|
};
|
|
@ -0,0 +1,5 @@
|
||||||
|
module.exports = {
|
||||||
|
extends: [
|
||||||
|
'./common.js',
|
||||||
|
],
|
||||||
|
};
|
|
@ -0,0 +1,40 @@
|
||||||
|
const yaml = require('yaml');
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
const ZERO_HOST = '0.0.0.0';
|
||||||
|
const LOCALHOST = '127.0.0.1';
|
||||||
|
const DEFAULT_PORT = 80;
|
||||||
|
|
||||||
|
const importConfig = () => {
|
||||||
|
try {
|
||||||
|
const doc = yaml.parse(fs.readFileSync('../AdguardHome.yaml', 'utf8'));
|
||||||
|
const { bind_host, bind_port } = doc;
|
||||||
|
return {
|
||||||
|
bind_host,
|
||||||
|
bind_port,
|
||||||
|
};
|
||||||
|
} catch (e) {
|
||||||
|
return {
|
||||||
|
bind_host: ZERO_HOST,
|
||||||
|
bind_port: DEFAULT_PORT,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getDevServerConfig = () => {
|
||||||
|
const { bind_host: host, bind_port: port } = importConfig();
|
||||||
|
const { DEV_SERVER_PORT } = process.env;
|
||||||
|
|
||||||
|
const devServerHost = host === ZERO_HOST ? LOCALHOST : host;
|
||||||
|
const devServerPort = 3000 || port + 8000;
|
||||||
|
|
||||||
|
return {
|
||||||
|
host: devServerHost,
|
||||||
|
port: devServerPort
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
importConfig,
|
||||||
|
getDevServerConfig
|
||||||
|
};
|
|
@ -0,0 +1,74 @@
|
||||||
|
const path = require('path');
|
||||||
|
const AntdDayjsWebpackPlugin = require('antd-dayjs-webpack-plugin');
|
||||||
|
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||||
|
const tsconfig = require('../../tsconfig.json');
|
||||||
|
|
||||||
|
const RESOURCES_PATH = path.resolve(__dirname, '../../');
|
||||||
|
const HTML_PATH = path.resolve(RESOURCES_PATH, 'public/index.html');
|
||||||
|
const HTML_INSTALL_PATH = path.resolve(RESOURCES_PATH, 'public/install.html');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
entry: {
|
||||||
|
install: './src/Install.tsx',
|
||||||
|
main: './src/App.tsx'
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
extensions: ['.tsx', '.ts', '.js', '.pcss'],
|
||||||
|
alias: Object.keys(tsconfig.compilerOptions.paths).reduce((aliases, key) => {
|
||||||
|
// Reduce to load aliases from ./tsconfig.json in appropriate for webpack form
|
||||||
|
const paths = tsconfig.compilerOptions.paths[key].map(p => p.replace('/*', ''));
|
||||||
|
aliases[key.replace('/*', '')] = path.resolve(
|
||||||
|
__dirname,
|
||||||
|
'../../',
|
||||||
|
tsconfig.compilerOptions.baseUrl,
|
||||||
|
...paths,
|
||||||
|
);
|
||||||
|
return aliases;
|
||||||
|
}, {}),
|
||||||
|
},
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.tsx?$/,
|
||||||
|
use: 'ts-loader',
|
||||||
|
exclude: /node_modules/,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.(woff|woff2)$/,
|
||||||
|
use: [{
|
||||||
|
loader: 'file-loader',
|
||||||
|
options:{
|
||||||
|
outputPath:'./',
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test:/\.(png|jpe?g|gif)$/,
|
||||||
|
exclude: /(node_modules)/,
|
||||||
|
use:[{
|
||||||
|
loader:'file-loader',
|
||||||
|
options:{
|
||||||
|
outputPath:'./images',
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
plugins: [
|
||||||
|
// new AntdDayjsWebpackPlugin()
|
||||||
|
new HtmlWebpackPlugin({
|
||||||
|
inject: true,
|
||||||
|
cache: false,
|
||||||
|
chunks: ['main'],
|
||||||
|
template: HTML_PATH,
|
||||||
|
}),
|
||||||
|
new HtmlWebpackPlugin({
|
||||||
|
inject: true,
|
||||||
|
cache: false,
|
||||||
|
chunks: ['install'],
|
||||||
|
filename: 'install.html',
|
||||||
|
template: HTML_INSTALL_PATH,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
};
|
|
@ -0,0 +1,113 @@
|
||||||
|
const history = require('connect-history-api-fallback');
|
||||||
|
const { merge } = require('webpack-merge');
|
||||||
|
const path = require('path');
|
||||||
|
const proxy = require('http-proxy-middleware');
|
||||||
|
const Webpack = require('webpack');
|
||||||
|
|
||||||
|
const { getDevServerConfig } = require('./helpers');
|
||||||
|
const baseConfig = require('./webpack.config.base');
|
||||||
|
|
||||||
|
const target = getDevServerConfig();
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
target: `http://${target.host}:${target.port}`, // target host
|
||||||
|
changeOrigin: true, // needed for virtual hosted sites
|
||||||
|
};
|
||||||
|
const apiProxy = proxy.createProxyMiddleware(options);
|
||||||
|
|
||||||
|
module.exports = merge(baseConfig, {
|
||||||
|
mode: 'development',
|
||||||
|
output: {
|
||||||
|
path: path.resolve(__dirname, '../../build2'),
|
||||||
|
filename: '[name].bundle.js',
|
||||||
|
},
|
||||||
|
optimization: {
|
||||||
|
noEmitOnErrors: true,
|
||||||
|
},
|
||||||
|
devServer: {
|
||||||
|
port: 4000,
|
||||||
|
historyApiFallback: true,
|
||||||
|
before: (app) => {
|
||||||
|
app.use('/control', apiProxy);
|
||||||
|
app.use(history({
|
||||||
|
rewrites: [
|
||||||
|
{
|
||||||
|
from: /\.(png|jpe?g|gif)$/,
|
||||||
|
to: (context) => {
|
||||||
|
const name = context.parsedUrl.pathname.split('/');
|
||||||
|
return `/images/${name[name.length - 1]}`
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
from: /\.(woff|woff2)$/,
|
||||||
|
to: (context) => {
|
||||||
|
const name = context.parsedUrl.pathname.split('/');
|
||||||
|
return `/${name[name.length - 1]}`
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
from: /\.(js|css)$/,
|
||||||
|
to: (context) => {
|
||||||
|
const name = context.parsedUrl.pathname.split('/');
|
||||||
|
return `/${name[name.length - 1]}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
devtool: 'eval-source-map',
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
enforce: 'pre',
|
||||||
|
test: /\.tsx?$/,
|
||||||
|
exclude: /node_modules/,
|
||||||
|
loader: 'eslint-loader',
|
||||||
|
options: {
|
||||||
|
configFile: path.resolve(__dirname, '../lint/dev.js'),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: (resource) => {
|
||||||
|
return (
|
||||||
|
resource.indexOf('.pcss')+1
|
||||||
|
|| resource.indexOf('.css')+1
|
||||||
|
|| resource.indexOf('.less')+1
|
||||||
|
) && !(resource.indexOf('.module.')+1);
|
||||||
|
},
|
||||||
|
use: ['style-loader', 'css-loader', 'postcss-loader', {
|
||||||
|
loader: 'less-loader',
|
||||||
|
options: {
|
||||||
|
javascriptEnabled: true,
|
||||||
|
},
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.module\.p?css$/,
|
||||||
|
use: [
|
||||||
|
'style-loader',
|
||||||
|
{
|
||||||
|
loader: 'css-loader',
|
||||||
|
options: {
|
||||||
|
modules: true,
|
||||||
|
sourceMap: true,
|
||||||
|
importLoaders: 1,
|
||||||
|
modules: {
|
||||||
|
localIdentName: "[name]__[local]___[hash:base64:5]",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'postcss-loader',
|
||||||
|
],
|
||||||
|
exclude: /node_modules/,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new Webpack.DefinePlugin({
|
||||||
|
DEV: true,
|
||||||
|
'process.env.DEV_SERVER_PORT': JSON.stringify(3000),
|
||||||
|
}),
|
||||||
|
new Webpack.HotModuleReplacementPlugin(),
|
||||||
|
new Webpack.ProgressPlugin(),
|
||||||
|
],
|
||||||
|
});
|
|
@ -0,0 +1,91 @@
|
||||||
|
const path = require('path');
|
||||||
|
const { merge } = require('webpack-merge');
|
||||||
|
const baseConfig = require('./webpack.config.base');
|
||||||
|
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
|
||||||
|
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
|
||||||
|
const TerserJSPlugin = require('terser-webpack-plugin');
|
||||||
|
const Webpack = require('webpack');
|
||||||
|
const CopyPlugin = require('copy-webpack-plugin');
|
||||||
|
|
||||||
|
module.exports = merge(baseConfig, {
|
||||||
|
mode: 'production',
|
||||||
|
devtool: 'source-map',
|
||||||
|
output: {
|
||||||
|
path: path.resolve(__dirname, '../../../build2/static'),
|
||||||
|
filename: '[name].bundle.[hash:5].js',
|
||||||
|
publicPath: '/'
|
||||||
|
},
|
||||||
|
optimization: {
|
||||||
|
minimizer: [new TerserJSPlugin({terserOptions: {
|
||||||
|
output: {
|
||||||
|
comments: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
extractComments: false,
|
||||||
|
}), new OptimizeCSSAssetsPlugin({})],
|
||||||
|
splitChunks: {
|
||||||
|
cacheGroups: {
|
||||||
|
styles: {
|
||||||
|
name: 'styles',
|
||||||
|
test: /\.css$/,
|
||||||
|
chunks: 'all',
|
||||||
|
enforce: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: (resource) => {
|
||||||
|
return (
|
||||||
|
resource.indexOf('.pcss')+1
|
||||||
|
|| resource.indexOf('.css')+1
|
||||||
|
|| resource.indexOf('.less')+1
|
||||||
|
) && !(resource.indexOf('.module.')+1);
|
||||||
|
},
|
||||||
|
use: [{
|
||||||
|
loader: MiniCssExtractPlugin.loader,
|
||||||
|
options: {
|
||||||
|
esModules: true,
|
||||||
|
}
|
||||||
|
}, 'css-loader', 'postcss-loader', {
|
||||||
|
loader: 'less-loader',
|
||||||
|
options: {
|
||||||
|
javascriptEnabled: true,
|
||||||
|
},
|
||||||
|
}],
|
||||||
|
exclude: /node_modules/,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.module\.p?css$/,
|
||||||
|
use: [
|
||||||
|
{
|
||||||
|
loader: MiniCssExtractPlugin.loader,
|
||||||
|
options: {
|
||||||
|
esModules: true,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
loader: 'css-loader',
|
||||||
|
options: {
|
||||||
|
modules: true,
|
||||||
|
sourceMap: true,
|
||||||
|
importLoaders: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'postcss-loader',
|
||||||
|
],
|
||||||
|
exclude: /node_modules/,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new Webpack.DefinePlugin({
|
||||||
|
DEV: false,
|
||||||
|
}),
|
||||||
|
new MiniCssExtractPlugin({
|
||||||
|
filename: '[name].[hash:5].css',
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
});
|
|
@ -0,0 +1,18 @@
|
||||||
|
import './main.pcss';
|
||||||
|
import './lib/ant/ant.less';
|
||||||
|
import React from 'react';
|
||||||
|
import ReactDOM from 'react-dom';
|
||||||
|
import Store, { storeValue } from 'Store';
|
||||||
|
import './lib/ant';
|
||||||
|
|
||||||
|
import App from './components/App';
|
||||||
|
|
||||||
|
const Container = () => {
|
||||||
|
return (
|
||||||
|
<Store.Provider value={storeValue}>
|
||||||
|
<App/>
|
||||||
|
</Store.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
ReactDOM.render(<Container />, document.getElementById('app'));
|
|
@ -0,0 +1,18 @@
|
||||||
|
import './main.pcss';
|
||||||
|
import './lib/ant/ant.less';
|
||||||
|
import React from 'react';
|
||||||
|
import ReactDOM from 'react-dom';
|
||||||
|
import Store, { storeValue } from 'Store/installStore';
|
||||||
|
import './lib/ant';
|
||||||
|
|
||||||
|
import Install from './components/Install';
|
||||||
|
|
||||||
|
const Container = () => {
|
||||||
|
return (
|
||||||
|
<Store.Provider value={storeValue}>
|
||||||
|
<Install/>
|
||||||
|
</Store.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
ReactDOM.render(<Container />, document.getElementById('app'));
|
Binary file not shown.
After Width: | Height: | Size: 7.0 KiB |
|
@ -0,0 +1,16 @@
|
||||||
|
import { observer } from 'mobx-react-lite';
|
||||||
|
import React, { FC, useContext } from 'react';
|
||||||
|
import Store from 'Store';
|
||||||
|
import Icons from 'Lib/theme/Icons';
|
||||||
|
|
||||||
|
const App: FC = observer(() => {
|
||||||
|
const store = useContext(Store);
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{store.ui.currentLang}
|
||||||
|
<Icons/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default App;
|
|
@ -0,0 +1,14 @@
|
||||||
|
.layout {
|
||||||
|
background-image: url('../../assets/img/background_min.png');
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
min-height: 100vh;
|
||||||
|
background-color: #f1f3f7;
|
||||||
|
}
|
||||||
|
.container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 40px;
|
||||||
|
}
|
||||||
|
.content {
|
||||||
|
max-width: 404px;
|
||||||
|
}
|
|
@ -0,0 +1,122 @@
|
||||||
|
import React, { FC } from 'react';
|
||||||
|
import { Layout } from 'antd';
|
||||||
|
import { Formik, FormikHelpers } from 'formik';
|
||||||
|
import { observer } from 'mobx-react-lite';
|
||||||
|
|
||||||
|
import { IInitialConfigurationBeta } from 'Entities/InitialConfigurationBeta';
|
||||||
|
import Icons from 'Lib/theme/Icons';
|
||||||
|
import {
|
||||||
|
DEFAULT_DNS_ADDRESS,
|
||||||
|
DEFAULT_DNS_PORT,
|
||||||
|
DEFAULT_IP_ADDRESS,
|
||||||
|
DEFAULT_IP_PORT,
|
||||||
|
} from 'Consts/install';
|
||||||
|
import { notifyError } from 'Common/ui';
|
||||||
|
import InstallStore from 'Store/stores/Install';
|
||||||
|
|
||||||
|
import AdminInterface from './components/AdminInterface';
|
||||||
|
import Auth from './components/Auth';
|
||||||
|
import DnsServer from './components/DnsServer';
|
||||||
|
import Stepper from './components/Stepper';
|
||||||
|
import Welcome from './components/Welcome';
|
||||||
|
import ConfigureDevices from './components/ConfigureDevices';
|
||||||
|
|
||||||
|
import s from './Install.module.pcss';
|
||||||
|
|
||||||
|
const { Content } = Layout;
|
||||||
|
|
||||||
|
export type FormValues = IInitialConfigurationBeta & { step: number };
|
||||||
|
|
||||||
|
const InstallForm: FC = observer(() => {
|
||||||
|
const initialValues: FormValues = {
|
||||||
|
step: 0,
|
||||||
|
web: {
|
||||||
|
ip: [DEFAULT_IP_ADDRESS],
|
||||||
|
port: DEFAULT_IP_PORT,
|
||||||
|
},
|
||||||
|
dns: {
|
||||||
|
ip: [DEFAULT_DNS_ADDRESS],
|
||||||
|
port: DEFAULT_DNS_PORT,
|
||||||
|
},
|
||||||
|
password: '',
|
||||||
|
username: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
const onNext = async (values: FormValues, { setFieldValue }: FormikHelpers<FormValues>) => {
|
||||||
|
const currentStep = values.step;
|
||||||
|
const checker = (condition: boolean, message: string) => {
|
||||||
|
if (condition) {
|
||||||
|
setFieldValue('step', currentStep + 1);
|
||||||
|
} else {
|
||||||
|
notifyError(message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
switch (currentStep) {
|
||||||
|
case 1: {
|
||||||
|
// web
|
||||||
|
const check = await InstallStore.checkConfig(values);
|
||||||
|
checker(check?.web?.status === '', check?.web?.status || '');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 3: {
|
||||||
|
// dns
|
||||||
|
const check = await InstallStore.checkConfig(values);
|
||||||
|
checker(check?.dns?.status === '', check?.dns?.status || '');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 4: {
|
||||||
|
// configure
|
||||||
|
const config = await InstallStore.configure(values);
|
||||||
|
if (config) {
|
||||||
|
const { web } = values;
|
||||||
|
window.location.href = `http://${web.ip[0]}:${web.port}`;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
setFieldValue('step', currentStep + 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Formik
|
||||||
|
initialValues={initialValues}
|
||||||
|
onSubmit={onNext}
|
||||||
|
>
|
||||||
|
{({ values, handleSubmit, setFieldValue }) => (
|
||||||
|
<form noValidate className={s.content} onSubmit={handleSubmit}>
|
||||||
|
<Stepper currentStep={values.step} />
|
||||||
|
{values.step === 0 && (
|
||||||
|
<Welcome onNext={() => setFieldValue('step', 1)}/>
|
||||||
|
)}
|
||||||
|
{values.step === 1 && (
|
||||||
|
<AdminInterface values={values} setFieldValue={setFieldValue} />
|
||||||
|
)}
|
||||||
|
{values.step === 2 && (
|
||||||
|
<Auth values={values} setFieldValue={setFieldValue} />
|
||||||
|
)}
|
||||||
|
{values.step === 3 && (
|
||||||
|
<DnsServer values={values} setFieldValue={setFieldValue} />
|
||||||
|
)}
|
||||||
|
{values.step === 4 && (
|
||||||
|
<ConfigureDevices values={values} setFieldValue={setFieldValue} />
|
||||||
|
)}
|
||||||
|
</form>
|
||||||
|
)}
|
||||||
|
</Formik>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const Install: FC = () => {
|
||||||
|
return (
|
||||||
|
<Layout className={s.layout}>
|
||||||
|
<Content className={s.container}>
|
||||||
|
<InstallForm />
|
||||||
|
</Content>
|
||||||
|
<Icons/>
|
||||||
|
</Layout>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Install;
|
|
@ -0,0 +1,17 @@
|
||||||
|
.manualOptions {
|
||||||
|
margin-bottom: 48px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.name {
|
||||||
|
padding-bottom: 5px;
|
||||||
|
border-bottom: 1px solid var(--gray300);
|
||||||
|
margin-bottom: 16px;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.manualOption {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: baseline;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,145 @@
|
||||||
|
import React, { FC, useContext } from 'react';
|
||||||
|
import cn from 'classnames';
|
||||||
|
import { observer } from 'mobx-react-lite';
|
||||||
|
import { FormikHelpers } from 'formik';
|
||||||
|
|
||||||
|
import { Input, Radio, Switch } from 'Common/controls';
|
||||||
|
import { DEFAULT_IP_ADDRESS } from 'Consts/install';
|
||||||
|
import { chechNetworkType, NETWORK_TYPE } from 'Helpers/installHelpers';
|
||||||
|
import theme from 'Lib/theme';
|
||||||
|
import Store from 'Store/installStore';
|
||||||
|
|
||||||
|
import s from './AdminInterface.module.pcss';
|
||||||
|
import { FormValues } from '../../Install';
|
||||||
|
import StepButtons from '../StepButtons';
|
||||||
|
|
||||||
|
enum NETWORK_OPTIONS {
|
||||||
|
ALL = 'all',
|
||||||
|
CUSTOM = 'custom',
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AdminInterfaceProps {
|
||||||
|
values: FormValues;
|
||||||
|
setFieldValue: FormikHelpers<FormValues>['setFieldValue'];
|
||||||
|
}
|
||||||
|
|
||||||
|
const AdminInterface: FC<AdminInterfaceProps> = observer(({
|
||||||
|
values,
|
||||||
|
setFieldValue,
|
||||||
|
}) => {
|
||||||
|
const { ui: { intl }, install: { addresses } } = useContext(Store);
|
||||||
|
const { web: { ip } } = values;
|
||||||
|
const radioValue = ip.length === 1 && ip[0] === DEFAULT_IP_ADDRESS
|
||||||
|
? NETWORK_OPTIONS.ALL : NETWORK_OPTIONS.CUSTOM;
|
||||||
|
|
||||||
|
const onSelectRadio = (v: string | number) => {
|
||||||
|
const value = v === NETWORK_OPTIONS.ALL
|
||||||
|
? [DEFAULT_IP_ADDRESS] : [];
|
||||||
|
setFieldValue('web.ip', value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getManualBlock = () => (
|
||||||
|
<div className={s.manualOptions}>
|
||||||
|
{addresses?.interfaces.map((a) => {
|
||||||
|
let name = '';
|
||||||
|
const type = chechNetworkType(a.name);
|
||||||
|
switch (type) {
|
||||||
|
case NETWORK_TYPE.ETHERNET:
|
||||||
|
name = `${intl.getMessage('ethernet')} (${a.name}) `;
|
||||||
|
break;
|
||||||
|
case NETWORK_TYPE.LOCAL:
|
||||||
|
name = `${intl.getMessage('localhost')} (${a.name}) `;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
name = a.name || '';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div key={a.name}>
|
||||||
|
<div>
|
||||||
|
<div className={s.name}>
|
||||||
|
{name}
|
||||||
|
</div>
|
||||||
|
{a.ipAddresses?.map((addrIp) => (
|
||||||
|
<div key={addrIp} className={s.manualOption}>
|
||||||
|
<div className={theme.typography.subtext}>
|
||||||
|
http://{addrIp}
|
||||||
|
</div>
|
||||||
|
<Switch
|
||||||
|
checked={values.web.ip.includes(addrIp)}
|
||||||
|
onChange={() => {
|
||||||
|
const temp = new Set(ip);
|
||||||
|
if (temp.has(addrIp)) {
|
||||||
|
temp.delete(addrIp);
|
||||||
|
} else {
|
||||||
|
temp.add(addrIp);
|
||||||
|
}
|
||||||
|
setFieldValue('web.ip', Array.from(temp.values()));
|
||||||
|
}}/>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={s.content}>
|
||||||
|
<div className={theme.typography.title}>
|
||||||
|
{intl.getMessage('install_admin_interface_title')}
|
||||||
|
</div>
|
||||||
|
<div className={cn(theme.typography.text, theme.typography.text_block)}>
|
||||||
|
{intl.getMessage('install_admin_interface_title_decs')}
|
||||||
|
</div>
|
||||||
|
<div className={theme.typography.subTitle}>
|
||||||
|
{intl.getMessage('install_admin_interface_where_interface')}
|
||||||
|
</div>
|
||||||
|
<div className={cn(theme.typography.text, theme.typography.text_base)}>
|
||||||
|
{intl.getMessage('install_admin_interface_where_interface_desc')}
|
||||||
|
</div>
|
||||||
|
<Radio
|
||||||
|
value={radioValue}
|
||||||
|
onSelect={onSelectRadio}
|
||||||
|
options={[
|
||||||
|
{
|
||||||
|
value: NETWORK_OPTIONS.ALL,
|
||||||
|
label: intl.getMessage('install_all_networks'),
|
||||||
|
desc: intl.getMessage('install_all_networks_description'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: NETWORK_OPTIONS.CUSTOM,
|
||||||
|
label: intl.getMessage('install_choose_networks'),
|
||||||
|
desc: intl.getMessage('install_choose_networks_desc'),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
{ radioValue !== NETWORK_OPTIONS.ALL && getManualBlock()}
|
||||||
|
<div className={theme.typography.subTitle}>
|
||||||
|
{intl.getMessage('install_admin_interface_port')}
|
||||||
|
</div>
|
||||||
|
<div className={cn(theme.typography.text, theme.typography.text_base)}>
|
||||||
|
{intl.getMessage('install_admin_interface_port_desc')}
|
||||||
|
</div>
|
||||||
|
<Input
|
||||||
|
label={`${intl.getMessage('port')}:`}
|
||||||
|
placeholder={intl.getMessage('port')}
|
||||||
|
type="number"
|
||||||
|
name="webPort"
|
||||||
|
value={values.web.port}
|
||||||
|
onChange={(v) => {
|
||||||
|
const port = v === '' ? '' : parseInt(v, 10);
|
||||||
|
setFieldValue('web.port', port);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<StepButtons
|
||||||
|
setFieldValue={setFieldValue}
|
||||||
|
currentStep={1}
|
||||||
|
values={values}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default AdminInterface;
|
|
@ -0,0 +1 @@
|
||||||
|
export { default } from './AdminInterface';
|
|
@ -0,0 +1,55 @@
|
||||||
|
import React, { FC, useContext } from 'react';
|
||||||
|
import cn from 'classnames';
|
||||||
|
import { observer } from 'mobx-react-lite';
|
||||||
|
import { FormikHelpers } from 'formik';
|
||||||
|
|
||||||
|
import { Input } from 'Common/controls';
|
||||||
|
import theme from 'Lib/theme';
|
||||||
|
import Store from 'Store/installStore';
|
||||||
|
|
||||||
|
import StepButtons from '../StepButtons';
|
||||||
|
import { FormValues } from '../../Install';
|
||||||
|
|
||||||
|
interface AuthProps {
|
||||||
|
values: FormValues;
|
||||||
|
setFieldValue: FormikHelpers<FormValues>['setFieldValue'];
|
||||||
|
}
|
||||||
|
|
||||||
|
const Auth: FC<AuthProps> = observer(({
|
||||||
|
values,
|
||||||
|
setFieldValue,
|
||||||
|
}) => {
|
||||||
|
const { ui: { intl } } = useContext(Store);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className={theme.typography.title}>
|
||||||
|
{intl.getMessage('install_auth_title')}
|
||||||
|
</div>
|
||||||
|
<div className={cn(theme.typography.text, theme.typography.text_block)}>
|
||||||
|
{intl.getMessage('install_auth_description')}
|
||||||
|
</div>
|
||||||
|
<Input
|
||||||
|
placeholder={intl.getMessage('login')}
|
||||||
|
type="username"
|
||||||
|
name="username"
|
||||||
|
value={values.username}
|
||||||
|
onChange={(v) => setFieldValue('username', v)}
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
placeholder={intl.getMessage('password')}
|
||||||
|
type="password"
|
||||||
|
name="password"
|
||||||
|
value={values.password}
|
||||||
|
onChange={(v) => setFieldValue('password', v)}
|
||||||
|
/>
|
||||||
|
<StepButtons
|
||||||
|
setFieldValue={setFieldValue}
|
||||||
|
currentStep={2}
|
||||||
|
values={values}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default Auth;
|
|
@ -0,0 +1 @@
|
||||||
|
export { default } from './Auth';
|
|
@ -0,0 +1,4 @@
|
||||||
|
.tabs {
|
||||||
|
width: 505px;
|
||||||
|
margin-left: -131px;
|
||||||
|
}
|
|
@ -0,0 +1,128 @@
|
||||||
|
import React, { FC, useContext } from 'react';
|
||||||
|
import { Tabs } from 'antd';
|
||||||
|
import cn from 'classnames';
|
||||||
|
import { FormikHelpers } from 'formik';
|
||||||
|
|
||||||
|
import Store from 'Store/installStore';
|
||||||
|
import theme from 'Lib/theme';
|
||||||
|
import { danger, p } from 'Common/formating';
|
||||||
|
import { DEFAULT_DNS_PORT, DEFAULT_IP_ADDRESS, DEFAULT_IP_PORT } from 'Consts/install';
|
||||||
|
|
||||||
|
import { FormValues } from '../../Install';
|
||||||
|
import StepButtons from '../StepButtons';
|
||||||
|
import s from './ConfigureDevices.module.pcss';
|
||||||
|
|
||||||
|
const { TabPane } = Tabs;
|
||||||
|
|
||||||
|
interface ConfigureDevicesProps {
|
||||||
|
values: FormValues;
|
||||||
|
setFieldValue: FormikHelpers<FormValues>['setFieldValue'];
|
||||||
|
}
|
||||||
|
|
||||||
|
const ConfigureDevices: FC<ConfigureDevicesProps> = ({
|
||||||
|
values, setFieldValue,
|
||||||
|
}) => {
|
||||||
|
const { ui: { intl }, install: { addresses } } = useContext(Store);
|
||||||
|
|
||||||
|
const dhcp = (e: string) => (
|
||||||
|
// TODO: link to dhcp
|
||||||
|
<a href="http://" target="_blank" rel="noopener noreferrer">{e}</a>
|
||||||
|
);
|
||||||
|
|
||||||
|
const allIps = addresses?.interfaces.reduce<string[]>((all, data) => {
|
||||||
|
const { ipAddresses } = data;
|
||||||
|
if (ipAddresses) {
|
||||||
|
all.push(...ipAddresses);
|
||||||
|
}
|
||||||
|
return all;
|
||||||
|
}, [] as string[]);
|
||||||
|
|
||||||
|
const { web: { ip: webIp }, dns: { ip: dnsIp } } = values;
|
||||||
|
const selectedWebIps = webIp.length === 1 && webIp[0] === DEFAULT_IP_ADDRESS
|
||||||
|
? allIps : webIp;
|
||||||
|
const selectedDnsIps = dnsIp.length === 1 && dnsIp[0] === DEFAULT_IP_ADDRESS
|
||||||
|
? allIps : dnsIp;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className={theme.typography.title}>
|
||||||
|
{intl.getMessage('install_configure_title')}
|
||||||
|
</div>
|
||||||
|
<div className={cn(theme.typography.text, theme.typography.text_block)}>
|
||||||
|
{intl.getMessage('install_configure_danger_notice', { danger })}
|
||||||
|
</div>
|
||||||
|
<div className={theme.typography.subTitle}>
|
||||||
|
{intl.getMessage('install_configure_how_to_title')}
|
||||||
|
</div>
|
||||||
|
<Tabs defaultActiveKey="1" tabPosition="left" className={s.tabs}>
|
||||||
|
<TabPane tab="Router" key="1">
|
||||||
|
<div className={cn(theme.typography.text, theme.typography.text_base)}>
|
||||||
|
{intl.getMessage('install_configure_router', { p })}
|
||||||
|
</div>
|
||||||
|
</TabPane>
|
||||||
|
<TabPane tab="Windows" key="2">
|
||||||
|
<div className={cn(theme.typography.text, theme.typography.text_base)}>
|
||||||
|
{intl.getMessage('install_configure_windows', { p })}
|
||||||
|
</div>
|
||||||
|
</TabPane>
|
||||||
|
<TabPane tab="Macos" key="3">
|
||||||
|
<div className={cn(theme.typography.text, theme.typography.text_base)}>
|
||||||
|
{intl.getMessage('install_configure_macos', { p })}
|
||||||
|
</div>
|
||||||
|
</TabPane>
|
||||||
|
<TabPane tab="Linux" key="4">
|
||||||
|
<div className={cn(theme.typography.text, theme.typography.text_base)}>
|
||||||
|
{/* TODO: add linux setup */}
|
||||||
|
{intl.getMessage('install_configure_router', { p })}
|
||||||
|
</div>
|
||||||
|
</TabPane>
|
||||||
|
<TabPane tab="Android" key="5">
|
||||||
|
<div className={cn(theme.typography.text, theme.typography.text_base)}>
|
||||||
|
{intl.getMessage('install_configure_android', { p })}
|
||||||
|
</div>
|
||||||
|
</TabPane>
|
||||||
|
<TabPane tab="iOs" key="6">
|
||||||
|
<div className={cn(theme.typography.text, theme.typography.text_base)}>
|
||||||
|
{intl.getMessage('install_configure_ios', { p })}
|
||||||
|
</div>
|
||||||
|
</TabPane>
|
||||||
|
</Tabs>
|
||||||
|
|
||||||
|
<div className={theme.typography.subTitle}>
|
||||||
|
{intl.getMessage('install_configure_adresses')}
|
||||||
|
</div>
|
||||||
|
<div className={cn(theme.typography.text, theme.typography.text_base)}>
|
||||||
|
<p>
|
||||||
|
{intl.getMessage('install_admin_interface_title')}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
{selectedWebIps?.map((ip) => (
|
||||||
|
<div key={ip}>
|
||||||
|
{ip}{values.web.port !== DEFAULT_IP_PORT && `:${values.web.port}`}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
{intl.getMessage('install_dns_server_title')}
|
||||||
|
</p>
|
||||||
|
<div>
|
||||||
|
{selectedDnsIps?.map((ip) => (
|
||||||
|
<div key={ip}>
|
||||||
|
{ip}{values.dns.port !== DEFAULT_DNS_PORT && `:${values.dns.port}`}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={cn(theme.typography.text, theme.typography.text_base)}>
|
||||||
|
{intl.getMessage('install_configure_dhcp', { dhcp })}
|
||||||
|
</div>
|
||||||
|
<StepButtons
|
||||||
|
setFieldValue={setFieldValue}
|
||||||
|
currentStep={4}
|
||||||
|
values={values}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ConfigureDevices;
|
|
@ -0,0 +1 @@
|
||||||
|
export { default } from './ConfigureDevices';
|
|
@ -0,0 +1,12 @@
|
||||||
|
.manualOptions {
|
||||||
|
margin-bottom: 48px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.manualOption {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: baseline;
|
||||||
|
padding-bottom: 16px;
|
||||||
|
border-bottom: 1px solid var(--gray300);
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
|
@ -0,0 +1,145 @@
|
||||||
|
import React, { FC, useContext } from 'react';
|
||||||
|
import cn from 'classnames';
|
||||||
|
import { observer } from 'mobx-react-lite';
|
||||||
|
import { FormikHelpers } from 'formik';
|
||||||
|
|
||||||
|
import { Input, Radio, Switch } from 'Common/controls';
|
||||||
|
import { DEFAULT_IP_ADDRESS } from 'Consts/install';
|
||||||
|
import { chechNetworkType, NETWORK_TYPE } from 'Helpers/installHelpers';
|
||||||
|
import theme from 'Lib/theme';
|
||||||
|
import Store from 'Store/installStore';
|
||||||
|
|
||||||
|
import s from './DnsServer.module.pcss';
|
||||||
|
import { FormValues } from '../../Install';
|
||||||
|
import StepButtons from '../StepButtons';
|
||||||
|
|
||||||
|
enum NETWORK_OPTIONS {
|
||||||
|
ALL = 'all',
|
||||||
|
CUSTOM = 'custom',
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DnsServerProps {
|
||||||
|
values: FormValues;
|
||||||
|
setFieldValue: FormikHelpers<FormValues>['setFieldValue'];
|
||||||
|
}
|
||||||
|
|
||||||
|
const DnsServer: FC<DnsServerProps> = observer(({
|
||||||
|
values,
|
||||||
|
setFieldValue,
|
||||||
|
}) => {
|
||||||
|
const { ui: { intl }, install: { addresses } } = useContext(Store);
|
||||||
|
const { dns: { ip } } = values;
|
||||||
|
const radioValue = ip.length === 1 && ip[0] === DEFAULT_IP_ADDRESS
|
||||||
|
? NETWORK_OPTIONS.ALL : NETWORK_OPTIONS.CUSTOM;
|
||||||
|
|
||||||
|
const onSelectRadio = (v: string | number) => {
|
||||||
|
const value = v === NETWORK_OPTIONS.ALL
|
||||||
|
? [DEFAULT_IP_ADDRESS] : [];
|
||||||
|
setFieldValue('dns.ip', value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getManualBlock = () => (
|
||||||
|
<div className={s.manualOptions}>
|
||||||
|
{addresses?.interfaces.map((a) => {
|
||||||
|
let name = '';
|
||||||
|
const type = chechNetworkType(a.name);
|
||||||
|
switch (type) {
|
||||||
|
case NETWORK_TYPE.ETHERNET:
|
||||||
|
name = `${intl.getMessage('ethernet')} (${a.name}) `;
|
||||||
|
break;
|
||||||
|
case NETWORK_TYPE.LOCAL:
|
||||||
|
name = `${intl.getMessage('localhost')} (${a.name}) `;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
name = a.name || '';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div key={a.name}>
|
||||||
|
<div>
|
||||||
|
<div className={s.name}>
|
||||||
|
{name}
|
||||||
|
</div>
|
||||||
|
{a.ipAddresses?.map((addrIp) => (
|
||||||
|
<div key={addrIp} className={s.manualOption}>
|
||||||
|
<div className={theme.typography.subtext}>
|
||||||
|
{addrIp}
|
||||||
|
</div>
|
||||||
|
<Switch
|
||||||
|
checked={values.dns.ip.includes(addrIp)}
|
||||||
|
onChange={() => {
|
||||||
|
const temp = new Set(ip);
|
||||||
|
if (temp.has(addrIp)) {
|
||||||
|
temp.delete(addrIp);
|
||||||
|
} else {
|
||||||
|
temp.add(addrIp);
|
||||||
|
}
|
||||||
|
setFieldValue('dns.ip', Array.from(temp.values()));
|
||||||
|
}}/>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className={theme.typography.title}>
|
||||||
|
{intl.getMessage('install_dns_server_title')}
|
||||||
|
</div>
|
||||||
|
<div className={cn(theme.typography.text, theme.typography.text_block)}>
|
||||||
|
{intl.getMessage('install_dns_server_desc')}
|
||||||
|
</div>
|
||||||
|
<div className={theme.typography.subTitle}>
|
||||||
|
{intl.getMessage('install_dns_server_network_interfaces')}
|
||||||
|
</div>
|
||||||
|
<div className={cn(theme.typography.text, theme.typography.text_base)}>
|
||||||
|
{intl.getMessage('install_dns_server_network_interfaces_desc')}
|
||||||
|
</div>
|
||||||
|
<Radio
|
||||||
|
value={radioValue}
|
||||||
|
onSelect={onSelectRadio}
|
||||||
|
options={[
|
||||||
|
{
|
||||||
|
value: NETWORK_OPTIONS.ALL,
|
||||||
|
label: intl.getMessage('install_all_networks'),
|
||||||
|
desc: intl.getMessage('install_all_networks_description'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: NETWORK_OPTIONS.CUSTOM,
|
||||||
|
label: intl.getMessage('install_choose_networks'),
|
||||||
|
desc: intl.getMessage('install_choose_networks_desc'),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
{ radioValue !== NETWORK_OPTIONS.ALL && getManualBlock()}
|
||||||
|
<div className={theme.typography.subTitle}>
|
||||||
|
{intl.getMessage('install_dns_server_port')}
|
||||||
|
</div>
|
||||||
|
<div className={cn(theme.typography.text, theme.typography.text_base)}>
|
||||||
|
{intl.getMessage('install_dns_server_port_desc')}
|
||||||
|
</div>
|
||||||
|
<Input
|
||||||
|
label={`${intl.getMessage('port')}:`}
|
||||||
|
placeholder={intl.getMessage('port')}
|
||||||
|
type="number"
|
||||||
|
name="dnsPort"
|
||||||
|
value={values.dns.port}
|
||||||
|
onChange={(v) => {
|
||||||
|
const port = v === '' ? '' : parseInt(v, 10);
|
||||||
|
setFieldValue('dns.port', port);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<StepButtons
|
||||||
|
setFieldValue={setFieldValue}
|
||||||
|
currentStep={3}
|
||||||
|
values={values}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default DnsServer;
|
|
@ -0,0 +1 @@
|
||||||
|
export { default } from './DnsServer';
|
|
@ -0,0 +1,8 @@
|
||||||
|
.button {
|
||||||
|
margin-top: 48px;
|
||||||
|
width: 190px;
|
||||||
|
|
||||||
|
&.inGroup {
|
||||||
|
margin-right: 24px;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
import React, { FC, useContext } from 'react';
|
||||||
|
import { Button } from 'antd';
|
||||||
|
import cn from 'classnames';
|
||||||
|
import { observer } from 'mobx-react-lite';
|
||||||
|
import { FormikHelpers } from 'formik';
|
||||||
|
|
||||||
|
import Store from 'Store/installStore';
|
||||||
|
|
||||||
|
import { FormValues } from '../../Install';
|
||||||
|
import s from './StepButtons.module.pcss';
|
||||||
|
|
||||||
|
interface StepButtonsProps {
|
||||||
|
setFieldValue: FormikHelpers<FormValues>['setFieldValue'];
|
||||||
|
currentStep: number;
|
||||||
|
values: FormValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
const StepButtons: FC<StepButtonsProps> = observer(({
|
||||||
|
setFieldValue,
|
||||||
|
currentStep,
|
||||||
|
}) => {
|
||||||
|
const { ui: { intl } } = useContext(Store);
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Button
|
||||||
|
size="large"
|
||||||
|
type="ghost"
|
||||||
|
className={cn(s.button, s.inGroup)}
|
||||||
|
onClick={() => setFieldValue('step', currentStep - 1)}
|
||||||
|
>
|
||||||
|
{intl.getMessage('back')}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
size="large"
|
||||||
|
type="primary"
|
||||||
|
htmlType="submit"
|
||||||
|
className={cn(s.button)}
|
||||||
|
>
|
||||||
|
{intl.getMessage('next')}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default StepButtons;
|
|
@ -0,0 +1 @@
|
||||||
|
export { default } from './StepButtons';
|
|
@ -0,0 +1,3 @@
|
||||||
|
.stepper {
|
||||||
|
margin-bottom: 48px;
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
import React, { FC } from 'react';
|
||||||
|
import { Steps } from 'antd';
|
||||||
|
|
||||||
|
import s from './Stepper.module.pcss';
|
||||||
|
|
||||||
|
interface StepperProps {
|
||||||
|
currentStep: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { Step } = Steps;
|
||||||
|
|
||||||
|
const Stepper: FC<StepperProps> = ({ currentStep }) => {
|
||||||
|
return (
|
||||||
|
<Steps progressDot current={currentStep} className={s.stepper}>
|
||||||
|
<Step/>
|
||||||
|
<Step/>
|
||||||
|
<Step/>
|
||||||
|
<Step/>
|
||||||
|
<Step/>
|
||||||
|
</Steps>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Stepper;
|
|
@ -0,0 +1 @@
|
||||||
|
export { default } from './Stepper';
|
|
@ -0,0 +1,15 @@
|
||||||
|
.iconContainer{
|
||||||
|
margin-bottom: 48px;
|
||||||
|
}
|
||||||
|
.icon {
|
||||||
|
width: 185px;
|
||||||
|
height: 57px;
|
||||||
|
}
|
||||||
|
.button {
|
||||||
|
margin-top: 48px;
|
||||||
|
width: 190px;
|
||||||
|
|
||||||
|
&.inGroup {
|
||||||
|
margin-right: 24px;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
import React, { FC, useContext } from 'react';
|
||||||
|
import { Button } from 'antd';
|
||||||
|
import { observer } from 'mobx-react-lite';
|
||||||
|
|
||||||
|
import Store from 'Store/installStore';
|
||||||
|
import Icon from 'Common/ui/Icon';
|
||||||
|
import theme from 'Lib/theme';
|
||||||
|
|
||||||
|
import s from './Welcome.module.pcss';
|
||||||
|
|
||||||
|
interface WelcomeProps {
|
||||||
|
onNext: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Welcome: FC<WelcomeProps> = observer(({ onNext }) => {
|
||||||
|
const { ui: { intl } } = useContext(Store);
|
||||||
|
return (
|
||||||
|
<div className={s.content}>
|
||||||
|
<div className={s.iconContainer}>
|
||||||
|
<Icon icon="mainLogo" className={s.icon} />
|
||||||
|
</div>
|
||||||
|
<div className={theme.typography.title}>
|
||||||
|
{intl.getMessage('install_wellcome_title')}
|
||||||
|
</div>
|
||||||
|
<div className={theme.typography.text}>
|
||||||
|
{intl.getMessage('install_wellcome_desc')}
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
size="large"
|
||||||
|
type="primary"
|
||||||
|
className={s.button}
|
||||||
|
onClick={onNext}
|
||||||
|
>
|
||||||
|
{intl.getMessage('install_wellcome_button')}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default Welcome;
|
|
@ -0,0 +1 @@
|
||||||
|
export { default } from './Welcome';
|
|
@ -0,0 +1 @@
|
||||||
|
export { default } from './Install';
|
|
@ -0,0 +1,146 @@
|
||||||
|
import React, { FC, FocusEvent, KeyboardEvent, ClipboardEvent, ChangeEvent, useState } from 'react';
|
||||||
|
import { Input as InputControl } from 'antd';
|
||||||
|
import { InputProps as InputControlProps } from 'antd/lib/input';
|
||||||
|
import cn from 'classnames';
|
||||||
|
|
||||||
|
import { Icon } from 'Common/ui';
|
||||||
|
import theme from 'Lib/theme';
|
||||||
|
|
||||||
|
interface AdminInterfaceProps {
|
||||||
|
autoComplete?: InputControlProps['autoComplete'];
|
||||||
|
autoFocus?: InputControlProps['autoFocus'];
|
||||||
|
className?: string;
|
||||||
|
description?: string;
|
||||||
|
disabled?: boolean;
|
||||||
|
error?: boolean;
|
||||||
|
id?: string;
|
||||||
|
inputMode?: InputControlProps['inputMode'];
|
||||||
|
label?: string;
|
||||||
|
wrapperClassName?: string;
|
||||||
|
name: string;
|
||||||
|
onBlur?: (e: FocusEvent<HTMLInputElement>) => void;
|
||||||
|
onChange?: (data: string, e?: ChangeEvent<HTMLInputElement>) => void;
|
||||||
|
onFocus?: (e: FocusEvent<HTMLInputElement>) => void;
|
||||||
|
onKeyDown?: (e: KeyboardEvent<HTMLInputElement>) => void;
|
||||||
|
onPaste?: (e: ClipboardEvent<HTMLInputElement>) => void;
|
||||||
|
pattern?: InputControlProps['pattern'];
|
||||||
|
placeholder: string;
|
||||||
|
prefix?: InputControlProps['prefix'];
|
||||||
|
size?: InputControlProps['size'];
|
||||||
|
suffix?: InputControlProps['suffix'];
|
||||||
|
type: InputControlProps['type'];
|
||||||
|
value: string | number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const InputComponent: FC<AdminInterfaceProps> = ({
|
||||||
|
autoComplete,
|
||||||
|
autoFocus,
|
||||||
|
className,
|
||||||
|
description,
|
||||||
|
disabled,
|
||||||
|
error,
|
||||||
|
id,
|
||||||
|
inputMode,
|
||||||
|
label,
|
||||||
|
wrapperClassName,
|
||||||
|
name,
|
||||||
|
onBlur,
|
||||||
|
onChange,
|
||||||
|
onFocus,
|
||||||
|
onKeyDown,
|
||||||
|
onPaste,
|
||||||
|
pattern,
|
||||||
|
placeholder,
|
||||||
|
prefix,
|
||||||
|
size = 'middle',
|
||||||
|
suffix,
|
||||||
|
type,
|
||||||
|
value,
|
||||||
|
}) => {
|
||||||
|
const [inputType, setInputType] = useState(type);
|
||||||
|
|
||||||
|
const inputClass = cn(
|
||||||
|
'input',
|
||||||
|
{ input_big: size === 'large' },
|
||||||
|
{ input_medium: size === 'middle' },
|
||||||
|
{ input_small: size === 'small' },
|
||||||
|
className,
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleBlur = (e: FocusEvent<HTMLInputElement>) => {
|
||||||
|
if (onBlur) {
|
||||||
|
onBlur(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const showPassword = () => {
|
||||||
|
if (inputType === 'password') {
|
||||||
|
setInputType('text');
|
||||||
|
} else {
|
||||||
|
setInputType('password');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const showPasswordIcon = () => {
|
||||||
|
const icon = inputType === 'password' ? 'visibility_disable' : 'visibility_enable';
|
||||||
|
return (
|
||||||
|
<Icon
|
||||||
|
icon={icon}
|
||||||
|
className={theme.form.reveal}
|
||||||
|
onClick={showPassword}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const validSuffix = (
|
||||||
|
<>
|
||||||
|
{!!suffix && suffix}
|
||||||
|
{(type === 'password') && showPasswordIcon()}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
let descriptionView = null;
|
||||||
|
if (description) {
|
||||||
|
descriptionView = (
|
||||||
|
<div className={theme.form.label}>
|
||||||
|
{description}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<label htmlFor={id || name} className={cn(theme.form.group, wrapperClassName)}>
|
||||||
|
{label && (
|
||||||
|
<div className={theme.form.label}>
|
||||||
|
{label}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<InputControl
|
||||||
|
autoComplete={autoComplete}
|
||||||
|
autoFocus={autoFocus}
|
||||||
|
className={inputClass}
|
||||||
|
disabled={disabled}
|
||||||
|
formNoValidate
|
||||||
|
id={id || name}
|
||||||
|
inputMode={inputMode}
|
||||||
|
name={name}
|
||||||
|
onBlur={handleBlur}
|
||||||
|
onChange={(e) => onChange && onChange(e.target.value ? e.target.value : '', e)}
|
||||||
|
onFocus={onFocus}
|
||||||
|
onKeyDown={onKeyDown}
|
||||||
|
onPaste={onPaste}
|
||||||
|
pattern={pattern}
|
||||||
|
placeholder={placeholder}
|
||||||
|
prefix={prefix}
|
||||||
|
size="large"
|
||||||
|
suffix={validSuffix}
|
||||||
|
type={inputType}
|
||||||
|
value={value}
|
||||||
|
data-error={error}
|
||||||
|
/>
|
||||||
|
{descriptionView}
|
||||||
|
</label>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default InputComponent;
|
|
@ -0,0 +1 @@
|
||||||
|
export { default as Input } from './Input';
|
|
@ -0,0 +1,16 @@
|
||||||
|
.group {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.radio {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
padding-bottom: 16px;
|
||||||
|
width: 100%;
|
||||||
|
border-bottom: 1px solid var(--gray300);
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
import React, { FC } from 'react';
|
||||||
|
import { Radio } from 'antd';
|
||||||
|
import { observer } from 'mobx-react-lite';
|
||||||
|
|
||||||
|
import theme from 'Lib/theme';
|
||||||
|
|
||||||
|
import s from './Radio.module.pcss';
|
||||||
|
|
||||||
|
const { Group } = Radio;
|
||||||
|
|
||||||
|
interface AdminInterfaceProps {
|
||||||
|
options: {
|
||||||
|
label: string;
|
||||||
|
desc?: string;
|
||||||
|
value: string | number;
|
||||||
|
}[];
|
||||||
|
onSelect: (value: string | number) => void;
|
||||||
|
value: string | number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const RadioComponent: FC<AdminInterfaceProps> = observer(({
|
||||||
|
options, onSelect, value,
|
||||||
|
}) => {
|
||||||
|
if (options.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Group
|
||||||
|
value={value}
|
||||||
|
onChange={(e) => {
|
||||||
|
onSelect(e.target.value);
|
||||||
|
}}
|
||||||
|
className={s.group}
|
||||||
|
>
|
||||||
|
{options.map((o) => (
|
||||||
|
<Radio
|
||||||
|
key={o.value}
|
||||||
|
value={o.value}
|
||||||
|
className={s.radio}
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
{o.label}
|
||||||
|
</div>
|
||||||
|
{o.desc && (
|
||||||
|
<div className={theme.typography.subtext}>
|
||||||
|
{o.desc}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Radio>
|
||||||
|
))}
|
||||||
|
</Group>
|
||||||
|
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default RadioComponent;
|
|
@ -0,0 +1 @@
|
||||||
|
export { default } from './Radio';
|
|
@ -0,0 +1,3 @@
|
||||||
|
import { Switch as SwitchE } from 'antd';
|
||||||
|
|
||||||
|
export default SwitchE;
|
|
@ -0,0 +1 @@
|
||||||
|
export { default as Switch } from './Switch';
|
|
@ -0,0 +1,3 @@
|
||||||
|
export { default as Radio } from './Radio';
|
||||||
|
export { Input } from './Input';
|
||||||
|
export { Switch } from './Switch';
|
|
@ -0,0 +1,12 @@
|
||||||
|
import React from 'react';
|
||||||
|
import theme from 'Lib/theme';
|
||||||
|
|
||||||
|
const danger = (e: string) => {
|
||||||
|
return (
|
||||||
|
<span className={theme.typography.danger}>
|
||||||
|
{e}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default danger;
|
|
@ -0,0 +1,2 @@
|
||||||
|
export { default as danger } from './danger';
|
||||||
|
export { default as p } from './p';
|
|
@ -0,0 +1,11 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
const danger = (e: string) => {
|
||||||
|
return (
|
||||||
|
<p>
|
||||||
|
{e}
|
||||||
|
</p>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default danger;
|
|
@ -0,0 +1,7 @@
|
||||||
|
.icon {
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
import React, { FC } from 'react';
|
||||||
|
import cn from 'classnames';
|
||||||
|
import { IconType } from 'Lib/theme/Icons';
|
||||||
|
|
||||||
|
import s from './Icon.module.pcss';
|
||||||
|
|
||||||
|
interface IconProps {
|
||||||
|
icon: IconType;
|
||||||
|
color?: string;
|
||||||
|
className?: string;
|
||||||
|
onClick?: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Icon: FC<IconProps> = ({ icon, color, className, onClick }) => {
|
||||||
|
const iconClass = cn(s.icon, color, className);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<svg className={iconClass} onClick={onClick}>
|
||||||
|
<use xlinkHref={`#${icon}`} />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Icon;
|
||||||
|
export { IconType } from 'Lib/theme/Icons';
|
|
@ -0,0 +1 @@
|
||||||
|
export { default, IconType } from './Icon';
|
|
@ -0,0 +1 @@
|
||||||
|
export { notifyError, notifySuccess } from './notifications';
|
|
@ -0,0 +1,42 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { notification } from 'antd';
|
||||||
|
|
||||||
|
import { DEFAULT_NOTIFICATION_DURATION } from 'Consts/common';
|
||||||
|
|
||||||
|
export const notifySuccess = (title: string, code?: string) => {
|
||||||
|
notification.success({
|
||||||
|
message: (
|
||||||
|
<div
|
||||||
|
data-notification={code || 'success'}
|
||||||
|
>
|
||||||
|
{title}
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
placement: 'bottomRight',
|
||||||
|
duration: DEFAULT_NOTIFICATION_DURATION,
|
||||||
|
className: 'notification',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const notifyError = (
|
||||||
|
title: string,
|
||||||
|
options?: {
|
||||||
|
btn?: React.ReactNode;
|
||||||
|
duration?: number;
|
||||||
|
onClose?: () => void;
|
||||||
|
},
|
||||||
|
) => {
|
||||||
|
const { btn, duration, onClose } = options || {};
|
||||||
|
notification.error({
|
||||||
|
onClose,
|
||||||
|
message: (
|
||||||
|
<div>
|
||||||
|
{title}
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
placement: 'bottomRight',
|
||||||
|
duration: typeof duration === 'number' ? duration : DEFAULT_NOTIFICATION_DURATION,
|
||||||
|
className: 'notification',
|
||||||
|
btn,
|
||||||
|
});
|
||||||
|
};
|
|
@ -0,0 +1,2 @@
|
||||||
|
export { default as Icon } from './Icon';
|
||||||
|
export { notifyError, notifySuccess } from './Notifications';
|
|
@ -0,0 +1,15 @@
|
||||||
|
.ant-radio {
|
||||||
|
margin-right: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-radio-inner {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
background-color: transparent;
|
||||||
|
border-color: var(--gray400);
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
.ant-steps {
|
||||||
|
display: flex;
|
||||||
|
margin-left: -67px;
|
||||||
|
.ant-steps-item-process {
|
||||||
|
.ant-steps-item-icon {
|
||||||
|
top: -4px;
|
||||||
|
box-sizing: content-box;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
.ant-steps-icon {
|
||||||
|
background: var(--green400);
|
||||||
|
.ant-steps-icon-dot {
|
||||||
|
background: var(--green400);
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.ant-steps-item-content {
|
||||||
|
width: 99px;
|
||||||
|
}
|
||||||
|
.ant-steps-item-finish > .ant-steps-item-container > .ant-steps-item-tail,
|
||||||
|
.ant-steps-item-wait > .ant-steps-item-container > .ant-steps-item-tail,
|
||||||
|
.ant-steps-item-process > .ant-steps-item-container > .ant-steps-item-tail {
|
||||||
|
top: 2px;
|
||||||
|
width: 100%;
|
||||||
|
margin: 0px 0px 0px 70px;
|
||||||
|
padding: 0;
|
||||||
|
&::after {
|
||||||
|
width: calc(100% - 8px);
|
||||||
|
height: 2px;
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.ant-steps-item-wait > .ant-steps-item-container > .ant-steps-item-tail::after,
|
||||||
|
.ant-steps-item-process > .ant-steps-item-container > .ant-steps-item-tail::after {
|
||||||
|
background-color: var(--gray400);
|
||||||
|
}
|
||||||
|
.ant-steps-item-finish > .ant-steps-item-container > .ant-steps-item-tail::after {
|
||||||
|
background-color: var(--green400);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-steps-item-finish {
|
||||||
|
.ant-steps-item-icon {
|
||||||
|
.ant-steps-icon {
|
||||||
|
background: var(--green400);
|
||||||
|
.ant-steps-icon-dot {
|
||||||
|
background: var(--green400);
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.ant-steps-item-icon {
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
.ant-steps-icon {
|
||||||
|
background: transparent;
|
||||||
|
.ant-steps-icon-dot {
|
||||||
|
background: transparent;
|
||||||
|
border: 2px solid var(--gray400);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
@primary-color: #67b279;
|
||||||
|
@success-color: #53d4b1;
|
||||||
|
@text-color: #000;
|
||||||
|
@link-hover-color: #1332BB;
|
||||||
|
@link-active-color: #246FFF;
|
||||||
|
|
||||||
|
@font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Arial, sans-serif;
|
||||||
|
@font-size-base: 14px;
|
|
@ -0,0 +1,2 @@
|
||||||
|
@import '~antd/dist/antd.less';
|
||||||
|
@import './ant-overrides.less';
|
|
@ -0,0 +1,5 @@
|
||||||
|
import './Step.pcss';
|
||||||
|
import './Radio.pcss';
|
||||||
|
|
||||||
|
const insertStyles = true;
|
||||||
|
export default insertStyles;
|
|
@ -0,0 +1,31 @@
|
||||||
|
// This file was autogenerated. Please do not change.
|
||||||
|
// All changes will be overwrited on commit.
|
||||||
|
export default class BlockedServicesApi {
|
||||||
|
static async blockedServicesList(): Promise<string[] | Error> {
|
||||||
|
return await fetch(`/control/blocked_services/list`, {
|
||||||
|
method: 'GET',
|
||||||
|
}).then(async (res) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
return res.json();
|
||||||
|
} else {
|
||||||
|
return new Error(String(res.status));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
static async blockedServicesSet(data: string[]): Promise<number | Error> {
|
||||||
|
return await fetch(`/control/blocked_services/set`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify(data),
|
||||||
|
}).then(async (res) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
return res.status;
|
||||||
|
} else {
|
||||||
|
return new Error(String(res.status));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,103 @@
|
||||||
|
import qs from 'qs';
|
||||||
|
import Client, { IClient } from 'Entities/Client';
|
||||||
|
import ClientDelete, { IClientDelete } from 'Entities/ClientDelete';
|
||||||
|
import ClientUpdate, { IClientUpdate } from 'Entities/ClientUpdate';
|
||||||
|
import Clients, { IClients } from 'Entities/Clients';
|
||||||
|
import ClientsFindEntry, { IClientsFindEntry } from 'Entities/ClientsFindEntry';
|
||||||
|
|
||||||
|
// This file was autogenerated. Please do not change.
|
||||||
|
// All changes will be overwrited on commit.
|
||||||
|
export default class ClientsApi {
|
||||||
|
static async clientsAdd(client: IClient): Promise<number | string[] | Error> {
|
||||||
|
const haveError: string[] = [];
|
||||||
|
const clientValid = new Client(client);
|
||||||
|
haveError.push(...clientValid.validate());
|
||||||
|
if (haveError.length > 0) {
|
||||||
|
return Promise.resolve(haveError);
|
||||||
|
}
|
||||||
|
return await fetch(`/control/clients/add`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify(clientValid.serialize()),
|
||||||
|
}).then(async (res) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
return res.status;
|
||||||
|
} else {
|
||||||
|
return new Error(String(res.status));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
static async clientsDelete(clientdelete: IClientDelete): Promise<number | string[] | Error> {
|
||||||
|
const haveError: string[] = [];
|
||||||
|
const clientdeleteValid = new ClientDelete(clientdelete);
|
||||||
|
haveError.push(...clientdeleteValid.validate());
|
||||||
|
if (haveError.length > 0) {
|
||||||
|
return Promise.resolve(haveError);
|
||||||
|
}
|
||||||
|
return await fetch(`/control/clients/delete`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify(clientdeleteValid.serialize()),
|
||||||
|
}).then(async (res) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
return res.status;
|
||||||
|
} else {
|
||||||
|
return new Error(String(res.status));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
static async clientsFind(ip0?: string): Promise<IClientsFindEntry[] | Error> {
|
||||||
|
const queryParams = {
|
||||||
|
ip0: ip0,
|
||||||
|
}
|
||||||
|
return await fetch(`/control/clients/find?${qs.stringify(queryParams, { arrayFormat: 'comma' })}`, {
|
||||||
|
method: 'GET',
|
||||||
|
}).then(async (res) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
return res.json();
|
||||||
|
} else {
|
||||||
|
return new Error(String(res.status));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
static async clientsStatus(): Promise<IClients | Error> {
|
||||||
|
return await fetch(`/control/clients`, {
|
||||||
|
method: 'GET',
|
||||||
|
}).then(async (res) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
return res.json();
|
||||||
|
} else {
|
||||||
|
return new Error(String(res.status));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
static async clientsUpdate(clientupdate: IClientUpdate): Promise<number | string[] | Error> {
|
||||||
|
const haveError: string[] = [];
|
||||||
|
const clientupdateValid = new ClientUpdate(clientupdate);
|
||||||
|
haveError.push(...clientupdateValid.validate());
|
||||||
|
if (haveError.length > 0) {
|
||||||
|
return Promise.resolve(haveError);
|
||||||
|
}
|
||||||
|
return await fetch(`/control/clients/update`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify(clientupdateValid.serialize()),
|
||||||
|
}).then(async (res) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
return res.status;
|
||||||
|
} else {
|
||||||
|
return new Error(String(res.status));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,110 @@
|
||||||
|
import DhcpConfig, { IDhcpConfig } from 'Entities/DhcpConfig';
|
||||||
|
import DhcpSearchResult, { IDhcpSearchResult } from 'Entities/DhcpSearchResult';
|
||||||
|
import DhcpStaticLease, { IDhcpStaticLease } from 'Entities/DhcpStaticLease';
|
||||||
|
import DhcpStatus, { IDhcpStatus } from 'Entities/DhcpStatus';
|
||||||
|
|
||||||
|
// This file was autogenerated. Please do not change.
|
||||||
|
// All changes will be overwrited on commit.
|
||||||
|
export default class DhcpApi {
|
||||||
|
static async checkActiveDhcp(): Promise<IDhcpSearchResult | Error> {
|
||||||
|
return await fetch(`/control/dhcp/find_active_dhcp`, {
|
||||||
|
method: 'POST',
|
||||||
|
}).then(async (res) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
return res.json();
|
||||||
|
} else {
|
||||||
|
return new Error(String(res.status));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
static async dhcpAddStaticLease(dhcpstaticlease: IDhcpStaticLease): Promise<number | string[] | Error> {
|
||||||
|
const haveError: string[] = [];
|
||||||
|
const dhcpstaticleaseValid = new DhcpStaticLease(dhcpstaticlease);
|
||||||
|
haveError.push(...dhcpstaticleaseValid.validate());
|
||||||
|
if (haveError.length > 0) {
|
||||||
|
return Promise.resolve(haveError);
|
||||||
|
}
|
||||||
|
return await fetch(`/control/dhcp/add_static_lease`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify(dhcpstaticleaseValid.serialize()),
|
||||||
|
}).then(async (res) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
return res.status;
|
||||||
|
} else {
|
||||||
|
return new Error(String(res.status));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
static async dhcpRemoveStaticLease(dhcpstaticlease: IDhcpStaticLease): Promise<number | string[] | Error> {
|
||||||
|
const haveError: string[] = [];
|
||||||
|
const dhcpstaticleaseValid = new DhcpStaticLease(dhcpstaticlease);
|
||||||
|
haveError.push(...dhcpstaticleaseValid.validate());
|
||||||
|
if (haveError.length > 0) {
|
||||||
|
return Promise.resolve(haveError);
|
||||||
|
}
|
||||||
|
return await fetch(`/control/dhcp/remove_static_lease`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify(dhcpstaticleaseValid.serialize()),
|
||||||
|
}).then(async (res) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
return res.status;
|
||||||
|
} else {
|
||||||
|
return new Error(String(res.status));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
static async dhcpReset(): Promise<number | Error> {
|
||||||
|
return await fetch(`/control/dhcp/reset`, {
|
||||||
|
method: 'POST',
|
||||||
|
}).then(async (res) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
return res.status;
|
||||||
|
} else {
|
||||||
|
return new Error(String(res.status));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
static async dhcpSetConfig(dhcpconfig: IDhcpConfig): Promise<number | string[] | Error> {
|
||||||
|
const haveError: string[] = [];
|
||||||
|
const dhcpconfigValid = new DhcpConfig(dhcpconfig);
|
||||||
|
haveError.push(...dhcpconfigValid.validate());
|
||||||
|
if (haveError.length > 0) {
|
||||||
|
return Promise.resolve(haveError);
|
||||||
|
}
|
||||||
|
return await fetch(`/control/dhcp/set_config`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify(dhcpconfigValid.serialize()),
|
||||||
|
}).then(async (res) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
return res.status;
|
||||||
|
} else {
|
||||||
|
return new Error(String(res.status));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
static async dhcpStatus(): Promise<IDhcpStatus | Error> {
|
||||||
|
return await fetch(`/control/dhcp/status`, {
|
||||||
|
method: 'GET',
|
||||||
|
}).then(async (res) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
return res.json();
|
||||||
|
} else {
|
||||||
|
return new Error(String(res.status));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,167 @@
|
||||||
|
import qs from 'qs';
|
||||||
|
import AddUrlRequest, { IAddUrlRequest } from 'Entities/AddUrlRequest';
|
||||||
|
import FilterCheckHostResponse, { IFilterCheckHostResponse } from 'Entities/FilterCheckHostResponse';
|
||||||
|
import FilterConfig, { IFilterConfig } from 'Entities/FilterConfig';
|
||||||
|
import FilterRefreshRequest, { IFilterRefreshRequest } from 'Entities/FilterRefreshRequest';
|
||||||
|
import FilterRefreshResponse, { IFilterRefreshResponse } from 'Entities/FilterRefreshResponse';
|
||||||
|
import FilterSetUrl, { IFilterSetUrl } from 'Entities/FilterSetUrl';
|
||||||
|
import FilterStatus, { IFilterStatus } from 'Entities/FilterStatus';
|
||||||
|
import RemoveUrlRequest, { IRemoveUrlRequest } from 'Entities/RemoveUrlRequest';
|
||||||
|
|
||||||
|
// This file was autogenerated. Please do not change.
|
||||||
|
// All changes will be overwrited on commit.
|
||||||
|
export default class FilteringApi {
|
||||||
|
static async filteringAddURL(addurlrequest: IAddUrlRequest): Promise<number | string[] | Error> {
|
||||||
|
const haveError: string[] = [];
|
||||||
|
const addurlrequestValid = new AddUrlRequest(addurlrequest);
|
||||||
|
haveError.push(...addurlrequestValid.validate());
|
||||||
|
if (haveError.length > 0) {
|
||||||
|
return Promise.resolve(haveError);
|
||||||
|
}
|
||||||
|
return await fetch(`/control/filtering/add_url`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify(addurlrequestValid.serialize()),
|
||||||
|
}).then(async (res) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
return res.status;
|
||||||
|
} else {
|
||||||
|
return new Error(String(res.status));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
static async filteringCheckHost(name?: string): Promise<IFilterCheckHostResponse | Error> {
|
||||||
|
const queryParams = {
|
||||||
|
name: name,
|
||||||
|
}
|
||||||
|
return await fetch(`/control/filtering/check_host?${qs.stringify(queryParams, { arrayFormat: 'comma' })}`, {
|
||||||
|
method: 'GET',
|
||||||
|
}).then(async (res) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
return res.json();
|
||||||
|
} else {
|
||||||
|
return new Error(String(res.status));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
static async filteringConfig(filterconfig: IFilterConfig): Promise<number | string[] | Error> {
|
||||||
|
const haveError: string[] = [];
|
||||||
|
const filterconfigValid = new FilterConfig(filterconfig);
|
||||||
|
haveError.push(...filterconfigValid.validate());
|
||||||
|
if (haveError.length > 0) {
|
||||||
|
return Promise.resolve(haveError);
|
||||||
|
}
|
||||||
|
return await fetch(`/control/filtering/config`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify(filterconfigValid.serialize()),
|
||||||
|
}).then(async (res) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
return res.status;
|
||||||
|
} else {
|
||||||
|
return new Error(String(res.status));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
static async filteringRefresh(filterrefreshrequest: IFilterRefreshRequest): Promise<IFilterRefreshResponse | string[] | Error> {
|
||||||
|
const haveError: string[] = [];
|
||||||
|
const filterrefreshrequestValid = new FilterRefreshRequest(filterrefreshrequest);
|
||||||
|
haveError.push(...filterrefreshrequestValid.validate());
|
||||||
|
if (haveError.length > 0) {
|
||||||
|
return Promise.resolve(haveError);
|
||||||
|
}
|
||||||
|
return await fetch(`/control/filtering/refresh`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify(filterrefreshrequestValid.serialize()),
|
||||||
|
}).then(async (res) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
return res.json();
|
||||||
|
} else {
|
||||||
|
return new Error(String(res.status));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
static async filteringRemoveURL(removeurlrequest: IRemoveUrlRequest): Promise<number | string[] | Error> {
|
||||||
|
const haveError: string[] = [];
|
||||||
|
const removeurlrequestValid = new RemoveUrlRequest(removeurlrequest);
|
||||||
|
haveError.push(...removeurlrequestValid.validate());
|
||||||
|
if (haveError.length > 0) {
|
||||||
|
return Promise.resolve(haveError);
|
||||||
|
}
|
||||||
|
return await fetch(`/control/filtering/remove_url`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify(removeurlrequestValid.serialize()),
|
||||||
|
}).then(async (res) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
return res.status;
|
||||||
|
} else {
|
||||||
|
return new Error(String(res.status));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
static async filteringSetRules(data: string): Promise<number | Error> {
|
||||||
|
const params = String(data);
|
||||||
|
return await fetch(`/control/filtering/set_rules`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'text/plain',
|
||||||
|
},
|
||||||
|
body: params,
|
||||||
|
}).then(async (res) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
return res.status;
|
||||||
|
} else {
|
||||||
|
return new Error(String(res.status));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
static async filteringSetURL(filterseturl: IFilterSetUrl): Promise<number | string[] | Error> {
|
||||||
|
const haveError: string[] = [];
|
||||||
|
const filterseturlValid = new FilterSetUrl(filterseturl);
|
||||||
|
haveError.push(...filterseturlValid.validate());
|
||||||
|
if (haveError.length > 0) {
|
||||||
|
return Promise.resolve(haveError);
|
||||||
|
}
|
||||||
|
return await fetch(`/control/filtering/set_url`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify(filterseturlValid.serialize()),
|
||||||
|
}).then(async (res) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
return res.status;
|
||||||
|
} else {
|
||||||
|
return new Error(String(res.status));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
static async filteringStatus(): Promise<IFilterStatus | Error> {
|
||||||
|
return await fetch(`/control/filtering/status`, {
|
||||||
|
method: 'GET',
|
||||||
|
}).then(async (res) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
return res.json();
|
||||||
|
} else {
|
||||||
|
return new Error(String(res.status));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,160 @@
|
||||||
|
import DNSConfig, { IDNSConfig } from 'Entities/DNSConfig';
|
||||||
|
import GetVersionRequest, { IGetVersionRequest } from 'Entities/GetVersionRequest';
|
||||||
|
import Login, { ILogin } from 'Entities/Login';
|
||||||
|
import ProfileInfo, { IProfileInfo } from 'Entities/ProfileInfo';
|
||||||
|
import ServerStatus, { IServerStatus } from 'Entities/ServerStatus';
|
||||||
|
import UpstreamsConfig, { IUpstreamsConfig } from 'Entities/UpstreamsConfig';
|
||||||
|
import UpstreamsConfigResponse, { IUpstreamsConfigResponse } from 'Entities/UpstreamsConfigResponse';
|
||||||
|
import VersionInfo, { IVersionInfo } from 'Entities/VersionInfo';
|
||||||
|
|
||||||
|
// This file was autogenerated. Please do not change.
|
||||||
|
// All changes will be overwrited on commit.
|
||||||
|
export default class GlobalApi {
|
||||||
|
static async beginUpdate(): Promise<number | Error> {
|
||||||
|
return await fetch(`/control/update`, {
|
||||||
|
method: 'POST',
|
||||||
|
}).then(async (res) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
return res.status;
|
||||||
|
} else {
|
||||||
|
return new Error(String(res.status));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
static async dnsConfig(dnsconfig: IDNSConfig): Promise<number | string[] | Error> {
|
||||||
|
const haveError: string[] = [];
|
||||||
|
const dnsconfigValid = new DNSConfig(dnsconfig);
|
||||||
|
haveError.push(...dnsconfigValid.validate());
|
||||||
|
if (haveError.length > 0) {
|
||||||
|
return Promise.resolve(haveError);
|
||||||
|
}
|
||||||
|
return await fetch(`/control/dns_config`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify(dnsconfigValid.serialize()),
|
||||||
|
}).then(async (res) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
return res.status;
|
||||||
|
} else {
|
||||||
|
return new Error(String(res.status));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
static async dnsInfo(): Promise<IDNSConfig | Error> {
|
||||||
|
return await fetch(`/control/dns_info`, {
|
||||||
|
method: 'GET',
|
||||||
|
}).then(async (res) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
return res.json();
|
||||||
|
} else {
|
||||||
|
return new Error(String(res.status));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
static async getProfile(): Promise<IProfileInfo | Error> {
|
||||||
|
return await fetch(`/control/profile`, {
|
||||||
|
method: 'GET',
|
||||||
|
}).then(async (res) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
return res.json();
|
||||||
|
} else {
|
||||||
|
return new Error(String(res.status));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
static async getVersionJson(getversionrequest: IGetVersionRequest): Promise<IVersionInfo | string[] | Error> {
|
||||||
|
const haveError: string[] = [];
|
||||||
|
const getversionrequestValid = new GetVersionRequest(getversionrequest);
|
||||||
|
haveError.push(...getversionrequestValid.validate());
|
||||||
|
if (haveError.length > 0) {
|
||||||
|
return Promise.resolve(haveError);
|
||||||
|
}
|
||||||
|
return await fetch(`/control/version.json`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify(getversionrequestValid.serialize()),
|
||||||
|
}).then(async (res) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
return res.json();
|
||||||
|
} else {
|
||||||
|
return new Error(String(res.status));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
static async login(login: ILogin): Promise<number | string[] | Error> {
|
||||||
|
const haveError: string[] = [];
|
||||||
|
const loginValid = new Login(login);
|
||||||
|
haveError.push(...loginValid.validate());
|
||||||
|
if (haveError.length > 0) {
|
||||||
|
return Promise.resolve(haveError);
|
||||||
|
}
|
||||||
|
return await fetch(`/control/login`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify(loginValid.serialize()),
|
||||||
|
}).then(async (res) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
return res.status;
|
||||||
|
} else {
|
||||||
|
return new Error(String(res.status));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
static async logout(): Promise<number | Error> {
|
||||||
|
return await fetch(`/control/logout`, {
|
||||||
|
method: 'GET',
|
||||||
|
}).then(async (res) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
return res.status;
|
||||||
|
} else {
|
||||||
|
return new Error(String(res.status));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
static async status(): Promise<IServerStatus | Error> {
|
||||||
|
return await fetch(`/control/status`, {
|
||||||
|
method: 'GET',
|
||||||
|
}).then(async (res) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
return res.json();
|
||||||
|
} else {
|
||||||
|
return new Error(String(res.status));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
static async testUpstreamDNS(upstreamsconfig: IUpstreamsConfig): Promise<IUpstreamsConfigResponse | string[] | Error> {
|
||||||
|
const haveError: string[] = [];
|
||||||
|
const upstreamsconfigValid = new UpstreamsConfig(upstreamsconfig);
|
||||||
|
haveError.push(...upstreamsconfigValid.validate());
|
||||||
|
if (haveError.length > 0) {
|
||||||
|
return Promise.resolve(haveError);
|
||||||
|
}
|
||||||
|
return await fetch(`/control/test_upstream_dns`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify(upstreamsconfigValid.serialize()),
|
||||||
|
}).then(async (res) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
return res.json();
|
||||||
|
} else {
|
||||||
|
return new Error(String(res.status));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
// This file was autogenerated. Please do not change.
|
||||||
|
// All changes will be overwrited on commit.
|
||||||
|
export default class I18nApi {
|
||||||
|
static async changeLanguage(data: string): Promise<number | Error> {
|
||||||
|
const params = String(data);
|
||||||
|
return await fetch(`/control/i18n/change_language`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'text/plain',
|
||||||
|
},
|
||||||
|
body: params,
|
||||||
|
}).then(async (res) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
return res.status;
|
||||||
|
} else {
|
||||||
|
return new Error(String(res.status));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
static async currentLanguage(): Promise<number | Error> {
|
||||||
|
return await fetch(`/control/i18n/current_language`, {
|
||||||
|
method: 'GET',
|
||||||
|
}).then(async (res) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
return res.status;
|
||||||
|
} else {
|
||||||
|
return new Error(String(res.status));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,123 @@
|
||||||
|
import AddressesInfo, { IAddressesInfo } from 'Entities/AddressesInfo';
|
||||||
|
import AddressesInfoBeta, { IAddressesInfoBeta } from 'Entities/AddressesInfoBeta';
|
||||||
|
import CheckConfigRequest, { ICheckConfigRequest } from 'Entities/CheckConfigRequest';
|
||||||
|
import CheckConfigRequestBeta, { ICheckConfigRequestBeta } from 'Entities/CheckConfigRequestBeta';
|
||||||
|
import CheckConfigResponse, { ICheckConfigResponse } from 'Entities/CheckConfigResponse';
|
||||||
|
import InitialConfiguration, { IInitialConfiguration } from 'Entities/InitialConfiguration';
|
||||||
|
import InitialConfigurationBeta, { IInitialConfigurationBeta } from 'Entities/InitialConfigurationBeta';
|
||||||
|
|
||||||
|
// This file was autogenerated. Please do not change.
|
||||||
|
// All changes will be overwrited on commit.
|
||||||
|
export default class InstallApi {
|
||||||
|
static async installCheckConfig(checkconfigrequest: ICheckConfigRequest): Promise<ICheckConfigResponse | string[] | Error> {
|
||||||
|
const haveError: string[] = [];
|
||||||
|
const checkconfigrequestValid = new CheckConfigRequest(checkconfigrequest);
|
||||||
|
haveError.push(...checkconfigrequestValid.validate());
|
||||||
|
if (haveError.length > 0) {
|
||||||
|
return Promise.resolve(haveError);
|
||||||
|
}
|
||||||
|
return await fetch(`/control/install/check_config`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify(checkconfigrequestValid.serialize()),
|
||||||
|
}).then(async (res) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
return res.json();
|
||||||
|
} else {
|
||||||
|
return new Error(String(res.status));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
static async installCheckConfigBeta(checkconfigrequestbeta: ICheckConfigRequestBeta): Promise<ICheckConfigResponse | string[] | Error> {
|
||||||
|
const haveError: string[] = [];
|
||||||
|
const checkconfigrequestbetaValid = new CheckConfigRequestBeta(checkconfigrequestbeta);
|
||||||
|
haveError.push(...checkconfigrequestbetaValid.validate());
|
||||||
|
if (haveError.length > 0) {
|
||||||
|
return Promise.resolve(haveError);
|
||||||
|
}
|
||||||
|
return await fetch(`/control/install/check_config_beta`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify(checkconfigrequestbetaValid.serialize()),
|
||||||
|
}).then(async (res) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
return res.json();
|
||||||
|
} else {
|
||||||
|
return new Error(String(res.status));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
static async installConfigure(initialconfiguration: IInitialConfiguration): Promise<number | string[] | Error> {
|
||||||
|
const haveError: string[] = [];
|
||||||
|
const initialconfigurationValid = new InitialConfiguration(initialconfiguration);
|
||||||
|
haveError.push(...initialconfigurationValid.validate());
|
||||||
|
if (haveError.length > 0) {
|
||||||
|
return Promise.resolve(haveError);
|
||||||
|
}
|
||||||
|
return await fetch(`/control/install/configure`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify(initialconfigurationValid.serialize()),
|
||||||
|
}).then(async (res) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
return res.status;
|
||||||
|
} else {
|
||||||
|
return new Error(String(res.status));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
static async installConfigureBeta(initialconfigurationbeta: IInitialConfigurationBeta): Promise<number | string[] | Error> {
|
||||||
|
const haveError: string[] = [];
|
||||||
|
const initialconfigurationbetaValid = new InitialConfigurationBeta(initialconfigurationbeta);
|
||||||
|
haveError.push(...initialconfigurationbetaValid.validate());
|
||||||
|
if (haveError.length > 0) {
|
||||||
|
return Promise.resolve(haveError);
|
||||||
|
}
|
||||||
|
return await fetch(`/control/install/configure_beta`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify(initialconfigurationbetaValid.serialize()),
|
||||||
|
}).then(async (res) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
return res.status;
|
||||||
|
} else {
|
||||||
|
return new Error(String(res.status));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
static async installGetAddresses(): Promise<IAddressesInfo | Error> {
|
||||||
|
return await fetch(`/control/install/get_addresses`, {
|
||||||
|
method: 'GET',
|
||||||
|
}).then(async (res) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
return res.json();
|
||||||
|
} else {
|
||||||
|
return new Error(String(res.status));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
static async installGetAddressesBeta(): Promise<IAddressesInfoBeta | Error> {
|
||||||
|
return await fetch(`/control/install/get_addresses_beta`, {
|
||||||
|
method: 'GET',
|
||||||
|
}).then(async (res) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
return res.json();
|
||||||
|
} else {
|
||||||
|
return new Error(String(res.status));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
import qs from 'qs';
|
||||||
|
import QueryLog, { IQueryLog } from 'Entities/QueryLog';
|
||||||
|
import QueryLogConfig, { IQueryLogConfig } from 'Entities/QueryLogConfig';
|
||||||
|
|
||||||
|
// This file was autogenerated. Please do not change.
|
||||||
|
// All changes will be overwrited on commit.
|
||||||
|
export default class LogApi {
|
||||||
|
static async queryLog(older_than?: string, offset?: number, limit?: number, search?: string, response_status?: string): Promise<IQueryLog | Error> {
|
||||||
|
const queryParams = {
|
||||||
|
older_than: older_than,
|
||||||
|
offset: offset,
|
||||||
|
limit: limit,
|
||||||
|
search: search,
|
||||||
|
response_status: response_status,
|
||||||
|
}
|
||||||
|
return await fetch(`/control/querylog?${qs.stringify(queryParams, { arrayFormat: 'comma' })}`, {
|
||||||
|
method: 'GET',
|
||||||
|
}).then(async (res) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
return res.json();
|
||||||
|
} else {
|
||||||
|
return new Error(String(res.status));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
static async queryLogConfig(querylogconfig: IQueryLogConfig): Promise<number | string[] | Error> {
|
||||||
|
const haveError: string[] = [];
|
||||||
|
const querylogconfigValid = new QueryLogConfig(querylogconfig);
|
||||||
|
haveError.push(...querylogconfigValid.validate());
|
||||||
|
if (haveError.length > 0) {
|
||||||
|
return Promise.resolve(haveError);
|
||||||
|
}
|
||||||
|
return await fetch(`/control/querylog_config`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify(querylogconfigValid.serialize()),
|
||||||
|
}).then(async (res) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
return res.status;
|
||||||
|
} else {
|
||||||
|
return new Error(String(res.status));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
static async queryLogInfo(): Promise<IQueryLogConfig | Error> {
|
||||||
|
return await fetch(`/control/querylog_info`, {
|
||||||
|
method: 'GET',
|
||||||
|
}).then(async (res) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
return res.json();
|
||||||
|
} else {
|
||||||
|
return new Error(String(res.status));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
static async querylogClear(): Promise<number | Error> {
|
||||||
|
return await fetch(`/control/querylog_clear`, {
|
||||||
|
method: 'POST',
|
||||||
|
}).then(async (res) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
return res.status;
|
||||||
|
} else {
|
||||||
|
return new Error(String(res.status));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
import qs from 'qs';
|
||||||
|
|
||||||
|
// This file was autogenerated. Please do not change.
|
||||||
|
// All changes will be overwrited on commit.
|
||||||
|
export default class MobileconfigApi {
|
||||||
|
static async mobileConfigDoH(host?: string): Promise<number | Error> {
|
||||||
|
const queryParams = {
|
||||||
|
host: host,
|
||||||
|
}
|
||||||
|
return await fetch(`/control/apple/doh.mobileconfig?${qs.stringify(queryParams, { arrayFormat: 'comma' })}`, {
|
||||||
|
method: 'GET',
|
||||||
|
}).then(async (res) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
return res.status;
|
||||||
|
} else {
|
||||||
|
return new Error(String(res.status));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
static async mobileConfigDoT(host?: string): Promise<number | Error> {
|
||||||
|
const queryParams = {
|
||||||
|
host: host,
|
||||||
|
}
|
||||||
|
return await fetch(`/control/apple/dot.mobileconfig?${qs.stringify(queryParams, { arrayFormat: 'comma' })}`, {
|
||||||
|
method: 'GET',
|
||||||
|
}).then(async (res) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
return res.status;
|
||||||
|
} else {
|
||||||
|
return new Error(String(res.status));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
// This file was autogenerated. Please do not change.
|
||||||
|
// All changes will be overwrited on commit.
|
||||||
|
export default class ParentalApi {
|
||||||
|
static async parentalDisable(): Promise<number | Error> {
|
||||||
|
return await fetch(`/control/parental/disable`, {
|
||||||
|
method: 'POST',
|
||||||
|
}).then(async (res) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
return res.status;
|
||||||
|
} else {
|
||||||
|
return new Error(String(res.status));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
static async parentalEnable(data: string): Promise<number | Error> {
|
||||||
|
const params = String(data);
|
||||||
|
return await fetch(`/control/parental/enable`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'text/plain',
|
||||||
|
},
|
||||||
|
body: params,
|
||||||
|
}).then(async (res) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
return res.status;
|
||||||
|
} else {
|
||||||
|
return new Error(String(res.status));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
static async parentalStatus(): Promise<any | Error> {
|
||||||
|
return await fetch(`/control/parental/status`, {
|
||||||
|
method: 'GET',
|
||||||
|
}).then(async (res) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
return res.json();
|
||||||
|
} else {
|
||||||
|
return new Error(String(res.status));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
import RewriteEntry, { IRewriteEntry } from 'Entities/RewriteEntry';
|
||||||
|
|
||||||
|
// This file was autogenerated. Please do not change.
|
||||||
|
// All changes will be overwrited on commit.
|
||||||
|
export default class RewriteApi {
|
||||||
|
static async rewriteAdd(rewriteentry: IRewriteEntry): Promise<number | string[] | Error> {
|
||||||
|
const haveError: string[] = [];
|
||||||
|
const rewriteentryValid = new RewriteEntry(rewriteentry);
|
||||||
|
haveError.push(...rewriteentryValid.validate());
|
||||||
|
if (haveError.length > 0) {
|
||||||
|
return Promise.resolve(haveError);
|
||||||
|
}
|
||||||
|
return await fetch(`/control/rewrite/add`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify(rewriteentryValid.serialize()),
|
||||||
|
}).then(async (res) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
return res.status;
|
||||||
|
} else {
|
||||||
|
return new Error(String(res.status));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
static async rewriteDelete(rewriteentry: IRewriteEntry): Promise<number | string[] | Error> {
|
||||||
|
const haveError: string[] = [];
|
||||||
|
const rewriteentryValid = new RewriteEntry(rewriteentry);
|
||||||
|
haveError.push(...rewriteentryValid.validate());
|
||||||
|
if (haveError.length > 0) {
|
||||||
|
return Promise.resolve(haveError);
|
||||||
|
}
|
||||||
|
return await fetch(`/control/rewrite/delete`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify(rewriteentryValid.serialize()),
|
||||||
|
}).then(async (res) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
return res.status;
|
||||||
|
} else {
|
||||||
|
return new Error(String(res.status));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
static async rewriteList(): Promise<IRewriteEntry[] | Error> {
|
||||||
|
return await fetch(`/control/rewrite/list`, {
|
||||||
|
method: 'GET',
|
||||||
|
}).then(async (res) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
return res.json();
|
||||||
|
} else {
|
||||||
|
return new Error(String(res.status));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
// This file was autogenerated. Please do not change.
|
||||||
|
// All changes will be overwrited on commit.
|
||||||
|
export default class SafebrowsingApi {
|
||||||
|
static async safebrowsingDisable(): Promise<number | Error> {
|
||||||
|
return await fetch(`/control/safebrowsing/disable`, {
|
||||||
|
method: 'POST',
|
||||||
|
}).then(async (res) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
return res.status;
|
||||||
|
} else {
|
||||||
|
return new Error(String(res.status));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
static async safebrowsingEnable(): Promise<number | Error> {
|
||||||
|
return await fetch(`/control/safebrowsing/enable`, {
|
||||||
|
method: 'POST',
|
||||||
|
}).then(async (res) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
return res.status;
|
||||||
|
} else {
|
||||||
|
return new Error(String(res.status));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
static async safebrowsingStatus(): Promise<any | Error> {
|
||||||
|
return await fetch(`/control/safebrowsing/status`, {
|
||||||
|
method: 'GET',
|
||||||
|
}).then(async (res) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
return res.json();
|
||||||
|
} else {
|
||||||
|
return new Error(String(res.status));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
// This file was autogenerated. Please do not change.
|
||||||
|
// All changes will be overwrited on commit.
|
||||||
|
export default class SafesearchApi {
|
||||||
|
static async safesearchDisable(): Promise<number | Error> {
|
||||||
|
return await fetch(`/control/safesearch/disable`, {
|
||||||
|
method: 'POST',
|
||||||
|
}).then(async (res) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
return res.status;
|
||||||
|
} else {
|
||||||
|
return new Error(String(res.status));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
static async safesearchEnable(): Promise<number | Error> {
|
||||||
|
return await fetch(`/control/safesearch/enable`, {
|
||||||
|
method: 'POST',
|
||||||
|
}).then(async (res) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
return res.status;
|
||||||
|
} else {
|
||||||
|
return new Error(String(res.status));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
static async safesearchStatus(): Promise<any | Error> {
|
||||||
|
return await fetch(`/control/safesearch/status`, {
|
||||||
|
method: 'GET',
|
||||||
|
}).then(async (res) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
return res.json();
|
||||||
|
} else {
|
||||||
|
return new Error(String(res.status));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
import Stats, { IStats } from 'Entities/Stats';
|
||||||
|
import StatsConfig, { IStatsConfig } from 'Entities/StatsConfig';
|
||||||
|
|
||||||
|
// This file was autogenerated. Please do not change.
|
||||||
|
// All changes will be overwrited on commit.
|
||||||
|
export default class StatsApi {
|
||||||
|
static async stats(): Promise<IStats | Error> {
|
||||||
|
return await fetch(`/control/stats`, {
|
||||||
|
method: 'GET',
|
||||||
|
}).then(async (res) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
return res.json();
|
||||||
|
} else {
|
||||||
|
return new Error(String(res.status));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
static async statsConfig(statsconfig: IStatsConfig): Promise<number | string[] | Error> {
|
||||||
|
const haveError: string[] = [];
|
||||||
|
const statsconfigValid = new StatsConfig(statsconfig);
|
||||||
|
haveError.push(...statsconfigValid.validate());
|
||||||
|
if (haveError.length > 0) {
|
||||||
|
return Promise.resolve(haveError);
|
||||||
|
}
|
||||||
|
return await fetch(`/control/stats_config`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify(statsconfigValid.serialize()),
|
||||||
|
}).then(async (res) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
return res.status;
|
||||||
|
} else {
|
||||||
|
return new Error(String(res.status));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
static async statsInfo(): Promise<IStatsConfig | Error> {
|
||||||
|
return await fetch(`/control/stats_info`, {
|
||||||
|
method: 'GET',
|
||||||
|
}).then(async (res) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
return res.json();
|
||||||
|
} else {
|
||||||
|
return new Error(String(res.status));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
static async statsReset(): Promise<number | Error> {
|
||||||
|
return await fetch(`/control/stats_reset`, {
|
||||||
|
method: 'POST',
|
||||||
|
}).then(async (res) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
return res.status;
|
||||||
|
} else {
|
||||||
|
return new Error(String(res.status));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
import TlsConfig, { ITlsConfig } from 'Entities/TlsConfig';
|
||||||
|
|
||||||
|
// This file was autogenerated. Please do not change.
|
||||||
|
// All changes will be overwrited on commit.
|
||||||
|
export default class TlsApi {
|
||||||
|
static async tlsConfigure(tlsconfig: ITlsConfig): Promise<ITlsConfig | string[] | Error> {
|
||||||
|
const haveError: string[] = [];
|
||||||
|
const tlsconfigValid = new TlsConfig(tlsconfig);
|
||||||
|
haveError.push(...tlsconfigValid.validate());
|
||||||
|
if (haveError.length > 0) {
|
||||||
|
return Promise.resolve(haveError);
|
||||||
|
}
|
||||||
|
return await fetch(`/control/tls/configure`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify(tlsconfigValid.serialize()),
|
||||||
|
}).then(async (res) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
return res.json();
|
||||||
|
} else {
|
||||||
|
return new Error(String(res.status));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
static async tlsStatus(): Promise<ITlsConfig | Error> {
|
||||||
|
return await fetch(`/control/tls/status`, {
|
||||||
|
method: 'GET',
|
||||||
|
}).then(async (res) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
return res.json();
|
||||||
|
} else {
|
||||||
|
return new Error(String(res.status));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
static async tlsValidate(tlsconfig: ITlsConfig): Promise<ITlsConfig | string[] | Error> {
|
||||||
|
const haveError: string[] = [];
|
||||||
|
const tlsconfigValid = new TlsConfig(tlsconfig);
|
||||||
|
haveError.push(...tlsconfigValid.validate());
|
||||||
|
if (haveError.length > 0) {
|
||||||
|
return Promise.resolve(haveError);
|
||||||
|
}
|
||||||
|
return await fetch(`/control/tls/validate`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify(tlsconfigValid.serialize()),
|
||||||
|
}).then(async (res) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
return res.json();
|
||||||
|
} else {
|
||||||
|
return new Error(String(res.status));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
export const DEFAULT_NOTIFICATION_DURATION = 5;
|
|
@ -0,0 +1 @@
|
||||||
|
export const EMPTY_FIELD_ERROR = 'empty_field';
|
|
@ -0,0 +1,7 @@
|
||||||
|
export const DEFAULT_IP_ADDRESS = '0.0.0.0';
|
||||||
|
|
||||||
|
export const DEFAULT_IP_PORT = 80;
|
||||||
|
|
||||||
|
export const DEFAULT_DNS_ADDRESS = '0.0.0.0';
|
||||||
|
|
||||||
|
export const DEFAULT_DNS_PORT = 53;
|
|
@ -0,0 +1,78 @@
|
||||||
|
// This file was autogenerated. Please do not change.
|
||||||
|
// All changes will be overwrited on commit.
|
||||||
|
export interface IAddUrlRequest {
|
||||||
|
name?: string;
|
||||||
|
url?: string;
|
||||||
|
whitelist?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class AddUrlRequest {
|
||||||
|
readonly _name: string | undefined;
|
||||||
|
|
||||||
|
get name(): string | undefined {
|
||||||
|
return this._name;
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly _url: string | undefined;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Description: URL or an absolute path to the file containing filtering rules.
|
||||||
|
*
|
||||||
|
* Example: https://filters.adtidy.org/windows/filters/15.txt
|
||||||
|
*/
|
||||||
|
get url(): string | undefined {
|
||||||
|
return this._url;
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly _whitelist: boolean | undefined;
|
||||||
|
|
||||||
|
get whitelist(): boolean | undefined {
|
||||||
|
return this._whitelist;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(props: IAddUrlRequest) {
|
||||||
|
if (typeof props.name === 'string') {
|
||||||
|
this._name = props.name.trim();
|
||||||
|
}
|
||||||
|
if (typeof props.url === 'string') {
|
||||||
|
this._url = props.url.trim();
|
||||||
|
}
|
||||||
|
if (typeof props.whitelist === 'boolean') {
|
||||||
|
this._whitelist = props.whitelist;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
serialize(): IAddUrlRequest {
|
||||||
|
const data: IAddUrlRequest = {
|
||||||
|
};
|
||||||
|
if (typeof this._name !== 'undefined') {
|
||||||
|
data.name = this._name;
|
||||||
|
}
|
||||||
|
if (typeof this._url !== 'undefined') {
|
||||||
|
data.url = this._url;
|
||||||
|
}
|
||||||
|
if (typeof this._whitelist !== 'undefined') {
|
||||||
|
data.whitelist = this._whitelist;
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
validate(): string[] {
|
||||||
|
const validate = {
|
||||||
|
name: !this._name ? true : typeof this._name === 'string' && !this._name ? true : this._name,
|
||||||
|
url: !this._url ? true : typeof this._url === 'string' && !this._url ? true : this._url,
|
||||||
|
whitelist: !this._whitelist ? true : typeof this._whitelist === 'boolean',
|
||||||
|
};
|
||||||
|
const isError: string[] = [];
|
||||||
|
Object.keys(validate).forEach((key) => {
|
||||||
|
if (!(validate as any)[key]) {
|
||||||
|
isError.push(key);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return isError;
|
||||||
|
}
|
||||||
|
|
||||||
|
update(props: Partial<IAddUrlRequest>): AddUrlRequest {
|
||||||
|
return new AddUrlRequest({ ...this.serialize(), ...props });
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
// This file was autogenerated. Please do not change.
|
||||||
|
// All changes will be overwrited on commit.
|
||||||
|
export interface IAddressInfo {
|
||||||
|
ip: string;
|
||||||
|
port: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class AddressInfo {
|
||||||
|
readonly _ip: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Description: undefined
|
||||||
|
* Example: 127.0.0.1
|
||||||
|
*/
|
||||||
|
get ip(): string {
|
||||||
|
return this._ip;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ipValidate(ip: string): boolean {
|
||||||
|
return typeof ip === 'string' && !!ip.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly _port: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Description: undefined
|
||||||
|
* Example: 53
|
||||||
|
*/
|
||||||
|
get port(): number {
|
||||||
|
return this._port;
|
||||||
|
}
|
||||||
|
|
||||||
|
static portValidate(port: number): boolean {
|
||||||
|
return typeof port === 'number';
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(props: IAddressInfo) {
|
||||||
|
this._ip = props.ip.trim();
|
||||||
|
this._port = props.port;
|
||||||
|
}
|
||||||
|
|
||||||
|
serialize(): IAddressInfo {
|
||||||
|
const data: IAddressInfo = {
|
||||||
|
ip: this._ip,
|
||||||
|
port: this._port,
|
||||||
|
};
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
validate(): string[] {
|
||||||
|
const validate = {
|
||||||
|
ip: typeof this._ip === 'string' && !this._ip ? true : this._ip,
|
||||||
|
port: typeof this._port === 'number',
|
||||||
|
};
|
||||||
|
const isError: string[] = [];
|
||||||
|
Object.keys(validate).forEach((key) => {
|
||||||
|
if (!(validate as any)[key]) {
|
||||||
|
isError.push(key);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return isError;
|
||||||
|
}
|
||||||
|
|
||||||
|
update(props: Partial<IAddressInfo>): AddressInfo {
|
||||||
|
return new AddressInfo({ ...this.serialize(), ...props });
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,71 @@
|
||||||
|
// This file was autogenerated. Please do not change.
|
||||||
|
// All changes will be overwrited on commit.
|
||||||
|
export interface IAddressInfoBeta {
|
||||||
|
ip: string[];
|
||||||
|
port: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class AddressInfoBeta {
|
||||||
|
readonly _ip: string[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Description: undefined
|
||||||
|
* Example: 127.0.0.1
|
||||||
|
*/
|
||||||
|
get ip(): string[] {
|
||||||
|
return this._ip;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get ipMinItems() {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ipValidate(ip: string[]): boolean {
|
||||||
|
return ip.reduce<boolean>((result, p) => result && (typeof p === 'string' && !!p.trim()), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly _port: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Description: undefined
|
||||||
|
* Example: 53
|
||||||
|
*/
|
||||||
|
get port(): number {
|
||||||
|
return this._port;
|
||||||
|
}
|
||||||
|
|
||||||
|
static portValidate(port: number): boolean {
|
||||||
|
return typeof port === 'number';
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(props: IAddressInfoBeta) {
|
||||||
|
this._ip = props.ip;
|
||||||
|
this._port = props.port;
|
||||||
|
}
|
||||||
|
|
||||||
|
serialize(): IAddressInfoBeta {
|
||||||
|
const data: IAddressInfoBeta = {
|
||||||
|
ip: this._ip,
|
||||||
|
port: this._port,
|
||||||
|
};
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
validate(): string[] {
|
||||||
|
const validate = {
|
||||||
|
ip: this._ip.reduce((result, p) => result && typeof p === 'string', true),
|
||||||
|
port: typeof this._port === 'number',
|
||||||
|
};
|
||||||
|
const isError: string[] = [];
|
||||||
|
Object.keys(validate).forEach((key) => {
|
||||||
|
if (!(validate as any)[key]) {
|
||||||
|
isError.push(key);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return isError;
|
||||||
|
}
|
||||||
|
|
||||||
|
update(props: Partial<IAddressInfoBeta>): AddressInfoBeta {
|
||||||
|
return new AddressInfoBeta({ ...this.serialize(), ...props });
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue