mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Merge branch 'master' into improve-dock-tabs-ux
This commit is contained in:
commit
7273082048
@ -1,95 +0,0 @@
|
||||
# Release Process
|
||||
|
||||
Lens releases are built by CICD automatically on git tags. The typical release process flow is the following:
|
||||
|
||||
1. It is recommended to perform the release process from a folder used solely meant for creating releases (i.e. not your dev folder), with the Lens repo initialized (origin set to https://github.com/lensapp/lens).
|
||||
|
||||
1. If doing a patch release checkout the `release/vMAJOR.MINOR` branch for the appropriate `MAJOR`/`MINOR` version and manually `cherry-pick` the PRs required for the patch that were commited to master. If there are any conflicts they must be resolved manually. If necessary, get assistance from the PR authors.
|
||||
|
||||
This can be helped (if you have the `gh` CLI and `jq` installed) by running:
|
||||
```
|
||||
gh api -XGET "/repos/lensapp/lens/pulls?state=closed&per_page=100" | jq -r '[.[] | select(.milestone.title == "<VERSION>") | select((.merged_at | type) == "string")] | sort_by(.merged_at) | map(.merge_commit_sha) | join(" ")'
|
||||
```
|
||||
|
||||
But you will probably need to verify that all the PRs have correct milestones.
|
||||
|
||||
1. From a clean and up to date `master` (or `release/vMAJOR.MINOR` if doing a patch release) run `npm version <version-type> --git-tag-version false` where `<version-type>` is one of the following:
|
||||
- `major`
|
||||
- `minor`
|
||||
- `patch`
|
||||
- `premajor [--preid=<prerelease-id>]`
|
||||
- `preminor [--preid=<prerelease-id>]`
|
||||
- `prepatch [--preid=<prerelease-id>]`
|
||||
- `prerelease [--preid=<prerelease-id>]`
|
||||
|
||||
where `<prerelease-id>` is generally one of:
|
||||
- `alpha`
|
||||
- `beta`
|
||||
- `rc`
|
||||
|
||||
This assumes origin is set to https://github.com/lensapp/lens.git. If not then set GIT_REMOTE to the remote that is set to https://github.com/lensapp/lens.git. For example run `GIT_REMOTE=upstream npm version ...`
|
||||
1. Open the PR (git should have printed a link to GitHub in the console) with the contents of all the accepted PRs since the last release. The PR description needs to be filled with the draft release description. From https://github.com/lensapp/lens click on Releases, the draft release should be first in the list, click `Edit` and copy/paste the markdown to the PR description. Add the `skip-changelog` label and click `Create Pull Request`. If this is a patch release be sure to set the PR base branch to `release/vMAJOR.MINOR` instead of `master`.
|
||||
|
||||
It might also help, if the release drafter isn't updating correctly. To grab the data for the PR description using `gh` and `jq`. You can run the following command to get all the titles:
|
||||
|
||||
```
|
||||
gh api -XGET "/repos/lensapp/lens/pulls?state=closed&per_page=100" | jq -r '[.[] | select(.milestone.title == "<VERSION>") | select((.merged_at | type) == "string")] | sort_by(.merged_at) | map([.title, " (**#", .number, "**) ", .user.html_url] | join ("")) | join("\n")' | pbcopy
|
||||
```
|
||||
|
||||
And if you want to specify just bug fixes then you can add the following to the end of the `[ ... ]` section in the above command (before the `sort_by`) to just have bug fixes. Switch to `== "enhancement"` for enhancements and `all()` instead of `any()` with `. != "bug and . != "enhancement"` for maintanence sections.
|
||||
|
||||
```
|
||||
| select(any(.labels | map(.name)[]; . == "bug"))
|
||||
```
|
||||
See notes below for ideas for building changelogs.
|
||||
|
||||
1. After the PR is accepted and passes CI (and before merging), go to the same branch and run `make tag-release` (set GIT_REMOTE if necessary). This additionally triggers the azure jobs to build the binaries and put them on S3.
|
||||
1. If the CI fails at this stage the problem needs to be fixed. Sometimes an azure job fails due to outside service issues (e.g. Apple signing occasionally fails), in which case the specific azure job can be rerun from https://dev.azure.com/lensapp/lensapp/_build. Otherwise changes to the codebase may need to be done and committed to the release branch and pushed to https://github.com/lensapp/lens. CI will run again. As well the release tag needs to be manually set to this new commit. You can do something like:
|
||||
- `git push origin :refs/tags/vX.Y.Z-beta.N` (removes the tag from https://github.com/lensapp/lens)
|
||||
- `git tag -fa vX.Y.Z-beta.N` (move the tag locally to the current commit)
|
||||
- `git push origin --tags` (update the tags on https://github.com/lensapp/lens to reflect this local change)
|
||||
|
||||
Once the tag has been updated on origin (e.g. by `git push origin --tags`) the azure jobs are automatically triggered again.
|
||||
|
||||
1. Once CI passes again go to the releases tab on GitHub. You can use the existing draft release prepared by k8slens-bot (select the correct tag). Or you can create a new release from the tag that was created, and make sure that the change log is the same as that of the PR, and the title is the tag. Either way, click the prerelease checkbox if this is not a new major, minor, or patch version before clicking `Publish release`.
|
||||
1. Merge the release PR after the release is published. If it is a patch release then there is no need to squash the cherry-picked commits as part of the merge. GitHub should delete the branch once it is merged.
|
||||
1. If you have just released a new major or minor version then create a new `release/vMAJOR.MINOR` branch from that same tag and push it to https://github.com/lensapp/lens. Given the commit of the merged release PR from the master branch you can do this like
|
||||
|
||||
`git push origin <commit>:refs/heads/release/vX.Y`
|
||||
|
||||
This will be the target for future patch releases and shouldn't be deleted.
|
||||
|
||||
Other tasks
|
||||
post release:
|
||||
- generate a changelog from the prerelease descriptions (for major/minor releases)
|
||||
- announce the release on lens and lens-hq slack channels (release is announced automatically on the community slack lens channel through the above publishing process)
|
||||
- announce on lens-hq that master is open for PR merges for the next release (for major/minor releases)
|
||||
- update issues on github (bump those that did not make it into the release to a subsequent release) (for major/minor/patch releases)
|
||||
|
||||
|
||||
### Rough Notes
|
||||
|
||||
To get all of the PRs since a certain date you can use something like:
|
||||
```
|
||||
gh api -XGET "/repos/lensapp/lens/pulls?state=closed&per_page=100&q=committer-date:>YYYY-MM-DD" > prs.json
|
||||
```
|
||||
|
||||
If there are more than 100 PRs you'll need to rerun for multiple pages:
|
||||
```
|
||||
gh api -XGET "/repos/lensapp/lens/pulls?state=closed&per_page=100&page=2&q=committer-date:>YYYY-MM-DD" >> prs.json
|
||||
```
|
||||
|
||||
To get all the bug PRs:
|
||||
```
|
||||
jq -r '[.[] | select((.merged_at | type) == "string") | select(any(.labels | map(.name)[]; . == "bug"))] | sort_by(.merged_at) | reverse | map([.title, " (**#", .number, "**) ", .user.html_url] | join ("")) | join("\n")' prs.json
|
||||
```
|
||||
|
||||
To get all the enhancement PRs:
|
||||
```
|
||||
jq -r '[.[] | select((.merged_at | type) == "string") | select(any(.labels | map(.name)[]; . == "enhancement"))] | sort_by(.merged_at) | reverse | map([.title, " (**#", .number, "**) ", .user.html_url] | join ("")) | join("\n")' prs.json
|
||||
```
|
||||
|
||||
To get all of the maintenance PRs:
|
||||
```
|
||||
jq -r '[.[] | select((.merged_at | type) == "string") | select(all(.labels | map(.name)[]; . != "bug" and . != "enhancement" and . != "skip-changelog"))] | sort_by(.merged_at) | reverse | map([.title, " (**#", .number, "**) ", .user.html_url] | join ("")) | join("\n")' prs.json
|
||||
```
|
||||
23
package.json
23
package.json
@ -204,13 +204,13 @@
|
||||
"@ogre-tools/injectable-react": "5.0.1",
|
||||
"@sentry/electron": "^2.5.4",
|
||||
"@sentry/integrations": "^6.15.0",
|
||||
"@types/circular-dependency-plugin": "5.0.4",
|
||||
"@types/circular-dependency-plugin": "5.0.5",
|
||||
"abort-controller": "^3.0.0",
|
||||
"auto-bind": "^4.0.0",
|
||||
"autobind-decorator": "^2.4.0",
|
||||
"await-lock": "^2.1.0",
|
||||
"byline": "^5.0.0",
|
||||
"chokidar": "^3.4.3",
|
||||
"chokidar": "^3.5.3",
|
||||
"conf": "^7.1.2",
|
||||
"crypto-js": "^4.1.1",
|
||||
"electron-devtools-installer": "^3.2.0",
|
||||
@ -264,7 +264,7 @@
|
||||
"url-parse": "^1.5.10",
|
||||
"uuid": "^8.3.2",
|
||||
"win-ca": "^3.4.5",
|
||||
"winston": "^3.3.3",
|
||||
"winston": "^3.6.0",
|
||||
"winston-console-format": "^1.0.8",
|
||||
"winston-transport-browserconsole": "^1.0.5",
|
||||
"ws": "^7.5.5"
|
||||
@ -282,7 +282,7 @@
|
||||
"@types/byline": "^4.2.33",
|
||||
"@types/chart.js": "^2.9.34",
|
||||
"@types/cli-progress": "^3.9.2",
|
||||
"@types/color": "^3.0.2",
|
||||
"@types/color": "^3.0.3",
|
||||
"@types/crypto-js": "^3.1.47",
|
||||
"@types/dompurify": "^2.3.1",
|
||||
"@types/electron-devtools-installer": "^2.2.0",
|
||||
@ -306,7 +306,7 @@
|
||||
"@types/npm": "^2.0.32",
|
||||
"@types/proper-lockfile": "^4.1.2",
|
||||
"@types/randomcolor": "^0.5.6",
|
||||
"@types/react": "^17.0.34",
|
||||
"@types/react": "^17.0.40",
|
||||
"@types/react-beautiful-dnd": "^13.1.2",
|
||||
"@types/react-dom": "^17.0.11",
|
||||
"@types/react-router-dom": "^5.3.2",
|
||||
@ -325,25 +325,26 @@
|
||||
"@types/tcp-port-used": "^1.0.0",
|
||||
"@types/tempy": "^0.3.0",
|
||||
"@types/triple-beam": "^1.3.2",
|
||||
"@types/url-parse": "^1.4.5",
|
||||
"@types/url-parse": "^1.4.8",
|
||||
"@types/uuid": "^8.3.3",
|
||||
"@types/webpack": "^5.28.0",
|
||||
"@types/webpack-dev-server": "^4.7.2",
|
||||
"@types/webpack-env": "^1.16.3",
|
||||
"@types/webpack-node-externals": "^2.5.3",
|
||||
"@typescript-eslint/eslint-plugin": "^5.10.1",
|
||||
"@typescript-eslint/eslint-plugin": "^5.14.0",
|
||||
"@typescript-eslint/parser": "^5.10.1",
|
||||
"ansi_up": "^5.1.0",
|
||||
"mock-http": "^1.1.0",
|
||||
"chart.js": "^2.9.4",
|
||||
"circular-dependency-plugin": "^5.2.2",
|
||||
"cli-progress": "^3.10.0",
|
||||
"color": "^3.2.1",
|
||||
"concurrently": "^7.0.0",
|
||||
"css-loader": "^6.5.1",
|
||||
"css-loader": "^6.7.1",
|
||||
"deepdash": "^5.3.9",
|
||||
"dompurify": "^2.3.4",
|
||||
"electron": "^14.2.4",
|
||||
"electron-builder": "^22.14.5",
|
||||
"electron-builder": "^22.14.13",
|
||||
"electron-notarize": "^0.3.0",
|
||||
"esbuild": "^0.13.15",
|
||||
"esbuild-loader": "^2.18.0",
|
||||
@ -378,11 +379,11 @@
|
||||
"react-refresh-typescript": "^2.0.3",
|
||||
"react-router-dom": "^5.3.0",
|
||||
"react-select": "3.2.0",
|
||||
"react-select-event": "^5.1.0",
|
||||
"react-select-event": "^5.3.0",
|
||||
"react-table": "^7.7.0",
|
||||
"react-window": "^1.8.6",
|
||||
"sass": "^1.45.1",
|
||||
"sass-loader": "^12.4.0",
|
||||
"sass-loader": "^12.6.0",
|
||||
"sharp": "^0.29.3",
|
||||
"style-loader": "^3.3.1",
|
||||
"tailwindcss": "^3.0.7",
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
|
||||
import moment from "moment";
|
||||
import { apiBase } from "../index";
|
||||
import type { IMetricsQuery } from "../../../main/routes/metrics-route";
|
||||
import type { IMetricsQuery } from "../../../main/routes/metrics/metrics-query";
|
||||
|
||||
export interface IMetrics {
|
||||
status: string;
|
||||
|
||||
@ -1,63 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { Router } from "../router";
|
||||
|
||||
jest.mock("electron", () => ({
|
||||
app: {
|
||||
getVersion: () => "99.99.99",
|
||||
getName: () => "lens",
|
||||
setName: jest.fn(),
|
||||
setPath: jest.fn(),
|
||||
getPath: () => "tmp",
|
||||
getLocale: () => "en",
|
||||
setLoginItemSettings: jest.fn(),
|
||||
},
|
||||
ipcMain: {
|
||||
on: jest.fn(),
|
||||
handle: jest.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
describe("Router", () => {
|
||||
it("blocks path traversal attacks", async () => {
|
||||
const response: any = {
|
||||
statusCode: 200,
|
||||
end: jest.fn(),
|
||||
};
|
||||
|
||||
await (Router as any).handleStaticFile({
|
||||
params: {
|
||||
path: "../index.ts",
|
||||
},
|
||||
response,
|
||||
raw: {},
|
||||
});
|
||||
|
||||
expect(response.statusCode).toEqual(404);
|
||||
});
|
||||
|
||||
it("serves files under static root", async () => {
|
||||
const response: any = {
|
||||
statusCode: 200,
|
||||
write: jest.fn(),
|
||||
setHeader: jest.fn(),
|
||||
end: jest.fn(),
|
||||
};
|
||||
const req: any = {
|
||||
url: "",
|
||||
};
|
||||
|
||||
await (Router as any).handleStaticFile({
|
||||
params: {
|
||||
path: "router.test.ts",
|
||||
},
|
||||
response,
|
||||
raw: { req },
|
||||
});
|
||||
|
||||
expect(response.statusCode).toEqual(200);
|
||||
});
|
||||
});
|
||||
66
src/main/__test__/static-file-route.test.ts
Normal file
66
src/main/__test__/static-file-route.test.ts
Normal file
@ -0,0 +1,66 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { LensApiRequest, Route } from "../router/router";
|
||||
import staticFileRouteInjectable from "../routes/static-file-route.injectable";
|
||||
import { getDiForUnitTesting } from "../getDiForUnitTesting";
|
||||
|
||||
jest.mock("electron", () => ({
|
||||
app: {
|
||||
getVersion: () => "99.99.99",
|
||||
getName: () => "lens",
|
||||
setName: jest.fn(),
|
||||
setPath: jest.fn(),
|
||||
getPath: () => "tmp",
|
||||
getLocale: () => "en",
|
||||
setLoginItemSettings: jest.fn(),
|
||||
},
|
||||
ipcMain: {
|
||||
on: jest.fn(),
|
||||
handle: jest.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
describe("static-file-route", () => {
|
||||
let handleStaticFileRoute: Route<Buffer>;
|
||||
|
||||
beforeEach(async () => {
|
||||
const di = getDiForUnitTesting({ doGeneralOverrides: true });
|
||||
|
||||
await di.runSetups();
|
||||
|
||||
handleStaticFileRoute = di.inject(staticFileRouteInjectable);
|
||||
});
|
||||
|
||||
it("blocks path traversal attacks", async () => {
|
||||
const request = {
|
||||
params: {
|
||||
path: "../index.ts",
|
||||
},
|
||||
raw: {},
|
||||
} as LensApiRequest<any>;
|
||||
|
||||
const result = await handleStaticFileRoute.handler(request);
|
||||
|
||||
expect(result).toEqual({ statusCode: 404 });
|
||||
});
|
||||
|
||||
it("serves files under static root", async () => {
|
||||
const req: any = {
|
||||
url: "",
|
||||
};
|
||||
|
||||
const request = {
|
||||
params: {
|
||||
path: "router.test.ts",
|
||||
},
|
||||
raw: { req },
|
||||
} as LensApiRequest<any>;
|
||||
|
||||
const result = await handleStaticFileRoute.handler(request);
|
||||
|
||||
expect(result).toEqual({ statusCode: 404 });
|
||||
});
|
||||
});
|
||||
@ -14,6 +14,7 @@ import appNameInjectable from "./app-paths/app-name/app-name.injectable";
|
||||
import registerChannelInjectable from "./app-paths/register-channel/register-channel.injectable";
|
||||
import writeJsonFileInjectable from "../common/fs/write-json-file.injectable";
|
||||
import readJsonFileInjectable from "../common/fs/read-json-file.injectable";
|
||||
import readFileInjectable from "../common/fs/read-file.injectable";
|
||||
import directoryForBundledBinariesInjectable from "../common/app-paths/directory-for-bundled-binaries/directory-for-bundled-binaries.injectable";
|
||||
|
||||
export const getDiForUnitTesting = (
|
||||
@ -53,6 +54,10 @@ export const getDiForUnitTesting = (
|
||||
di.override(readJsonFileInjectable, () => () => {
|
||||
throw new Error("Tried to read JSON file from file system without specifying explicit override.");
|
||||
});
|
||||
|
||||
di.override(readFileInjectable, () => () => {
|
||||
throw new Error("Tried to read file from file system without specifying explicit override.");
|
||||
});
|
||||
}
|
||||
|
||||
return di;
|
||||
|
||||
@ -90,9 +90,11 @@ export async function upgradeRelease(name: string, chart: string, values: any, n
|
||||
];
|
||||
|
||||
try {
|
||||
const output = await execHelm(args);
|
||||
|
||||
return {
|
||||
log: await execHelm(args),
|
||||
release: getRelease(name, namespace, kubeconfigPath, kubectlPath),
|
||||
log: output,
|
||||
release: await getRelease(name, namespace, kubeconfigPath, kubectlPath),
|
||||
};
|
||||
} finally {
|
||||
await fse.unlink(valuesFilePath);
|
||||
|
||||
@ -8,7 +8,7 @@ import type http from "http";
|
||||
import spdy from "spdy";
|
||||
import type httpProxy from "http-proxy";
|
||||
import { apiPrefix, apiKubePrefix } from "../common/vars";
|
||||
import type { Router } from "./router";
|
||||
import type { Router } from "./router/router";
|
||||
import type { ContextHandler } from "./context-handler/context-handler";
|
||||
import logger from "./logger";
|
||||
import { Singleton } from "../common/utils";
|
||||
@ -84,7 +84,7 @@ export class LensProxy extends Singleton {
|
||||
|
||||
if (!cluster) {
|
||||
logger.error(`[LENS-PROXY]: Could not find cluster for upgrade request from url=${req.url}`);
|
||||
|
||||
|
||||
return socket.destroy();
|
||||
}
|
||||
|
||||
|
||||
@ -1,197 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import Call from "@hapi/call";
|
||||
import Subtext from "@hapi/subtext";
|
||||
import type http from "http";
|
||||
import type httpProxy from "http-proxy";
|
||||
import path from "path";
|
||||
import { readFile } from "fs-extra";
|
||||
import type { Cluster } from "../common/cluster/cluster";
|
||||
import { apiPrefix, appName, publicPath } from "../common/vars";
|
||||
import { HelmApiRoute, KubeconfigRoute, MetricsRoute, PortForwardRoute, ResourceApplierApiRoute, VersionRoute } from "./routes";
|
||||
import logger from "./logger";
|
||||
|
||||
export interface RouterRequestOpts {
|
||||
req: http.IncomingMessage;
|
||||
res: http.ServerResponse;
|
||||
cluster: Cluster;
|
||||
params: RouteParams;
|
||||
url: URL;
|
||||
}
|
||||
|
||||
export interface RouteParams extends Record<string, string> {
|
||||
path?: string; // *-route
|
||||
namespace?: string;
|
||||
service?: string;
|
||||
account?: string;
|
||||
release?: string;
|
||||
repo?: string;
|
||||
chart?: string;
|
||||
}
|
||||
|
||||
export interface LensApiRequest<P = any> {
|
||||
path: string;
|
||||
payload: P;
|
||||
params: RouteParams;
|
||||
cluster: Cluster;
|
||||
response: http.ServerResponse;
|
||||
query: URLSearchParams;
|
||||
raw: {
|
||||
req: http.IncomingMessage;
|
||||
res: http.ServerResponse;
|
||||
};
|
||||
}
|
||||
|
||||
function getMimeType(filename: string) {
|
||||
const mimeTypes: Record<string, string> = {
|
||||
html: "text/html",
|
||||
txt: "text/plain",
|
||||
css: "text/css",
|
||||
gif: "image/gif",
|
||||
jpg: "image/jpeg",
|
||||
png: "image/png",
|
||||
svg: "image/svg+xml",
|
||||
js: "application/javascript",
|
||||
woff2: "font/woff2",
|
||||
ttf: "font/ttf",
|
||||
};
|
||||
|
||||
return mimeTypes[path.extname(filename).slice(1)] || "text/plain";
|
||||
}
|
||||
|
||||
interface Dependencies {
|
||||
routePortForward: (request: LensApiRequest) => Promise<void>;
|
||||
httpProxy?: httpProxy;
|
||||
}
|
||||
|
||||
export class Router {
|
||||
protected router = new Call.Router();
|
||||
protected static rootPath = path.resolve(__static);
|
||||
|
||||
public constructor(private dependencies: Dependencies) {
|
||||
this.addRoutes();
|
||||
}
|
||||
|
||||
public async route(cluster: Cluster, req: http.IncomingMessage, res: http.ServerResponse): Promise<boolean> {
|
||||
const url = new URL(req.url, "http://localhost");
|
||||
const path = url.pathname;
|
||||
const method = req.method.toLowerCase();
|
||||
const matchingRoute = this.router.route(method, path);
|
||||
const routeFound = !matchingRoute.isBoom;
|
||||
|
||||
if (routeFound) {
|
||||
const request = await this.getRequest({ req, res, cluster, url, params: matchingRoute.params });
|
||||
|
||||
await matchingRoute.route(request);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected async getRequest(opts: RouterRequestOpts): Promise<LensApiRequest> {
|
||||
const { req, res, url, cluster, params } = opts;
|
||||
const { payload } = await Subtext.parse(req, null, {
|
||||
parse: true,
|
||||
output: "data",
|
||||
});
|
||||
|
||||
return {
|
||||
cluster,
|
||||
path: url.pathname,
|
||||
raw: {
|
||||
req, res,
|
||||
},
|
||||
response: res,
|
||||
query: url.searchParams,
|
||||
payload,
|
||||
params,
|
||||
};
|
||||
}
|
||||
|
||||
protected static async handleStaticFile({ params, response }: LensApiRequest): Promise<void> {
|
||||
let filePath = params.path;
|
||||
|
||||
for (let retryCount = 0; retryCount < 5; retryCount += 1) {
|
||||
const asset = path.join(Router.rootPath, filePath);
|
||||
const normalizedFilePath = path.resolve(asset);
|
||||
|
||||
if (!normalizedFilePath.startsWith(Router.rootPath)) {
|
||||
response.statusCode = 404;
|
||||
|
||||
return response.end();
|
||||
}
|
||||
|
||||
try {
|
||||
const data = await readFile(asset);
|
||||
|
||||
response.setHeader("Content-Type", getMimeType(asset));
|
||||
response.write(data);
|
||||
response.end();
|
||||
} catch (err) {
|
||||
if (retryCount > 5) {
|
||||
logger.error("handleStaticFile:", err.toString());
|
||||
response.statusCode = 404;
|
||||
|
||||
return response.end();
|
||||
}
|
||||
|
||||
filePath = `${publicPath}/${appName}.html`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected addRoutes() {
|
||||
// Static assets
|
||||
if (this.dependencies.httpProxy) {
|
||||
this.router.add({ method: "get", path: "/{path*}" }, (apiReq: LensApiRequest) => {
|
||||
const { req, res } = apiReq.raw;
|
||||
|
||||
if (req.url === "/" || !req.url.startsWith("/build/")) {
|
||||
req.url = `${publicPath}/${appName}.html`;
|
||||
}
|
||||
|
||||
this.dependencies.httpProxy.web(req, res, {
|
||||
target: "http://127.0.0.1:8080",
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this.router.add({ method: "get", path: "/{path*}" }, Router.handleStaticFile);
|
||||
}
|
||||
|
||||
|
||||
this.router.add({ method: "get", path: "/version" }, VersionRoute.getVersion);
|
||||
this.router.add({ method: "get", path: `${apiPrefix}/kubeconfig/service-account/{namespace}/{account}` }, KubeconfigRoute.routeServiceAccountRoute);
|
||||
|
||||
// Metrics API
|
||||
this.router.add({ method: "post", path: `${apiPrefix}/metrics` }, MetricsRoute.routeMetrics);
|
||||
this.router.add({ method: "get", path: `${apiPrefix}/metrics/providers` }, MetricsRoute.routeMetricsProviders);
|
||||
|
||||
// Port-forward API (the container port and local forwarding port are obtained from the query parameters)
|
||||
this.router.add({ method: "post", path: `${apiPrefix}/pods/port-forward/{namespace}/{resourceType}/{resourceName}` }, this.dependencies.routePortForward);
|
||||
this.router.add({ method: "get", path: `${apiPrefix}/pods/port-forward/{namespace}/{resourceType}/{resourceName}` }, PortForwardRoute.routeCurrentPortForward);
|
||||
this.router.add({ method: "delete", path: `${apiPrefix}/pods/port-forward/{namespace}/{resourceType}/{resourceName}` }, PortForwardRoute.routeCurrentPortForwardStop);
|
||||
|
||||
// Helm API
|
||||
this.router.add({ method: "get", path: `${apiPrefix}/v2/charts` }, HelmApiRoute.listCharts);
|
||||
this.router.add({ method: "get", path: `${apiPrefix}/v2/charts/{repo}/{chart}` }, HelmApiRoute.getChart);
|
||||
this.router.add({ method: "get", path: `${apiPrefix}/v2/charts/{repo}/{chart}/values` }, HelmApiRoute.getChartValues);
|
||||
|
||||
this.router.add({ method: "post", path: `${apiPrefix}/v2/releases` }, HelmApiRoute.installChart);
|
||||
this.router.add({ method: `put`, path: `${apiPrefix}/v2/releases/{namespace}/{release}` }, HelmApiRoute.updateRelease);
|
||||
this.router.add({ method: `put`, path: `${apiPrefix}/v2/releases/{namespace}/{release}/rollback` }, HelmApiRoute.rollbackRelease);
|
||||
this.router.add({ method: "get", path: `${apiPrefix}/v2/releases/{namespace?}` }, HelmApiRoute.listReleases);
|
||||
this.router.add({ method: "get", path: `${apiPrefix}/v2/releases/{namespace}/{release}` }, HelmApiRoute.getRelease);
|
||||
this.router.add({ method: "get", path: `${apiPrefix}/v2/releases/{namespace}/{release}/values` }, HelmApiRoute.getReleaseValues);
|
||||
this.router.add({ method: "get", path: `${apiPrefix}/v2/releases/{namespace}/{release}/history` }, HelmApiRoute.getReleaseHistory);
|
||||
this.router.add({ method: "delete", path: `${apiPrefix}/v2/releases/{namespace}/{release}` }, HelmApiRoute.deleteRelease);
|
||||
|
||||
// Resource Applier API
|
||||
this.router.add({ method: "post", path: `${apiPrefix}/stack` }, ResourceApplierApiRoute.applyResource);
|
||||
this.router.add({ method: "patch", path: `${apiPrefix}/stack` }, ResourceApplierApiRoute.patchResource);
|
||||
}
|
||||
}
|
||||
13
src/main/router/parse-request.injectable.ts
Normal file
13
src/main/router/parse-request.injectable.ts
Normal file
@ -0,0 +1,13 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import Subtext from "@hapi/subtext";
|
||||
|
||||
const parseRequestInjectable = getInjectable({
|
||||
id: "parse-http-request",
|
||||
instantiate: () => Subtext.parse,
|
||||
});
|
||||
|
||||
export default parseRequestInjectable;
|
||||
86
src/main/router/router-content-types.ts
Normal file
86
src/main/router/router-content-types.ts
Normal file
@ -0,0 +1,86 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import type { LensApiResult } from "./router";
|
||||
|
||||
export interface LensApiResultContentType {
|
||||
resultMapper: (result: LensApiResult<any>) => ({
|
||||
statusCode: number;
|
||||
content: any;
|
||||
headers: Record<string, string>;
|
||||
});
|
||||
}
|
||||
|
||||
const resultMapperFor =
|
||||
(contentType: string): LensApiResultContentType["resultMapper"] =>
|
||||
({ response, error, statusCode= error ? 400 : 200, headers = {}}) => ({
|
||||
statusCode,
|
||||
content: error || response,
|
||||
headers: { ...headers, "Content-Type": contentType },
|
||||
});
|
||||
|
||||
export type SupportedFileExtension = "json" | "txt" | "html" | "css" | "gif" | "jpg" | "png" | "svg" | "js" | "woff2" | "ttf";
|
||||
|
||||
export const contentTypes: Record<SupportedFileExtension, LensApiResultContentType> = {
|
||||
json: {
|
||||
resultMapper: (result) => {
|
||||
const resultMapper = resultMapperFor("application/json");
|
||||
|
||||
const mappedResult = resultMapper(result);
|
||||
|
||||
const contentIsObject = typeof mappedResult.content === "object";
|
||||
const contentIsBuffer = mappedResult.content instanceof Buffer;
|
||||
const contentShouldBeStringified = contentIsObject && !contentIsBuffer;
|
||||
|
||||
const content = contentShouldBeStringified
|
||||
? JSON.stringify(mappedResult.content)
|
||||
: mappedResult.content;
|
||||
|
||||
return {
|
||||
...mappedResult,
|
||||
content,
|
||||
};
|
||||
},
|
||||
},
|
||||
|
||||
txt: {
|
||||
resultMapper: resultMapperFor("text/plain"),
|
||||
},
|
||||
|
||||
html: {
|
||||
resultMapper: resultMapperFor("text/html"),
|
||||
},
|
||||
|
||||
css: {
|
||||
resultMapper: resultMapperFor("text/css"),
|
||||
},
|
||||
|
||||
gif: {
|
||||
resultMapper: resultMapperFor("image/gif"),
|
||||
},
|
||||
|
||||
jpg: {
|
||||
resultMapper: resultMapperFor("image/jpeg"),
|
||||
},
|
||||
|
||||
png: {
|
||||
resultMapper: resultMapperFor("image/png"),
|
||||
},
|
||||
|
||||
svg: {
|
||||
resultMapper: resultMapperFor("image/svg+xml"),
|
||||
},
|
||||
|
||||
js: {
|
||||
resultMapper: resultMapperFor("application/javascript"),
|
||||
},
|
||||
|
||||
woff2: {
|
||||
resultMapper: resultMapperFor("font/woff2"),
|
||||
},
|
||||
|
||||
ttf: {
|
||||
resultMapper: resultMapperFor("font/ttf"),
|
||||
},
|
||||
};
|
||||
@ -2,23 +2,22 @@
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import isDevelopmentInjectable from "../../common/vars/is-development.injectable";
|
||||
import httpProxy from "http-proxy";
|
||||
import { Router } from "../router";
|
||||
import routePortForwardInjectable
|
||||
from "../routes/port-forward/route-port-forward/route-port-forward.injectable";
|
||||
import { getInjectable, getInjectionToken } from "@ogre-tools/injectable";
|
||||
import { Route, Router } from "./router";
|
||||
import parseRequestInjectable from "./parse-request.injectable";
|
||||
|
||||
export const routeInjectionToken = getInjectionToken<Route<any>>({
|
||||
id: "route-injection-token",
|
||||
});
|
||||
|
||||
const routerInjectable = getInjectable({
|
||||
id: "router",
|
||||
|
||||
instantiate: (di) => {
|
||||
const isDevelopment = di.inject(isDevelopmentInjectable);
|
||||
const proxy = isDevelopment ? httpProxy.createProxy() : undefined;
|
||||
const routes = di.injectMany(routeInjectionToken);
|
||||
|
||||
return new Router({
|
||||
routePortForward: di.inject(routePortForwardInjectable),
|
||||
httpProxy: proxy,
|
||||
return new Router(routes, {
|
||||
parseRequest: di.inject(parseRequestInjectable),
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
294
src/main/router/router.test.ts
Normal file
294
src/main/router/router.test.ts
Normal file
@ -0,0 +1,294 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import routerInjectable, { routeInjectionToken } from "./router.injectable";
|
||||
import { getDiForUnitTesting } from "../getDiForUnitTesting";
|
||||
import type { Router, RouteHandler, Route } from "./router";
|
||||
import type { Cluster } from "../../common/cluster/cluster";
|
||||
import { Request } from "mock-http";
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import type { AsyncFnMock } from "@async-fn/jest";
|
||||
import asyncFn from "@async-fn/jest";
|
||||
import parseRequestInjectable from "./parse-request.injectable";
|
||||
import { contentTypes } from "./router-content-types";
|
||||
import mockFs from "mock-fs";
|
||||
|
||||
describe("router", () => {
|
||||
let router: Router;
|
||||
let routeHandlerMock: AsyncFnMock<RouteHandler<any>>;
|
||||
|
||||
beforeEach(async () => {
|
||||
routeHandlerMock = asyncFn();
|
||||
|
||||
const di = getDiForUnitTesting({ doGeneralOverrides: true });
|
||||
|
||||
mockFs();
|
||||
|
||||
di.override(parseRequestInjectable, () => () => Promise.resolve({ payload: "some-payload" }));
|
||||
|
||||
await di.runSetups();
|
||||
|
||||
const injectable = getInjectable({
|
||||
id: "some-route",
|
||||
|
||||
instantiate: () => ({
|
||||
method: "get",
|
||||
path: "/some-path",
|
||||
handler: routeHandlerMock,
|
||||
} as Route<any>),
|
||||
|
||||
injectionToken: routeInjectionToken,
|
||||
});
|
||||
|
||||
di.register(injectable);
|
||||
|
||||
router = di.inject(routerInjectable);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mockFs.restore();
|
||||
});
|
||||
|
||||
describe("when navigating to the route", () => {
|
||||
let actualPromise: Promise<boolean>;
|
||||
let clusterStub: Cluster;
|
||||
let requestStub: Request;
|
||||
let responseStub: any;
|
||||
|
||||
beforeEach(() => {
|
||||
requestStub = new Request({
|
||||
url: "/some-path",
|
||||
method: "get",
|
||||
headers: {
|
||||
"content-type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
responseStub = { end: jest.fn(), setHeader: jest.fn(), write: jest.fn(), statusCode: undefined };
|
||||
|
||||
clusterStub = {} as Cluster;
|
||||
|
||||
actualPromise = router.route(clusterStub, requestStub, responseStub);
|
||||
});
|
||||
|
||||
it("calls handler with the request", () => {
|
||||
expect(routeHandlerMock).toHaveBeenCalledWith({
|
||||
cluster: clusterStub,
|
||||
params: {},
|
||||
path: "/some-path",
|
||||
payload: "some-payload",
|
||||
query: expect.any(URLSearchParams),
|
||||
raw: { req: requestStub, res: responseStub },
|
||||
});
|
||||
});
|
||||
|
||||
it("given no content-type is specified, when handler resolves, resolves with JSON", async () => {
|
||||
await routeHandlerMock.resolve({ response: "some-response-from-route-handler" });
|
||||
|
||||
await actualPromise;
|
||||
|
||||
expect(responseStub.setHeader.mock.calls).toEqual([
|
||||
["Content-Type", "application/json"],
|
||||
]);
|
||||
});
|
||||
|
||||
it("given JSON content-type is specified, when handler resolves with object, resolves with JSON", async () => {
|
||||
await routeHandlerMock.resolve({ response: { some: "object" }});
|
||||
|
||||
await actualPromise;
|
||||
|
||||
expect(responseStub.end).toHaveBeenCalledWith('{"some":"object"}');
|
||||
});
|
||||
|
||||
describe("when handler resolves without any result", () => {
|
||||
beforeEach(async () => {
|
||||
await routeHandlerMock.resolve(undefined);
|
||||
|
||||
await actualPromise;
|
||||
});
|
||||
|
||||
it("resolves as plain text", () => {
|
||||
expect(responseStub.setHeader.mock.calls).toEqual([["Content-Type", "text/plain"]]);
|
||||
});
|
||||
|
||||
it("resolves with status code for no content", async () => {
|
||||
expect(responseStub.statusCode).toBe(204);
|
||||
});
|
||||
|
||||
it("resolves without content", async () => {
|
||||
expect(responseStub.end.mock.calls).toEqual([[]]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("when handler rejects", () => {
|
||||
beforeEach(async () => {
|
||||
await routeHandlerMock.reject(new Error("some-error"));
|
||||
|
||||
await actualPromise;
|
||||
});
|
||||
|
||||
it("resolves as plain text", () => {
|
||||
expect(responseStub.setHeader.mock.calls).toEqual([["Content-Type", "text/plain"]]);
|
||||
});
|
||||
|
||||
it('resolves with "500" status code', () => {
|
||||
expect(responseStub.statusCode).toBe(500);
|
||||
});
|
||||
|
||||
it("resolves with error", () => {
|
||||
expect(responseStub.end).toHaveBeenCalledWith("Error: some-error");
|
||||
});
|
||||
});
|
||||
|
||||
[
|
||||
{ contentType: "text/plain", contentTypeObject: contentTypes.txt },
|
||||
{ contentType: "application/json", contentTypeObject: contentTypes.json },
|
||||
{ contentType: "text/html", contentTypeObject: contentTypes.html },
|
||||
{ contentType: "text/css", contentTypeObject: contentTypes.css },
|
||||
{ contentType: "image/gif", contentTypeObject: contentTypes.gif },
|
||||
{ contentType: "image/jpeg", contentTypeObject: contentTypes.jpg },
|
||||
{ contentType: "image/png", contentTypeObject: contentTypes.png },
|
||||
{ contentType: "image/svg+xml", contentTypeObject: contentTypes.svg },
|
||||
{ contentType: "application/javascript", contentTypeObject: contentTypes.js },
|
||||
{ contentType: "font/woff2", contentTypeObject: contentTypes.woff2 },
|
||||
{ contentType: "font/ttf", contentTypeObject: contentTypes.ttf },
|
||||
].forEach(scenario => {
|
||||
describe(`given content type is "${scenario.contentType}", when handler resolves with response`, () => {
|
||||
beforeEach(async () => {
|
||||
await routeHandlerMock.resolve({ response: "some-response", contentType: scenario.contentTypeObject });
|
||||
|
||||
await actualPromise;
|
||||
});
|
||||
|
||||
it("has content type specific headers", () => {
|
||||
expect(responseStub.setHeader.mock.calls).toEqual([
|
||||
["Content-Type", scenario.contentType],
|
||||
]);
|
||||
});
|
||||
|
||||
it("defaults to successful status code", () => {
|
||||
expect(responseStub.statusCode).toBe(200);
|
||||
});
|
||||
|
||||
it("has response as body", () => {
|
||||
expect(responseStub.end).toHaveBeenCalledWith("some-response");
|
||||
});
|
||||
});
|
||||
|
||||
it(`given content type is "${scenario.contentType}", when handler resolves with success and custom status code, defaults to "200" as status code`, async () => {
|
||||
await routeHandlerMock.resolve({
|
||||
response: "some-response",
|
||||
statusCode: 204,
|
||||
contentType: scenario.contentTypeObject,
|
||||
});
|
||||
|
||||
await actualPromise;
|
||||
|
||||
expect(responseStub.statusCode).toBe(204);
|
||||
});
|
||||
|
||||
it(`given content type is "${scenario.contentType}", when handler resolves with success but without status code, defaults to "200" as status code`, async () => {
|
||||
await routeHandlerMock.resolve({
|
||||
response: "some-response",
|
||||
contentType: scenario.contentTypeObject,
|
||||
});
|
||||
|
||||
await actualPromise;
|
||||
|
||||
expect(responseStub.statusCode).toBe(200);
|
||||
});
|
||||
|
||||
|
||||
it(`given content type is "${scenario.contentType}", when handler resolves without response, has no body`, async () => {
|
||||
await routeHandlerMock.resolve({
|
||||
response: undefined,
|
||||
contentType: scenario.contentTypeObject,
|
||||
});
|
||||
|
||||
await actualPromise;
|
||||
|
||||
expect(responseStub.end.mock.calls).toEqual([[]]);
|
||||
});
|
||||
|
||||
it(`given content type is "${scenario.contentType}", when handler resolves with error, has error as body`, async () => {
|
||||
await routeHandlerMock.resolve({
|
||||
error: "some-error",
|
||||
contentType: scenario.contentTypeObject,
|
||||
});
|
||||
|
||||
await actualPromise;
|
||||
|
||||
expect(responseStub.end).toHaveBeenCalledWith("some-error");
|
||||
});
|
||||
|
||||
it(`given content type is "${scenario.contentType}", when handler resolves with error and status code, has custom status code`, async () => {
|
||||
await routeHandlerMock.resolve({
|
||||
error: "some-error",
|
||||
statusCode: 414,
|
||||
contentType: scenario.contentTypeObject,
|
||||
});
|
||||
|
||||
await actualPromise;
|
||||
|
||||
expect(responseStub.statusCode).toBe(414);
|
||||
});
|
||||
|
||||
it(`given content type is "${scenario.contentType}", when handler resolves with error but without status code, defaults to "400" as status code`, async () => {
|
||||
await routeHandlerMock.resolve({
|
||||
error: "some-error",
|
||||
contentType: scenario.contentTypeObject,
|
||||
});
|
||||
|
||||
await actualPromise;
|
||||
|
||||
expect(responseStub.statusCode).toBe(400);
|
||||
});
|
||||
|
||||
it(`given content type is "${scenario.contentType}", when handler resolves custom headers, resolves with content type specific headers and custom headers`, async () => {
|
||||
await routeHandlerMock.resolve({
|
||||
response: "irrelevant",
|
||||
|
||||
headers: {
|
||||
"Content-Type": "some-content-type-to-be-overridden",
|
||||
"Some-Header": "some-header-value",
|
||||
},
|
||||
|
||||
contentType: scenario.contentTypeObject,
|
||||
});
|
||||
|
||||
await actualPromise;
|
||||
|
||||
expect(responseStub.setHeader.mock.calls).toEqual([
|
||||
["Content-Type", scenario.contentType],
|
||||
["Some-Header", "some-header-value"],
|
||||
]);
|
||||
|
||||
});
|
||||
|
||||
describe(`given content type is "${scenario.contentType}", when handler resolves with binary content`, () => {
|
||||
let responseBufferStub: Buffer;
|
||||
|
||||
beforeEach(async () => {
|
||||
responseBufferStub = Buffer.from("some-binary-content");
|
||||
|
||||
await routeHandlerMock.resolve({
|
||||
response: responseBufferStub,
|
||||
contentType: scenario.contentTypeObject,
|
||||
});
|
||||
|
||||
await actualPromise;
|
||||
});
|
||||
|
||||
it("writes binary content to response", () => {
|
||||
expect(responseStub.write).toHaveBeenCalledWith(responseBufferStub);
|
||||
});
|
||||
|
||||
it("does not end with the response", () => {
|
||||
expect(responseStub.end.mock.calls[0]).toEqual([]);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
196
src/main/router/router.ts
Normal file
196
src/main/router/router.ts
Normal file
@ -0,0 +1,196 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import Call from "@hapi/call";
|
||||
import type http from "http";
|
||||
import type httpProxy from "http-proxy";
|
||||
import { toPairs } from "lodash/fp";
|
||||
import path from "path";
|
||||
import type { Cluster } from "../../common/cluster/cluster";
|
||||
import type { LensApiResultContentType } from "./router-content-types";
|
||||
import { contentTypes } from "./router-content-types";
|
||||
|
||||
// TODO: Import causes side effect, sets value for __static
|
||||
import "../../common/vars";
|
||||
|
||||
export interface RouterRequestOpts {
|
||||
req: http.IncomingMessage;
|
||||
res: http.ServerResponse;
|
||||
cluster: Cluster;
|
||||
params: RouteParams;
|
||||
url: URL;
|
||||
}
|
||||
|
||||
export interface RouteParams extends Record<string, string> {
|
||||
path?: string; // *-route
|
||||
namespace?: string;
|
||||
service?: string;
|
||||
account?: string;
|
||||
release?: string;
|
||||
repo?: string;
|
||||
chart?: string;
|
||||
}
|
||||
|
||||
export interface LensApiRequest<P = any> {
|
||||
path: string;
|
||||
payload: P;
|
||||
params: RouteParams;
|
||||
cluster: Cluster;
|
||||
query: URLSearchParams;
|
||||
raw: {
|
||||
req: http.IncomingMessage;
|
||||
res: http.ServerResponse;
|
||||
};
|
||||
}
|
||||
|
||||
interface Dependencies {
|
||||
parseRequest: (request: http.IncomingMessage, _: null, options: { parse: boolean; output: string }) => Promise<{ payload: any }>;
|
||||
}
|
||||
|
||||
export class Router {
|
||||
protected router = new Call.Router();
|
||||
protected static rootPath = path.resolve(__static);
|
||||
|
||||
constructor(routes: Route<unknown>[], private dependencies: Dependencies) {
|
||||
routes.forEach(route => {
|
||||
this.router.add({ method: route.method, path: route.path }, handleRoute(route));
|
||||
});
|
||||
}
|
||||
|
||||
public async route(cluster: Cluster, req: http.IncomingMessage, res: http.ServerResponse): Promise<boolean> {
|
||||
const url = new URL(req.url, "http://localhost");
|
||||
const path = url.pathname;
|
||||
const method = req.method.toLowerCase();
|
||||
const matchingRoute = this.router.route(method, path);
|
||||
const routeFound = !matchingRoute.isBoom;
|
||||
|
||||
if (routeFound) {
|
||||
const request = await this.getRequest({ req, res, cluster, url, params: matchingRoute.params });
|
||||
|
||||
await matchingRoute.route(request, res);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected async getRequest(opts: RouterRequestOpts): Promise<LensApiRequest> {
|
||||
const { req, res, url, cluster, params } = opts;
|
||||
|
||||
const { payload } = await this.dependencies.parseRequest(req, null, {
|
||||
parse: true,
|
||||
output: "data",
|
||||
});
|
||||
|
||||
return {
|
||||
cluster,
|
||||
path: url.pathname,
|
||||
raw: {
|
||||
req, res,
|
||||
},
|
||||
query: url.searchParams,
|
||||
payload,
|
||||
params,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export interface LensApiResult<TResult> {
|
||||
statusCode?: number;
|
||||
response?: TResult;
|
||||
error?: any;
|
||||
contentType?: LensApiResultContentType;
|
||||
headers?: { [name: string]: string };
|
||||
proxy?: httpProxy;
|
||||
}
|
||||
|
||||
export type RouteHandler<TResponse> = (
|
||||
request: LensApiRequest
|
||||
) =>
|
||||
| Promise<LensApiResult<TResponse>>
|
||||
| Promise<void>
|
||||
| LensApiResult<TResponse>
|
||||
| void;
|
||||
|
||||
export interface Route<TResponse> {
|
||||
path: string;
|
||||
method: "get" | "post" | "put" | "patch" | "delete";
|
||||
handler: RouteHandler<TResponse>;
|
||||
}
|
||||
|
||||
const handleRoute = (route: Route<unknown>) => async (request: LensApiRequest, response: http.ServerResponse) => {
|
||||
let result: LensApiResult<any> | void;
|
||||
|
||||
const writeServerResponse = writeServerResponseFor(response);
|
||||
|
||||
try {
|
||||
result = await route.handler(request);
|
||||
} catch(error) {
|
||||
const mappedResult = contentTypes.txt.resultMapper({
|
||||
statusCode: 500,
|
||||
error: error.toString(),
|
||||
});
|
||||
|
||||
writeServerResponse(mappedResult);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
const mappedResult = contentTypes.txt.resultMapper({
|
||||
statusCode: 204,
|
||||
response: undefined,
|
||||
});
|
||||
|
||||
writeServerResponse(mappedResult);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (result.proxy) {
|
||||
return;
|
||||
}
|
||||
|
||||
const contentType = result.contentType || contentTypes.json;
|
||||
|
||||
const mappedResult = contentType.resultMapper(result);
|
||||
|
||||
writeServerResponse(mappedResult);
|
||||
};
|
||||
|
||||
const writeServerResponseFor =
|
||||
(serverResponse: http.ServerResponse) =>
|
||||
({
|
||||
statusCode,
|
||||
content,
|
||||
headers,
|
||||
}: {
|
||||
statusCode: number;
|
||||
content: any;
|
||||
headers: { [name: string]: string };
|
||||
}) => {
|
||||
serverResponse.statusCode = statusCode;
|
||||
|
||||
const headerPairs = toPairs<string>(headers);
|
||||
|
||||
headerPairs.forEach(([name, value]) => {
|
||||
serverResponse.setHeader(name, value);
|
||||
});
|
||||
|
||||
if (content instanceof Buffer) {
|
||||
serverResponse.write(content);
|
||||
|
||||
serverResponse.end();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (content) {
|
||||
serverResponse.end(content);
|
||||
} else {
|
||||
serverResponse.end();
|
||||
}
|
||||
};
|
||||
@ -1,148 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { LensApiRequest } from "../router";
|
||||
import { helmService } from "../helm/helm-service";
|
||||
import logger from "../logger";
|
||||
import { respondJson, respondText } from "../utils/http-responses";
|
||||
import { getBoolean } from "../utils/parse-query";
|
||||
|
||||
export class HelmApiRoute {
|
||||
static async listCharts(request: LensApiRequest) {
|
||||
const { response } = request;
|
||||
const charts = await helmService.listCharts();
|
||||
|
||||
respondJson(response, charts);
|
||||
}
|
||||
|
||||
static async getChart(request: LensApiRequest) {
|
||||
const { params, query, response } = request;
|
||||
|
||||
try {
|
||||
const chart = await helmService.getChart(params.repo, params.chart, query.get("version"));
|
||||
|
||||
respondJson(response, chart);
|
||||
} catch (error) {
|
||||
respondText(response, error?.toString() || "Error getting chart", 422);
|
||||
}
|
||||
}
|
||||
|
||||
static async getChartValues(request: LensApiRequest) {
|
||||
const { params, query, response } = request;
|
||||
|
||||
try {
|
||||
const values = await helmService.getChartValues(params.repo, params.chart, query.get("version"));
|
||||
|
||||
respondJson(response, values);
|
||||
} catch (error) {
|
||||
respondText(response, error?.toString() || "Error getting chart values", 422);
|
||||
}
|
||||
}
|
||||
|
||||
static async installChart(request: LensApiRequest) {
|
||||
const { payload, cluster, response } = request;
|
||||
|
||||
try {
|
||||
const result = await helmService.installChart(cluster, payload);
|
||||
|
||||
respondJson(response, result, 201);
|
||||
} catch (error) {
|
||||
logger.debug(error);
|
||||
respondText(response, error?.toString() || "Error installing chart", 422);
|
||||
}
|
||||
}
|
||||
|
||||
static async updateRelease(request: LensApiRequest) {
|
||||
const { cluster, params, payload, response } = request;
|
||||
|
||||
try {
|
||||
const result = await helmService.updateRelease(cluster, params.release, params.namespace, payload );
|
||||
|
||||
respondJson(response, result);
|
||||
} catch (error) {
|
||||
logger.debug(error);
|
||||
respondText(response, error?.toString() || "Error updating chart", 422);
|
||||
}
|
||||
}
|
||||
|
||||
static async rollbackRelease(request: LensApiRequest) {
|
||||
const { cluster, params, payload, response } = request;
|
||||
|
||||
try {
|
||||
await helmService.rollback(cluster, params.release, params.namespace, payload.revision);
|
||||
|
||||
response.end();
|
||||
} catch (error) {
|
||||
logger.debug(error);
|
||||
respondText(response, error?.toString() || "Error rolling back chart", 422);
|
||||
}
|
||||
}
|
||||
|
||||
static async listReleases(request: LensApiRequest) {
|
||||
const { cluster, params, response } = request;
|
||||
|
||||
try {
|
||||
const result = await helmService.listReleases(cluster, params.namespace);
|
||||
|
||||
respondJson(response, result);
|
||||
} catch(error) {
|
||||
logger.debug(error);
|
||||
respondText(response, error?.toString() || "Error listing release", 422);
|
||||
}
|
||||
}
|
||||
|
||||
static async getRelease(request: LensApiRequest) {
|
||||
const { cluster, params, response } = request;
|
||||
|
||||
try {
|
||||
const result = await helmService.getRelease(cluster, params.release, params.namespace);
|
||||
|
||||
respondJson(response, result);
|
||||
} catch (error) {
|
||||
logger.debug(error);
|
||||
respondText(response, error?.toString() || "Error getting release", 422);
|
||||
}
|
||||
}
|
||||
|
||||
static async getReleaseValues(request: LensApiRequest) {
|
||||
const { cluster, params: { namespace, release }, response, query } = request;
|
||||
const all = getBoolean(query, "all");
|
||||
|
||||
try {
|
||||
const result = await helmService.getReleaseValues(release, { cluster, namespace, all });
|
||||
|
||||
respondText(response, result);
|
||||
} catch (error) {
|
||||
logger.debug(error);
|
||||
respondText(response, error?.toString() || "Error getting release values", 422);
|
||||
}
|
||||
}
|
||||
|
||||
static async getReleaseHistory(request: LensApiRequest) {
|
||||
const { cluster, params, response } = request;
|
||||
|
||||
try {
|
||||
const result = await helmService.getReleaseHistory(cluster, params.release, params.namespace);
|
||||
|
||||
respondJson(response, result);
|
||||
} catch (error) {
|
||||
logger.debug(error);
|
||||
respondText(response, error?.toString() || "Error getting release history", 422);
|
||||
}
|
||||
}
|
||||
|
||||
static async deleteRelease(request: LensApiRequest) {
|
||||
const { cluster, params, response } = request;
|
||||
|
||||
try {
|
||||
const result = await helmService.deleteRelease(cluster, params.release, params.namespace);
|
||||
|
||||
respondJson(response, result);
|
||||
} catch (error) {
|
||||
logger.debug(error);
|
||||
respondText(response, error?.toString() || "Error deleting release", 422);
|
||||
}
|
||||
}
|
||||
}
|
||||
36
src/main/routes/helm/charts/get-chart-route.injectable.ts
Normal file
36
src/main/routes/helm/charts/get-chart-route.injectable.ts
Normal file
@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { routeInjectionToken } from "../../../router/router.injectable";
|
||||
import type { Route } from "../../../router/router";
|
||||
import { helmService } from "../../../helm/helm-service";
|
||||
import { apiPrefix } from "../../../../common/vars";
|
||||
import type { RawHelmChart } from "../../../../common/k8s-api/endpoints/helm-charts.api";
|
||||
|
||||
interface GetChartResponse {
|
||||
readme: string;
|
||||
versions: RawHelmChart[];
|
||||
}
|
||||
|
||||
const getChartRouteInjectable = getInjectable({
|
||||
id: "get-chart-route",
|
||||
|
||||
instantiate: (): Route<GetChartResponse> => ({
|
||||
method: "get",
|
||||
path: `${apiPrefix}/v2/charts/{repo}/{chart}`,
|
||||
|
||||
handler: async ({ params, query }) => ({
|
||||
response: await helmService.getChart(
|
||||
params.repo,
|
||||
params.chart,
|
||||
query.get("version"),
|
||||
),
|
||||
}),
|
||||
}),
|
||||
|
||||
injectionToken: routeInjectionToken,
|
||||
});
|
||||
|
||||
export default getChartRouteInjectable;
|
||||
@ -0,0 +1,33 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { routeInjectionToken } from "../../../router/router.injectable";
|
||||
import type { Route } from "../../../router/router";
|
||||
import { helmService } from "../../../helm/helm-service";
|
||||
import { apiPrefix } from "../../../../common/vars";
|
||||
|
||||
const getChartRouteValuesInjectable = getInjectable({
|
||||
id: "get-chart-route-values",
|
||||
|
||||
instantiate: (): Route<string> => ({
|
||||
method: "get",
|
||||
path: `${apiPrefix}/v2/charts/{repo}/{chart}/values`,
|
||||
|
||||
handler: async ({
|
||||
params,
|
||||
query,
|
||||
}) => ({
|
||||
response: await helmService.getChartValues(
|
||||
params.repo,
|
||||
params.chart,
|
||||
query.get("version"),
|
||||
),
|
||||
}),
|
||||
}),
|
||||
|
||||
injectionToken: routeInjectionToken,
|
||||
});
|
||||
|
||||
export default getChartRouteValuesInjectable;
|
||||
26
src/main/routes/helm/charts/list-charts-route.injectable.ts
Normal file
26
src/main/routes/helm/charts/list-charts-route.injectable.ts
Normal file
@ -0,0 +1,26 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { routeInjectionToken } from "../../../router/router.injectable";
|
||||
import type { Route } from "../../../router/router";
|
||||
import { helmService } from "../../../helm/helm-service";
|
||||
import { apiPrefix } from "../../../../common/vars";
|
||||
|
||||
const listChartsRouteInjectable = getInjectable({
|
||||
id: "list-charts-route",
|
||||
|
||||
instantiate: (): Route<any> => ({
|
||||
method: "get",
|
||||
path: `${apiPrefix}/v2/charts`,
|
||||
|
||||
handler: async () => ({
|
||||
response: await helmService.listCharts(),
|
||||
}),
|
||||
}),
|
||||
|
||||
injectionToken: routeInjectionToken,
|
||||
});
|
||||
|
||||
export default listChartsRouteInjectable;
|
||||
@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { apiPrefix } from "../../../../common/vars";
|
||||
import type { Route } from "../../../router/router";
|
||||
import { helmService } from "../../../helm/helm-service";
|
||||
import { routeInjectionToken } from "../../../router/router.injectable";
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
|
||||
const deleteReleaseRouteInjectable = getInjectable({
|
||||
id: "delete-release-route",
|
||||
|
||||
instantiate: (): Route<string> => ({
|
||||
method: "delete",
|
||||
path: `${apiPrefix}/v2/releases/{namespace}/{release}`,
|
||||
|
||||
handler: async (request) => {
|
||||
const { cluster, params } = request;
|
||||
|
||||
return {
|
||||
response: await helmService.deleteRelease(
|
||||
cluster,
|
||||
params.release,
|
||||
params.namespace,
|
||||
),
|
||||
};
|
||||
},
|
||||
}),
|
||||
|
||||
injectionToken: routeInjectionToken,
|
||||
});
|
||||
|
||||
export default deleteReleaseRouteInjectable;
|
||||
@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { apiPrefix } from "../../../../common/vars";
|
||||
import type { Route } from "../../../router/router";
|
||||
import { helmService } from "../../../helm/helm-service";
|
||||
import { routeInjectionToken } from "../../../router/router.injectable";
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
|
||||
const getReleaseRouteHistoryInjectable = getInjectable({
|
||||
id: "get-release-history-route",
|
||||
|
||||
instantiate: (): Route<any> => ({
|
||||
method: "get",
|
||||
path: `${apiPrefix}/v2/releases/{namespace}/{release}/history`,
|
||||
|
||||
handler: async (request) => {
|
||||
const { cluster, params } = request;
|
||||
|
||||
return {
|
||||
response: await helmService.getReleaseHistory(
|
||||
cluster,
|
||||
params.release,
|
||||
params.namespace,
|
||||
),
|
||||
};
|
||||
},
|
||||
}),
|
||||
|
||||
injectionToken: routeInjectionToken,
|
||||
});
|
||||
|
||||
export default getReleaseRouteHistoryInjectable;
|
||||
@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { apiPrefix } from "../../../../common/vars";
|
||||
import type { Route } from "../../../router/router";
|
||||
import { helmService } from "../../../helm/helm-service";
|
||||
import { routeInjectionToken } from "../../../router/router.injectable";
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
|
||||
const getReleaseRouteInjectable = getInjectable({
|
||||
id: "get-release-route",
|
||||
|
||||
instantiate: (): Route<any> => ({
|
||||
method: "get",
|
||||
path: `${apiPrefix}/v2/releases/{namespace}/{release}`,
|
||||
|
||||
handler: async (request) => {
|
||||
const { cluster, params } = request;
|
||||
|
||||
return {
|
||||
response: await helmService.getRelease(
|
||||
cluster,
|
||||
params.release,
|
||||
params.namespace,
|
||||
),
|
||||
};
|
||||
},
|
||||
}),
|
||||
|
||||
injectionToken: routeInjectionToken,
|
||||
});
|
||||
|
||||
export default getReleaseRouteInjectable;
|
||||
@ -0,0 +1,39 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { apiPrefix } from "../../../../common/vars";
|
||||
import type { Route } from "../../../router/router";
|
||||
import { helmService } from "../../../helm/helm-service";
|
||||
import { routeInjectionToken } from "../../../router/router.injectable";
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { getBoolean } from "../../../utils/parse-query";
|
||||
import { contentTypes } from "../../../router/router-content-types";
|
||||
|
||||
const getReleaseRouteValuesInjectable = getInjectable({
|
||||
id: "get-release-values-route",
|
||||
|
||||
instantiate: (): Route<string> => ({
|
||||
method: "get",
|
||||
path: `${apiPrefix}/v2/releases/{namespace}/{release}/values`,
|
||||
|
||||
handler: async (request) => {
|
||||
const { cluster, params: { namespace, release }, query } = request;
|
||||
const all = getBoolean(query, "all");
|
||||
|
||||
return {
|
||||
response: await helmService.getReleaseValues(release, {
|
||||
cluster,
|
||||
namespace,
|
||||
all,
|
||||
}),
|
||||
|
||||
contentType: contentTypes.txt,
|
||||
};
|
||||
},
|
||||
}),
|
||||
|
||||
injectionToken: routeInjectionToken,
|
||||
});
|
||||
|
||||
export default getReleaseRouteValuesInjectable;
|
||||
@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { apiPrefix } from "../../../../common/vars";
|
||||
import type { Route } from "../../../router/router";
|
||||
import { helmService } from "../../../helm/helm-service";
|
||||
import { routeInjectionToken } from "../../../router/router.injectable";
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
|
||||
interface InstallChartResponse {
|
||||
log: string;
|
||||
release: { name: string; namespace: string };
|
||||
}
|
||||
|
||||
const installChartRouteInjectable = getInjectable({
|
||||
id: "install-chart-route",
|
||||
|
||||
instantiate: () : Route<InstallChartResponse> => ({
|
||||
method: "post",
|
||||
path: `${apiPrefix}/v2/releases`,
|
||||
|
||||
handler: async (request) => {
|
||||
const { payload, cluster } = request;
|
||||
|
||||
return {
|
||||
response: await helmService.installChart(cluster, payload),
|
||||
statusCode: 201,
|
||||
};
|
||||
},
|
||||
}),
|
||||
|
||||
injectionToken: routeInjectionToken,
|
||||
});
|
||||
|
||||
export default installChartRouteInjectable;
|
||||
@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { apiPrefix } from "../../../../common/vars";
|
||||
import type { Route } from "../../../router/router";
|
||||
import { helmService } from "../../../helm/helm-service";
|
||||
import { routeInjectionToken } from "../../../router/router.injectable";
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
|
||||
const listReleasesRouteInjectable = getInjectable({
|
||||
id: "list-releases-route",
|
||||
|
||||
instantiate: (): Route<Record<string, any>> => ({
|
||||
method: "get",
|
||||
path: `${apiPrefix}/v2/releases/{namespace?}`,
|
||||
|
||||
handler: async (request) => {
|
||||
const { cluster, params } = request;
|
||||
|
||||
return {
|
||||
response: await helmService.listReleases(cluster, params.namespace),
|
||||
};
|
||||
},
|
||||
}),
|
||||
|
||||
injectionToken: routeInjectionToken,
|
||||
});
|
||||
|
||||
export default listReleasesRouteInjectable;
|
||||
@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { apiPrefix } from "../../../../common/vars";
|
||||
import type { Route } from "../../../router/router";
|
||||
import { helmService } from "../../../helm/helm-service";
|
||||
import { routeInjectionToken } from "../../../router/router.injectable";
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
|
||||
const rollbackReleaseRouteInjectable = getInjectable({
|
||||
id: "rollback-release-route",
|
||||
|
||||
instantiate: (): Route<void> => ({
|
||||
method: "put",
|
||||
path: `${apiPrefix}/v2/releases/{namespace}/{release}/rollback`,
|
||||
|
||||
handler: async (request) => {
|
||||
const { cluster, params, payload } = request;
|
||||
|
||||
await helmService.rollback(cluster, params.release, params.namespace, payload.revision);
|
||||
},
|
||||
}),
|
||||
|
||||
injectionToken: routeInjectionToken,
|
||||
});
|
||||
|
||||
export default rollbackReleaseRouteInjectable;
|
||||
@ -0,0 +1,40 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { apiPrefix } from "../../../../common/vars";
|
||||
import type { Route } from "../../../router/router";
|
||||
import { helmService } from "../../../helm/helm-service";
|
||||
import { routeInjectionToken } from "../../../router/router.injectable";
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
|
||||
interface UpdateReleaseResponse {
|
||||
log: string;
|
||||
release: { name: string; namespace: string };
|
||||
}
|
||||
|
||||
const updateReleaseRouteInjectable = getInjectable({
|
||||
id: "update-release-route",
|
||||
|
||||
instantiate: (): Route<UpdateReleaseResponse> => ({
|
||||
method: "put",
|
||||
path: `${apiPrefix}/v2/releases/{namespace}/{release}`,
|
||||
|
||||
handler: async ({
|
||||
cluster,
|
||||
params,
|
||||
payload,
|
||||
}) => ({
|
||||
response: await helmService.updateRelease(
|
||||
cluster,
|
||||
params.release,
|
||||
params.namespace,
|
||||
payload,
|
||||
),
|
||||
}),
|
||||
}),
|
||||
|
||||
injectionToken: routeInjectionToken,
|
||||
});
|
||||
|
||||
export default updateReleaseRouteInjectable;
|
||||
@ -1,11 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
export * from "./kubeconfig-route";
|
||||
export * from "./metrics-route";
|
||||
export * from "./port-forward-route";
|
||||
export * from "./helm-route";
|
||||
export * from "./resource-applier-route";
|
||||
export * from "./version-route";
|
||||
@ -1,62 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { LensApiRequest } from "../router";
|
||||
import { respondJson } from "../utils/http-responses";
|
||||
import type { Cluster } from "../../common/cluster/cluster";
|
||||
import { CoreV1Api, V1Secret } from "@kubernetes/client-node";
|
||||
|
||||
function generateKubeConfig(username: string, secret: V1Secret, cluster: Cluster) {
|
||||
const tokenData = Buffer.from(secret.data["token"], "base64");
|
||||
|
||||
return {
|
||||
"apiVersion": "v1",
|
||||
"kind": "Config",
|
||||
"clusters": [
|
||||
{
|
||||
"name": cluster.contextName,
|
||||
"cluster": {
|
||||
"server": cluster.apiUrl,
|
||||
"certificate-authority-data": secret.data["ca.crt"],
|
||||
},
|
||||
},
|
||||
],
|
||||
"users": [
|
||||
{
|
||||
"name": username,
|
||||
"user": {
|
||||
"token": tokenData.toString("utf8"),
|
||||
},
|
||||
},
|
||||
],
|
||||
"contexts": [
|
||||
{
|
||||
"name": cluster.contextName,
|
||||
"context": {
|
||||
"user": username,
|
||||
"cluster": cluster.contextName,
|
||||
"namespace": secret.metadata.namespace,
|
||||
},
|
||||
},
|
||||
],
|
||||
"current-context": cluster.contextName,
|
||||
};
|
||||
}
|
||||
|
||||
export class KubeconfigRoute {
|
||||
static async routeServiceAccountRoute(request: LensApiRequest) {
|
||||
const { params, response, cluster } = request;
|
||||
const client = (await cluster.getProxyKubeconfig()).makeApiClient(CoreV1Api);
|
||||
const secretList = await client.listNamespacedSecret(params.namespace);
|
||||
const secret = secretList.body.items.find(secret => {
|
||||
const { annotations } = secret.metadata;
|
||||
|
||||
return annotations && annotations["kubernetes.io/service-account.name"] == params.account;
|
||||
});
|
||||
const data = generateKubeConfig(params.account, secret, cluster);
|
||||
|
||||
respondJson(response, data);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,75 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { apiPrefix } from "../../../common/vars";
|
||||
import type { Route } from "../../router/router";
|
||||
import { routeInjectionToken } from "../../router/router.injectable";
|
||||
import type { Cluster } from "../../../common/cluster/cluster";
|
||||
import { CoreV1Api, V1Secret } from "@kubernetes/client-node";
|
||||
|
||||
const getServiceAccountRouteInjectable = getInjectable({
|
||||
id: "get-service-account-route",
|
||||
|
||||
instantiate: (): Route<ReturnType<typeof generateKubeConfig>> => ({
|
||||
method: "get",
|
||||
path: `${apiPrefix}/kubeconfig/service-account/{namespace}/{account}`,
|
||||
|
||||
handler: async (request) => {
|
||||
const { params, cluster } = request;
|
||||
const client = (await cluster.getProxyKubeconfig()).makeApiClient(CoreV1Api);
|
||||
const secretList = await client.listNamespacedSecret(params.namespace);
|
||||
|
||||
const secret = secretList.body.items.find(secret => {
|
||||
const { annotations } = secret.metadata;
|
||||
|
||||
return annotations && annotations["kubernetes.io/service-account.name"] == params.account;
|
||||
});
|
||||
|
||||
return { response: generateKubeConfig(params.account, secret, cluster) };
|
||||
},
|
||||
}),
|
||||
|
||||
injectionToken: routeInjectionToken,
|
||||
});
|
||||
|
||||
export default getServiceAccountRouteInjectable;
|
||||
|
||||
function generateKubeConfig(username: string, secret: V1Secret, cluster: Cluster) {
|
||||
const tokenData = Buffer.from(secret.data["token"], "base64");
|
||||
|
||||
return {
|
||||
"apiVersion": "v1",
|
||||
"kind": "Config",
|
||||
"clusters": [
|
||||
{
|
||||
"name": cluster.contextName,
|
||||
"cluster": {
|
||||
"server": cluster.apiUrl,
|
||||
"certificate-authority-data": secret.data["ca.crt"],
|
||||
},
|
||||
},
|
||||
],
|
||||
"users": [
|
||||
{
|
||||
"name": username,
|
||||
"user": {
|
||||
"token": tokenData.toString("utf8"),
|
||||
},
|
||||
},
|
||||
],
|
||||
"contexts": [
|
||||
{
|
||||
"name": cluster.contextName,
|
||||
"context": {
|
||||
"user": username,
|
||||
"cluster": cluster.contextName,
|
||||
"namespace": secret.metadata.namespace,
|
||||
},
|
||||
},
|
||||
],
|
||||
"current-context": cluster.contextName,
|
||||
};
|
||||
}
|
||||
@ -1,109 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { LensApiRequest } from "../router";
|
||||
import { respondJson } from "../utils/http-responses";
|
||||
import type { Cluster } from "../../common/cluster/cluster";
|
||||
import { ClusterMetadataKey, ClusterPrometheusMetadata } from "../../common/cluster-types";
|
||||
import logger from "../logger";
|
||||
import { getMetrics } from "../k8s-request";
|
||||
import { PrometheusProviderRegistry } from "../prometheus";
|
||||
|
||||
export type IMetricsQuery = string | string[] | {
|
||||
[metricName: string]: string;
|
||||
};
|
||||
|
||||
// This is used for backoff retry tracking.
|
||||
const ATTEMPTS = [false, false, false, false, true];
|
||||
|
||||
// prometheus metrics loader
|
||||
async function loadMetrics(promQueries: string[], cluster: Cluster, prometheusPath: string, queryParams: Record<string, string>): Promise<any[]> {
|
||||
const queries = promQueries.map(p => p.trim());
|
||||
const loaders = new Map<string, Promise<any>>();
|
||||
|
||||
async function loadMetric(query: string): Promise<any> {
|
||||
async function loadMetricHelper(): Promise<any> {
|
||||
for (const [attempt, lastAttempt] of ATTEMPTS.entries()) { // retry
|
||||
try {
|
||||
return await getMetrics(cluster, prometheusPath, { query, ...queryParams });
|
||||
} catch (error) {
|
||||
if (lastAttempt || (error?.statusCode >= 400 && error?.statusCode < 500)) {
|
||||
logger.error("[Metrics]: metrics not available", error?.response ? error.response?.body : error);
|
||||
throw new Error("Metrics not available");
|
||||
}
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, (attempt + 1) * 1000)); // add delay before repeating request
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return loaders.get(query) ?? loaders.set(query, loadMetricHelper()).get(query);
|
||||
}
|
||||
|
||||
return Promise.all(queries.map(loadMetric));
|
||||
}
|
||||
|
||||
interface MetricProviderInfo {
|
||||
name: string;
|
||||
id: string;
|
||||
isConfigurable: boolean;
|
||||
}
|
||||
|
||||
export class MetricsRoute {
|
||||
static async routeMetrics({ response, cluster, payload, query }: LensApiRequest) {
|
||||
const queryParams: IMetricsQuery = Object.fromEntries(query.entries());
|
||||
const prometheusMetadata: ClusterPrometheusMetadata = {};
|
||||
|
||||
try {
|
||||
const { prometheusPath, provider } = await cluster.contextHandler.getPrometheusDetails();
|
||||
|
||||
prometheusMetadata.provider = provider?.id;
|
||||
prometheusMetadata.autoDetected = !cluster.preferences.prometheusProvider?.type;
|
||||
|
||||
if (!prometheusPath) {
|
||||
prometheusMetadata.success = false;
|
||||
|
||||
return respondJson(response, {});
|
||||
}
|
||||
|
||||
// return data in same structure as query
|
||||
if (typeof payload === "string") {
|
||||
const [data] = await loadMetrics([payload], cluster, prometheusPath, queryParams);
|
||||
|
||||
respondJson(response, data);
|
||||
} else if (Array.isArray(payload)) {
|
||||
const data = await loadMetrics(payload, cluster, prometheusPath, queryParams);
|
||||
|
||||
respondJson(response, data);
|
||||
} else {
|
||||
const queries = Object.entries<Record<string, string>>(payload)
|
||||
.map(([queryName, queryOpts]) => (
|
||||
provider.getQuery(queryOpts, queryName)
|
||||
));
|
||||
const result = await loadMetrics(queries, cluster, prometheusPath, queryParams);
|
||||
const data = Object.fromEntries(Object.keys(payload).map((metricName, i) => [metricName, result[i]]));
|
||||
|
||||
respondJson(response, data);
|
||||
}
|
||||
prometheusMetadata.success = true;
|
||||
} catch (error) {
|
||||
prometheusMetadata.success = false;
|
||||
respondJson(response, {});
|
||||
logger.warn(`[METRICS-ROUTE]: failed to get metrics for clusterId=${cluster.id}:`, error);
|
||||
} finally {
|
||||
cluster.metadata[ClusterMetadataKey.PROMETHEUS] = prometheusMetadata;
|
||||
}
|
||||
}
|
||||
|
||||
static async routeMetricsProviders({ response }: LensApiRequest) {
|
||||
const providers: MetricProviderInfo[] = [];
|
||||
|
||||
for (const { name, id, isConfigurable } of PrometheusProviderRegistry.getInstance().providers.values()) {
|
||||
providers.push({ name, id, isConfigurable });
|
||||
}
|
||||
|
||||
respondJson(response, providers);
|
||||
}
|
||||
}
|
||||
108
src/main/routes/metrics/add-metrics-route.injectable.ts
Normal file
108
src/main/routes/metrics/add-metrics-route.injectable.ts
Normal file
@ -0,0 +1,108 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { apiPrefix } from "../../../common/vars";
|
||||
import type { LensApiRequest, Route } from "../../router/router";
|
||||
import { routeInjectionToken } from "../../router/router.injectable";
|
||||
import { ClusterMetadataKey, ClusterPrometheusMetadata } from "../../../common/cluster-types";
|
||||
import logger from "../../logger";
|
||||
import type { Cluster } from "../../../common/cluster/cluster";
|
||||
import { getMetrics } from "../../k8s-request";
|
||||
import type { IMetricsQuery } from "./metrics-query";
|
||||
|
||||
// This is used for backoff retry tracking.
|
||||
const ATTEMPTS = [false, false, false, false, true];
|
||||
|
||||
async function loadMetrics(promQueries: string[], cluster: Cluster, prometheusPath: string, queryParams: Record<string, string>): Promise<any[]> {
|
||||
const queries = promQueries.map(p => p.trim());
|
||||
const loaders = new Map<string, Promise<any>>();
|
||||
|
||||
async function loadMetric(query: string): Promise<any> {
|
||||
async function loadMetricHelper(): Promise<any> {
|
||||
for (const [attempt, lastAttempt] of ATTEMPTS.entries()) { // retry
|
||||
try {
|
||||
return await getMetrics(cluster, prometheusPath, { query, ...queryParams });
|
||||
} catch (error) {
|
||||
if (lastAttempt || (error?.statusCode >= 400 && error?.statusCode < 500)) {
|
||||
logger.error("[Metrics]: metrics not available", error?.response ? error.response?.body : error);
|
||||
throw new Error("Metrics not available");
|
||||
}
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, (attempt + 1) * 1000)); // add delay before repeating request
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return loaders.get(query) ?? loaders.set(query, loadMetricHelper()).get(query);
|
||||
}
|
||||
|
||||
return Promise.all(queries.map(loadMetric));
|
||||
}
|
||||
|
||||
const addMetricsRoute = async ({ cluster, payload, query }: LensApiRequest) => {
|
||||
const queryParams: IMetricsQuery = Object.fromEntries(query.entries());
|
||||
const prometheusMetadata: ClusterPrometheusMetadata = {};
|
||||
|
||||
try {
|
||||
const { prometheusPath, provider } = await cluster.contextHandler.getPrometheusDetails();
|
||||
|
||||
prometheusMetadata.provider = provider?.id;
|
||||
prometheusMetadata.autoDetected = !cluster.preferences.prometheusProvider?.type;
|
||||
|
||||
if (!prometheusPath) {
|
||||
prometheusMetadata.success = false;
|
||||
|
||||
return { response: {}};
|
||||
}
|
||||
|
||||
// return data in same structure as query
|
||||
if (typeof payload === "string") {
|
||||
const [data] = await loadMetrics([payload], cluster, prometheusPath, queryParams);
|
||||
|
||||
return { response: data };
|
||||
}
|
||||
|
||||
if (Array.isArray(payload)) {
|
||||
const data = await loadMetrics(payload, cluster, prometheusPath, queryParams);
|
||||
|
||||
return { response: data };
|
||||
}
|
||||
|
||||
const queries = Object.entries<Record<string, string>>(payload)
|
||||
.map(([queryName, queryOpts]) => (
|
||||
provider.getQuery(queryOpts, queryName)
|
||||
));
|
||||
|
||||
const result = await loadMetrics(queries, cluster, prometheusPath, queryParams);
|
||||
const data = Object.fromEntries(Object.keys(payload).map((metricName, i) => [metricName, result[i]]));
|
||||
|
||||
prometheusMetadata.success = true;
|
||||
|
||||
return { response: data };
|
||||
} catch (error) {
|
||||
prometheusMetadata.success = false;
|
||||
|
||||
logger.warn(`[METRICS-ROUTE]: failed to get metrics for clusterId=${cluster.id}:`, error);
|
||||
|
||||
return { response: {}};
|
||||
} finally {
|
||||
cluster.metadata[ClusterMetadataKey.PROMETHEUS] = prometheusMetadata;
|
||||
}
|
||||
};
|
||||
|
||||
const addMetricsRouteInjectable = getInjectable({
|
||||
id: "add-metrics-route",
|
||||
|
||||
instantiate: (): Route<any> => ({
|
||||
method: "post",
|
||||
path: `${apiPrefix}/metrics`,
|
||||
handler: addMetricsRoute,
|
||||
}),
|
||||
|
||||
injectionToken: routeInjectionToken,
|
||||
});
|
||||
|
||||
export default addMetricsRouteInjectable;
|
||||
@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { apiPrefix } from "../../../common/vars";
|
||||
import type { Route } from "../../router/router";
|
||||
import { routeInjectionToken } from "../../router/router.injectable";
|
||||
import { PrometheusProviderRegistry } from "../../prometheus";
|
||||
import type { MetricProviderInfo } from "../../../common/k8s-api/endpoints/metrics.api";
|
||||
|
||||
const getMetricProvidersRouteInjectable = getInjectable({
|
||||
id: "get-metric-providers-route",
|
||||
|
||||
instantiate: (): Route<MetricProviderInfo[]> => ({
|
||||
method: "get",
|
||||
path: `${apiPrefix}/metrics/providers`,
|
||||
|
||||
handler: () => {
|
||||
const providers: MetricProviderInfo[] = [];
|
||||
|
||||
for (const { name, id, isConfigurable } of PrometheusProviderRegistry.getInstance().providers.values()) {
|
||||
providers.push({ name, id, isConfigurable });
|
||||
}
|
||||
|
||||
return { response: providers };
|
||||
},
|
||||
}),
|
||||
|
||||
injectionToken: routeInjectionToken,
|
||||
});
|
||||
|
||||
export default getMetricProvidersRouteInjectable;
|
||||
7
src/main/routes/metrics/metrics-query.ts
Normal file
7
src/main/routes/metrics/metrics-query.ts
Normal file
@ -0,0 +1,7 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
export type IMetricsQuery = string | string[] | {
|
||||
[metricName: string]: string;
|
||||
};
|
||||
@ -1,48 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { LensApiRequest } from "../router";
|
||||
import logger from "../logger";
|
||||
import { respondJson } from "../utils/http-responses";
|
||||
import { PortForward } from "./port-forward/port-forward";
|
||||
|
||||
export class PortForwardRoute {
|
||||
static async routeCurrentPortForward(request: LensApiRequest) {
|
||||
const { params, query, response, cluster } = request;
|
||||
const { namespace, resourceType, resourceName } = params;
|
||||
const port = Number(query.get("port"));
|
||||
const forwardPort = Number(query.get("forwardPort"));
|
||||
|
||||
const portForward = PortForward.getPortforward({
|
||||
clusterId: cluster.id, kind: resourceType, name: resourceName,
|
||||
namespace, port, forwardPort,
|
||||
});
|
||||
|
||||
respondJson(response, { port: portForward?.forwardPort ?? null });
|
||||
}
|
||||
|
||||
static async routeCurrentPortForwardStop(request: LensApiRequest) {
|
||||
const { params, query, response, cluster } = request;
|
||||
const { namespace, resourceType, resourceName } = params;
|
||||
const port = Number(query.get("port"));
|
||||
const forwardPort = Number(query.get("forwardPort"));
|
||||
|
||||
const portForward = PortForward.getPortforward({
|
||||
clusterId: cluster.id, kind: resourceType, name: resourceName,
|
||||
namespace, port, forwardPort,
|
||||
});
|
||||
|
||||
try {
|
||||
await portForward.stop();
|
||||
respondJson(response, { status: true });
|
||||
} catch (error) {
|
||||
logger.error("[PORT-FORWARD-ROUTE]: error stopping a port-forward", { namespace, port, forwardPort, resourceType, resourceName });
|
||||
|
||||
return respondJson(response, {
|
||||
message: `error stopping a forward port ${port}`,
|
||||
}, 400);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -4,7 +4,7 @@
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { PortForward, PortForwardArgs } from "./port-forward";
|
||||
import bundledKubectlInjectable from "../../kubectl/bundled-kubectl.injectable";
|
||||
import bundledKubectlInjectable from "../../../kubectl/bundled-kubectl.injectable";
|
||||
|
||||
const createPortForwardInjectable = getInjectable({
|
||||
id: "create-port-forward",
|
||||
@ -2,8 +2,8 @@
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import logger from "../../logger";
|
||||
import { getPortFrom } from "../../utils/get-port";
|
||||
import logger from "../../../logger";
|
||||
import { getPortFrom } from "../../../utils/get-port";
|
||||
import { spawn, ChildProcessWithoutNullStreams } from "child_process";
|
||||
import * as tcpPortUsed from "tcp-port-used";
|
||||
|
||||
@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { routeInjectionToken } from "../../router/router.injectable";
|
||||
import type { LensApiRequest, Route } from "../../router/router";
|
||||
import { apiPrefix } from "../../../common/vars";
|
||||
import { PortForward } from "./functionality/port-forward";
|
||||
|
||||
const getCurrentPortForward = async (request: LensApiRequest) => {
|
||||
const { params, query, cluster } = request;
|
||||
const { namespace, resourceType, resourceName } = params;
|
||||
const port = Number(query.get("port"));
|
||||
const forwardPort = Number(query.get("forwardPort"));
|
||||
|
||||
const portForward = PortForward.getPortforward({
|
||||
clusterId: cluster.id, kind: resourceType, name: resourceName,
|
||||
namespace, port, forwardPort,
|
||||
});
|
||||
|
||||
return { response: { port: portForward?.forwardPort ?? null }};
|
||||
};
|
||||
|
||||
const getCurrentPortForwardRouteInjectable = getInjectable({
|
||||
id: "get-current-port-forward-route",
|
||||
|
||||
instantiate: (): Route<{ port: number }> => ({
|
||||
method: "get",
|
||||
path: `${apiPrefix}/pods/port-forward/{namespace}/{resourceType}/{resourceName}`,
|
||||
handler: getCurrentPortForward,
|
||||
}),
|
||||
|
||||
injectionToken: routeInjectionToken,
|
||||
});
|
||||
|
||||
export default getCurrentPortForwardRouteInjectable;
|
||||
@ -1,17 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { routePortForward } from "./route-port-forward";
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import createPortForwardInjectable from "../create-port-forward.injectable";
|
||||
|
||||
const routePortForwardInjectable = getInjectable({
|
||||
id: "route-port-forward",
|
||||
|
||||
instantiate: (di) => routePortForward({
|
||||
createPortForward: di.inject(createPortForwardInjectable),
|
||||
}),
|
||||
});
|
||||
|
||||
export default routePortForwardInjectable;
|
||||
@ -1,86 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import type { LensApiRequest } from "../../../router";
|
||||
import logger from "../../../logger";
|
||||
import { respondJson } from "../../../utils/http-responses";
|
||||
import { PortForward, PortForwardArgs } from "../port-forward";
|
||||
|
||||
interface Dependencies {
|
||||
createPortForward: (pathToKubeConfig: string, args: PortForwardArgs) => PortForward;
|
||||
}
|
||||
|
||||
export const routePortForward =
|
||||
({ createPortForward }: Dependencies) =>
|
||||
async (request: LensApiRequest) => {
|
||||
const { params, query, response, cluster } = request;
|
||||
const { namespace, resourceType, resourceName } = params;
|
||||
const port = Number(query.get("port"));
|
||||
const forwardPort = Number(query.get("forwardPort"));
|
||||
|
||||
try {
|
||||
let portForward = PortForward.getPortforward({
|
||||
clusterId: cluster.id,
|
||||
kind: resourceType,
|
||||
name: resourceName,
|
||||
namespace,
|
||||
port,
|
||||
forwardPort,
|
||||
});
|
||||
|
||||
if (!portForward) {
|
||||
logger.info(
|
||||
`Creating a new port-forward ${namespace}/${resourceType}/${resourceName}:${port}`,
|
||||
);
|
||||
|
||||
const thePort =
|
||||
0 < forwardPort && forwardPort < 65536 ? forwardPort : 0;
|
||||
|
||||
portForward = createPortForward(await cluster.getProxyKubeconfigPath(), {
|
||||
clusterId: cluster.id,
|
||||
kind: resourceType,
|
||||
namespace,
|
||||
name: resourceName,
|
||||
port,
|
||||
forwardPort: thePort,
|
||||
});
|
||||
|
||||
const started = await portForward.start();
|
||||
|
||||
if (!started) {
|
||||
logger.error("[PORT-FORWARD-ROUTE]: failed to start a port-forward", {
|
||||
namespace,
|
||||
port,
|
||||
resourceType,
|
||||
resourceName,
|
||||
});
|
||||
|
||||
return respondJson(
|
||||
response,
|
||||
{
|
||||
message: `Failed to forward port ${port} to ${
|
||||
thePort ? forwardPort : "random port"
|
||||
}`,
|
||||
},
|
||||
400,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
respondJson(response, { port: portForward.forwardPort });
|
||||
} catch (error) {
|
||||
logger.error(
|
||||
`[PORT-FORWARD-ROUTE]: failed to open a port-forward: ${error}`,
|
||||
{ namespace, port, resourceType, resourceName },
|
||||
);
|
||||
|
||||
return respondJson(
|
||||
response,
|
||||
{
|
||||
message: `Failed to forward port ${port}`,
|
||||
},
|
||||
400,
|
||||
);
|
||||
}
|
||||
};
|
||||
@ -0,0 +1,97 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { routeInjectionToken } from "../../router/router.injectable";
|
||||
import type { LensApiRequest, Route } from "../../router/router";
|
||||
import { apiPrefix } from "../../../common/vars";
|
||||
import { PortForward, PortForwardArgs } from "./functionality/port-forward";
|
||||
import logger from "../../logger";
|
||||
import createPortForwardInjectable from "./functionality/create-port-forward.injectable";
|
||||
|
||||
interface Dependencies {
|
||||
createPortForward: (pathToKubeConfig: string, args: PortForwardArgs) => PortForward;
|
||||
}
|
||||
|
||||
const startPortForward = ({ createPortForward }: Dependencies) => async (request: LensApiRequest) => {
|
||||
const { params, query, cluster } = request;
|
||||
const { namespace, resourceType, resourceName } = params;
|
||||
const port = Number(query.get("port"));
|
||||
const forwardPort = Number(query.get("forwardPort"));
|
||||
|
||||
try {
|
||||
let portForward = PortForward.getPortforward({
|
||||
clusterId: cluster.id,
|
||||
kind: resourceType,
|
||||
name: resourceName,
|
||||
namespace,
|
||||
port,
|
||||
forwardPort,
|
||||
});
|
||||
|
||||
if (!portForward) {
|
||||
logger.info(
|
||||
`Creating a new port-forward ${namespace}/${resourceType}/${resourceName}:${port}`,
|
||||
);
|
||||
|
||||
const thePort =
|
||||
0 < forwardPort && forwardPort < 65536 ? forwardPort : 0;
|
||||
|
||||
portForward = createPortForward(await cluster.getProxyKubeconfigPath(), {
|
||||
clusterId: cluster.id,
|
||||
kind: resourceType,
|
||||
namespace,
|
||||
name: resourceName,
|
||||
port,
|
||||
forwardPort: thePort,
|
||||
});
|
||||
|
||||
const started = await portForward.start();
|
||||
|
||||
if (!started) {
|
||||
logger.error("[PORT-FORWARD-ROUTE]: failed to start a port-forward", {
|
||||
namespace,
|
||||
port,
|
||||
resourceType,
|
||||
resourceName,
|
||||
});
|
||||
|
||||
return {
|
||||
error: {
|
||||
message: `Failed to forward port ${port} to ${
|
||||
thePort ? forwardPort : "random port"
|
||||
}`,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return { response: { port: portForward.forwardPort }};
|
||||
} catch (error) {
|
||||
logger.error(
|
||||
`[PORT-FORWARD-ROUTE]: failed to open a port-forward: ${error}`,
|
||||
{ namespace, port, resourceType, resourceName },
|
||||
);
|
||||
|
||||
return {
|
||||
error: {
|
||||
message: `Failed to forward port ${port}`,
|
||||
},
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const startPortForwardRouteInjectable = getInjectable({
|
||||
id: "start-current-port-forward-route",
|
||||
|
||||
instantiate: (di): Route<{ port: number }> => ({
|
||||
method: "post",
|
||||
path: `${apiPrefix}/pods/port-forward/{namespace}/{resourceType}/{resourceName}`,
|
||||
handler: startPortForward({ createPortForward: di.inject(createPortForwardInjectable) }),
|
||||
}),
|
||||
|
||||
injectionToken: routeInjectionToken,
|
||||
});
|
||||
|
||||
export default startPortForwardRouteInjectable;
|
||||
@ -0,0 +1,51 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { routeInjectionToken } from "../../router/router.injectable";
|
||||
import type { LensApiRequest, Route } from "../../router/router";
|
||||
import { apiPrefix } from "../../../common/vars";
|
||||
import { PortForward } from "./functionality/port-forward";
|
||||
import logger from "../../logger";
|
||||
|
||||
const stopCurrentPortForward = async (request: LensApiRequest) => {
|
||||
const { params, query, cluster } = request;
|
||||
const { namespace, resourceType, resourceName } = params;
|
||||
const port = Number(query.get("port"));
|
||||
const forwardPort = Number(query.get("forwardPort"));
|
||||
|
||||
const portForward = PortForward.getPortforward({
|
||||
clusterId: cluster.id, kind: resourceType, name: resourceName,
|
||||
namespace, port, forwardPort,
|
||||
});
|
||||
|
||||
try {
|
||||
await portForward.stop();
|
||||
|
||||
return { response: { status: true }};
|
||||
} catch (error) {
|
||||
logger.error("[PORT-FORWARD-ROUTE]: error stopping a port-forward", { namespace, port, forwardPort, resourceType, resourceName });
|
||||
|
||||
return {
|
||||
error: {
|
||||
message: `error stopping a forward port ${port}`,
|
||||
},
|
||||
};
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
const stopCurrentPortForwardRouteInjectable = getInjectable({
|
||||
id: "stop-current-port-forward-route",
|
||||
|
||||
instantiate: (): Route<{ status: boolean }> => ({
|
||||
method: "delete",
|
||||
path: `${apiPrefix}/pods/port-forward/{namespace}/{resourceType}/{resourceName}`,
|
||||
handler: stopCurrentPortForward,
|
||||
}),
|
||||
|
||||
injectionToken: routeInjectionToken,
|
||||
});
|
||||
|
||||
export default stopCurrentPortForwardRouteInjectable;
|
||||
@ -1,34 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { LensApiRequest } from "../router";
|
||||
import { respondJson, respondText } from "../utils/http-responses";
|
||||
import { ResourceApplier } from "../resource-applier";
|
||||
|
||||
export class ResourceApplierApiRoute {
|
||||
static async applyResource(request: LensApiRequest) {
|
||||
const { response, cluster, payload } = request;
|
||||
|
||||
try {
|
||||
const resource = await new ResourceApplier(cluster).apply(payload);
|
||||
|
||||
respondJson(response, resource, 200);
|
||||
} catch (error) {
|
||||
respondText(response, error, 422);
|
||||
}
|
||||
}
|
||||
|
||||
static async patchResource(request: LensApiRequest) {
|
||||
const { response, cluster, payload } = request;
|
||||
|
||||
try {
|
||||
const resource = await new ResourceApplier(cluster).patch(payload.name, payload.kind, payload.patch, payload.ns);
|
||||
|
||||
respondJson(response, resource, 200);
|
||||
} catch (error) {
|
||||
respondText(response, error, 422);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,26 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { routeInjectionToken } from "../../router/router.injectable";
|
||||
import type { Route } from "../../router/router";
|
||||
import { apiPrefix } from "../../../common/vars";
|
||||
import { ResourceApplier } from "../../resource-applier";
|
||||
|
||||
const applyResourceRouteInjectable = getInjectable({
|
||||
id: "apply-resource-route",
|
||||
|
||||
instantiate: (): Route<string> => ({
|
||||
method: "post",
|
||||
path: `${apiPrefix}/stack`,
|
||||
|
||||
handler: async ({ cluster, payload }) => ({
|
||||
response: await new ResourceApplier(cluster).apply(payload),
|
||||
}),
|
||||
}),
|
||||
|
||||
injectionToken: routeInjectionToken,
|
||||
});
|
||||
|
||||
export default applyResourceRouteInjectable;
|
||||
@ -0,0 +1,31 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { routeInjectionToken } from "../../router/router.injectable";
|
||||
import type { Route } from "../../router/router";
|
||||
import { apiPrefix } from "../../../common/vars";
|
||||
import { ResourceApplier } from "../../resource-applier";
|
||||
|
||||
const patchResourceRouteInjectable = getInjectable({
|
||||
id: "patch-resource-route",
|
||||
|
||||
instantiate: (): Route<string> => ({
|
||||
method: "patch",
|
||||
path: `${apiPrefix}/stack`,
|
||||
|
||||
handler: async ({ cluster, payload }) => ({
|
||||
response: await new ResourceApplier(cluster).patch(
|
||||
payload.name,
|
||||
payload.kind,
|
||||
payload.patch,
|
||||
payload.ns,
|
||||
),
|
||||
}),
|
||||
}),
|
||||
|
||||
injectionToken: routeInjectionToken,
|
||||
});
|
||||
|
||||
export default patchResourceRouteInjectable;
|
||||
94
src/main/routes/static-file-route.injectable.ts
Normal file
94
src/main/routes/static-file-route.injectable.ts
Normal file
@ -0,0 +1,94 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import type { LensApiRequest, Route } from "../router/router";
|
||||
import { contentTypes, SupportedFileExtension } from "../router/router-content-types";
|
||||
import logger from "../logger";
|
||||
import { routeInjectionToken } from "../router/router.injectable";
|
||||
import { appName, publicPath } from "../../common/vars";
|
||||
import path from "path";
|
||||
import readFileInjectable from "../../common/fs/read-file.injectable";
|
||||
import isDevelopmentInjectable from "../../common/vars/is-development.injectable";
|
||||
import httpProxy from "http-proxy";
|
||||
|
||||
interface ProductionDependencies {
|
||||
readFile: (path: string) => Promise<Buffer>;
|
||||
}
|
||||
|
||||
const handleStaticFileInProduction =
|
||||
({ readFile }: ProductionDependencies) =>
|
||||
async ({ params }: LensApiRequest) => {
|
||||
const staticPath = path.resolve(__static);
|
||||
let filePath = params.path;
|
||||
|
||||
for (let retryCount = 0; retryCount < 5; retryCount += 1) {
|
||||
const asset = path.join(staticPath, filePath);
|
||||
const normalizedFilePath = path.resolve(asset);
|
||||
|
||||
if (!normalizedFilePath.startsWith(staticPath)) {
|
||||
return { statusCode: 404 };
|
||||
}
|
||||
|
||||
try {
|
||||
const fileExtension = path
|
||||
.extname(asset)
|
||||
.slice(1) as SupportedFileExtension;
|
||||
|
||||
const contentType = contentTypes[fileExtension] || contentTypes.txt;
|
||||
|
||||
return { response: await readFile(asset), contentType };
|
||||
} catch (err) {
|
||||
if (retryCount > 5) {
|
||||
logger.error("handleStaticFile:", err.toString());
|
||||
|
||||
return { statusCode: 404 };
|
||||
}
|
||||
|
||||
filePath = `${publicPath}/${appName}.html`;
|
||||
}
|
||||
}
|
||||
|
||||
return { statusCode: 404 };
|
||||
};
|
||||
|
||||
interface DevelopmentDependencies {
|
||||
proxy: httpProxy;
|
||||
}
|
||||
|
||||
const handleStaticFileInDevelopment =
|
||||
({ proxy }: DevelopmentDependencies) =>
|
||||
(apiReq: LensApiRequest) => {
|
||||
const { req, res } = apiReq.raw;
|
||||
|
||||
if (req.url === "/" || !req.url.startsWith("/build/")) {
|
||||
req.url = `${publicPath}/${appName}.html`;
|
||||
}
|
||||
|
||||
proxy.web(req, res, {
|
||||
target: "http://127.0.0.1:8080",
|
||||
});
|
||||
|
||||
return { proxy };
|
||||
};
|
||||
|
||||
const staticFileRouteInjectable = getInjectable({
|
||||
id: "static-file-route",
|
||||
|
||||
instantiate: (di): Route<Buffer> => {
|
||||
const isDevelopment = di.inject(isDevelopmentInjectable);
|
||||
|
||||
return {
|
||||
method: "get",
|
||||
path: `/{path*}`,
|
||||
handler: isDevelopment
|
||||
? handleStaticFileInDevelopment({ proxy: httpProxy.createProxy() })
|
||||
: handleStaticFileInProduction({ readFile: di.inject(readFileInjectable) }),
|
||||
};
|
||||
},
|
||||
|
||||
injectionToken: routeInjectionToken,
|
||||
});
|
||||
|
||||
export default staticFileRouteInjectable;
|
||||
@ -1,16 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { LensApiRequest } from "../router";
|
||||
import { respondJson } from "../utils/http-responses";
|
||||
import { getAppVersion } from "../../common/utils";
|
||||
|
||||
export class VersionRoute {
|
||||
static async getVersion(request: LensApiRequest) {
|
||||
const { response } = request;
|
||||
|
||||
respondJson(response, { version: getAppVersion() }, 200);
|
||||
}
|
||||
}
|
||||
23
src/main/routes/versions/get-version-route.injectable.ts
Normal file
23
src/main/routes/versions/get-version-route.injectable.ts
Normal file
@ -0,0 +1,23 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import type { Route } from "../../router/router";
|
||||
import { routeInjectionToken } from "../../router/router.injectable";
|
||||
import { getAppVersion } from "../../../common/utils";
|
||||
|
||||
const getVersionRouteInjectable = getInjectable({
|
||||
id: "get-version-route",
|
||||
|
||||
instantiate: (): Route<{ version: string }> => ({
|
||||
method: "get",
|
||||
path: `/version`,
|
||||
|
||||
handler: () => ({ response: { version: getAppVersion() }}),
|
||||
}),
|
||||
|
||||
injectionToken: routeInjectionToken,
|
||||
});
|
||||
|
||||
export default getVersionRouteInjectable;
|
||||
@ -13,10 +13,33 @@
|
||||
justify-content: space-between;
|
||||
|
||||
/* Use topbar as draggable region */
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
-webkit-app-region: drag;
|
||||
}
|
||||
|
||||
.topBar .items {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
height: 100%;
|
||||
-webkit-app-region: no-drag;
|
||||
|
||||
&:first-of-type {
|
||||
padding-left: 1.5rem;
|
||||
|
||||
& > * {
|
||||
margin-right: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
&:last-of-type {
|
||||
padding-right: 1.5rem;
|
||||
|
||||
& > * {
|
||||
margin-left: 1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:global(.is-mac) .topBar {
|
||||
padding-left: var(--hotbar-width);
|
||||
}
|
||||
@ -39,23 +62,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
.tools {
|
||||
@apply flex items-center;
|
||||
-webkit-app-region: no-drag;
|
||||
}
|
||||
|
||||
.controls {
|
||||
align-self: flex-end;
|
||||
padding-right: 1.5rem;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
height: 100%;
|
||||
-webkit-app-region: no-drag;
|
||||
}
|
||||
|
||||
.windowButtons {
|
||||
display: flex;
|
||||
margin-left: 1.5rem;
|
||||
margin-right: -1.5rem;
|
||||
|
||||
> div {
|
||||
@ -94,7 +102,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
.minimize, .maximize {
|
||||
.minimize,
|
||||
.maximize {
|
||||
&:hover {
|
||||
background-color: var(--borderFaintColor);
|
||||
}
|
||||
|
||||
@ -22,7 +22,7 @@ import { WindowAction } from "../../../../common/ipc/window";
|
||||
import isLinuxInjectable from "../../../../common/vars/is-linux.injectable";
|
||||
import isWindowsInjectable from "../../../../common/vars/is-windows.injectable";
|
||||
|
||||
export interface TopBarProps extends React.HTMLAttributes<any> {}
|
||||
export interface TopBarProps {}
|
||||
|
||||
interface Dependencies {
|
||||
items: IComputedValue<TopBarRegistration[]>;
|
||||
@ -41,7 +41,7 @@ ipcRendererOn("history:can-go-forward", (event, state: boolean) => {
|
||||
nextEnabled.set(state);
|
||||
});
|
||||
|
||||
const NonInjectedTopBar = observer(({ items, children, isWindows, isLinux, ...rest }: TopBarProps & Dependencies) => {
|
||||
const NonInjectedTopBar = observer(({ items, isWindows, isLinux }: TopBarProps & Dependencies) => {
|
||||
const elem = useRef<HTMLDivElement>();
|
||||
|
||||
const openAppContextMenu = () => {
|
||||
@ -61,12 +61,9 @@ const NonInjectedTopBar = observer(({ items, children, isWindows, isLinux, ...re
|
||||
};
|
||||
|
||||
const windowSizeToggle = (evt: React.MouseEvent) => {
|
||||
if (elem.current != evt.target) {
|
||||
// Skip clicking on child elements
|
||||
return;
|
||||
if (elem.current === evt.target) {
|
||||
toggleMaximize();
|
||||
}
|
||||
|
||||
toggleMaximize();
|
||||
};
|
||||
|
||||
const minimizeWindow = () => {
|
||||
@ -84,8 +81,8 @@ const NonInjectedTopBar = observer(({ items, children, isWindows, isLinux, ...re
|
||||
useEffect(() => watchHistoryState(), []);
|
||||
|
||||
return (
|
||||
<div className={styles.topBar} onDoubleClick={windowSizeToggle} ref={elem} {...rest}>
|
||||
<div className={styles.tools}>
|
||||
<div className={styles.topBar} onDoubleClick={windowSizeToggle} ref={elem}>
|
||||
<div className={styles.items}>
|
||||
{(isWindows || isLinux) && (
|
||||
<div className={styles.winMenu}>
|
||||
<div onClick={openAppContextMenu} data-testid="window-menu">
|
||||
@ -100,28 +97,24 @@ const NonInjectedTopBar = observer(({ items, children, isWindows, isLinux, ...re
|
||||
<Icon
|
||||
data-testid="home-button"
|
||||
material="home"
|
||||
className="ml-4"
|
||||
onClick={goHome}
|
||||
disabled={isActiveRoute(catalogRoute)}
|
||||
/>
|
||||
<Icon
|
||||
data-testid="history-back"
|
||||
material="arrow_back"
|
||||
className="ml-5"
|
||||
onClick={goBack}
|
||||
disabled={!prevEnabled.get()}
|
||||
/>
|
||||
<Icon
|
||||
data-testid="history-forward"
|
||||
material="arrow_forward"
|
||||
className="ml-5"
|
||||
onClick={goForward}
|
||||
disabled={!nextEnabled.get()}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.controls}>
|
||||
<div className={styles.items}>
|
||||
{renderRegisteredItems(items.get())}
|
||||
{children}
|
||||
{(isWindows || isLinux) && (
|
||||
<div className={cssNames(styles.windowButtons, { [styles.linuxButtons]: isLinux })}>
|
||||
<div className={styles.minimize} data-testid="window-minimize" onClick={minimizeWindow}>
|
||||
@ -147,23 +140,15 @@ const NonInjectedTopBar = observer(({ items, children, isWindows, isLinux, ...re
|
||||
});
|
||||
|
||||
const renderRegisteredItems = (items: TopBarRegistration[]) => (
|
||||
<div>
|
||||
{items.map((registration, index) => {
|
||||
if (!registration?.components?.Item) {
|
||||
return null;
|
||||
}
|
||||
items.map((registration, index) => {
|
||||
if (!registration?.components?.Item) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div key={index}>
|
||||
<registration.components.Item />
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
return <registration.components.Item key={index} />;
|
||||
})
|
||||
);
|
||||
|
||||
|
||||
|
||||
export const TopBar = withInjectables<Dependencies, TopBarProps>(NonInjectedTopBar, {
|
||||
getProps: (di, props) => ({
|
||||
items: di.inject(topBarItemsInjectable),
|
||||
|
||||
425
yarn.lock
425
yarn.lock
@ -330,6 +330,11 @@
|
||||
exec-sh "^0.3.2"
|
||||
minimist "^1.2.0"
|
||||
|
||||
"@colors/colors@1.5.0":
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9"
|
||||
integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==
|
||||
|
||||
"@cspotcode/source-map-consumer@0.8.0":
|
||||
version "0.8.0"
|
||||
resolved "https://registry.yarnpkg.com/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz#33bf4b7b39c178821606f669bbc447a6a629786b"
|
||||
@ -1232,6 +1237,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82"
|
||||
integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==
|
||||
|
||||
"@tootallnate/once@2":
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf"
|
||||
integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==
|
||||
|
||||
"@tsconfig/node10@^1.0.7":
|
||||
version "1.0.8"
|
||||
resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.8.tgz#c1e4e80d6f964fbecb3359c43bd48b40f7cadad9"
|
||||
@ -1352,12 +1362,13 @@
|
||||
dependencies:
|
||||
moment "^2.10.2"
|
||||
|
||||
"@types/circular-dependency-plugin@5.0.4":
|
||||
version "5.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/circular-dependency-plugin/-/circular-dependency-plugin-5.0.4.tgz#c5ccbd1d2bbb39b60e9859b39c6b826f60567ef2"
|
||||
integrity sha512-J4XkMJfkGv3o3q2Ca821cufIBNBFms45fz+xD9tEESR0YqL5BlwETOwm2desSCdki2zdcPRhG9ZQCm/WITCEPQ==
|
||||
"@types/circular-dependency-plugin@5.0.5":
|
||||
version "5.0.5"
|
||||
resolved "https://registry.yarnpkg.com/@types/circular-dependency-plugin/-/circular-dependency-plugin-5.0.5.tgz#431979052a11bdb192c2961cc4ae8b47ca24a38c"
|
||||
integrity sha512-JU1sYQWNbUluWHseLUfokakx18+BXRA9Bxji56hdY5NW0nvrJSJd4SNAl0Btpm5ima9BnUkoGEcW/2PH1QuWQA==
|
||||
dependencies:
|
||||
"@types/webpack" "^4"
|
||||
"@types/node" "*"
|
||||
webpack "^5.1.0"
|
||||
|
||||
"@types/clean-css@*":
|
||||
version "4.2.1"
|
||||
@ -1385,10 +1396,10 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0"
|
||||
integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==
|
||||
|
||||
"@types/color@^3.0.2":
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/color/-/color-3.0.2.tgz#3779043e782f562aa9157b5fc6bd07e14fd8e7f3"
|
||||
integrity sha512-INiJl6sfNn8iyC5paxVzqiVUEj2boIlFki02uRTAkKwAj++7aAF+ZfEv/XrIeBa0XI/fTZuDHW8rEEcEVnON+Q==
|
||||
"@types/color@^3.0.3":
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/color/-/color-3.0.3.tgz#e6d8d72b7aaef4bb9fe80847c26c7c786191016d"
|
||||
integrity sha512-X//qzJ3d3Zj82J9sC/C18ZY5f43utPbAJ6PhYt/M7uG6etcF6MRpKdN880KBy43B0BMzSfeT96MzrsNjFI3GbA==
|
||||
dependencies:
|
||||
"@types/color-convert" "*"
|
||||
|
||||
@ -1431,7 +1442,7 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/electron-devtools-installer/-/electron-devtools-installer-2.2.0.tgz#32ee4ebbe99b3daf9847a6d2097dc00b5de94f10"
|
||||
integrity sha512-HJNxpaOXuykCK4rQ6FOMxAA0NLFYsf7FiPFGmab0iQmtVBHSAfxzy3MRFpLTTDDWbV0yD2YsHOQvdu8yCqtCfw==
|
||||
|
||||
"@types/eslint-scope@^3.7.0", "@types/eslint-scope@^3.7.3":
|
||||
"@types/eslint-scope@^3.7.3":
|
||||
version "3.7.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.3.tgz#125b88504b61e3c8bc6f870882003253005c3224"
|
||||
integrity sha512-PB3ldyrcnAicT35TWPs5IcwKD8S333HMaa2VVv4+wdvebJkjWuW/xESoB8IwRcog8HYVYamb1g/R31Qv5Bx03g==
|
||||
@ -1447,7 +1458,7 @@
|
||||
"@types/estree" "*"
|
||||
"@types/json-schema" "*"
|
||||
|
||||
"@types/estree@*", "@types/estree@^0.0.50":
|
||||
"@types/estree@*":
|
||||
version "0.0.50"
|
||||
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.50.tgz#1e0caa9364d3fccd2931c3ed96fdbeaa5d4cca83"
|
||||
integrity sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==
|
||||
@ -1842,10 +1853,10 @@
|
||||
dependencies:
|
||||
"@types/react" "*"
|
||||
|
||||
"@types/react@*", "@types/react@^17.0.34":
|
||||
version "17.0.34"
|
||||
resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.34.tgz#797b66d359b692e3f19991b6b07e4b0c706c0102"
|
||||
integrity sha512-46FEGrMjc2+8XhHXILr+3+/sTe3OfzSPU9YGKILLrUYbQ1CLQC9Daqo1KzENGXAWwrFwiY0l4ZbF20gRvgpWTg==
|
||||
"@types/react@*", "@types/react@^17.0.40":
|
||||
version "17.0.40"
|
||||
resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.40.tgz#dc010cee6254d5239a138083f3799a16638e6bad"
|
||||
integrity sha512-UrXhD/JyLH+W70nNSufXqMZNuUD2cXHu6UjCllC6pmOQgBX4SGXOH8fjRka0O0Ee0HrFxapDD8Bwn81Kmiz6jQ==
|
||||
dependencies:
|
||||
"@types/prop-types" "*"
|
||||
"@types/scheduler" "*"
|
||||
@ -2036,10 +2047,10 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/underscore/-/underscore-1.10.0.tgz#5cb0dff2a5f616fc8e0c61b482bf01fa20a03cec"
|
||||
integrity sha512-ZAbqul7QAKpM2h1PFGa5ETN27ulmqtj0QviYHasw9LffvXZvVHuraOx/FOsIPPDNGZN0Qo1nASxxSfMYOtSoCw==
|
||||
|
||||
"@types/url-parse@^1.4.5":
|
||||
version "1.4.5"
|
||||
resolved "https://registry.yarnpkg.com/@types/url-parse/-/url-parse-1.4.5.tgz#bda0b813cc8f79d767dc8ca645fdabc238c6ae77"
|
||||
integrity sha512-8Wje3itJpk/FX+QItca9vjNLjGx5jlEYBw/CpMi03Fphk2DSVeZDUqWTE81BeCI5Bl6Z+zmA1O9L/8e3ZUSeLg==
|
||||
"@types/url-parse@^1.4.8":
|
||||
version "1.4.8"
|
||||
resolved "https://registry.yarnpkg.com/@types/url-parse/-/url-parse-1.4.8.tgz#c3825047efbca1295b7f1646f38203d9145130d6"
|
||||
integrity sha512-zqqcGKyNWgTLFBxmaexGUKQyWqeG7HjXj20EuQJSJWwXe54BjX0ihIo5cJB9yAQzH8dNugJ9GvkBYMjPXs/PJw==
|
||||
|
||||
"@types/uuid@^8.3.3":
|
||||
version "8.3.3"
|
||||
@ -2148,14 +2159,14 @@
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@typescript-eslint/eslint-plugin@^5.10.1":
|
||||
version "5.10.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.10.1.tgz#870195d0f2146b36d11fc71131b75aba52354c69"
|
||||
integrity sha512-xN3CYqFlyE/qOcy978/L0xLR2HlcAGIyIK5sMOasxaaAPfQRj/MmMV6OC3I7NZO84oEUdWCOju34Z9W8E0pFDQ==
|
||||
"@typescript-eslint/eslint-plugin@^5.14.0":
|
||||
version "5.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.14.0.tgz#5119b67152356231a0e24b998035288a9cd21335"
|
||||
integrity sha512-ir0wYI4FfFUDfLcuwKzIH7sMVA+db7WYen47iRSaCGl+HMAZI9fpBwfDo45ZALD3A45ZGyHWDNLhbg8tZrMX4w==
|
||||
dependencies:
|
||||
"@typescript-eslint/scope-manager" "5.10.1"
|
||||
"@typescript-eslint/type-utils" "5.10.1"
|
||||
"@typescript-eslint/utils" "5.10.1"
|
||||
"@typescript-eslint/scope-manager" "5.14.0"
|
||||
"@typescript-eslint/type-utils" "5.14.0"
|
||||
"@typescript-eslint/utils" "5.14.0"
|
||||
debug "^4.3.2"
|
||||
functional-red-black-tree "^1.0.1"
|
||||
ignore "^5.1.8"
|
||||
@ -2181,12 +2192,20 @@
|
||||
"@typescript-eslint/types" "5.10.1"
|
||||
"@typescript-eslint/visitor-keys" "5.10.1"
|
||||
|
||||
"@typescript-eslint/type-utils@5.10.1":
|
||||
version "5.10.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.10.1.tgz#5e526c00142585e40ab1503e83f1ff608c367405"
|
||||
integrity sha512-AfVJkV8uck/UIoDqhu+ptEdBoQATON9GXnhOpPLzkQRJcSChkvD//qsz9JVffl2goxX+ybs5klvacE9vmrQyCw==
|
||||
"@typescript-eslint/scope-manager@5.14.0":
|
||||
version "5.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.14.0.tgz#ea518962b42db8ed0a55152ea959c218cb53ca7b"
|
||||
integrity sha512-LazdcMlGnv+xUc5R4qIlqH0OWARyl2kaP8pVCS39qSL3Pd1F7mI10DbdXeARcE62sVQE4fHNvEqMWsypWO+yEw==
|
||||
dependencies:
|
||||
"@typescript-eslint/utils" "5.10.1"
|
||||
"@typescript-eslint/types" "5.14.0"
|
||||
"@typescript-eslint/visitor-keys" "5.14.0"
|
||||
|
||||
"@typescript-eslint/type-utils@5.14.0":
|
||||
version "5.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.14.0.tgz#711f08105860b12988454e91df433567205a8f0b"
|
||||
integrity sha512-d4PTJxsqaUpv8iERTDSQBKUCV7Q5yyXjqXUl3XF7Sd9ogNLuKLkxz82qxokqQ4jXdTPZudWpmNtr/JjbbvUixw==
|
||||
dependencies:
|
||||
"@typescript-eslint/utils" "5.14.0"
|
||||
debug "^4.3.2"
|
||||
tsutils "^3.21.0"
|
||||
|
||||
@ -2195,6 +2214,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.10.1.tgz#dca9bd4cb8c067fc85304a31f38ec4766ba2d1ea"
|
||||
integrity sha512-ZvxQ2QMy49bIIBpTqFiOenucqUyjTQ0WNLhBM6X1fh1NNlYAC6Kxsx8bRTY3jdYsYg44a0Z/uEgQkohbR0H87Q==
|
||||
|
||||
"@typescript-eslint/types@5.14.0":
|
||||
version "5.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.14.0.tgz#96317cf116cea4befabc0defef371a1013f8ab11"
|
||||
integrity sha512-BR6Y9eE9360LNnW3eEUqAg6HxS9Q35kSIs4rp4vNHRdfg0s+/PgHgskvu5DFTM7G5VKAVjuyaN476LCPrdA7Mw==
|
||||
|
||||
"@typescript-eslint/typescript-estree@5.10.1":
|
||||
version "5.10.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.10.1.tgz#b268e67be0553f8790ba3fe87113282977adda15"
|
||||
@ -2208,15 +2232,28 @@
|
||||
semver "^7.3.5"
|
||||
tsutils "^3.21.0"
|
||||
|
||||
"@typescript-eslint/utils@5.10.1":
|
||||
version "5.10.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.10.1.tgz#fa682a33af47080ba2c4368ee0ad2128213a1196"
|
||||
integrity sha512-RRmlITiUbLuTRtn/gcPRi4202niF+q7ylFLCKu4c+O/PcpRvZ/nAUwQ2G00bZgpWkhrNLNnvhZLbDn8Ml0qsQw==
|
||||
"@typescript-eslint/typescript-estree@5.14.0":
|
||||
version "5.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.14.0.tgz#78b7f7385d5b6f2748aacea5c9b7f6ae62058314"
|
||||
integrity sha512-QGnxvROrCVtLQ1724GLTHBTR0lZVu13izOp9njRvMkCBgWX26PKvmMP8k82nmXBRD3DQcFFq2oj3cKDwr0FaUA==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "5.14.0"
|
||||
"@typescript-eslint/visitor-keys" "5.14.0"
|
||||
debug "^4.3.2"
|
||||
globby "^11.0.4"
|
||||
is-glob "^4.0.3"
|
||||
semver "^7.3.5"
|
||||
tsutils "^3.21.0"
|
||||
|
||||
"@typescript-eslint/utils@5.14.0":
|
||||
version "5.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.14.0.tgz#6c8bc4f384298cbbb32b3629ba7415f9f80dc8c4"
|
||||
integrity sha512-EHwlII5mvUA0UsKYnVzySb/5EE/t03duUTweVy8Zqt3UQXBrpEVY144OTceFKaOe4xQXZJrkptCf7PjEBeGK4w==
|
||||
dependencies:
|
||||
"@types/json-schema" "^7.0.9"
|
||||
"@typescript-eslint/scope-manager" "5.10.1"
|
||||
"@typescript-eslint/types" "5.10.1"
|
||||
"@typescript-eslint/typescript-estree" "5.10.1"
|
||||
"@typescript-eslint/scope-manager" "5.14.0"
|
||||
"@typescript-eslint/types" "5.14.0"
|
||||
"@typescript-eslint/typescript-estree" "5.14.0"
|
||||
eslint-scope "^5.1.1"
|
||||
eslint-utils "^3.0.0"
|
||||
|
||||
@ -2228,6 +2265,14 @@
|
||||
"@typescript-eslint/types" "5.10.1"
|
||||
eslint-visitor-keys "^3.0.0"
|
||||
|
||||
"@typescript-eslint/visitor-keys@5.14.0":
|
||||
version "5.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.14.0.tgz#1927005b3434ccd0d3ae1b2ecf60e65943c36986"
|
||||
integrity sha512-yL0XxfzR94UEkjBqyymMLgCBdojzEuy/eim7N9/RIcTNxpJudAcqsU8eRyfzBbcEzGoPWfdM3AGak3cN08WOIw==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "5.14.0"
|
||||
eslint-visitor-keys "^3.0.0"
|
||||
|
||||
"@webassemblyjs/ast@1.11.1":
|
||||
version "1.11.1"
|
||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.1.tgz#2bfd767eae1a6996f432ff7e8d7fc75679c0b6a7"
|
||||
@ -2648,10 +2693,10 @@ app-builder-bin@3.7.1:
|
||||
resolved "https://registry.yarnpkg.com/app-builder-bin/-/app-builder-bin-3.7.1.tgz#cb0825c5e12efc85b196ac3ed9c89f076c61040e"
|
||||
integrity sha512-ql93vEUq6WsstGXD+SBLSIQw6SNnhbDEM0swzgugytMxLp3rT24Ag/jcC80ZHxiPRTdew1niuR7P3/FCrDqIjw==
|
||||
|
||||
app-builder-lib@22.14.5:
|
||||
version "22.14.5"
|
||||
resolved "https://registry.yarnpkg.com/app-builder-lib/-/app-builder-lib-22.14.5.tgz#a61a50b132b858e98fdc70b6b88994ae99b4f96d"
|
||||
integrity sha512-k3VwKP4kpsnUaXoUkm1s4zaSHPHIMFnN4kPMU9yXaKmE1LfHHqBaEah5bXeTAX5V/BC41wFdg8CF5vOjvgy8Rg==
|
||||
app-builder-lib@22.14.13:
|
||||
version "22.14.13"
|
||||
resolved "https://registry.yarnpkg.com/app-builder-lib/-/app-builder-lib-22.14.13.tgz#c1f5b6afc86596357598bb90b69eef06c7c2eeb3"
|
||||
integrity sha512-SufmrtxU+D0Tn948fjEwAOlCN9757UXLkzzTWXMwZKR/5hisvgqeeBepWfphMIE6OkDGz0fbzEhL1P2Pty4XMg==
|
||||
dependencies:
|
||||
"7zip-bin" "~5.1.1"
|
||||
"@develar/schema-utils" "~2.6.5"
|
||||
@ -2659,13 +2704,13 @@ app-builder-lib@22.14.5:
|
||||
"@malept/flatpak-bundler" "^0.4.0"
|
||||
async-exit-hook "^2.0.1"
|
||||
bluebird-lst "^1.0.9"
|
||||
builder-util "22.14.5"
|
||||
builder-util-runtime "8.9.1"
|
||||
builder-util "22.14.13"
|
||||
builder-util-runtime "8.9.2"
|
||||
chromium-pickle-js "^0.2.0"
|
||||
debug "^4.3.2"
|
||||
ejs "^3.1.6"
|
||||
electron-osx-sign "^0.5.0"
|
||||
electron-publish "22.14.5"
|
||||
electron-publish "22.14.13"
|
||||
form-data "^4.0.0"
|
||||
fs-extra "^10.0.0"
|
||||
hosted-git-info "^4.0.2"
|
||||
@ -2852,10 +2897,10 @@ async@^2.6.2:
|
||||
dependencies:
|
||||
lodash "^4.17.14"
|
||||
|
||||
async@^3.1.0:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/async/-/async-3.2.0.tgz#b3a2685c5ebb641d3de02d161002c60fc9f85720"
|
||||
integrity sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==
|
||||
async@^3.2.3:
|
||||
version "3.2.3"
|
||||
resolved "https://registry.yarnpkg.com/async/-/async-3.2.3.tgz#ac53dafd3f4720ee9e8a160628f18ea91df196c9"
|
||||
integrity sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g==
|
||||
|
||||
asynckit@^0.4.0:
|
||||
version "0.4.0"
|
||||
@ -3270,21 +3315,31 @@ builder-util-runtime@8.9.1:
|
||||
debug "^4.3.2"
|
||||
sax "^1.2.4"
|
||||
|
||||
builder-util@22.14.5:
|
||||
version "22.14.5"
|
||||
resolved "https://registry.yarnpkg.com/builder-util/-/builder-util-22.14.5.tgz#42a18608d2a566c0846e91266464776c8bfb0cc9"
|
||||
integrity sha512-zqIHDFJwmA7jV7SC9aI+33MWwT2mWoijH+Ol9IntNAwuuRXoS+7XeJwnhLBXOhcDBzXT4kDzHnRk4JKeaygEYA==
|
||||
builder-util-runtime@8.9.2:
|
||||
version "8.9.2"
|
||||
resolved "https://registry.yarnpkg.com/builder-util-runtime/-/builder-util-runtime-8.9.2.tgz#a9669ae5b5dcabfe411ded26678e7ae997246c28"
|
||||
integrity sha512-rhuKm5vh7E0aAmT6i8aoSfEjxzdYEFX7zDApK+eNgOhjofnWb74d9SRJv0H/8nsgOkos0TZ4zxW0P8J4N7xQ2A==
|
||||
dependencies:
|
||||
debug "^4.3.2"
|
||||
sax "^1.2.4"
|
||||
|
||||
builder-util@22.14.13:
|
||||
version "22.14.13"
|
||||
resolved "https://registry.yarnpkg.com/builder-util/-/builder-util-22.14.13.tgz#41b5b7b4ee53aff4e09cc007fb144522598f3ce6"
|
||||
integrity sha512-oePC/qrrUuerhmH5iaCJzPRAKlSBylrhzuAJmRQClTyWnZUv6jbaHh+VoHMbEiE661wrj2S2aV7/bQh12cj1OA==
|
||||
dependencies:
|
||||
"7zip-bin" "~5.1.1"
|
||||
"@types/debug" "^4.1.6"
|
||||
"@types/fs-extra" "^9.0.11"
|
||||
app-builder-bin "3.7.1"
|
||||
bluebird-lst "^1.0.9"
|
||||
builder-util-runtime "8.9.1"
|
||||
builder-util-runtime "8.9.2"
|
||||
chalk "^4.1.1"
|
||||
cross-spawn "^7.0.3"
|
||||
debug "^4.3.2"
|
||||
fs-extra "^10.0.0"
|
||||
http-proxy-agent "^5.0.0"
|
||||
https-proxy-agent "^5.0.0"
|
||||
is-ci "^3.0.0"
|
||||
js-yaml "^4.1.0"
|
||||
source-map-support "^0.5.19"
|
||||
@ -3517,22 +3572,7 @@ chartjs-color@^2.1.0:
|
||||
chartjs-color-string "^0.6.0"
|
||||
color-convert "^1.9.3"
|
||||
|
||||
"chokidar@>=3.0.0 <4.0.0", chokidar@^3.4.3, chokidar@^3.5.2:
|
||||
version "3.5.2"
|
||||
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.2.tgz#dba3976fcadb016f66fd365021d91600d01c1e75"
|
||||
integrity sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==
|
||||
dependencies:
|
||||
anymatch "~3.1.2"
|
||||
braces "~3.0.2"
|
||||
glob-parent "~5.1.2"
|
||||
is-binary-path "~2.1.0"
|
||||
is-glob "~4.0.1"
|
||||
normalize-path "~3.0.0"
|
||||
readdirp "~3.6.0"
|
||||
optionalDependencies:
|
||||
fsevents "~2.3.2"
|
||||
|
||||
chokidar@^3.4.2, chokidar@^3.5.3:
|
||||
"chokidar@>=3.0.0 <4.0.0", chokidar@^3.4.2, chokidar@^3.5.2, chokidar@^3.5.3:
|
||||
version "3.5.3"
|
||||
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd"
|
||||
integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==
|
||||
@ -3825,7 +3865,7 @@ colors@1.0.3:
|
||||
resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b"
|
||||
integrity sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=
|
||||
|
||||
colors@^1.1.2, colors@^1.2.1, colors@^1.4.0:
|
||||
colors@^1.1.2, colors@^1.4.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78"
|
||||
integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==
|
||||
@ -4179,18 +4219,18 @@ css-box-model@^1.2.0:
|
||||
dependencies:
|
||||
tiny-invariant "^1.0.6"
|
||||
|
||||
css-loader@^6.5.1:
|
||||
version "6.5.1"
|
||||
resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-6.5.1.tgz#0c43d4fbe0d97f699c91e9818cb585759091d1b1"
|
||||
integrity sha512-gEy2w9AnJNnD9Kuo4XAP9VflW/ujKoS9c/syO+uWMlm5igc7LysKzPXaDoR2vroROkSwsTS2tGr1yGGEbZOYZQ==
|
||||
css-loader@^6.7.1:
|
||||
version "6.7.1"
|
||||
resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-6.7.1.tgz#e98106f154f6e1baf3fc3bc455cb9981c1d5fd2e"
|
||||
integrity sha512-yB5CNFa14MbPJcomwNh3wLThtkZgcNyI2bNMRt8iE5Z8Vwl7f8vQXFAzn2HDOJvtDq2NTZBUGMSUNNyrv3/+cw==
|
||||
dependencies:
|
||||
icss-utils "^5.1.0"
|
||||
postcss "^8.2.15"
|
||||
postcss "^8.4.7"
|
||||
postcss-modules-extract-imports "^3.0.0"
|
||||
postcss-modules-local-by-default "^4.0.0"
|
||||
postcss-modules-scope "^3.0.0"
|
||||
postcss-modules-values "^4.0.0"
|
||||
postcss-value-parser "^4.1.0"
|
||||
postcss-value-parser "^4.2.0"
|
||||
semver "^7.3.5"
|
||||
|
||||
css-parse@~2.0.0:
|
||||
@ -4611,14 +4651,14 @@ dlv@^1.1.3:
|
||||
resolved "https://registry.yarnpkg.com/dlv/-/dlv-1.1.3.tgz#5c198a8a11453596e751494d49874bc7732f2e79"
|
||||
integrity sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==
|
||||
|
||||
dmg-builder@22.14.5:
|
||||
version "22.14.5"
|
||||
resolved "https://registry.yarnpkg.com/dmg-builder/-/dmg-builder-22.14.5.tgz#137c0b55e639badcc0b119eb060e6fa4ed61d948"
|
||||
integrity sha512-1GvFGQE332bvPamcMwZDqWqfWfJTyyDLOsHMcGi0zs+Jh7JOn6/zuBkHJIWHdsj2QJbhzLVyd2/ZqttOKv7I8w==
|
||||
dmg-builder@22.14.13:
|
||||
version "22.14.13"
|
||||
resolved "https://registry.yarnpkg.com/dmg-builder/-/dmg-builder-22.14.13.tgz#cc613f3c18e889b8777d525991fd52f50a564f8c"
|
||||
integrity sha512-xNOugB6AbIRETeU2uID15sUfjdZZcKdxK8xkFnwIggsM00PJ12JxpLNPTjcRoUnfwj3WrPjilrO64vRMwNItQg==
|
||||
dependencies:
|
||||
app-builder-lib "22.14.5"
|
||||
builder-util "22.14.5"
|
||||
builder-util-runtime "8.9.1"
|
||||
app-builder-lib "22.14.13"
|
||||
builder-util "22.14.13"
|
||||
builder-util-runtime "8.9.2"
|
||||
fs-extra "^10.0.0"
|
||||
iconv-lite "^0.6.2"
|
||||
js-yaml "^4.1.0"
|
||||
@ -4823,17 +4863,17 @@ ejs@^3.1.6:
|
||||
dependencies:
|
||||
jake "^10.6.1"
|
||||
|
||||
electron-builder@^22.14.5:
|
||||
version "22.14.5"
|
||||
resolved "https://registry.yarnpkg.com/electron-builder/-/electron-builder-22.14.5.tgz#3a25547bd4fe3728d4704da80956a794c5c31496"
|
||||
integrity sha512-N73hSbXFz6Mz5Z6h6C5ly6CB+dUN6k1LuCDJjI8VF47bMXv/QE0HE+Kkb0GPKqTqM7Hsk/yIYX+kHCfSkR5FGg==
|
||||
electron-builder@^22.14.13:
|
||||
version "22.14.13"
|
||||
resolved "https://registry.yarnpkg.com/electron-builder/-/electron-builder-22.14.13.tgz#fd40564685cf5422a8f8d667940af3d3776f4fb8"
|
||||
integrity sha512-3fgLxqF2TXVKiUPeg74O4V3l0l3j7ERLazo8sUbRkApw0+4iVAf2BJkHsHMaXiigsgCoEzK/F4/rB5rne/VAnw==
|
||||
dependencies:
|
||||
"@types/yargs" "^17.0.1"
|
||||
app-builder-lib "22.14.5"
|
||||
builder-util "22.14.5"
|
||||
builder-util-runtime "8.9.1"
|
||||
app-builder-lib "22.14.13"
|
||||
builder-util "22.14.13"
|
||||
builder-util-runtime "8.9.2"
|
||||
chalk "^4.1.1"
|
||||
dmg-builder "22.14.5"
|
||||
dmg-builder "22.14.13"
|
||||
fs-extra "^10.0.0"
|
||||
is-ci "^3.0.0"
|
||||
lazy-val "^1.0.5"
|
||||
@ -4871,14 +4911,14 @@ electron-osx-sign@^0.5.0:
|
||||
minimist "^1.2.0"
|
||||
plist "^3.0.1"
|
||||
|
||||
electron-publish@22.14.5:
|
||||
version "22.14.5"
|
||||
resolved "https://registry.yarnpkg.com/electron-publish/-/electron-publish-22.14.5.tgz#34bcdce671f0e651330db20040d6919c77c94bd6"
|
||||
integrity sha512-h+NANRdaA0PqGF15GKvorseWPzh1PXa/zx4I37//PIokW8eKIov8ky23foUSb55ZFWUHGpxQJux7y2NCfBtQeg==
|
||||
electron-publish@22.14.13:
|
||||
version "22.14.13"
|
||||
resolved "https://registry.yarnpkg.com/electron-publish/-/electron-publish-22.14.13.tgz#8b71e6975af8cc6ac5b21f293ade23f8704047c7"
|
||||
integrity sha512-0oP3QiNj3e8ewOaEpEJV/o6Zrmy2VarVvZ/bH7kyO/S/aJf9x8vQsKVWpsdmSiZ5DJEHgarFIXrnO0ZQf0P9iQ==
|
||||
dependencies:
|
||||
"@types/fs-extra" "^9.0.11"
|
||||
builder-util "22.14.5"
|
||||
builder-util-runtime "8.9.1"
|
||||
builder-util "22.14.13"
|
||||
builder-util-runtime "8.9.2"
|
||||
chalk "^4.1.1"
|
||||
fs-extra "^10.0.0"
|
||||
lazy-val "^1.0.5"
|
||||
@ -4969,7 +5009,7 @@ end-of-stream@^1.0.0, end-of-stream@^1.1.0, end-of-stream@^1.4.1:
|
||||
dependencies:
|
||||
once "^1.4.0"
|
||||
|
||||
enhanced-resolve@^5.0.0, enhanced-resolve@^5.8.3:
|
||||
enhanced-resolve@^5.0.0:
|
||||
version "5.8.3"
|
||||
resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.8.3.tgz#6d552d465cce0423f5b3d718511ea53826a7b2f0"
|
||||
integrity sha512-EGAbGvH7j7Xt2nc0E7D99La1OiEs8LnyimkRgwExpUMScN6O+3x9tIWs7PLQZVNx4YD+00skHXPXi1yQHpAmZA==
|
||||
@ -4977,10 +5017,10 @@ enhanced-resolve@^5.0.0, enhanced-resolve@^5.8.3:
|
||||
graceful-fs "^4.2.4"
|
||||
tapable "^2.2.0"
|
||||
|
||||
enhanced-resolve@^5.9.0:
|
||||
version "5.9.0"
|
||||
resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.9.0.tgz#49ac24953ac8452ed8fed2ef1340fc8e043667ee"
|
||||
integrity sha512-weDYmzbBygL7HzGGS26M3hGQx68vehdEg6VUmqSOaFzXExFqlnKuSvsEJCVGQHScS8CQMbrAqftT+AzzHNt/YA==
|
||||
enhanced-resolve@^5.9.2:
|
||||
version "5.9.2"
|
||||
resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.9.2.tgz#0224dcd6a43389ebfb2d55efee517e5466772dd9"
|
||||
integrity sha512-GIm3fQfwLJ8YZx2smuHpBKkXC1yOk+OBEmKckVyL0i/ea8mqDEykK3ld5dgH1QYPNyT/lIllxV2LULnxCHaHkA==
|
||||
dependencies:
|
||||
graceful-fs "^4.2.4"
|
||||
tapable "^2.2.0"
|
||||
@ -5857,11 +5897,6 @@ fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6:
|
||||
resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
|
||||
integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=
|
||||
|
||||
fast-safe-stringify@^2.0.4:
|
||||
version "2.0.7"
|
||||
resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz#124aa885899261f68aedb42a7c080de9da608743"
|
||||
integrity sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==
|
||||
|
||||
fastest-levenshtein@^1.0.12:
|
||||
version "1.0.12"
|
||||
resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz#9990f7d3a88cc5a9ffd1f1745745251700d497e2"
|
||||
@ -6832,6 +6867,15 @@ http-proxy-agent@^4.0.1:
|
||||
agent-base "6"
|
||||
debug "4"
|
||||
|
||||
http-proxy-agent@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43"
|
||||
integrity sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==
|
||||
dependencies:
|
||||
"@tootallnate/once" "2"
|
||||
agent-base "6"
|
||||
debug "4"
|
||||
|
||||
http-proxy-middleware@^2.0.0:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-2.0.2.tgz#94d7593790aad6b3de48164f13792262f656c332"
|
||||
@ -6978,12 +7022,7 @@ ignore@^4.0.6:
|
||||
resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc"
|
||||
integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==
|
||||
|
||||
ignore@^5.1.4, ignore@^5.1.8:
|
||||
version "5.1.8"
|
||||
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57"
|
||||
integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==
|
||||
|
||||
ignore@^5.2.0:
|
||||
ignore@^5.1.4, ignore@^5.1.8, ignore@^5.2.0:
|
||||
version "5.2.0"
|
||||
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a"
|
||||
integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==
|
||||
@ -8827,15 +8866,15 @@ lodash@4.x, lodash@^4.17.10, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15,
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
|
||||
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
|
||||
|
||||
logform@^2.2.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/logform/-/logform-2.2.0.tgz#40f036d19161fc76b68ab50fdc7fe495544492f2"
|
||||
integrity sha512-N0qPlqfypFx7UHNn4B3lzS/b0uLqt2hmuoa+PpuXNYgozdJYAyauF5Ky0BWVjrxDlMWiT3qN4zPq3vVAfZy7Yg==
|
||||
logform@^2.2.0, logform@^2.3.2, logform@^2.4.0:
|
||||
version "2.4.0"
|
||||
resolved "https://registry.yarnpkg.com/logform/-/logform-2.4.0.tgz#131651715a17d50f09c2a2c1a524ff1a4164bcfe"
|
||||
integrity sha512-CPSJw4ftjf517EhXZGGvTHHkYobo7ZCc0kvwUoOYcjfR2UVrI66RHj8MCrfAdEitdmFqbu2BYdYs8FHHZSb6iw==
|
||||
dependencies:
|
||||
colors "^1.2.1"
|
||||
fast-safe-stringify "^2.0.4"
|
||||
"@colors/colors" "1.5.0"
|
||||
fecha "^4.2.0"
|
||||
ms "^2.1.1"
|
||||
safe-stable-stringify "^2.3.1"
|
||||
triple-beam "^1.3.0"
|
||||
|
||||
loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1, loose-envify@^1.4.0:
|
||||
@ -9047,6 +9086,11 @@ merge2@^1.3.0:
|
||||
resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae"
|
||||
integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==
|
||||
|
||||
mergee@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/mergee/-/mergee-1.0.0.tgz#027c5addc650f6ecbe4bf56100bd00dae763fda7"
|
||||
integrity sha512-hbbXD4LOcxVkpS+mp3BMEhkSDf+lTVENFeEeqACgjjL8WrgKuW2EyLT0fOHyTbyDiuRLZJZ1HrHNeiX4iOd79Q==
|
||||
|
||||
methods@~1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
|
||||
@ -9290,6 +9334,13 @@ mock-fs@^5.1.2:
|
||||
resolved "https://registry.yarnpkg.com/mock-fs/-/mock-fs-5.1.2.tgz#6fa486e06d00f8793a8d2228de980eff93ce6db7"
|
||||
integrity sha512-YkjQkdLulFrz0vD4BfNQdQRVmgycXTV7ykuHMlyv+C8WCHazpkiQRDthwa02kSyo8wKnY9wRptHfQLgmf0eR+A==
|
||||
|
||||
mock-http@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/mock-http/-/mock-http-1.1.0.tgz#b89380a718a103fc5801095804bedd0b20f7638c"
|
||||
integrity sha512-H2HMGaHNQPWY8PdeEw4RFux2WEOHD6eJAtN3+iFELik5kGjPKAcoyPWcsC2vgDiTa2yimAEDssmMed51e+cBKQ==
|
||||
dependencies:
|
||||
mergee "^1.0.0"
|
||||
|
||||
moment-timezone@^0.5.34:
|
||||
version "0.5.34"
|
||||
resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.34.tgz#a75938f7476b88f155d3504a9343f7519d9a405c"
|
||||
@ -9371,10 +9422,10 @@ nan@^2.14.0:
|
||||
resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.1.tgz#d7be34dfa3105b91494c3147089315eff8874b01"
|
||||
integrity sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==
|
||||
|
||||
nanoid@^3.1.30:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.2.0.tgz#62667522da6673971cca916a6d3eff3f415ff80c"
|
||||
integrity sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA==
|
||||
nanoid@^3.3.1:
|
||||
version "3.3.1"
|
||||
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.1.tgz#6347a18cac88af88f58af0b3594b723d5e99bb35"
|
||||
integrity sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==
|
||||
|
||||
nanomatch@^1.2.9:
|
||||
version "1.2.13"
|
||||
@ -9417,12 +9468,7 @@ negotiator@0.6.2:
|
||||
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb"
|
||||
integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==
|
||||
|
||||
neo-async@^2.6.0:
|
||||
version "2.6.1"
|
||||
resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.1.tgz#ac27ada66167fa8849a6addd837f6b189ad2081c"
|
||||
integrity sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==
|
||||
|
||||
neo-async@^2.6.2:
|
||||
neo-async@^2.6.0, neo-async@^2.6.2:
|
||||
version "2.6.2"
|
||||
resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f"
|
||||
integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==
|
||||
@ -10697,12 +10743,7 @@ postcss-value-parser@^3.3.0:
|
||||
resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz#9ff822547e2893213cf1c30efa51ac5fd1ba8281"
|
||||
integrity sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==
|
||||
|
||||
postcss-value-parser@^4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz#443f6a20ced6481a2bda4fa8532a6e55d789a2cb"
|
||||
integrity sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==
|
||||
|
||||
postcss-value-parser@^4.2.0:
|
||||
postcss-value-parser@^4.1.0, postcss-value-parser@^4.2.0:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514"
|
||||
integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==
|
||||
@ -10716,14 +10757,14 @@ postcss@^6.0.14, postcss@^6.0.2:
|
||||
source-map "^0.6.1"
|
||||
supports-color "^5.4.0"
|
||||
|
||||
postcss@^8.1.6, postcss@^8.2.15, postcss@^8.3.0, postcss@^8.4.5:
|
||||
version "8.4.5"
|
||||
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.5.tgz#bae665764dfd4c6fcc24dc0fdf7e7aa00cc77f95"
|
||||
integrity sha512-jBDboWM8qpaqwkMwItqTQTiFikhs/67OYVvblFFTM7MrZjt6yMKd6r2kgXizEbTTljacm4NldIlZnhbjr84QYg==
|
||||
postcss@^8.1.6, postcss@^8.3.0, postcss@^8.4.5, postcss@^8.4.7:
|
||||
version "8.4.8"
|
||||
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.8.tgz#dad963a76e82c081a0657d3a2f3602ce10c2e032"
|
||||
integrity sha512-2tXEqGxrjvAO6U+CJzDL2Fk2kPHTv1jQsYkSoMeOis2SsYaXRO2COxTdQp99cYvif9JTXaAk9lYGc3VhJt7JPQ==
|
||||
dependencies:
|
||||
nanoid "^3.1.30"
|
||||
nanoid "^3.3.1"
|
||||
picocolors "^1.0.0"
|
||||
source-map-js "^1.0.1"
|
||||
source-map-js "^1.0.2"
|
||||
|
||||
prebuild-install@^7.0.0:
|
||||
version "7.0.0"
|
||||
@ -11125,10 +11166,10 @@ react-router@5.2.1, react-router@^5.2.0:
|
||||
tiny-invariant "^1.0.2"
|
||||
tiny-warning "^1.0.0"
|
||||
|
||||
react-select-event@^5.1.0:
|
||||
version "5.1.0"
|
||||
resolved "https://registry.yarnpkg.com/react-select-event/-/react-select-event-5.1.0.tgz#d45ef68f2a9c872903e8c9725f3ae6e7576f7be0"
|
||||
integrity sha512-D5DzJlYCdZsGbDVFMQFynrG0OLalJM3ZzDT7KQADNVWE604JCeQF9bIuvPZqVD7IzhnPsFzOUCsilzDA6w6WRQ==
|
||||
react-select-event@^5.3.0:
|
||||
version "5.3.0"
|
||||
resolved "https://registry.yarnpkg.com/react-select-event/-/react-select-event-5.3.0.tgz#4548fffd615a47176951cbb301ee21a0c60b582a"
|
||||
integrity sha512-Novkl7X9JJKmDV5LyYaKwl0vffWtqPrBa1vuI0v43P/f87mSA7JfdYxU93SFb99RssphVzBSIAbcnbX1w21QIQ==
|
||||
dependencies:
|
||||
"@testing-library/dom" ">=7"
|
||||
|
||||
@ -11281,7 +11322,7 @@ read@1, read@~1.0.1, read@~1.0.7:
|
||||
dependencies:
|
||||
mute-stream "~0.0.4"
|
||||
|
||||
"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.6, readable-stream@^2.3.7, readable-stream@~2.3.6:
|
||||
"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.6, readable-stream@~2.3.6:
|
||||
version "2.3.7"
|
||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"
|
||||
integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==
|
||||
@ -11708,6 +11749,11 @@ safe-regex@^1.1.0:
|
||||
dependencies:
|
||||
ret "~0.1.10"
|
||||
|
||||
safe-stable-stringify@^2.3.1:
|
||||
version "2.3.1"
|
||||
resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.3.1.tgz#ab67cbe1fe7d40603ca641c5e765cb942d04fc73"
|
||||
integrity sha512-kYBSfT+troD9cDA85VDnHZ1rpHC50O0g1e6WlGHVCz/g+JS+9WKLj+XwFYyR8UbrZN8ll9HUpDAAddY58MGisg==
|
||||
|
||||
"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@^2.1.2, safer-buffer@~2.1.0:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
|
||||
@ -11735,10 +11781,10 @@ sanitize-filename@^1.6.2, sanitize-filename@^1.6.3:
|
||||
dependencies:
|
||||
truncate-utf8-bytes "^1.0.0"
|
||||
|
||||
sass-loader@^12.4.0:
|
||||
version "12.4.0"
|
||||
resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-12.4.0.tgz#260b0d51a8a373bb8e88efc11f6ba5583fea0bcf"
|
||||
integrity sha512-7xN+8khDIzym1oL9XyS6zP6Ges+Bo2B2xbPrjdMHEYyV3AQYhd/wXeru++3ODHF0zMjYmVadblSKrPrjEkL8mg==
|
||||
sass-loader@^12.6.0:
|
||||
version "12.6.0"
|
||||
resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-12.6.0.tgz#5148362c8e2cdd4b950f3c63ac5d16dbfed37bcb"
|
||||
integrity sha512-oLTaH0YCtX4cfnJZxKSLAyglED0naiYfNG1iXfU5w1LNZ+ukoA5DtyDIN5zmKVZwYNJP4KRc5Y3hkWga+7tYfA==
|
||||
dependencies:
|
||||
klona "^2.0.4"
|
||||
neo-async "^2.6.2"
|
||||
@ -12178,11 +12224,16 @@ source-list-map@^2.0.1:
|
||||
resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34"
|
||||
integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==
|
||||
|
||||
"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.1:
|
||||
"source-map-js@>=0.6.2 <2.0.0":
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.1.tgz#a1741c131e3c77d048252adfa24e23b908670caf"
|
||||
integrity sha512-4+TN2b3tqOCd/kaGRJ/sTYA0tR0mdXx26ipdolxcwtJVqEnqNYvlCAt1q3ypy4QMlYus+Zh34RNtYLoq2oQ4IA==
|
||||
|
||||
source-map-js@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
|
||||
integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==
|
||||
|
||||
source-map-resolve@^0.5.0, source-map-resolve@^0.5.2:
|
||||
version "0.5.3"
|
||||
resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a"
|
||||
@ -13029,7 +13080,7 @@ tree-kill@^1.2.2:
|
||||
resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc"
|
||||
integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==
|
||||
|
||||
triple-beam@^1.2.0, triple-beam@^1.3.0:
|
||||
triple-beam@^1.3.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/triple-beam/-/triple-beam-1.3.0.tgz#a595214c7298db8339eeeee083e4d10bd8cb8dd9"
|
||||
integrity sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==
|
||||
@ -13718,40 +13769,10 @@ webpack-sources@^3.2.3:
|
||||
resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde"
|
||||
integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==
|
||||
|
||||
webpack@^5:
|
||||
version "5.67.0"
|
||||
resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.67.0.tgz#cb43ca2aad5f7cc81c4cd36b626e6b819805dbfd"
|
||||
integrity sha512-LjFbfMh89xBDpUMgA1W9Ur6Rn/gnr2Cq1jjHFPo4v6a79/ypznSYbAyPgGhwsxBtMIaEmDD1oJoA7BEYw/Fbrw==
|
||||
dependencies:
|
||||
"@types/eslint-scope" "^3.7.0"
|
||||
"@types/estree" "^0.0.50"
|
||||
"@webassemblyjs/ast" "1.11.1"
|
||||
"@webassemblyjs/wasm-edit" "1.11.1"
|
||||
"@webassemblyjs/wasm-parser" "1.11.1"
|
||||
acorn "^8.4.1"
|
||||
acorn-import-assertions "^1.7.6"
|
||||
browserslist "^4.14.5"
|
||||
chrome-trace-event "^1.0.2"
|
||||
enhanced-resolve "^5.8.3"
|
||||
es-module-lexer "^0.9.0"
|
||||
eslint-scope "5.1.1"
|
||||
events "^3.2.0"
|
||||
glob-to-regexp "^0.4.1"
|
||||
graceful-fs "^4.2.9"
|
||||
json-parse-better-errors "^1.0.2"
|
||||
loader-runner "^4.2.0"
|
||||
mime-types "^2.1.27"
|
||||
neo-async "^2.6.2"
|
||||
schema-utils "^3.1.0"
|
||||
tapable "^2.1.1"
|
||||
terser-webpack-plugin "^5.1.3"
|
||||
watchpack "^2.3.1"
|
||||
webpack-sources "^3.2.3"
|
||||
|
||||
webpack@^5.69.0:
|
||||
version "5.69.0"
|
||||
resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.69.0.tgz#c9eb607d4f6c49f1e5755492323a7b055c3450e3"
|
||||
integrity sha512-E5Fqu89Gu8fR6vejRqu26h8ld/k6/dCVbeGUcuZjc+goQHDfCPU9rER71JmdtBYGmci7Ec2aFEATQ2IVXKy2wg==
|
||||
webpack@^5, webpack@^5.1.0, webpack@^5.69.0:
|
||||
version "5.70.0"
|
||||
resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.70.0.tgz#3461e6287a72b5e6e2f4872700bc8de0d7500e6d"
|
||||
integrity sha512-ZMWWy8CeuTTjCxbeaQI21xSswseF2oNOwc70QSKNePvmxE7XW36i7vpBMYZFAUHPwQiEbNGCEYIOOlyRbdGmxw==
|
||||
dependencies:
|
||||
"@types/eslint-scope" "^3.7.3"
|
||||
"@types/estree" "^0.0.51"
|
||||
@ -13762,7 +13783,7 @@ webpack@^5.69.0:
|
||||
acorn-import-assertions "^1.7.6"
|
||||
browserslist "^4.14.5"
|
||||
chrome-trace-event "^1.0.2"
|
||||
enhanced-resolve "^5.9.0"
|
||||
enhanced-resolve "^5.9.2"
|
||||
es-module-lexer "^0.9.0"
|
||||
eslint-scope "5.1.1"
|
||||
events "^3.2.0"
|
||||
@ -13904,28 +13925,30 @@ winston-transport-browserconsole@^1.0.5:
|
||||
winston "^3.2.1"
|
||||
winston-transport "^4.3.0"
|
||||
|
||||
winston-transport@^4.3.0, winston-transport@^4.4.0:
|
||||
version "4.4.0"
|
||||
resolved "https://registry.yarnpkg.com/winston-transport/-/winston-transport-4.4.0.tgz#17af518daa690d5b2ecccaa7acf7b20ca7925e59"
|
||||
integrity sha512-Lc7/p3GtqtqPBYYtS6KCN3c77/2QCev51DvcJKbkFPQNoj1sinkGwLGFDxkXY9J6p9+EPnYs+D90uwbnaiURTw==
|
||||
winston-transport@^4.3.0, winston-transport@^4.5.0:
|
||||
version "4.5.0"
|
||||
resolved "https://registry.yarnpkg.com/winston-transport/-/winston-transport-4.5.0.tgz#6e7b0dd04d393171ed5e4e4905db265f7ab384fa"
|
||||
integrity sha512-YpZzcUzBedhlTAfJg6vJDlyEai/IFMIVcaEZZyl3UXIl4gmqRpU7AE89AHLkbzLUsv0NVmw7ts+iztqKxxPW1Q==
|
||||
dependencies:
|
||||
readable-stream "^2.3.7"
|
||||
triple-beam "^1.2.0"
|
||||
logform "^2.3.2"
|
||||
readable-stream "^3.6.0"
|
||||
triple-beam "^1.3.0"
|
||||
|
||||
winston@^3.2.1, winston@^3.3.3:
|
||||
version "3.3.3"
|
||||
resolved "https://registry.yarnpkg.com/winston/-/winston-3.3.3.tgz#ae6172042cafb29786afa3d09c8ff833ab7c9170"
|
||||
integrity sha512-oEXTISQnC8VlSAKf1KYSSd7J6IWuRPQqDdo8eoRNaYKLvwSb5+79Z3Yi1lrl6KDpU6/VWaxpakDAtb1oQ4n9aw==
|
||||
winston@^3.2.1, winston@^3.6.0:
|
||||
version "3.6.0"
|
||||
resolved "https://registry.yarnpkg.com/winston/-/winston-3.6.0.tgz#be32587a099a292b88c49fac6fa529d478d93fb6"
|
||||
integrity sha512-9j8T75p+bcN6D00sF/zjFVmPp+t8KMPB1MzbbzYjeN9VWxdsYnTB40TkbNUEXAmILEfChMvAMgidlX64OG3p6w==
|
||||
dependencies:
|
||||
"@dabh/diagnostics" "^2.0.2"
|
||||
async "^3.1.0"
|
||||
async "^3.2.3"
|
||||
is-stream "^2.0.0"
|
||||
logform "^2.2.0"
|
||||
logform "^2.4.0"
|
||||
one-time "^1.0.0"
|
||||
readable-stream "^3.4.0"
|
||||
safe-stable-stringify "^2.3.1"
|
||||
stack-trace "0.0.x"
|
||||
triple-beam "^1.3.0"
|
||||
winston-transport "^4.4.0"
|
||||
winston-transport "^4.5.0"
|
||||
|
||||
word-wrap@^1.2.3, word-wrap@~1.2.3:
|
||||
version "1.2.3"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user