Export-Source-Commit: 754d2f1b0238a99b266936a27562314c9c74f85b
This commit is contained in:
Ryan Clark
2026-08-25 11:35:10 -05:00
committed by Gus Rivera
parent 6dbccdd8ad
commit 89ba17b08f
275 changed files with 896 additions and 4059 deletions
-20
View File
@@ -1,20 +0,0 @@
/**
* Teleport
* Copyright (C) 2024 Gravitational, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import eslint from './web/packages/build/eslint.config.mjs';
export default eslint;
+201
View File
@@ -0,0 +1,201 @@
{
"$schema": "https://unpkg.com/knip@6/schema.json",
"workspaces": {
".": {
"entry": [
// Required from an inline actions/github-script block in docs-amplify.yaml.
".github/scripts/post-preview-links.js",
// Run by `node --test`.
".github/scripts/test-*.js",
"web/.storybook/main.mts",
"web/.storybook/preview.tsx",
"web/.storybook/vite.config.mts",
"web/.storybook/mocks/**/*.{ts,tsx,js}"
],
"project": [
".github/scripts/**/*.js",
"web/.storybook/**/*.{ts,tsx,mts,mjs,js}",
"web/@types/*.d.ts"
],
"ignoreDependencies": [
// Only the generated gRPC clients under gen/proto/ts reference this, and those are not
// part of any project.
"@grpc/grpc-js",
// Referenced from a JSDoc type annotation in jest.config.js.
"@jest/types",
// Named as a string in web/.storybook/main.mts `addons`.
"@storybook/addon-vitest",
// Loaded by name through `jsPlugins` in .oxlintrc.jsonc. The plugins pull in eslint
// itself at runtime via @typescript-eslint/utils.
"eslint",
"eslint-plugin-jest-dom",
"eslint-plugin-storybook",
"eslint-plugin-testing-library",
"eslint-plugin-unused-imports"
]
},
"web/packages/build": {
"entry": [
"vite/**/*.{ts,mts,mjs}",
"jest/**/*.{ts,js}",
"storybook.ts",
"**/*.test.{ts,tsx,js,jsx}"
],
// These are loaded by name through the SWC plugin config in vite/react.mjs.
"ignoreDependencies": [
"@gravitational/swc-plugin-jsx-marker",
"@gravitational/swc-plugin-styled-components",
"@swc/core"
]
},
"web/packages/design": {
"entry": [
"src/**/*.story.{ts,tsx,js,jsx}",
"src/**/*.test.{ts,tsx,js,jsx}"
],
"project": ["src/**/*.{ts,tsx,js,jsx,mts,mjs}", "@types/*.d.ts"],
"paths": {
"design/*": ["./src/*"],
"design": ["./src/"]
},
// Compile-time only assertions, checked by tsc rather than imported anywhere.
"ignore": ["src/**/*.type-test.tsx"]
},
"web/packages/shared": {
"entry": ["**/*.story.{ts,tsx,js,jsx}", "**/*.test.{ts,tsx,js,jsx}"],
"project": ["**/*.{ts,tsx,js,jsx,mts,mjs}", "!libs/ironrdp/pkg/**"],
"paths": {
"design/*": ["../design/src/*"],
"design": ["../design/src/"],
"shared/*": ["./*"],
"gen-proto-ts/*": ["../../../gen/proto/ts/*"]
},
"ignoreDependencies": [
// Reached through the `design/*` path alias rather than the package name.
"@gravitational/design",
// Browser polyfill, imported as the bare Node builtin specifier.
"events"
]
},
"web/packages/teleport": {
"entry": [
"babel.config.js",
"index.html",
"src/**/*.story.{ts,tsx,js,jsx}",
"src/**/*.test.{ts,tsx,js,jsx}",
"src/services/audit/gen-event-reference/index.ts"
],
"project": ["src/**/*.{ts,tsx,js,jsx,mts,mjs}"],
"paths": {
"build/*": ["../build/*"],
"shared/*": ["../shared/*"],
"design/*": ["../design/src/*"],
"design": ["../design/src/"],
"teleport/*": ["./src/*"],
"teleport": ["./src/"],
"teleterm/*": ["../teleterm/src/*"],
"e-teleport/*": ["../../../e/web/teleport/src/*"],
"gen-proto-ts/*": ["../../../gen/proto/ts/*"]
},
"ignoreDependencies": [
// Reached through path aliases rather than package names.
"@gravitational/design",
"@gravitational/shared",
"@gravitational/teleport",
// Declared here but named as strings by web/packages/build/.babelrc.js.
"babel-plugin-transform-import-meta",
"babel-plugin-transform-vite-meta-env",
"babel-plugin-transform-vite-meta-hot",
// Browser polyfill, imported as the bare Node builtin specifier.
"events"
]
},
"web/packages/teleterm": {
"entry": [
"electron-builder-config.js",
"index.html",
"src/main.ts",
"src/preload.ts",
"src/sharedProcess/sharedProcess.ts",
"src/agentCleanupDaemon/agentCleanupDaemon.js",
"src/**/*.story.{ts,tsx,js,jsx}",
"src/**/*.test.{ts,tsx,js,jsx}",
"src/**/*Process.mjs"
],
"project": ["src/**/*.{ts,tsx,js,jsx,mts,mjs}", "csp.ts"],
"paths": {
"build/*": ["../build/*"],
"shared/*": ["../shared/*"],
"design/*": ["../design/src/*"],
"design": ["../design/src/"],
"teleport/*": ["../teleport/src/*"],
"teleterm/*": ["./src/*"],
"gen-proto-ts/*": ["../../../gen/proto/ts/*"]
},
"ignoreDependencies": [
// Reached through path aliases rather than package names.
"@gravitational/design",
"@gravitational/shared",
// Browser polyfill, imported as the bare Node builtin specifier.
"events"
]
},
"e/web/teleport": {
"entry": [
"babel.config.js",
"index.html",
"src/**/*.story.{ts,tsx,js,jsx}",
"src/**/*.test.{ts,tsx,js,jsx}",
"src/SessionRecordings/summary/generate-mitre-lookup.ts"
],
"project": ["src/**/*.{ts,tsx,js,jsx,mts,mjs}"],
// Generated protobuf bindings.
"ignore": ["src/**/*_pb.ts", "src/**/*_pb.*.ts"],
"paths": {
"build/*": ["../../../web/packages/build/*"],
"shared/*": ["../../../web/packages/shared/*"],
"design/*": ["../../../web/packages/design/src/*"],
"design": ["../../../web/packages/design/src/"],
"teleport/*": ["../../../web/packages/teleport/src/*"],
"teleport": ["../../../web/packages/teleport/src/"],
"teleterm/*": ["../../../web/packages/teleterm/src/*"],
"e-teleport/*": ["./src/*"],
"gen-proto-ts/*": ["../../../gen/proto/ts/*"]
}
},
"e2e": {
"entry": ["tests/**/*.{ts,tsx}", "scripts/**/*.{ts,tsx,js,mjs}"],
"project": [
"{tests,scripts,helpers,aws,config,node,runner}/**/*.{ts,tsx,js,mjs}"
]
},
"e/e2e": {
"entry": ["tests/**/*.{ts,tsx}"],
"project": ["{tests,helpers,config}/**/*.{ts,tsx,js,mjs}"]
}
},
"ignoreBinaries": [
// Run by .github/workflows/doc-tests.yaml against the separately checked out docs site.
"spellcheck",
"markdown-lint",
"build",
"make"
],
"ignoreUnresolved": [
// Provided transitively by jest.
"babel-jest",
// Declared by web/packages/teleport but named as strings by web/packages/build/.babelrc.js.
"babel-plugin-transform-import-meta",
"babel-plugin-transform-vite-meta-env",
"babel-plugin-transform-vite-meta-hot",
// Deliberately missing, so the code under test has to handle a failed spawn.
"./testProcess-nonExistent.js",
"./testProcess-nonExisting.mjs"
],
"rules": {
// The design system deliberately keeps deprecated aliases (OutlineDanger, the FieldSelect
// default export) alongside their replacements.
"duplicates": "off",
"enumMembers": "off"
}
}
+3 -3
View File
@@ -13,13 +13,12 @@
"package-term": "pnpm --filter=@gravitational/teleterm package",
"storybook": "./web/scripts/run-storybook.sh",
"storybook-smoke-test": "storybook dev -p 9002 -c web/.storybook --ci --smoke-test",
"test-storybook": "NODE_TLS_REJECT_UNAUTHORIZED=0 test-storybook -c web/.storybook --url https://localhost:9002 --skipTags=skip-test --browsers=chromium",
"test": "jest",
"test-coverage": "jest --coverage && web/scripts/print-coverage-link.sh",
"test-update-snapshot": "pnpm run test --updateSnapshot",
"tdd": "jest --watch",
"oxlint": "oxlint --quiet --report-unused-disable-directives-severity error web/ e/ e2e/",
"lint": "pnpm oxlint && pnpm format-check",
"lint": "pnpm oxlint && pnpm format-check && pnpm knip",
"lint-fix": "pnpm oxlint --fix && pnpm format",
"type-check": "tsc --build",
"optimize-resource-icons": "svgo --multipass --quiet -rf web/packages/design/src/ResourceIcon/assets",
@@ -27,6 +26,7 @@
"format": "oxfmt 'e/**/*.{ts,tsx,js,jsx,mts}' 'web/**/*.{ts,tsx,js,jsx,mts}' 'e2e/**/*.{ts,tsx,js,jsx,mts}'",
"process-icons": "pnpm optimize-resource-icons && node web/packages/design/src/Icon/script/script.js && pnpm oxfmt 'web/packages/design/src/Icon/**/*.tsx'",
"list-installed-versions": "./web/scripts/list-installed-versions.sh",
"knip": "KNIP=1 knip",
"postinstall": "pnpm generate-theme",
"generate-theme": "design-system typegen",
"nop": "exit 0"
@@ -46,7 +46,6 @@
"@types/react-dom": "^19.2.4",
"@types/react-transition-group": "^4.4.12",
"@types/wicg-file-system-access": "^2023.10.7",
"cspell": "^10.0.1",
"eslint": "^10.8.1",
"eslint-plugin-jest-dom": "^5.10.1",
"eslint-plugin-storybook": "^10.5.7",
@@ -54,6 +53,7 @@
"eslint-plugin-unused-imports": "^4.4.1",
"jest": "^30.4.2",
"jsdom-testing-mocks": "^1.16.0",
"knip": "^6.32.2",
"msw": "2.15.0",
"msw-storybook-addon": "^3.0.0",
"oxfmt": "^0.62.0",
+338 -748
View File
File diff suppressed because it is too large Load Diff
-2
View File
@@ -2,7 +2,6 @@
"name": "@gravitational/build",
"version": "1.0.1",
"description": "build scripts",
"main": "index.js",
"license": "Apache-2.0",
"repository": {
"type": "git",
@@ -19,7 +18,6 @@
"@swc/core": "^1.15.47",
"@vitejs/plugin-react-swc": "^4.3.3",
"babel-plugin-styled-components": "^2.3.0",
"globals": "^17.9.0",
"jest-environment-jsdom": "^30.4.1",
"jest-fail-on-console": "^3.3.4",
"rollup-plugin-visualizer": "^7.0.1",
+6 -2
View File
@@ -40,9 +40,13 @@ export function createViteConfig(
outputDirectory: string
) {
return defineConfig(({ mode }) => {
// Knip loads this config to enumerate entry points. There is no dev server in that context, so
// skip the parts of the setup it cannot satisfy: the proxy target notice and the certificates.
const isKnip = !!process.env.KNIP;
let target = resolveTargetURL(process.env.PROXY_TARGET);
if (mode === 'development') {
if (mode === 'development' && !isKnip) {
if (process.env.PROXY_TARGET) {
// eslint-disable-next-line no-console
console.log(
@@ -213,7 +217,7 @@ export function createViteConfig(
key: readFileSync(process.env.VITE_HTTPS_KEY),
cert: readFileSync(process.env.VITE_HTTPS_CERT),
};
} else {
} else if (!isKnip) {
const certsDirectory = resolve(rootDirectory, 'web/certs');
if (!existsSync(certsDirectory)) {
-2
View File
@@ -2,7 +2,6 @@
"name": "@gravitational/design",
"version": "1.0.0",
"description": "Gravitational design system",
"main": "index.js",
"license": "Apache-2.0",
"repository": {
"type": "git",
@@ -15,7 +14,6 @@
"styled-system": "^5.1.5"
},
"devDependencies": {
"@gravitational/build": "workspace:*",
"@types/styled-system": "^5.1.25",
"jest-styled-components": "^7.4.0"
}
+1 -1
View File
@@ -116,7 +116,7 @@ export const Button = <E extends React.ElementType = 'button'>({
Button.displayName = 'Button';
export type ThemedButtonProps<E extends React.ElementType> = ButtonProps<E> & {
type ThemedButtonProps<E extends React.ElementType> = ButtonProps<E> & {
theme: Theme;
fill: ButtonFill;
intent: ButtonIntent;
@@ -22,7 +22,6 @@ import CardError, {
LoginFailed,
LogoutFailed,
NotFound,
Offline,
BadRequest,
} from './CardError';
@@ -32,7 +31,6 @@ export {
LoginFailed,
AccessDenied,
NotFound,
Offline,
LogoutFailed,
BadRequest,
};
+1 -1
View File
@@ -122,7 +122,7 @@ type TableColumnBase<T> = {
export type PagerPosition = 'top' | 'bottom' | 'both';
export type PaginationConfig<T> = {
type PaginationConfig<T> = {
pageSize?: number;
/**
* "undefined" will show both pagers if data on current page is some
+1 -22
View File
@@ -240,28 +240,7 @@ export const Primary = (props: LabelPropsWithoutKind) => (
/** @deprecated Use `<Tag>` from `design/Tag`. */
export const Secondary = (props: LabelPropsWithoutKind) => (
<Label kind="secondary" {...props} />
);
/** @deprecated Use `<Status kind="warning" variant="filled">` from `design/Status`. */
export const Warning = (props: LabelPropsWithoutKind) => (
<Label kind="warning" {...props} />
);
/** @deprecated Use `<Status kind="danger" variant="filled">` from `design/Status`. */
export const Danger = (props: LabelPropsWithoutKind) => (
<Label kind="danger" {...props} />
);
/** @deprecated Use `<Tag variant="outline">` from `design/Tag`. */
); /** @deprecated Use `<Tag variant="outline">` from `design/Tag`. */
export const SecondaryOutlined = (props: LabelPropsWithoutKind) => (
<Label kind="outline-secondary" {...props} />
);
/** @deprecated Use `<Status kind="success">` from `design/Status`. */
export const SuccessOutlined = (props: LabelPropsWithoutKind) => (
<Label kind="outline-success" {...props} />
);
/** @deprecated Use `<Status kind="warning">` from `design/Status`. */
export const WarningOutlined = (props: LabelPropsWithoutKind) => (
<Label kind="outline-warning" {...props} />
);
/** @deprecated Use `<Status kind="danger">` from `design/Status`. */
export const DangerOutlined = (props: LabelPropsWithoutKind) => (
<Label kind="outline-danger" {...props} />
);
+1 -12
View File
@@ -16,18 +16,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import Label, {
Danger,
DangerOutlined,
Primary,
Secondary,
SecondaryOutlined,
SuccessOutlined,
Warning,
WarningOutlined,
} from './Label';
import Label from './Label';
export default Label;
export { Primary, Secondary, Warning, Danger };
export { SecondaryOutlined, SuccessOutlined, WarningOutlined, DangerOutlined };
export type { LabelKind } from './Label';
@@ -30,12 +30,7 @@ import {
} from 'design/system';
import { Theme } from 'design/theme/themes/types';
export type LabelKind =
| 'primary'
| 'secondary'
| 'warning'
| 'danger'
| 'success';
type LabelKind = 'primary' | 'secondary' | 'warning' | 'danger' | 'success';
interface KindsProps {
kind?: LabelKind;
@@ -106,18 +101,3 @@ const LabelState = styled.span<LabelStateProps>`
`;
export default LabelState;
type LabelStatePropsWithoutKind = Omit<LabelStateProps, 'kind'>;
export const StateDanger = (props: LabelStatePropsWithoutKind) => (
<LabelState kind="danger" {...props} />
);
export const StateInfo = (props: LabelStatePropsWithoutKind) => (
<LabelState kind="secondary" {...props} />
);
export const StateWarning = (props: LabelStatePropsWithoutKind) => (
<LabelState kind="warning" {...props} />
);
export const StateSuccess = (props: LabelStatePropsWithoutKind) => (
<LabelState kind="success" {...props} />
);
+1 -8
View File
@@ -16,13 +16,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import LabelState, {
StateDanger,
StateInfo,
StateSuccess,
StateWarning,
} from './LabelState';
import LabelState from './LabelState';
export default LabelState;
export { StateDanger, StateInfo, StateWarning, StateSuccess };
+3 -3
View File
@@ -56,8 +56,8 @@ export type Origin = {
vertical: VerticalOrigin;
};
export type HorizontalOrigin = 'left' | 'center' | 'right' | number;
export type VerticalOrigin = 'top' | 'center' | 'bottom' | number;
type HorizontalOrigin = 'left' | 'center' | 'right' | number;
type VerticalOrigin = 'top' | 'center' | 'bottom' | number;
export type GrowDirections = 'top-left' | 'bottom-right';
export type Position = 'top' | 'right' | 'bottom' | 'left';
@@ -437,7 +437,7 @@ function getArrowPaddingProp(
}
}
export class Popover extends Component<Props> {
class Popover extends Component<Props> {
paperRef = createRef<HTMLDivElement>();
handleResize: () => void = () => {};
-3
View File
@@ -21,10 +21,7 @@ import Popover from './Popover';
export {
StyledPopover,
type Origin,
type HorizontalOrigin,
type VerticalOrigin,
type GrowDirections,
type Position as Orientation,
type Props as PopoverProps,
} from './Popover';
@@ -152,7 +152,7 @@ function indicatorSize(props: { rbSize?: RadioButtonSize }) {
return { width: s, height: s };
}
export const RadioButtonInternal = styled.input.attrs({ type: 'radio' })<{
const RadioButtonInternal = styled.input.attrs({ type: 'radio' })<{
rbSize?: RadioButtonSize;
}>`
appearance: none;
-3
View File
@@ -17,6 +17,3 @@
*/
export { TeleportGearIcon } from './TeleportGearIcon';
export { SVGIcon } from './SVGIcon';
export type { SVGIconProps } from './common';
+1 -2
View File
@@ -18,5 +18,4 @@
export { Status } from './Status';
export { StatusDot } from './StatusDot';
export type { StatusDotProps } from './StatusDot';
export type { StatusKind, StatusVariant, StatusProps } from './Status';
export type { StatusKind, StatusVariant } from './Status';
+1 -1
View File
@@ -24,7 +24,7 @@ import { space, type SpaceProps } from 'design/system';
import { pillBase } from '../pillStyles';
export type TagVariant = 'subtle' | 'outline';
type TagVariant = 'subtle' | 'outline';
export interface TagProps
extends SpaceProps, Omit<HTMLAttributes<HTMLSpanElement>, 'color'> {
-1
View File
@@ -17,4 +17,3 @@
*/
export { Tag } from './Tag';
export type { TagVariant, TagProps } from './Tag';
@@ -67,7 +67,7 @@ export interface TextAreaProps
onClick?: React.MouseEventHandler<HTMLInputElement>;
}
export const textAreaGeometry: {
const textAreaGeometry: {
[s in TextAreaSize]: {
height: number;
typography: keyof Theme['typography'];
@@ -86,11 +86,6 @@ export function displayDateWithPrefixedTime(date: Date): string {
}
}
/** Converts a UNIX timestamp do the Date object. */
export function unixTimestampToDate(seconds: number): Date {
return new Date(seconds * 1000);
}
function dateToUtc(date: Date): Date {
return new Date(date.getTime() + date.getTimezoneOffset() * 60 * 1000);
}
+1 -7
View File
@@ -40,17 +40,15 @@ import { Indicator } from './Indicator';
import Input from './Input';
import Label from './Label';
import { LabelInput } from './LabelInput';
import LabelState from './LabelState';
import Link from './Link';
import { Mark } from './Mark';
import Menu, { MenuItem, MenuItemIcon } from './Menu';
import Menu, { MenuItem } from './Menu';
import { Pill } from './Pill';
import Popover from './Popover';
import { ResourceIcon } from './ResourceIcon';
import { Status } from './Status';
import { StepSlider } from './StepSlider';
import { SyncStamp } from './SyncStamp';
import { Tag } from './Tag';
import Text, {
H1,
H2,
@@ -98,12 +96,10 @@ export {
Input,
Label,
LabelInput,
LabelState,
Link,
Mark,
Menu,
MenuItem,
MenuItemIcon,
P1,
P2,
P3,
@@ -115,13 +111,11 @@ export {
Subtitle2,
Subtitle3,
SyncStamp,
Tag,
Text,
TextArea,
Toggle,
TopNav,
};
export type { TextAreaProps } from './TextArea';
export * from './keyframes';
export { Stack } from './Flex';
export { breakpointsPx } from './theme';
-8
View File
@@ -42,7 +42,6 @@ import {
minWidth,
overflow,
ResponsiveValue,
size,
space,
style,
textAlign,
@@ -51,7 +50,6 @@ import {
type AlignItemsProps,
type AlignSelfProps,
type BackgroundProps,
type BorderColorProps,
type BorderProps,
type BorderRadiusProps,
type BordersProps,
@@ -61,7 +59,6 @@ import {
type FlexProps,
type FlexWrapProps,
type FontSizeProps,
type FontWeightProps,
type HeightProps,
type JustifyContentProps,
type JustifySelfProps,
@@ -71,7 +68,6 @@ import {
type MinHeightProps,
type MinWidthProps,
type OverflowProps,
type SizeProps,
type SpaceProps,
type TextAlignProps,
type WidthProps,
@@ -144,7 +140,6 @@ export {
border,
type BorderProps,
borderColor,
type BorderColorProps,
borders,
type BordersProps,
borderRadius,
@@ -162,7 +157,6 @@ export {
fontSize,
type FontSizeProps,
fontWeight,
type FontWeightProps,
height,
type HeightProps,
justifyContent,
@@ -181,8 +175,6 @@ export {
type MinWidthProps,
overflow,
type OverflowProps,
size,
type SizeProps,
space,
type SpaceProps,
textAlign,
@@ -63,7 +63,7 @@ function clamp(value, min = 0, max = 1) {
* @param {string} color - Hex color, i.e. #nnn or #nnnnnn
* @returns {string} A CSS rgb color string
*/
export function convertHexToRGB(color) {
function convertHexToRGB(color) {
color = color.substr(1);
const re = new RegExp(`.{1,${color.length / 3}}`, 'g');
@@ -76,28 +76,6 @@ export function convertHexToRGB(color) {
return colors ? `rgb(${colors.map(n => parseInt(n, 16)).join(', ')})` : '';
}
/**
* Converts a color from CSS rgb format to CSS hex format.
*
* @param {string} color - RGB color, i.e. rgb(n, n, n)
* @returns {string} A CSS rgb color string, i.e. #nnnnnn
*/
export function rgbToHex(color) {
// Pass hex straight through
if (color.indexOf('#') === 0) {
return color;
}
function intToHex(c) {
const hex = c.toString(16);
return hex.length === 1 ? `0${hex}` : hex;
}
let { values } = decomposeColor(color);
values = values.map(n => intToHex(n));
return `#${values.join('')}`;
}
/**
* Returns an object with the type and values of a color.
*
@@ -142,7 +120,7 @@ export function decomposeColor(color) {
* @param {array} color.values - [n,n,n] or [n,n,n,n]
* @returns {string} A CSS color string
*/
export function recomposeColor(color) {
function recomposeColor(color) {
const { type } = color;
let { values } = color;
@@ -183,7 +161,7 @@ export function getContrastRatio(foreground, background) {
* @param {string} color - CSS color, i.e. one of: #nnn, #nnnnnn, rgb(), rgba(), hsl(), hsla()
* @returns {number} The relative brightness of the color in the range 0 - 1
*/
export function getLuminance(color) {
function getLuminance(color) {
const decomposedColor = decomposeColor(color);
if (decomposedColor.type.indexOf('rgb') !== -1) {
@@ -243,7 +221,7 @@ export function darken(color, coefficient) {
* @param {number} coefficient - multiplier in the range 0 - 1
* @returns {string} A CSS color string. Hex input values are returned as rgb
*/
export function lighten(color, coefficient) {
function lighten(color, coefficient) {
color = decomposeColor(color);
coefficient = clamp(coefficient);
@@ -45,8 +45,6 @@ export default function renderHook<T extends (...args: any) => any>(
return result;
}
export { act };
// A wrapper to execute hooks during renderer process
function TestHook(props: any) {
// trigger hooks and assessing its results to props.current
@@ -26,7 +26,6 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import {
act,
fireEvent,
getByTestId,
prettyDOM,
screen,
render as testingRender,
@@ -234,9 +233,7 @@ export {
testThemeSystem,
tick,
render,
prettyDOM,
waitFor,
getByTestId,
MemoryRouter as Router,
userEvent,
waitForElementToBeRemoved,
@@ -17,4 +17,3 @@
*/
export { RequestDelete } from './RequestDelete';
export type { RequestDeleteProps } from './RequestDelete';
@@ -391,7 +391,7 @@ export function RequestorTimestamp({
);
}
export function Timestamp({
function Timestamp({
author,
display,
state,
@@ -533,9 +533,3 @@ export const requestRoleEmpty: AccessRequest = {
roles: ['empty-values'],
id: 'ffc11a95-e8af-581c-ba82-47c429c841e8',
};
export const requests = [
requestRolePending,
requestRoleDenied,
requestRoleApproved,
];
@@ -1,124 +0,0 @@
/**
* Teleport
* Copyright (C) 2023 Gravitational, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { useEffect, useMemo, useRef, useState } from 'react';
import {
KeywordHighlight,
SelectedLines,
TerminalContent,
} from 'shared/components/AnimatedTerminal/TerminalContent';
import { Window } from 'shared/components/Window';
import { BufferEntry, createTerminalContent, TerminalLine } from './content';
interface AnimatedTerminalProps {
lines: TerminalLine[];
startDelay?: number;
highlights?: KeywordHighlight[];
selectedLines?: SelectedLines;
stopped?: boolean;
onCompleted?: () => void;
}
export function AnimatedTerminal(props: AnimatedTerminalProps) {
const lastLineIndex = useRef(0);
const content = useMemo(
() => createTerminalContent(props.lines, lastLineIndex.current),
[props.lines]
);
const [counter, setCounter] = useState(0);
const [completed, setCompletedState] = useState(false);
const lines = useRef<BufferEntry[]>([]);
function setCompleted(completed: boolean) {
setCompletedState(completed);
if (completed) {
props.onCompleted && props.onCompleted();
}
}
useEffect(() => {
let timeout: number;
let request: number;
async function animate() {
const { value, done } = await content.next();
if (value) {
if (value.length) {
const nextLineIndex = value[value.length - 1].id + 1;
if (nextLineIndex > lastLineIndex.current) {
lastLineIndex.current = nextLineIndex;
}
}
lines.current = value;
setCounter(count => count + 1);
}
if (done) {
setCompleted(true);
setCounter(count => count + 1);
return;
}
request = requestAnimationFrame(animate);
}
function setup() {
request = requestAnimationFrame(animate);
}
if (!props.startDelay) {
setup();
} else {
timeout = window.setTimeout(setup, props.startDelay);
}
return () => {
cancelAnimationFrame(request);
clearTimeout(timeout);
};
}, [props.startDelay, props.lines, content]);
let renderedLines = lines.current;
if (props.stopped) {
renderedLines = props.lines.map((line, index) => ({
id: index,
text: line.text,
isCommand: line.isCommand,
isCurrent: index === props.lines.length - 1,
}));
}
return (
<Window title="Terminal">
<TerminalContent
lines={renderedLines}
completed={completed}
counter={counter}
highlights={props.highlights}
selectedLines={props.selectedLines}
/>
</Window>
);
}
@@ -1,279 +0,0 @@
/**
* Teleport
* Copyright (C) 2023 Gravitational, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { Fragment, useEffect, useLayoutEffect, useRef } from 'react';
import styled from 'styled-components';
import { BufferEntry } from 'shared/components/AnimatedTerminal/content';
export interface SelectedLines {
start: number;
end: number;
}
interface TerminalContentProps {
lines: BufferEntry[];
completed: boolean;
counter: number;
selectedLines?: SelectedLines;
highlights?: KeywordHighlight[];
}
export interface KeywordHighlight {
key: string;
match?: RegExp;
keywords?: string[];
color: TerminalColor;
}
export enum TerminalColor {
Argument = '#cfa7ff',
Keyword = '#5af78e',
Error = '#f07278',
Label = 'rgba(255, 255, 255, 0.7)',
Punctuation = '#81ceee',
}
const SelectedLinesOverlay = styled.div`
width: 100%;
background: rgba(255, 255, 255, 0.3);
position: absolute;
left: 0;
z-index: 0;
transform: translate3d(0, 0, 0);
transition-property: height;
`;
const Lines = styled.div`
position: relative;
z-index: 1;
`;
export function TerminalContent(props: TerminalContentProps) {
const ref = useRef<HTMLDivElement>(null);
useLayoutEffect(() => {
ref.current.scrollTop = ref.current.scrollHeight;
}, [props.counter]);
const renderedLines = useRef<HTMLDivElement>(null);
useEffect(() => {
if (!props.selectedLines) {
return;
}
const numberOfLines = props.selectedLines.end - props.selectedLines.start;
const id = window.setTimeout(() => {
renderedLines.current.style.height = `${20 * (numberOfLines + 1)}px`;
}, 1000);
return () => clearTimeout(id);
}, [props.selectedLines]);
let selectedLines;
if (props.selectedLines) {
const numberOfLines = props.selectedLines.end - props.selectedLines.start;
selectedLines = (
<SelectedLinesOverlay
ref={renderedLines}
style={{
top: 20 * (props.selectedLines.start + 1),
transitionTimingFunction: `steps(${numberOfLines + 2}, jump-none)`,
transitionDuration: `${numberOfLines * 0.08}s`,
height: 0,
}}
/>
);
}
return (
<TerminalContentContainer ref={ref}>
<TerminalCode>
<Lines>{renderLines(props.lines, props.highlights)}</Lines>
{selectedLines}
</TerminalCode>
</TerminalContentContainer>
);
}
function renderLines(lines: BufferEntry[], highlights?: KeywordHighlight[]) {
if (!lines.length) {
return (
<Prompt key="cursor">
$ <Cursor />
</Prompt>
);
}
const result = lines.map(line => (
<Fragment key={line.id}>
{line.isCommand ? (
<Prompt>${line.text.length > 0 ? ' ' : ''}</Prompt>
) : null}
{formatText(line.text, line.isCommand, highlights)}
{line.isCurrent && line.isCommand ? <Cursor /> : null}
<br />
</Fragment>
));
return result;
}
function highlightWords(content: string, highlight: KeywordHighlight) {
const regex = new RegExp(`(${highlight.keywords.join('|')})`);
if (regex.test(content)) {
const split = content.split(regex);
return split
.map((item, index) => {
if (!item) {
return;
}
// all odd occurrences are matches, the rest remain unchanged
if (index % 2 === 0) {
return <span key={index}>{item}</span>;
}
return (
<span
key={`${highlight.key}-${index}`}
style={{ color: highlight.color }}
>
{item}
</span>
);
})
.filter(Boolean);
}
return null;
}
function formatText(
source: string,
isCommand: boolean,
highlights?: KeywordHighlight[]
) {
let text = source;
let comment;
const commentStartIndex = text.indexOf('#');
if (commentStartIndex > -1) {
text = source.substring(0, commentStartIndex);
comment = (
<Comment>{source.substring(commentStartIndex, source.length)}</Comment>
);
}
const words = text.split(' ');
const result = [];
outer: for (const [index, word] of words.entries()) {
if (!isCommand && /(https?:\/\/\S+)/g.test(word)) {
result.push(
<Fragment key={index}>
<a
key={index}
style={{ color: '#feaa01', textDecoration: 'underline' }}
href={word}
target="_blank"
rel="noopener noreferrer"
>
{word}
</a>{' '}
</Fragment>
);
continue;
}
if (highlights) {
for (const entry of highlights) {
const highlightedWords = highlightWords(word, entry);
if (highlightedWords) {
result.push(
<Word key={`${entry.key}-${index}`}>{highlightedWords} </Word>
);
continue outer;
}
}
}
result.push(<Word key={index}>{word} </Word>);
}
return (
<>
{result}
{comment}
</>
);
}
const Word = styled.span`
user-select: none;
color: ${props => props.theme.colors.light};
`;
const Prompt = styled.span`
user-select: none;
color: rgb(204, 204, 204);
`;
const Comment = styled.span`
user-select: none;
color: rgb(255, 255, 255, 0.4);
`;
const Cursor = styled.span`
display: inline-block;
width: 6px;
height: 15px;
background: #ffffff;
vertical-align: middle;
`;
export const TerminalContentContainer = styled.div`
background: #04162c;
height: inherit;
overflow-y: auto;
border-bottom-left-radius: 5px;
border-bottom-right-radius: 5px;
`;
export const TerminalCode = styled.div`
font-size: 12px;
font-family:
Menlo,
DejaVu Sans Mono,
Consolas,
Lucida Console,
monospace;
line-height: 20px;
white-space: pre-wrap;
margin: 10px 16px;
position: relative;
`;
@@ -1,177 +0,0 @@
/**
* Teleport
* Copyright (C) 2023 Gravitational, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { wait } from 'shared/utils/wait';
export interface BufferEntry {
id: number;
text: string;
isCommand: boolean;
isCurrent: boolean;
}
type FrameFunction = () => Frame;
interface Frame {
text: string;
delay?: number;
}
export interface TerminalLine {
text?: string;
isCommand: boolean;
delay?: number;
hasFinished?: () => boolean;
frames?: FrameFunction[];
}
export async function* createTerminalContent(
lines: TerminalLine[],
lineIndex: number
): AsyncIterableIterator<BufferEntry[]> {
let linePosition = 0;
let frameIndex = 0;
let hasFrame = false;
const buffer: BufferEntry[] = [];
if (lineIndex > 0) {
for (let i = 0; i < lineIndex; i++) {
buffer.push({
id: i,
text: lines[i].text,
isCommand: lines[i].isCommand,
isCurrent: i === lineIndex,
});
}
yield buffer;
}
while (true) {
if (lineIndex < lines.length) {
if (!lines[lineIndex].isCommand) {
const delay = lines[lineIndex].delay;
if (!isNaN(delay)) {
await wait(delay);
yield buffer;
}
const frames = lines[lineIndex].frames;
if (!frames) {
buffer.push({
id: lineIndex,
text: lines[lineIndex].text,
isCommand: false,
isCurrent: false,
});
yield buffer;
linePosition = 0;
lineIndex += 1;
} else if (frameIndex < frames.length) {
const frame = frames[frameIndex]();
if (frameIndex === 0 && !hasFrame) {
hasFrame = true;
buffer.push({
id: lineIndex,
text: frame.text,
isCurrent: false,
isCommand: false,
});
}
buffer[lineIndex].text = frame.text;
if (!isNaN(frame.delay)) {
yield buffer;
await wait(frame.delay);
yield buffer;
}
frameIndex += 1;
} else {
if (hasFrame && lines[lineIndex + 1]) {
buffer[lineIndex].text = lines[lineIndex].text;
linePosition = 0;
frameIndex = 0;
lineIndex += 1;
hasFrame = false;
}
frameIndex = 0;
}
} else if (linePosition > lines[lineIndex].text.length) {
buffer[lineIndex].isCurrent = lineIndex === lines.length - 1;
linePosition = 0;
yield buffer;
await wait(300);
lineIndex += 1;
} else {
const delay = lines[lineIndex].delay;
if (!isNaN(delay)) {
yield buffer;
await wait(delay);
yield buffer;
}
if (linePosition === 0) {
await wait(100);
buffer.push({
id: lineIndex,
text: '',
isCommand: lines[lineIndex].isCommand,
isCurrent: true,
});
yield buffer;
await wait(600);
}
buffer[lineIndex].text = lines[lineIndex].text.substring(
0,
linePosition
);
linePosition += 1;
}
yield buffer;
} else {
yield buffer;
return buffer;
}
}
}
@@ -1,20 +0,0 @@
/**
* Teleport
* Copyright (C) 2023 Gravitational, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
export { AnimatedTerminal } from './AnimatedTerminal';
export { TerminalColor } from './TerminalContent';
@@ -1,24 +0,0 @@
/**
* Teleport
* Copyright (C) 2023 Gravitational, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @deprecated Use `ButtonWithAddIcon` instead.
*
* TODO (bernardjkim): Remove once components in e repo have been updated.
*/
export { ButtonWithAddIcon as ButtonTextWithAddIcon } from '../ButtonWithAddIcon';
@@ -332,7 +332,7 @@ function commonFeaturePossible(
* Determines whether clipboard sharing is/should-be possible based on whether it's allowed by the acl
* and whether it's supported by the browser.
*/
export function clipboardSharingPossible(
function clipboardSharingPossible(
clipboardSharingState: ClipboardSharingState
): boolean {
return commonFeaturePossible(clipboardSharingState);
@@ -1,325 +0,0 @@
/**
* Teleport
* Copyright (C) 2023 Gravitational, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import React, { useState } from 'react';
import styled, { keyframes } from 'styled-components';
import {
KeywordHighlight,
TerminalColor,
} from 'shared/components/AnimatedTerminal/TerminalContent';
import { Language } from 'shared/components/Editor/Language';
import { Tabs } from 'shared/components/Editor/Tabs';
import {
WindowCode,
WindowContainer,
WindowContentContainer,
WindowTitleBar,
} from 'shared/components/Window';
import { File, FileProps } from './File';
interface EditorProps {
title: string;
}
export function Editor(props: React.PropsWithChildren<EditorProps>) {
const [activeTabIndex, setActiveTabIndex] = useState(0);
const files = React.Children.map(
props.children,
(child: React.ReactElement<FileProps>) => {
if (child.type === File) {
return {
name: child.props.name,
content: child.props.code,
language: child.props.language,
};
}
return null;
}
).filter(Boolean);
const tabs = files.map(file => file.name);
const { content, language } = files[activeTabIndex];
const parsed = parse(content, language);
const lineNumbers = [];
if (content) {
const numberOfLines = content.split('\n').length;
for (let i = 0; i <= numberOfLines; i++) {
lineNumbers.push(
<LineNumber
key={i}
data-line-number={i + 1}
active={i === numberOfLines}
/>
);
}
} else {
lineNumbers.push(<LineNumber key={0} data-line-number={1} active />);
}
return (
<WindowContainer>
<WindowTitleBar title={props.title} />
<Tabs
items={tabs}
activeIndex={activeTabIndex}
onSelect={setActiveTabIndex}
/>
<WindowContentContainer style={{ height: 585 }}>
<WindowCode style={{ display: 'flex' }}>
<LineNumbers>{lineNumbers}</LineNumbers>
<Code>
{parsed}
<ActiveLine>
<Cursor />
</ActiveLine>
</Code>
</WindowCode>
</WindowContentContainer>
</WindowContainer>
);
}
function parse(code: string, language: Language) {
switch (language) {
case Language.YAML:
return parseYAML(code);
default:
throw new Error('Language not supported');
}
}
function parseYAML(code: string) {
if (!code) {
return [];
}
const highlights = [
{
key: 'string',
keywords: [`'\\*'`],
color: TerminalColor.Keyword,
},
{
key: 'certificate',
match: /(-----.*?-----)/,
color: TerminalColor.Punctuation,
},
];
const lines = code.split('\n');
const result = [];
for (const [index, line] of lines.entries()) {
const highlightSemicolonMultiline = highlight(
line,
': |',
index,
highlights
);
if (highlightSemicolonMultiline) {
result.push(highlightSemicolonMultiline);
continue;
}
const highlightSemicolon = highlight(line, ':', index, highlights);
if (highlightSemicolon) {
result.push(highlightSemicolon);
continue;
}
if (line) {
result.push(<div key={index}>{highlightValue(line, highlights)}</div>);
continue;
}
result.push(<div key={index}>&nbsp;</div>);
}
return result;
}
function highlight(
code: string,
symbol: string,
index: number,
highlights: KeywordHighlight[]
) {
if (!code.includes(symbol)) {
return;
}
const symbolIndex = code.indexOf(symbol);
let value = code.substring(symbolIndex + symbol.length, code.length);
return (
<div key={index}>
<Keyword>{code.substring(0, symbolIndex)}</Keyword>
<Punctuation>{symbol}</Punctuation>
{highlightValue(value, highlights)}
</div>
);
}
function highlightValue(content: string, highlights: KeywordHighlight[]) {
for (const entry of highlights) {
if (entry.match && entry.match.test(content)) {
const split = content.split(entry.match);
return split
.map((item, index) => {
if (!item) {
return;
}
// all odd occurrences are matches, the rest remain unchanged
if (index % 2 === 0) {
return <span key={index}>{item}</span>;
}
return (
<span key={`${entry.key}-${index}`} style={{ color: entry.color }}>
{item}
</span>
);
})
.filter(Boolean);
}
}
const words = content.split(' ');
const result = [];
outer: for (const [index, word] of words.entries()) {
for (const entry of highlights) {
if (entry.keywords) {
const highlightedWords = highlightWord(word, entry);
if (highlightedWords) {
result.push(
<span key={`${entry.key}-${index}`}>{highlightedWords} </span>
);
continue outer;
}
}
}
result.push(<span key={index}>{word} </span>);
}
return result;
}
function highlightWord(content: string, highlight: KeywordHighlight) {
const regex = new RegExp(`(${highlight.keywords.join('|')})`);
if (regex.test(content)) {
const split = content.split(regex);
return split
.map((item, index) => {
if (!item) {
return;
}
// all odd occurrences are matches, the rest remain unchanged
if (index % 2 === 0) {
return <span key={index}>{item}</span>;
}
return (
<span
key={`${highlight.key}-${index}`}
style={{ color: highlight.color }}
>
{item}
</span>
);
})
.filter(Boolean);
}
return null;
}
const Keyword = styled.span`
color: #d4656b;
`;
const Punctuation = styled.span`
color: #81ceee;
`;
const blink = keyframes`
0% {
opacity: 0;
}
`;
const Cursor = styled.span`
display: inline-block;
width: 2px;
height: 15px;
background: #ffffff;
vertical-align: middle;
animation: ${blink} 1.5s steps(2) infinite;
`;
const LineNumbers = styled.div`
user-select: none;
width: 55px;
`;
interface LineNumberProps {
active?: boolean;
}
const LineNumber = styled.div<LineNumberProps>`
background: ${p => (p.active ? 'rgba(0, 0, 0, 0.3)' : 'none')};
color: ${p =>
p.active ? 'rgba(255, 255, 255, 0.6)' : 'rgba(255, 255, 255, 0.3)'};
text-align: right;
padding-right: 20px;
&:before {
content: attr(data-line-number);
}
`;
const Code = styled.div`
width: 100%;
color: white;
`;
const ActiveLine = styled.div`
background: rgba(0, 0, 0, 0.3);
width: 100%;
`;
@@ -1,30 +0,0 @@
/**
* Teleport
* Copyright (C) 2023 Gravitational, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { Language } from './Language';
export interface FileProps {
name: string;
language: Language;
code: string;
}
// eslint-disable-next-line no-unused-vars
export function File(props: FileProps) {
return null;
}
@@ -1,21 +0,0 @@
/**
* Teleport
* Copyright (C) 2023 Gravitational, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
export enum Language {
YAML,
}
@@ -41,12 +41,12 @@ export function Tabs(props: TabsProps) {
return <TabsContainer>{tabs}</TabsContainer>;
}
export const TabsContainer = styled.div`
const TabsContainer = styled.div`
background: #0a102c;
display: flex;
`;
export const Tab = styled.div<{ active: boolean }>`
const Tab = styled.div<{ active: boolean }>`
background: rgba(255, 255, 255, 0.1);
padding: 8px 20px 10px 15px;
cursor: pointer;
@@ -1,21 +0,0 @@
/**
* Teleport
* Copyright (C) 2023 Gravitational, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
export { File } from './File';
export { Editor } from './Editor';
export { Language } from './Language';
@@ -100,15 +100,15 @@ export const DetailsTab = ({
);
};
export const Title = styled(Text)`
const Title = styled(Text)`
font-weight: bold;
`;
export const Description = styled(Text)`
const Description = styled(Text)`
font-size: ${p => p.theme.fontSizes[1]}px;
`;
export const Feature = styled(Box)<{ $isSliding?: boolean; $active?: boolean }>`
const Feature = styled(Box)<{ $isSliding?: boolean; $active?: boolean }>`
height: var(--feature-height);
line-height: 20px;
@@ -20,9 +20,4 @@ import FieldSelect from './FieldSelect';
export default FieldSelect;
export { FieldSelect, FieldSelectAsync } from './FieldSelect';
export { resolveUndefinedOptions } from './shared';
export {
FieldSelectCreatable,
FieldSelectCreatableAsync,
} from './FieldSelectCreatable';
export { FieldSelectCreatable } from './FieldSelectCreatable';
@@ -33,12 +33,6 @@ import { Rule } from '../Validation/rules';
export const defaultRule = () => () => ({ valid: true });
export const LabelTip = ({ text }) => (
<span
css={{ fontWeight: 'normal', textTransform: 'none' }}
>{` - ${text}`}</span>
);
type FieldSelectWrapperPropsBase<Opt, IsMulti extends boolean> = {
label?: string;
toolTipContent?: React.ReactNode;
@@ -94,7 +94,7 @@ export function FileTransfer(props: FileTransferProps) {
);
}
export function FileTransferDialog(
function FileTransferDialog(
props: Pick<FileTransferProps, 'transferHandlers' | 'errorText'> & {
openedDialog: FileTransferDialogDirection;
onCloseDialog(isAnyTransferInProgress: boolean): void;
@@ -17,7 +17,6 @@
*/
export { FileTransfer } from './FileTransfer';
export { FileTransferContainer } from './FileTransferContainer';
export {
FileTransferContextProvider,
useFileTransferContext,
@@ -1,55 +0,0 @@
/**
* Teleport
* Copyright (C) 2023 Gravitational, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { Flex } from 'design';
import { StyledArrowBtn } from 'design/DataTable/Pager/StyledPager';
import { StyledPanel } from 'design/DataTable/StyledTable';
import { CircleArrowLeft, CircleArrowRight } from 'design/Icon';
export function SearchPagination({ prevPage, nextPage }: Props) {
return (
<StyledPanel>
<Flex justifyContent="flex-end" width="100%">
<Flex alignItems="center" mr={2}></Flex>
<Flex>
<StyledArrowBtn
onClick={prevPage}
title="Previous page"
disabled={!prevPage}
mx={0}
>
<CircleArrowLeft />
</StyledArrowBtn>
<StyledArrowBtn
ml={0}
onClick={nextPage}
title="Next page"
disabled={!nextPage}
>
<CircleArrowRight />
</StyledArrowBtn>
</Flex>
</Flex>
</StyledPanel>
);
}
type Props = {
prevPage?: () => void;
nextPage?: () => void;
};
@@ -17,4 +17,3 @@
*/
export * from './SearchPanel';
export * from './SearchPagination';
@@ -30,7 +30,7 @@ import { CreatableProps as ReactSelectCreatableProps } from 'react-select/creata
export type SelectSize = 'large' | 'medium' | 'small';
export type CommonProps<Opt, IsMulti extends boolean> = {
type CommonProps<Opt, IsMulti extends boolean> = {
size?: SelectSize;
hasError?: boolean;
/**
@@ -376,7 +376,7 @@ export function shouldWarnResourceStatus(
return status === 'mixed' || status === 'unhealthy';
}
export const StyledUl = styled.ul`
const StyledUl = styled.ul`
margin: 0;
padding-left: ${p => p.theme.space[4]}px;
padding-bottom: ${p => p.theme.space[1]}px;
@@ -175,7 +175,7 @@ export function makeUnifiedResourceViewItemDesktop(
};
}
export function makeUnifiedResourceViewItemLinuxDesktop(
function makeUnifiedResourceViewItemLinuxDesktop(
resource: UnifiedResourceDesktop,
ui: UnifiedResourceUi
): UnifiedResourceViewItem {
@@ -195,7 +195,7 @@ export function makeUnifiedResourceViewItemLinuxDesktop(
};
}
export function makeUnifiedResourceViewItemUserGroup(
function makeUnifiedResourceViewItemUserGroup(
resource: UnifiedResourceUserGroup,
ui: UnifiedResourceUi
): UnifiedResourceViewItem {
@@ -213,7 +213,7 @@ export function makeUnifiedResourceViewItemUserGroup(
};
}
export function makeUnifiedResourceViewItemGitServer(
function makeUnifiedResourceViewItemGitServer(
resource: UnifiedResourceGitServer,
ui: UnifiedResourceUi
): UnifiedResourceViewItem {
@@ -17,4 +17,3 @@
*/
export { UserDisplayName } from './UserDisplayName';
export type { UserDisplayNameLayout } from './UserDisplayName';
@@ -353,7 +353,7 @@ const isIamRoleNameValid = (roleName: string) => {
);
};
export interface EmailValidationResult extends ValidationResult {
interface EmailValidationResult extends ValidationResult {
kind?: 'empty' | 'invalid';
}
@@ -487,7 +487,7 @@ const requiredAll =
};
/** A result of the {@link arrayOf} validation rule. */
export type ArrayValidationResult<R = ValidationResult> = ValidationResult & {
type ArrayValidationResult<R = ValidationResult> = ValidationResult & {
/** Results of validating each separate item. */
results: R[];
};
@@ -1,36 +0,0 @@
/**
* Teleport
* Copyright (C) 2023 Gravitational, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import React from 'react';
import { WindowContainer, WindowContentContainer } from './WindowComponents';
import { WindowTitleBar } from './WindowTitleBar';
interface WindowProps {
title: string;
}
export function Window(props: React.PropsWithChildren<WindowProps>) {
return (
<WindowContainer>
<WindowTitleBar title={props.title} />
<WindowContentContainer>{props.children}</WindowContentContainer>
</WindowContainer>
);
}
@@ -1,71 +0,0 @@
/**
* Teleport
* Copyright (C) 2023 Gravitational, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import styled from 'styled-components';
export const WindowContainer = styled.div`
border-radius: 5px;
width: 100%;
box-shadow: 0px 0px 20px 0px rgba(0, 0, 0, 0.43);
`;
export const WindowTitleBarContainer = styled.div`
background: #040b1d;
height: 32px;
position: relative;
display: flex;
align-items: center;
justify-content: center;
border-top-left-radius: 5px;
border-top-right-radius: 5px;
`;
export const WindowTitleBarButtons = styled.div`
display: flex;
position: absolute;
top: 50%;
left: 10px;
transform: translate(0, -50%);
`;
export const WindowTitleBarButton = styled.div`
width: 12px;
height: 12px;
border-radius: 50%;
margin-right: 5px;
`;
export const WindowContentContainer = styled.div`
background: #04162c;
height: var(--content-height, 660px);
overflow-y: auto;
border-bottom-left-radius: 5px;
border-bottom-right-radius: 5px;
`;
export const WindowCode = styled.div`
font-size: 12px;
font-family:
Menlo,
DejaVu Sans Mono,
Consolas,
Lucida Console,
monospace;
line-height: 20px;
white-space: pre-wrap;
`;
@@ -1,44 +0,0 @@
/**
* Teleport
* Copyright (C) 2023 Gravitational, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import {
WindowTitleBarButton,
WindowTitleBarButtons,
WindowTitleBarContainer,
} from './WindowComponents';
interface WindowTitleBarProps {
title: string;
}
export function WindowTitleBar(props: WindowTitleBarProps) {
return (
<WindowTitleBarContainer
css={`
color: ${props => props.theme.colors.light};
`}
>
<WindowTitleBarButtons>
<WindowTitleBarButton style={{ backgroundColor: '#f95e57' }} />
<WindowTitleBarButton style={{ backgroundColor: '#fbbe2e' }} />
<WindowTitleBarButton style={{ backgroundColor: '#31c842' }} />
</WindowTitleBarButtons>
{props.title}
</WindowTitleBarContainer>
);
}
@@ -1,28 +0,0 @@
/**
* Teleport
* Copyright (C) 2023 Gravitational, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
export { WindowTitleBar } from './WindowTitleBar';
export { Window } from './Window';
export {
WindowContentContainer,
WindowCode,
WindowContainer,
WindowTitleBarButton,
WindowTitleBarButtons,
WindowTitleBarContainer,
} from './WindowComponents';
@@ -43,7 +43,7 @@ function makeTestResources(
}));
}
export function newDOMAbortError() {
function newDOMAbortError() {
return new DOMException('Aborted', 'AbortError');
}
+1 -1
View File
@@ -121,7 +121,7 @@ type EventMap = {
'terminal.webauthn': [string];
};
export enum LogType {
enum LogType {
OFF = 'OFF',
ERROR = 'ERROR',
WARN = 'WARN',
+1 -1
View File
@@ -161,7 +161,7 @@ export enum Severity {
/**
* @throws {Error} if an invalid severity is passed
*/
export function toSeverity(severity: number): Severity {
function toSeverity(severity: number): Severity {
if (severity === Severity.Info) {
return Severity.Info;
} else if (severity === Severity.Warning) {
-1
View File
@@ -2,7 +2,6 @@
"name": "@gravitational/shared",
"version": "1.0.0",
"description": "Gravitational library of shared components",
"main": "index.js",
"license": "Apache-2.0",
"repository": {
"type": "git",
+1 -2
View File
@@ -17,6 +17,5 @@
*/
import { processRedirectUri } from './processRedirectUri';
import { validateClientRedirect } from './urlValidation';
export { processRedirectUri, validateClientRedirect };
export { processRedirectUri };
@@ -137,7 +137,7 @@ export type ResourceId = {
};
// ResourceDetails holds optional details for a resource.
export type ResourceDetails = {
type ResourceDetails = {
// hostname is the resource hostname.
// TODO(mdwn): Remove hostname as it's no longer used.
hostname?: string;
@@ -134,8 +134,6 @@ export const formatDatabaseInfo = (type: DbType, protocol: DbProtocol) => {
}
};
export type DatabaseInfo = ReturnType<typeof formatDatabaseInfo>;
export type DbNameRequirement = 'unsupported' | 'optional' | 'required';
const dbNameRequirementsByProtocol: Record<DbProtocol, DbNameRequirement> = {
+1 -1
View File
@@ -16,7 +16,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
export type MCPServerConfig = {
type MCPServerConfig = {
command: string;
args: string[];
};
-33
View File
@@ -1,33 +0,0 @@
/**
* Teleport
* Copyright (C) 2023 Gravitational, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* pipe takes an array of unary functions as an argument. It returns a function that accepts a value
* that's going to be passed through the supplied functions.
*
* @example
* // Without pipe.
* const add1ThenDouble = (x) => double(add1(x));
*
* // With pipe.
* const add1ThenDouble = pipe(add1, double);
*/
export const pipe =
<PipeSubject>(...fns: Array<(pipeSubject: PipeSubject) => PipeSubject>) =>
(x: PipeSubject) =>
fns.reduce((v, f) => f(v), x);
+1 -1
View File
@@ -2,7 +2,6 @@
"name": "@gravitational/teleport",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "pnpm build-wasm && vite",
"build": "pnpm build-wasm && vite build",
@@ -46,6 +45,7 @@
"devDependencies": {
"@gravitational/build": "workspace:*",
"@gravitational/teleport": "workspace:*",
"ace-builds": "1.44.0",
"babel-plugin-transform-import-meta": "2.3.3",
"babel-plugin-transform-vite-meta-env": "^1.0.3",
"babel-plugin-transform-vite-meta-hot": "^1.0.0",
@@ -1,25 +0,0 @@
/**
* Teleport
* Copyright (C) 2024 Gravitational, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// Compatibility aliases to prevent e/ from breaking.
// TODO(bl-nero): Remove these once e/ stops referring to them.
import { Account } from './Account';
export type { EnterpriseComponentProps } from './Account';
export default Account;
@@ -39,31 +39,3 @@ export const dummyPasskey: MfaDevice = {
type: 'webauthn',
usage: 'passwordless',
};
/** A dummy MFA device for test purposes. */
export const dummyAuthenticatorApp: MfaDevice = {
id: '4',
description: 'Authenticator App',
name: 'iphone 12',
registeredDate: new Date(1623722252),
lastUsedDate: new Date(1623981452),
type: 'totp',
usage: 'mfa',
};
/**
* Repeats devices twice to make sure we support multiple devices of the same
* type and purpose.
*/
function twice(arr) {
return [...arr, ...arr];
}
/** Dummy devices for testing purposes. */
export const deviceCases: Record<string, MfaDevice[]> = {
all: twice([dummyAuthenticatorApp, dummyHardwareDevice, dummyPasskey]),
authApps: twice([dummyAuthenticatorApp]),
mfaDevices: twice([dummyHardwareDevice]),
passkeys: twice([dummyPasskey]),
none: [],
};
@@ -24,7 +24,7 @@ import cfg from 'teleport/config';
import { preferencesHeadings } from './Preferences';
import { securityHeadings } from './SecuritySettings';
export interface Heading {
interface Heading {
name: string;
id: string;
}
+1 -1
View File
@@ -16,4 +16,4 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
export { AccountPage, Account } from './Account';
export { AccountPage } from './Account';
@@ -75,10 +75,7 @@ export default function EventList(props: Props) {
);
}
export const renderActionCell = (
event: Event,
onShowDetails: (e: Event) => void
) => (
const renderActionCell = (event: Event, onShowDetails: (e: Event) => void) => (
<Cell align="right">
<Flex gap={2} justifyContent="flex-end">
<ViewInPolicyButton event={event} />
@@ -1,51 +0,0 @@
/**
* Teleport
* Copyright (C) 2023 Gravitational, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { ButtonBorder } from 'design';
import { Cell } from 'design/DataTable';
import { displayDateTime } from 'design/datetime';
import { Event } from 'teleport/services/audit';
export const ActionCell = props => {
const { rowIndex, onViewDetails, data } = props;
const event: Event = data[rowIndex];
function onClick() {
onViewDetails(event);
}
return (
<Cell align="right">
<ButtonBorder size="small" onClick={onClick} width="87px">
Details
</ButtonBorder>
</Cell>
);
};
export const TimeCell = props => {
const { rowIndex, data } = props;
const { time } = data[rowIndex];
return <Cell style={{ minWidth: '120px' }}>{displayDateTime(time)}</Cell>;
};
export function DescCell(props) {
const { rowIndex, data } = props;
const { message } = data[rowIndex];
return <Cell style={{ wordBreak: 'break-word' }}>{message}</Cell>;
}
@@ -25,7 +25,7 @@ import Text from 'design/Text/Text';
import { Event, EventCode, eventCodes } from 'teleport/services/audit';
export const EventIconMap: Record<EventCode, any> = {
const EventIconMap: Record<EventCode, any> = {
[eventCodes.AUTH_ATTEMPT_FAILURE]: Icons.Info,
[eventCodes.EXEC_FAILURE]: Icons.Cli,
[eventCodes.EXEC]: Icons.Cli,
+1 -1
View File
@@ -16,4 +16,4 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
export { Audit, AuditContainer } from './Audit';
export { AuditContainer } from './Audit';
@@ -193,7 +193,7 @@ export function LocalConnectorTile({
);
}
export const ConnectorBox = styled(Box)<{ disabled?: boolean }>`
const ConnectorBox = styled(Box)<{ disabled?: boolean }>`
display: flex;
flex-direction: row;
justify-content: space-between;
@@ -46,9 +46,6 @@ import DeleteConnectorDialog from './DeleteConnectorDialog';
import EmptyList from './EmptyList';
import templates from './templates';
export const description =
'Auth connectors allow Teleport to authenticate users via an external identity source such as Okta, Microsoft Entra ID, GitHub, etc. This authentication method is commonly known as single sign-on (SSO).';
/**
* AuthConnectorsContainer is the container for the Auth Connectors feature and handles routing to the relevant page based on the URL.
*/
@@ -16,4 +16,4 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
export { AuthConnectors, AuthConnectorsContainer } from './AuthConnectors';
export { AuthConnectorsContainer } from './AuthConnectors';
@@ -18,7 +18,7 @@
import styled from 'styled-components';
import { Box, Button, Subtitle1 } from 'design';
import { Button } from 'design';
import { FeatureHeader } from 'teleport/components/Layout';
@@ -35,23 +35,6 @@ export const ResponsiveFeatureHeader = styled(FeatureHeader)`
}
`;
export const MobileDescription = styled(Subtitle1)`
margin-bottom: ${p => p.theme.space[3]}px;
@media screen and (min-width: ${p => p.theme.breakpoints.tablet}) {
display: none;
}
`;
export const DesktopDescription = styled(Box)`
margin-left: ${p => p.theme.space[4]}px;
width: 240px;
color: ${p => p.theme.colors.text.main};
flex-shrink: 0;
@media screen and (max-width: ${p => p.theme.breakpoints.tablet}) {
display: none;
}
`;
export const ResponsiveAddButton = styled(Button)`
width: 240px;
@media screen and (max-width: ${p => p.theme.breakpoints.tablet}) {
@@ -1,42 +0,0 @@
/**
* Teleport
* Copyright (C) 2023 Gravitational, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import styled from 'styled-components';
import { Flex } from 'design';
import { ButtonLockedFeature } from 'teleport/components/ButtonLockedFeature';
export const LockedFeatureContainer = styled(Flex)`
flex-wrap: wrap;
position: relative;
justify-content: center;
min-width: 224px;
`;
export const LockedFeatureButton = styled(ButtonLockedFeature)`
position: absolute;
width: 80%;
right: 10%;
bottom: -10px;
@media screen and (max-width: ${props => props.theme.breakpoints.tablet}) {
width: 100%;
right: 1px;
}
`;
@@ -150,7 +150,7 @@ const TitleContainer = styled(Flex)`
overflow: hidden;
`;
export const TitleText = styled(Text).attrs({
const TitleText = styled(Text).attrs({
as: 'h2',
typography: 'h2',
})`
@@ -210,7 +210,7 @@ const TitleContainer = styled(Flex)`
min-height: ${p => p.theme.space[8]}px;
`;
export const TitleText = styled(Text).attrs({
const TitleText = styled(Text).attrs({
as: 'h2',
typography: 'h2',
})``;
@@ -19,5 +19,7 @@
import { getBotInstance } from 'teleport/services/bot/bot';
import { createQueryHook } from 'teleport/services/queryHelpers';
export const { queryKey: GetBotInstanceQueryKey, useQuery: useGetBotInstance } =
createQueryHook(['bot_instance', 'get'], getBotInstance);
export const { useQuery: useGetBotInstance } = createQueryHook(
['bot_instance', 'get'],
getBotInstance
);
@@ -481,7 +481,7 @@ function stripWildcard(labels: KubernetesLabel[]) {
}
const labelNameRegex = /^([a-z0-9:]+|\*)$/; // 1-n alphanumerics or colon, or a single asterisk
export const requireValidLabelName: Rule = value => () => {
const requireValidLabelName: Rule = value => () => {
const match = labelNameRegex.test(value.trim());
return {
valid: match,
@@ -489,7 +489,7 @@ export const requireValidLabelName: Rule = value => () => {
};
};
export const requireValidLabelValue: Rule = value => () => {
const requireValidLabelValue: Rule = value => () => {
const trimmed = value.trim();
return {
valid: trimmed.length > 0,
@@ -32,7 +32,7 @@ export function makeGhaWorkflow(params: {
);
}
export const GHA_WORKFLOW = `# This file contains a GitHub Actions workflow which enrolls with Teleport in
const GHA_WORKFLOW = `# This file contains a GitHub Actions workflow which enrolls with Teleport in
# order to access a Kubernetes cluster using kubectl or other tools compatible
# with kubeconfig, such as Helm.
@@ -17,4 +17,3 @@
*/
export { GitHubActionsSsh as default } from './GitHubActionsSsh';
export { ConnectGitHub as ConfigureBot } from './ConnectGitHub';
@@ -208,7 +208,7 @@ export type Rule = {
actor: string;
};
export const defaultRule: Rule = {
const defaultRule: Rule = {
workflowName: '',
environment: '',
ref: '',
@@ -24,7 +24,7 @@ import cfg from 'teleport/config';
import { FlowStepProps } from './GuidedFlow';
export type ButtonState = {
type ButtonState = {
disabled?: boolean;
hidden?: boolean;
};
@@ -95,10 +95,3 @@ export function makeKubernetesAccessChecker(selected: KubernetesLabel[]) {
}
export type KubernetesLabel = { name: string; values: string[] };
export type KubernetesResourceRule = {
kind: string;
name: string;
namespace: string;
verbs: string[];
};
@@ -23,13 +23,9 @@ import styled, { useTheme } from 'styled-components';
import { Box, ButtonPrimary, Flex, H1, Image, Text } from 'design';
import { ResourceIcon } from 'design/ResourceIcon';
import {
Description,
DetailsTab,
Feature,
FeatureContainer,
FeatureProps,
FeatureSlider,
Title,
} from 'shared/components/EmptyState/EmptyState';
import { DisplayTile } from 'teleport/Bots/Add/AddBotsPicker';
@@ -128,30 +124,12 @@ export function EmptyState() {
);
}
export const EliminateSecrets = ({
active,
onClick,
isSliding,
}: FeatureProps) => {
return (
<Feature $active={active} onClick={onClick} $isSliding={isSliding}>
<Title>
Eliminate secrets and shared credentials from CI/CD workflows.
</Title>
<Description>
Teleport Machine ID replaces passwords, API, and static keys with
short-lived SSH and x.509 certificates.
</Description>
</Feature>
);
};
const eliminateSecretsImages = {
light: elimiateSecretsLightImage,
dark: elimiateSecretsImage,
};
export const EliminateSecretsPreview = () => {
const EliminateSecretsPreview = () => {
const theme = useTheme();
return (
<PreviewBox includeShadow>
@@ -165,7 +143,7 @@ const controlWorkflowsImages = {
dark: controlWorkflowsDarkImage,
};
export const ControlWorkflowsPreview = () => {
const ControlWorkflowsPreview = () => {
const theme = useTheme();
return (
<PreviewBox includeShadow>
@@ -208,7 +186,7 @@ const integrationsBottom = [
},
];
export const BotTiles = () => {
const BotTiles = () => {
return (
<PreviewBox>
<Flex>
@@ -1,19 +0,0 @@
/**
* Teleport
* Copyright (C) 2023 Gravitational, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
export * from './Bots';
+4 -4
View File
@@ -22,7 +22,7 @@ import { createQueryHook } from 'teleport/services/queryHelpers';
export const { createQueryKey: createGetBotQueryKey, useQuery: useGetBot } =
createQueryHook(['bot', 'get'], getBot);
export const {
createQueryKey: createListBotTokensQueryKey,
useQuery: useListBotTokens,
} = createQueryHook(['bot', 'token', 'list'], listBotTokens);
export const { useQuery: useListBotTokens } = createQueryHook(
['bot', 'token', 'list'],
listBotTokens
);
-19
View File
@@ -17,8 +17,6 @@
*/
import { Dispatch, SetStateAction } from 'react';
import { Attempt } from 'shared/hooks/useAttemptNext';
import { FlatBot } from 'teleport/services/bot/types';
export type BotOptionsCellProps = {
@@ -42,13 +40,6 @@ export type BotListProps = {
setSelectedBot: Dispatch<SetStateAction<FlatBot>>;
};
export type DeleteBotProps = {
attempt: Attempt;
name: string;
onClose: () => void;
onDelete: () => void;
};
export type ViewBotProps = {
bot: FlatBot;
onClose: () => void;
@@ -58,13 +49,3 @@ export enum BotFlowType {
GitHubActionsSsh = 'github-actions-ssh',
GitHubActionsK8s = 'github-actions-k8s',
}
export type EditBotProps = {
fetchRoles: (input: string) => Promise<string[]>;
attempt: Attempt;
name: string;
onClose: () => void;
onEdit: () => void;
selectedRoles: string[];
setSelectedRoles: Dispatch<SetStateAction<string[]>>;
};
@@ -81,7 +81,7 @@ export function ManageCluster() {
);
}
export function ManageClusterHeader({ clusterId }: { clusterId: string }) {
function ManageClusterHeader({ clusterId }: { clusterId: string }) {
return (
<FeatureHeader>
<Flex alignItems="center">
@@ -111,7 +111,7 @@ type ClusterInformationProps = {
attempt: Attempt<ClusterInfo>;
} & BoxProps;
export function ClusterInformation({
function ClusterInformation({
cluster,
style,
attempt,
@@ -170,7 +170,7 @@ export function ClusterInformation({
);
}
export const IconBox = styled(Box)`
const IconBox = styled(Box)`
line-height: 0;
padding: ${props => props.theme.space[2]}px;
border-radius: ${props => props.theme.radii[3]}px;
@@ -178,7 +178,7 @@ export const IconBox = styled(Box)`
background: ${props => props.theme.colors.interactive.tonal.neutral[0]};
`;
export const DataItem = ({ title = '', data = null, isLoading = false }) => (
const DataItem = ({ title = '', data = null, isLoading = false }) => (
<DataItemFlex>
<Text typography="body2" bold style={{ width: '136px' }}>
{title}:
@@ -1,19 +0,0 @@
/**
* Teleport
* Copyright (C) 2024 Gravitational, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
export { ManageCluster } from './ManageCluster';
@@ -103,7 +103,7 @@ export default function useNodes({ clusterId, id }: stores.DocumentNodes) {
}
// sort logins by making 'root' as the first in the list
export const sortLogins = (logins: string[]) => {
const sortLogins = (logins: string[]) => {
const noRoot = logins.filter(l => l !== 'root');
noRoot.sort();
if (noRoot.length === logins.length) {
@@ -1,21 +0,0 @@
/**
* Teleport
* Copyright (C) 2023 Gravitational, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import ShareSession from './ShareSession';
export default ShareSession;
@@ -36,7 +36,7 @@ export default function TabsContainer(props: Props) {
return <Tabs {...props} parties={ctx.storeParties.state} />;
}
export function Tabs(props: Props & { parties: stores.Parties }) {
function Tabs(props: Props & { parties: stores.Parties }) {
const {
items,
parties,
@@ -22,7 +22,7 @@ import { useStore } from 'shared/libs/stores';
import ConsoleContext from './consoleContext';
export const ConsoleReactContext = React.createContext<ConsoleContext>(null);
const ConsoleReactContext = React.createContext<ConsoleContext>(null);
const ConsoleContextProvider: React.FC<
PropsWithChildren<{

Some files were not shown because too many files have changed in this diff Show More