working gitea scaffolding
This commit is contained in:
parent
4e2e48d80c
commit
fe842bed99
27 changed files with 25449 additions and 63 deletions
|
@ -28,6 +28,8 @@
|
|||
"@backstage/plugin-catalog-import": "^0.10.5",
|
||||
"@backstage/plugin-catalog-react": "^1.9.3",
|
||||
"@backstage/plugin-github-actions": "^0.6.10",
|
||||
"@backstage/plugin-home": "^0.6.1",
|
||||
"@backstage/plugin-kubernetes": "^0.11.4",
|
||||
"@backstage/plugin-org": "^0.6.19",
|
||||
"@backstage/plugin-permission-react": "^0.4.19",
|
||||
"@backstage/plugin-scaffolder": "^1.17.1",
|
||||
|
@ -39,6 +41,7 @@
|
|||
"@backstage/plugin-techdocs-react": "^1.1.15",
|
||||
"@backstage/plugin-user-settings": "^0.8.0",
|
||||
"@backstage/theme": "^0.5.0",
|
||||
"@internal/plugin-cnoe-ui": "^0.1.0",
|
||||
"@material-ui/core": "^4.12.2",
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
"history": "^5.0.0",
|
||||
|
@ -51,10 +54,10 @@
|
|||
"devDependencies": {
|
||||
"@backstage/test-utils": "^1.4.7",
|
||||
"@playwright/test": "^1.32.3",
|
||||
"@testing-library/dom": "^9.0.0",
|
||||
"@testing-library/jest-dom": "^6.0.0",
|
||||
"@testing-library/react": "^14.0.0",
|
||||
"@testing-library/user-event": "^14.0.0",
|
||||
"@testing-library/dom": "^9.0.0",
|
||||
"@types/react-dom": "*",
|
||||
"cross-env": "^7.0.0"
|
||||
},
|
||||
|
|
|
@ -22,20 +22,49 @@ import {
|
|||
import { TechDocsAddons } from '@backstage/plugin-techdocs-react';
|
||||
import { ReportIssue } from '@backstage/plugin-techdocs-module-addons-contrib';
|
||||
import { UserSettingsPage } from '@backstage/plugin-user-settings';
|
||||
import { apis } from './apis';
|
||||
import {apis, keycloakOIDCAuthApiRef} from './apis';
|
||||
import { entityPage } from './components/catalog/EntityPage';
|
||||
import { searchPage } from './components/search/SearchPage';
|
||||
import { Root } from './components/Root';
|
||||
|
||||
import { AlertDisplay, OAuthRequestDialog } from '@backstage/core-components';
|
||||
import {AlertDisplay, OAuthRequestDialog, SignInPage} from '@backstage/core-components';
|
||||
import { createApp } from '@backstage/app-defaults';
|
||||
import { AppRouter, FlatRoutes } from '@backstage/core-app-api';
|
||||
import { CatalogGraphPage } from '@backstage/plugin-catalog-graph';
|
||||
import { RequirePermission } from '@backstage/plugin-permission-react';
|
||||
import { ThemeProvider } from '@material-ui/core/styles';
|
||||
import CssBaseline from '@material-ui/core/CssBaseline';
|
||||
import { catalogEntityCreatePermission } from '@backstage/plugin-catalog-common/alpha';
|
||||
import LightIcon from '@material-ui/icons/WbSunny';
|
||||
import {
|
||||
CNOEHomepage,
|
||||
cnoeLightTheme,
|
||||
cnoeDarkTheme,
|
||||
} from '@internal/plugin-cnoe-ui';
|
||||
import {configApiRef, useApi} from "@backstage/core-plugin-api";
|
||||
|
||||
const app = createApp({
|
||||
apis,
|
||||
components: {
|
||||
SignInPage: props => {
|
||||
const configApi = useApi(configApiRef);
|
||||
if (configApi.getString('auth.environment') === 'local') {
|
||||
return <SignInPage {...props} auto providers={['guest']} />;
|
||||
}
|
||||
return (
|
||||
<SignInPage
|
||||
{...props}
|
||||
provider={{
|
||||
id: 'keycloak-oidc',
|
||||
title: 'Keycloak',
|
||||
message: 'Sign in using Keycloak',
|
||||
apiRef: keycloakOIDCAuthApiRef,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
},
|
||||
},
|
||||
|
||||
bindRoutes({ bind }) {
|
||||
bind(catalogPlugin.externalRoutes, {
|
||||
createComponent: scaffolderPlugin.routes.root,
|
||||
|
@ -53,11 +82,36 @@ const app = createApp({
|
|||
catalogIndex: catalogPlugin.routes.catalogIndex,
|
||||
});
|
||||
},
|
||||
themes: [
|
||||
{
|
||||
id: 'cnoe-light-theme',
|
||||
title: 'Light Theme',
|
||||
variant: 'light',
|
||||
icon: <LightIcon />,
|
||||
Provider: ({ children }) => (
|
||||
<ThemeProvider theme={cnoeLightTheme}>
|
||||
<CssBaseline>{children}</CssBaseline>
|
||||
</ThemeProvider>
|
||||
),
|
||||
},
|
||||
{
|
||||
id: 'cnoe-dark-theme',
|
||||
title: 'Dark Theme',
|
||||
variant: 'dark',
|
||||
icon: <LightIcon />,
|
||||
Provider: ({ children }) => (
|
||||
<ThemeProvider theme={cnoeDarkTheme}>
|
||||
<CssBaseline>{children}</CssBaseline>
|
||||
</ThemeProvider>
|
||||
),
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const routes = (
|
||||
<FlatRoutes>
|
||||
<Route path="/" element={<Navigate to="catalog" />} />
|
||||
<Route path="/home" element={<CNOEHomepage />} />
|
||||
<Route path="/catalog" element={<CatalogIndexPage />} />
|
||||
<Route
|
||||
path="/catalog/:namespace/:kind/:name"
|
||||
|
|
|
@ -4,16 +4,42 @@ import {
|
|||
ScmAuth,
|
||||
} from '@backstage/integration-react';
|
||||
import {
|
||||
AnyApiFactory,
|
||||
AnyApiFactory, ApiRef, BackstageIdentityApi,
|
||||
configApiRef,
|
||||
createApiFactory,
|
||||
createApiFactory, createApiRef, discoveryApiRef, oauthRequestApiRef, OpenIdConnectApi, ProfileInfoApi, SessionApi,
|
||||
} from '@backstage/core-plugin-api';
|
||||
import {OAuth2} from "@backstage/core-app-api";
|
||||
|
||||
export const keycloakOIDCAuthApiRef: ApiRef<
|
||||
OpenIdConnectApi & ProfileInfoApi & BackstageIdentityApi & SessionApi
|
||||
> = createApiRef({
|
||||
id: 'auth.keycloak-oidc-provider',
|
||||
});
|
||||
export const apis: AnyApiFactory[] = [
|
||||
createApiFactory({
|
||||
api: scmIntegrationsApiRef,
|
||||
deps: { configApi: configApiRef },
|
||||
factory: ({ configApi }) => ScmIntegrationsApi.fromConfig(configApi),
|
||||
deps: {configApi: configApiRef},
|
||||
factory: ({configApi}) => ScmIntegrationsApi.fromConfig(configApi),
|
||||
}),
|
||||
ScmAuth.createDefaultApiFactory(),
|
||||
createApiFactory({
|
||||
api: keycloakOIDCAuthApiRef,
|
||||
deps: {
|
||||
discoveryApi: discoveryApiRef,
|
||||
oauthRequestApi: oauthRequestApiRef,
|
||||
configApi: configApiRef,
|
||||
},
|
||||
factory: ({ discoveryApi, oauthRequestApi, configApi }) =>
|
||||
OAuth2.create({
|
||||
discoveryApi,
|
||||
oauthRequestApi,
|
||||
provider: {
|
||||
id: 'keycloak-oidc',
|
||||
title: 'Keycloak OIDC',
|
||||
icon: () => null,
|
||||
},
|
||||
environment: configApi.getOptionalString('auth.environment'),
|
||||
defaultScopes: ['openid', 'profile', 'email', 'groups'],
|
||||
}),
|
||||
}),
|
||||
];
|
||||
|
|
|
@ -16,26 +16,31 @@
|
|||
"build-image": "docker build ../.. -f Dockerfile --tag backstage"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/backend-common": "^0.20.1",
|
||||
"@backstage/backend-tasks": "^0.5.14",
|
||||
"@backstage/catalog-client": "^1.5.2",
|
||||
"@backstage/catalog-model": "^1.4.3",
|
||||
"@backstage/config": "^1.1.1",
|
||||
"@backstage/plugin-app-backend": "^0.3.57",
|
||||
"@backstage/plugin-auth-backend": "^0.20.3",
|
||||
"@backstage/plugin-auth-node": "^0.4.3",
|
||||
"@backstage/plugin-catalog-backend": "^1.16.1",
|
||||
"@backstage/plugin-catalog-backend-module-scaffolder-entity-model": "^0.1.6",
|
||||
"@backstage/plugin-permission-common": "^0.7.12",
|
||||
"@backstage/plugin-permission-node": "^0.7.20",
|
||||
"@backstage/plugin-proxy-backend": "^0.4.7",
|
||||
"@backstage/plugin-scaffolder-backend": "^1.20.0",
|
||||
"@backstage/plugin-search-backend": "^1.4.9",
|
||||
"@backstage/plugin-search-backend-module-catalog": "^0.1.13",
|
||||
"@backstage/plugin-search-backend-module-pg": "^0.5.18",
|
||||
"@backstage/plugin-search-backend-module-techdocs": "^0.1.13",
|
||||
"@backstage/plugin-search-backend-node": "^1.2.13",
|
||||
"@backstage/plugin-techdocs-backend": "^1.9.2",
|
||||
"@backstage/backend-common": "~0.20.1",
|
||||
"@backstage/backend-tasks": "~0.5.14",
|
||||
"@backstage/catalog-client": "~1.5.2",
|
||||
"@backstage/catalog-model": "~1.4.3",
|
||||
"@backstage/config": "~1.1.1",
|
||||
"@backstage/errors": "~1.2.3",
|
||||
"@backstage/integration": "~1.8.0",
|
||||
"@backstage/plugin-app-backend": "~0.3.57",
|
||||
"@backstage/plugin-auth-backend": "~0.20.3",
|
||||
"@backstage/plugin-auth-node": "~0.4.3",
|
||||
"@backstage/plugin-catalog-backend": "~1.16.1",
|
||||
"@backstage/plugin-catalog-backend-module-scaffolder-entity-model": "~0.1.6",
|
||||
"@backstage/plugin-kubernetes-backend": "~0.14.1",
|
||||
"@backstage/plugin-permission-common": "~0.7.12",
|
||||
"@backstage/plugin-permission-node": "~0.7.20",
|
||||
"@backstage/plugin-proxy-backend": "~0.4.7",
|
||||
"@backstage/plugin-scaffolder-backend": "~1.20.0",
|
||||
"@backstage/plugin-scaffolder-node": "~0.2.9",
|
||||
"@backstage/plugin-search-backend": "~1.4.9",
|
||||
"@backstage/plugin-search-backend-module-catalog": "~0.1.13",
|
||||
"@backstage/plugin-search-backend-module-pg": "~0.5.18",
|
||||
"@backstage/plugin-search-backend-module-techdocs": "~0.1.13",
|
||||
"@backstage/plugin-search-backend-node": "~1.2.13",
|
||||
"@backstage/plugin-techdocs-backend": "~1.9.2",
|
||||
"@backstage/types": "~1.1.1",
|
||||
"app": "link:../app",
|
||||
"better-sqlite3": "^9.0.0",
|
||||
"dockerode": "^3.3.1",
|
||||
|
|
|
@ -32,6 +32,8 @@ import { PluginEnvironment } from './types';
|
|||
import { ServerPermissionClient } from '@backstage/plugin-permission-node';
|
||||
import { DefaultIdentityClient } from '@backstage/plugin-auth-node';
|
||||
|
||||
import kubernetes from './plugins/kubernetes';
|
||||
|
||||
function makeCreateEnv(config: Config) {
|
||||
const root = getRootLogger();
|
||||
const reader = UrlReaders.default({ logger: root, config });
|
||||
|
@ -86,6 +88,8 @@ async function main() {
|
|||
const searchEnv = useHotMemoize(module, () => createEnv('search'));
|
||||
const appEnv = useHotMemoize(module, () => createEnv('app'));
|
||||
|
||||
const kubernetesEnv = useHotMemoize(module, () => createEnv('kubernetes'));
|
||||
|
||||
const apiRouter = Router();
|
||||
apiRouter.use('/catalog', await catalog(catalogEnv));
|
||||
apiRouter.use('/scaffolder', await scaffolder(scaffolderEnv));
|
||||
|
@ -94,6 +98,8 @@ async function main() {
|
|||
apiRouter.use('/proxy', await proxy(proxyEnv));
|
||||
apiRouter.use('/search', await search(searchEnv));
|
||||
|
||||
apiRouter.use('/kubernetes', await kubernetes(kubernetesEnv));
|
||||
|
||||
// Add backends ABOVE this line; this 404 handler is the catch-all fallback
|
||||
apiRouter.use(notFoundHandler());
|
||||
|
||||
|
|
|
@ -5,11 +5,16 @@ import {
|
|||
} from '@backstage/plugin-auth-backend';
|
||||
import { Router } from 'express';
|
||||
import { PluginEnvironment } from '../types';
|
||||
import {
|
||||
DEFAULT_NAMESPACE,
|
||||
stringifyEntityRef,
|
||||
} from '@backstage/catalog-model';
|
||||
import { JsonArray } from '@backstage/types';
|
||||
|
||||
export default async function createPlugin(
|
||||
env: PluginEnvironment,
|
||||
): Promise<Router> {
|
||||
return await createRouter({
|
||||
const opts = {
|
||||
logger: env.logger,
|
||||
config: env.config,
|
||||
database: env.database,
|
||||
|
@ -17,38 +22,36 @@ export default async function createPlugin(
|
|||
tokenManager: env.tokenManager,
|
||||
providerFactories: {
|
||||
...defaultAuthProviderFactories,
|
||||
|
||||
// This replaces the default GitHub auth provider with a customized one.
|
||||
// The `signIn` option enables sign-in for this provider, using the
|
||||
// identity resolution logic that's provided in the `resolver` callback.
|
||||
//
|
||||
// This particular resolver makes all users share a single "guest" identity.
|
||||
// It should only be used for testing and trying out Backstage.
|
||||
//
|
||||
// If you want to use a production ready resolver you can switch to
|
||||
// the one that is commented out below, it looks up a user entity in the
|
||||
// catalog using the GitHub username of the authenticated user.
|
||||
// That resolver requires you to have user entities populated in the catalog,
|
||||
// for example using https://backstage.io/docs/integrations/github/org
|
||||
//
|
||||
// There are other resolvers to choose from, and you can also create
|
||||
// your own, see the auth documentation for more details:
|
||||
//
|
||||
// https://backstage.io/docs/auth/identity-resolver
|
||||
github: providers.github.create({
|
||||
signIn: {
|
||||
resolver(_, ctx) {
|
||||
const userRef = 'user:default/guest'; // Must be a full entity reference
|
||||
return ctx.issueToken({
|
||||
claims: {
|
||||
sub: userRef, // The user's own identity
|
||||
ent: [userRef], // A list of identities that the user claims ownership through
|
||||
},
|
||||
});
|
||||
},
|
||||
// resolver: providers.github.resolvers.usernameMatchingUserEntityName(),
|
||||
},
|
||||
}),
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const envName = env.config
|
||||
.getOptionalConfig('auth')
|
||||
?.getOptionalString('auth');
|
||||
if (envName === 'local') {
|
||||
return await createRouter(opts);
|
||||
}
|
||||
|
||||
const keycloakAuth = (opts.providerFactories['keycloak-oidc'] =
|
||||
providers.oidc.create({
|
||||
signIn: {
|
||||
resolver(info, ctx) {
|
||||
const userRef = stringifyEntityRef({
|
||||
kind: 'User',
|
||||
name: info.result.userinfo.sub,
|
||||
namespace: DEFAULT_NAMESPACE,
|
||||
});
|
||||
return ctx.issueToken({
|
||||
claims: {
|
||||
sub: userRef,
|
||||
ent: [userRef],
|
||||
groups: (info.result.userinfo.groups as JsonArray) || [],
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
}));
|
||||
opts.providerFactories['keycloak-oidc'] = keycloakAuth;
|
||||
|
||||
return await createRouter(opts);
|
||||
}
|
||||
|
|
610
packages/backend/src/plugins/gitea-actions.ts
Normal file
610
packages/backend/src/plugins/gitea-actions.ts
Normal file
|
@ -0,0 +1,610 @@
|
|||
import { InputError } from '@backstage/errors';
|
||||
import { Config } from '@backstage/config';
|
||||
import {
|
||||
getGiteaRequestOptions,
|
||||
GiteaIntegrationConfig,
|
||||
ScmIntegrationRegistry, ScmIntegrations,
|
||||
} from '@backstage/integration';
|
||||
import {
|
||||
ActionContext,
|
||||
createTemplateAction,
|
||||
getRepoSourceDirectory,
|
||||
initRepoAndPush,
|
||||
TemplateExample
|
||||
} from '@backstage/plugin-scaffolder-node';
|
||||
import crypto from 'crypto';
|
||||
import yaml from 'yaml';
|
||||
|
||||
export const examples: TemplateExample[] = [
|
||||
{
|
||||
description:
|
||||
'Initializes a Gitea repository using the content of the workspace and publish it to Gitea with default configuration.',
|
||||
example: yaml.stringify({
|
||||
steps: [
|
||||
{
|
||||
id: 'publish',
|
||||
action: 'publish:gitea',
|
||||
name: 'Publish to Gitea',
|
||||
input: {
|
||||
repoUrl: 'gitea.com?repo=repo&owner=owner',
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
},
|
||||
{
|
||||
description: 'Initializes a Gitea repository with a description.',
|
||||
example: yaml.stringify({
|
||||
steps: [
|
||||
{
|
||||
id: 'publish',
|
||||
action: 'publish:gitea',
|
||||
name: 'Publish to Gitea',
|
||||
input: {
|
||||
repoUrl: 'gitea.com?repo=repo&owner=owner',
|
||||
description: 'Initialize a gitea repository',
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
},
|
||||
{
|
||||
description:
|
||||
'Initializes a Gitea repository with a default Branch, if not set defaults to main',
|
||||
example: yaml.stringify({
|
||||
steps: [
|
||||
{
|
||||
id: 'publish',
|
||||
action: 'publish:gitea',
|
||||
name: 'Publish to Gitea',
|
||||
input: {
|
||||
repoUrl: 'gitea.com?repo=repo&owner=owner',
|
||||
defaultBranch: 'main',
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
},
|
||||
{
|
||||
description:
|
||||
'Initializes a Gitea repository with an initial commit message, if not set defaults to initial commit',
|
||||
example: yaml.stringify({
|
||||
steps: [
|
||||
{
|
||||
id: 'publish',
|
||||
action: 'publish:gitea',
|
||||
name: 'Publish to Gitea',
|
||||
input: {
|
||||
repoUrl: 'gitea.com?repo=repo&owner=owner',
|
||||
gitCommitMessage: 'Initial Commit Message',
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
},
|
||||
{
|
||||
description:
|
||||
'Initializes a Gitea repository with a repo Author Name, if not set defaults to Scaffolder',
|
||||
example: yaml.stringify({
|
||||
steps: [
|
||||
{
|
||||
id: 'publish',
|
||||
action: 'publish:gitea',
|
||||
name: 'Publish to Gitea',
|
||||
input: {
|
||||
repoUrl: 'gitea.com?repo=repo&owner=owner',
|
||||
gitAuthorName: 'John Doe',
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
},
|
||||
{
|
||||
description: 'Initializes a Gitea repository with a repo Author Email',
|
||||
example: yaml.stringify({
|
||||
steps: [
|
||||
{
|
||||
id: 'publish',
|
||||
action: 'publish:gitea',
|
||||
name: 'Publish to Gitea',
|
||||
input: {
|
||||
repoUrl: 'gitea.com?repo=repo&owner=owner',
|
||||
gitAuthorEmail: 'johndoe@email.com',
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
},
|
||||
{
|
||||
description:
|
||||
'Path within the workspace that will be used as the repository root. If omitted, the entire workspace will be published as the repository',
|
||||
example: yaml.stringify({
|
||||
steps: [
|
||||
{
|
||||
id: 'publish',
|
||||
action: 'publish:gitea',
|
||||
name: 'Publish to Gitea',
|
||||
input: {
|
||||
repoUrl: 'gitea.com?repo=repo&owner=owner',
|
||||
sourcePath: 'repository/',
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
},
|
||||
{
|
||||
description: 'Initializes a Gitea repository with all properties being set',
|
||||
example: yaml.stringify({
|
||||
steps: [
|
||||
{
|
||||
id: 'publish',
|
||||
action: 'publish:gitea',
|
||||
name: 'Publish to Gitea',
|
||||
input: {
|
||||
repoUrl: 'gitea.com?repo=repo&owner=owner',
|
||||
description: 'Initialize a gitea repository',
|
||||
defaultBranch: 'staging',
|
||||
gitCommitMessage: 'Initial Commit Message',
|
||||
gitAuthorName: 'John Doe',
|
||||
gitAuthorEmail: 'johndoe@email.com',
|
||||
sourcePath: 'repository/',
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
},
|
||||
];
|
||||
|
||||
const parseRepoUrl = (
|
||||
repoUrl: string,
|
||||
integrations: ScmIntegrationRegistry,
|
||||
): {
|
||||
repo: string;
|
||||
host: string;
|
||||
owner?: string;
|
||||
organization?: string;
|
||||
workspace?: string;
|
||||
project?: string;
|
||||
} => {
|
||||
let parsed;
|
||||
try {
|
||||
parsed = new URL(`https://${repoUrl}`);
|
||||
} catch (error) {
|
||||
throw new InputError(
|
||||
`Invalid repo URL passed to publisher, got ${repoUrl}, ${error}`,
|
||||
);
|
||||
}
|
||||
const host = parsed.host;
|
||||
const owner = parsed.searchParams.get('owner') ?? undefined;
|
||||
const organization = parsed.searchParams.get('organization') ?? undefined;
|
||||
const workspace = parsed.searchParams.get('workspace') ?? undefined;
|
||||
const project = parsed.searchParams.get('project') ?? undefined;
|
||||
|
||||
const type = integrations.byHost(host)?.type;
|
||||
|
||||
if (!type) {
|
||||
throw new InputError(
|
||||
`No matching integration configuration for host ${host}, please check your integrations config`,
|
||||
);
|
||||
}
|
||||
|
||||
const repo: string = parsed.searchParams.get('repo')!;
|
||||
switch (type) {
|
||||
case 'bitbucket': {
|
||||
if (host === 'www.bitbucket.org') {
|
||||
checkRequiredParams(parsed, 'workspace');
|
||||
}
|
||||
checkRequiredParams(parsed, 'project', 'repo');
|
||||
break;
|
||||
}
|
||||
case 'gitlab': {
|
||||
// project is the projectID, and if defined, owner and repo won't be needed.
|
||||
if (!project) {
|
||||
checkRequiredParams(parsed, 'owner', 'repo');
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'gitea': {
|
||||
checkRequiredParams(parsed, 'repo');
|
||||
break;
|
||||
}
|
||||
case 'gerrit': {
|
||||
checkRequiredParams(parsed, 'repo');
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
checkRequiredParams(parsed, 'repo', 'owner');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return { host, owner, repo, organization, workspace, project };
|
||||
};
|
||||
|
||||
function checkRequiredParams(repoUrl: URL, ...params: string[]) {
|
||||
for (let i = 0; i < params.length; i++) {
|
||||
if (!repoUrl.searchParams.get(params[i])) {
|
||||
throw new InputError(
|
||||
`Invalid repo URL passed to publisher: ${repoUrl.toString()}, missing ${
|
||||
params[i]
|
||||
}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
const checkGiteaContentUrl = async (
|
||||
config: GiteaIntegrationConfig,
|
||||
options: {
|
||||
owner?: string;
|
||||
repo: string;
|
||||
defaultBranch?: string;
|
||||
},
|
||||
): Promise<Response> => {
|
||||
const { owner, repo, defaultBranch } = options;
|
||||
let response: Response;
|
||||
const getOptions: RequestInit = {
|
||||
method: 'GET',
|
||||
};
|
||||
|
||||
try {
|
||||
response = await fetch(
|
||||
`${config.baseUrl}/${owner}/${repo}/src/branch/${defaultBranch}`,
|
||||
getOptions,
|
||||
);
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
`Unable to get the repository: ${owner}/${repo} metadata , ${e}`,
|
||||
);
|
||||
}
|
||||
return response;
|
||||
};
|
||||
|
||||
const checkGiteaOrg = async (
|
||||
config: GiteaIntegrationConfig,
|
||||
options: {
|
||||
owner: string;
|
||||
},
|
||||
): Promise<void> => {
|
||||
const { owner } = options;
|
||||
let response: Response;
|
||||
// check first if the org = owner exists
|
||||
const getOptions: RequestInit = {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
...getGiteaRequestOptions(config).headers,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
};
|
||||
try {
|
||||
response = await fetch(
|
||||
`${config.baseUrl}/api/v1/orgs/${owner}`,
|
||||
getOptions,
|
||||
);
|
||||
} catch (e) {
|
||||
throw new Error(`Unable to get the Organization: ${owner}, ${e}`);
|
||||
}
|
||||
if (response.status !== 200) {
|
||||
throw new Error(
|
||||
`Organization ${owner} do not exist. Please create it first !`,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const createGiteaProject = async (
|
||||
config: GiteaIntegrationConfig,
|
||||
options: {
|
||||
projectName: string;
|
||||
owner?: string;
|
||||
description: string;
|
||||
},
|
||||
): Promise<void> => {
|
||||
const { projectName, description, owner } = options;
|
||||
|
||||
/*
|
||||
Several options exist to create a repository using either the user or organisation
|
||||
User: https://gitea.com/api/swagger#/user/createCurrentUserRepo
|
||||
Api: URL/api/v1/user/repos
|
||||
Remark: The user is the username defined part of the backstage integration config for the gitea URL !
|
||||
|
||||
Org: https://gitea.com/api/swagger#/organization/createOrgRepo
|
||||
Api: URL/api/v1/orgs/${org_owner}/repos
|
||||
This is the default scenario that we support currently
|
||||
*/
|
||||
let response: Response;
|
||||
|
||||
const postOptions: RequestInit = {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
name: projectName,
|
||||
description,
|
||||
}),
|
||||
headers: {
|
||||
...getGiteaRequestOptions(config).headers,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
};
|
||||
if (owner) {
|
||||
try {
|
||||
response = await fetch(
|
||||
`${config.baseUrl}/api/v1/orgs/${owner}/repos`,
|
||||
postOptions,
|
||||
);
|
||||
} catch (e) {
|
||||
throw new Error(`Unable to create repository, ${e}`);
|
||||
}
|
||||
if (response.status !== 201) {
|
||||
throw new Error(
|
||||
`Unable to create repository, ${response.status} ${
|
||||
response.statusText
|
||||
}, ${await response.text()}`,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
response = await fetch(
|
||||
`${config.baseUrl}/api/v1/user/repos`,
|
||||
postOptions,
|
||||
);
|
||||
} catch (e) {
|
||||
throw new Error(`Unable to create repository, ${e}`);
|
||||
}
|
||||
if (response.status !== 201) {
|
||||
throw new Error(
|
||||
`Unable to create repository, ${response.status} ${
|
||||
response.statusText
|
||||
}, ${await response.text()}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
const generateCommitMessage = (
|
||||
config: Config,
|
||||
commitSubject?: string,
|
||||
): string => {
|
||||
const changeId = crypto.randomBytes(20).toString('hex');
|
||||
const msg = `${
|
||||
config.getOptionalString('scaffolder.defaultCommitMessage') || commitSubject
|
||||
}\n\nChange-Id: I${changeId}`;
|
||||
return msg;
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if the provided function can be executed within a specific period of time limit.
|
||||
* @param fn
|
||||
* @param timeLimit
|
||||
*/
|
||||
async function checkDurationLimit(fn: () => void, timeLimit: number): Promise<boolean> {
|
||||
|
||||
const startTime = process.hrtime();
|
||||
|
||||
// Call the function
|
||||
await fn();
|
||||
|
||||
const endTime = process.hrtime(startTime);
|
||||
const durationInMs = endTime[0] * 1000 + endTime[1] / 1e6;
|
||||
|
||||
// Check if the duration exceeds the time limit
|
||||
return durationInMs <= timeLimit;
|
||||
}
|
||||
|
||||
async function checkAvailabilityGiteaRepository(
|
||||
integrationConfig: GiteaIntegrationConfig,
|
||||
options: {
|
||||
owner?: string;
|
||||
repo: string;
|
||||
defaultBranch: string;
|
||||
ctx: ActionContext<any>;
|
||||
},
|
||||
) {
|
||||
const { owner, repo, defaultBranch, ctx } = options;
|
||||
const sleep = (ms: number | undefined) => new Promise(r => setTimeout(r, ms));
|
||||
let response: Response;
|
||||
|
||||
const p = new Promise<void>((resolve, reject) => {
|
||||
setTimeout(async () => {
|
||||
response = await checkGiteaContentUrl(integrationConfig, {
|
||||
owner,
|
||||
repo,
|
||||
defaultBranch,
|
||||
});
|
||||
|
||||
while (response.status !== 200) {
|
||||
if (ctx.signal?.aborted) return;
|
||||
await sleep(1000);
|
||||
response = await checkGiteaContentUrl(integrationConfig, {
|
||||
owner,
|
||||
repo,
|
||||
defaultBranch,
|
||||
});
|
||||
}
|
||||
resolve()
|
||||
},
|
||||
5000
|
||||
)
|
||||
})
|
||||
return p
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new action that initializes a git repository using the content of the workspace.
|
||||
* and publishes it to a Gitea instance.
|
||||
* @public
|
||||
*/
|
||||
export function createPublishGiteaAction(options: {
|
||||
integrations: ScmIntegrations;
|
||||
config: Config;
|
||||
}) {
|
||||
const { integrations, config } = options;
|
||||
|
||||
return createTemplateAction<{
|
||||
repoUrl: string;
|
||||
description: string;
|
||||
defaultBranch?: string;
|
||||
gitCommitMessage?: string;
|
||||
gitAuthorName?: string;
|
||||
gitAuthorEmail?: string;
|
||||
sourcePath?: string;
|
||||
}>({
|
||||
id: 'publish:gitea',
|
||||
description:
|
||||
'Initializes a git repository using the content of the workspace, and publishes it to Gitea.',
|
||||
examples,
|
||||
schema: {
|
||||
input: {
|
||||
type: 'object',
|
||||
required: ['repoUrl'],
|
||||
properties: {
|
||||
repoUrl: {
|
||||
title: 'Repository Location',
|
||||
type: 'string',
|
||||
},
|
||||
description: {
|
||||
title: 'Repository Description',
|
||||
type: 'string',
|
||||
},
|
||||
defaultBranch: {
|
||||
title: 'Default Branch',
|
||||
type: 'string',
|
||||
description: `Sets the default branch on the repository. The default value is 'main'`,
|
||||
},
|
||||
gitCommitMessage: {
|
||||
title: 'Git Commit Message',
|
||||
type: 'string',
|
||||
description: `Sets the commit message on the repository. The default value is 'initial commit'`,
|
||||
},
|
||||
gitAuthorName: {
|
||||
title: 'Default Author Name',
|
||||
type: 'string',
|
||||
description: `Sets the default author name for the commit. The default value is 'Scaffolder'`,
|
||||
},
|
||||
gitAuthorEmail: {
|
||||
title: 'Default Author Email',
|
||||
type: 'string',
|
||||
description: `Sets the default author email for the commit.`,
|
||||
},
|
||||
sourcePath: {
|
||||
title: 'Source Path',
|
||||
type: 'string',
|
||||
description: `Path within the workspace that will be used as the repository root. If omitted, the entire workspace will be published as the repository.`,
|
||||
},
|
||||
},
|
||||
},
|
||||
output: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
remoteUrl: {
|
||||
title: 'A URL to the repository with the provider',
|
||||
type: 'string',
|
||||
},
|
||||
repoContentsUrl: {
|
||||
title: 'A URL to the root of the repository',
|
||||
type: 'string',
|
||||
},
|
||||
commitHash: {
|
||||
title: 'The git commit hash of the initial commit',
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
async handler(ctx) {
|
||||
|
||||
const {
|
||||
repoUrl,
|
||||
description,
|
||||
defaultBranch = 'main',
|
||||
gitAuthorName,
|
||||
gitAuthorEmail,
|
||||
gitCommitMessage = 'initial commit',
|
||||
sourcePath,
|
||||
} = ctx.input;
|
||||
const { repo, host, owner } = parseRepoUrl(repoUrl, integrations);
|
||||
const integrationConfig = integrations.gitea.byHost(host);
|
||||
if (!integrationConfig) {
|
||||
throw new InputError(
|
||||
`No matching integration configuration for host ${host}, please check your integrations config`,
|
||||
);
|
||||
}
|
||||
const { username, password } = integrationConfig.config;
|
||||
|
||||
if (!username || !password) {
|
||||
throw new Error('Credentials for the gitea ${host} required.');
|
||||
}
|
||||
|
||||
// check if the org exists within the gitea server
|
||||
if (owner && owner !== username) {
|
||||
await checkGiteaOrg(integrationConfig.config, { owner });
|
||||
}
|
||||
|
||||
await createGiteaProject(integrationConfig.config, {
|
||||
description,
|
||||
owner: owner,
|
||||
projectName: repo,
|
||||
});
|
||||
|
||||
const auth = {
|
||||
username: username,
|
||||
password: password,
|
||||
};
|
||||
const gitAuthorInfo = {
|
||||
name: gitAuthorName
|
||||
? gitAuthorName
|
||||
: config.getOptionalString('scaffolder.defaultAuthor.name'),
|
||||
email: gitAuthorEmail
|
||||
? gitAuthorEmail
|
||||
: config.getOptionalString('scaffolder.defaultAuthor.email'),
|
||||
};
|
||||
// The owner to be used should be either the org name or user authenticated with the gitea server
|
||||
const repoOwner = owner ? owner: username
|
||||
const remoteUrl = `${integrationConfig.config.baseUrl}/${repoOwner}/${repo}.git`;
|
||||
const commitResult = await initRepoAndPush({
|
||||
dir: getRepoSourceDirectory(ctx.workspacePath, sourcePath),
|
||||
remoteUrl,
|
||||
auth,
|
||||
defaultBranch,
|
||||
logger: ctx.logger,
|
||||
commitMessage: generateCommitMessage(config, gitCommitMessage),
|
||||
gitAuthorInfo,
|
||||
});
|
||||
|
||||
// Check if the gitea repo URL is available before to exit
|
||||
const operationTimeLimit = 5000; // 20 seconds
|
||||
const sleep = (ms: number | undefined) => new Promise(r => setTimeout(r, ms));
|
||||
await sleep(operationTimeLimit);
|
||||
// await checkAvailabilityGiteaRepository(
|
||||
// integrationConfig.config, {
|
||||
// repoOwner,
|
||||
// repo,
|
||||
// defaultBranch,
|
||||
// ctx,
|
||||
// }
|
||||
// )
|
||||
// const checkDuration = await checkDurationLimit(
|
||||
// async () =>
|
||||
// await checkAvailabilityGiteaRepository(integrationConfig.config, {
|
||||
// repoOwner,
|
||||
// repo,
|
||||
// defaultBranch,
|
||||
// ctx,
|
||||
// }),
|
||||
// operationTimeLimit,
|
||||
// );
|
||||
//
|
||||
// if (!checkDuration) {
|
||||
// console.log('Operation exceeded the time limit.');
|
||||
// }
|
||||
|
||||
const repoContentsUrl = `${integrationConfig.config.baseUrl}/${repoOwner}/${repo}/src/branch/${defaultBranch}/`;
|
||||
ctx.output('remoteUrl', remoteUrl);
|
||||
ctx.output('commitHash', commitResult?.commitHash);
|
||||
ctx.output('repoContentsUrl', repoContentsUrl);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
18
packages/backend/src/plugins/kubernetes.ts
Normal file
18
packages/backend/src/plugins/kubernetes.ts
Normal file
|
@ -0,0 +1,18 @@
|
|||
import { KubernetesBuilder } from '@backstage/plugin-kubernetes-backend';
|
||||
import { Router } from 'express';
|
||||
import { PluginEnvironment } from '../types';
|
||||
import { CatalogClient } from '@backstage/catalog-client';
|
||||
|
||||
export default async function createPlugin(
|
||||
env: PluginEnvironment,
|
||||
): Promise<Router> {
|
||||
const catalogApi = new CatalogClient({ discoveryApi: env.discovery });
|
||||
const { router } = await KubernetesBuilder.createBuilder({
|
||||
logger: env.logger,
|
||||
config: env.config,
|
||||
catalogApi,
|
||||
permissions: env.permissions,
|
||||
}).build();
|
||||
|
||||
return router;
|
||||
}
|
|
@ -1,7 +1,9 @@
|
|||
import { CatalogClient } from '@backstage/catalog-client';
|
||||
import { createRouter } from '@backstage/plugin-scaffolder-backend';
|
||||
import {createBuiltinActions, createRouter} from '@backstage/plugin-scaffolder-backend';
|
||||
import { Router } from 'express';
|
||||
import type { PluginEnvironment } from '../types';
|
||||
import { ScmIntegrations } from '@backstage/integration';
|
||||
import {createPublishGiteaAction} from "./gitea-actions";
|
||||
|
||||
export default async function createPlugin(
|
||||
env: PluginEnvironment,
|
||||
|
@ -10,7 +12,28 @@ export default async function createPlugin(
|
|||
discoveryApi: env.discovery,
|
||||
});
|
||||
|
||||
const integrations = ScmIntegrations.fromConfig(env.config);
|
||||
const builtInActions = createBuiltinActions({
|
||||
integrations,
|
||||
catalogClient,
|
||||
config: env.config,
|
||||
reader: env.reader,
|
||||
});
|
||||
|
||||
const options = {
|
||||
integrations: integrations,
|
||||
config: env.config,
|
||||
}
|
||||
|
||||
const cnoeActions = [createPublishGiteaAction(options)]
|
||||
|
||||
const actions = [
|
||||
...builtInActions,
|
||||
...cnoeActions,
|
||||
];
|
||||
|
||||
return await createRouter({
|
||||
actions: actions,
|
||||
logger: env.logger,
|
||||
config: env.config,
|
||||
database: env.database,
|
||||
|
|
1
plugins/cnoe-ui/.eslintrc.js
Normal file
1
plugins/cnoe-ui/.eslintrc.js
Normal file
|
@ -0,0 +1 @@
|
|||
module.exports = require('@backstage/cli/config/eslint-factory')(__dirname);
|
13
plugins/cnoe-ui/README.md
Normal file
13
plugins/cnoe-ui/README.md
Normal file
|
@ -0,0 +1,13 @@
|
|||
# cnoe-ui
|
||||
|
||||
Welcome to the cnoe-ui plugin!
|
||||
|
||||
_This plugin was created through the Backstage CLI_
|
||||
|
||||
## Getting started
|
||||
|
||||
Your plugin has been added to the example app in this repository, meaning you'll be able to access it by running `yarn start` in the root directory, and then navigating to [/cnoe-ui](http://localhost:3000/cnoe-ui).
|
||||
|
||||
You can also serve the plugin in isolation by running `yarn start` in the plugin directory.
|
||||
This method of serving the plugin provides quicker iteration speed and a faster startup and hot reloads.
|
||||
It is only meant for local development, and the setup for it can be found inside the [/dev](./dev) directory.
|
12
plugins/cnoe-ui/dev/index.tsx
Normal file
12
plugins/cnoe-ui/dev/index.tsx
Normal file
|
@ -0,0 +1,12 @@
|
|||
import React from 'react';
|
||||
import { createDevApp } from '@backstage/dev-utils';
|
||||
import { cnoeUiPlugin, CnoeUiPage } from '../src/plugin';
|
||||
|
||||
createDevApp()
|
||||
.registerPlugin(cnoeUiPlugin)
|
||||
.addPage({
|
||||
element: <CnoeUiPage />,
|
||||
title: 'Root Page',
|
||||
path: '/cnoe-ui'
|
||||
})
|
||||
.render();
|
51
plugins/cnoe-ui/package.json
Normal file
51
plugins/cnoe-ui/package.json
Normal file
|
@ -0,0 +1,51 @@
|
|||
{
|
||||
"name": "@internal/plugin-cnoe-ui",
|
||||
"version": "0.1.0",
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"license": "Apache-2.0",
|
||||
"private": true,
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"main": "dist/index.esm.js",
|
||||
"types": "dist/index.d.ts"
|
||||
},
|
||||
"backstage": {
|
||||
"role": "frontend-plugin"
|
||||
},
|
||||
"sideEffects": false,
|
||||
"scripts": {
|
||||
"start": "backstage-cli package start",
|
||||
"build": "backstage-cli package build",
|
||||
"lint": "backstage-cli package lint",
|
||||
"test": "backstage-cli package test",
|
||||
"clean": "backstage-cli package clean",
|
||||
"prepack": "backstage-cli package prepack",
|
||||
"postpack": "backstage-cli package postpack"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/core-components": "^0.13.10",
|
||||
"@backstage/core-plugin-api": "^1.8.2",
|
||||
"@backstage/theme": "^0.5.0",
|
||||
"@material-ui/core": "^4.9.13",
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
"@material-ui/lab": "^4.0.0-alpha.61",
|
||||
"react-use": "^17.2.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.13.1 || ^17.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.25.1",
|
||||
"@backstage/core-app-api": "^1.11.3",
|
||||
"@backstage/dev-utils": "^1.0.26",
|
||||
"@backstage/test-utils": "^1.4.7",
|
||||
"@testing-library/jest-dom": "^5.10.1",
|
||||
"@testing-library/react": "^12.1.3",
|
||||
"@testing-library/user-event": "^14.0.0",
|
||||
"msw": "^1.0.0"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
]
|
||||
}
|
83
plugins/cnoe-ui/src/components/Homepage.tsx
Normal file
83
plugins/cnoe-ui/src/components/Homepage.tsx
Normal file
|
@ -0,0 +1,83 @@
|
|||
import { Content, Page } from '@backstage/core-components';
|
||||
import { HomePageSearchBar } from '@backstage/plugin-search';
|
||||
import { SearchContextProvider } from '@backstage/plugin-search-react';
|
||||
import { Grid, makeStyles } from '@material-ui/core';
|
||||
import React from 'react';
|
||||
|
||||
import {
|
||||
HomePageToolkit,
|
||||
HomePageCompanyLogo,
|
||||
HomePageStarredEntities,
|
||||
TemplateBackstageLogoIcon,
|
||||
} from '@backstage/plugin-home';
|
||||
|
||||
import {
|
||||
LogoBig,
|
||||
} from './logos';
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
searchBar: {
|
||||
display: 'flex',
|
||||
maxWidth: '60vw',
|
||||
backgroundColor: theme.palette.background.paper,
|
||||
boxShadow: theme.shadows[1],
|
||||
padding: '8px 0',
|
||||
borderRadius: '50px',
|
||||
margin: 'auto',
|
||||
},
|
||||
}));
|
||||
|
||||
const useLogoStyles = makeStyles(theme => ({
|
||||
container: {
|
||||
margin: theme.spacing(5, 0),
|
||||
},
|
||||
svg: {
|
||||
width: 'auto',
|
||||
height: 100,
|
||||
},
|
||||
path: {
|
||||
fill: '#00568c',
|
||||
},
|
||||
}));
|
||||
|
||||
export const CNOEHomepage = () => {
|
||||
const classes = useStyles();
|
||||
const { container } = useLogoStyles();
|
||||
|
||||
return (
|
||||
<SearchContextProvider>
|
||||
<Page themeId="home">
|
||||
<Content>
|
||||
<Grid container justifyContent="center" spacing={6}>
|
||||
<HomePageCompanyLogo className={container} logo={<LogoBig />} />
|
||||
<Grid container item xs={12} alignItems="center" direction="row">
|
||||
<HomePageSearchBar classes={{ root: classes.searchBar }} placeholder="Search" />
|
||||
</Grid>
|
||||
<Grid container item xs={12}>
|
||||
<Grid item xs={12} md={6}>
|
||||
<HomePageStarredEntities />
|
||||
</Grid>
|
||||
<Grid item xs={12} md={6}>
|
||||
<HomePageToolkit
|
||||
title="Quick Links"
|
||||
tools={[
|
||||
{
|
||||
url: '/catalog',
|
||||
label: 'Catalog',
|
||||
icon: <TemplateBackstageLogoIcon/>,
|
||||
},
|
||||
{
|
||||
url: '/docs',
|
||||
label: 'Tech Docs',
|
||||
icon: <TemplateBackstageLogoIcon />,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Content>
|
||||
</Page>
|
||||
</SearchContextProvider>
|
||||
);
|
||||
};
|
56
plugins/cnoe-ui/src/components/logos/LogoBig.tsx
Normal file
56
plugins/cnoe-ui/src/components/logos/LogoBig.tsx
Normal file
|
@ -0,0 +1,56 @@
|
|||
import React from 'react';
|
||||
import { makeStyles } from '@material-ui/core';
|
||||
|
||||
const useStyles = makeStyles({
|
||||
svg: {
|
||||
width: 'auto',
|
||||
height: 190,
|
||||
},
|
||||
whitePath: {
|
||||
fill: '#ffffff',
|
||||
stroke: 'none',
|
||||
},
|
||||
bluePath: {
|
||||
fill: '#00568c',
|
||||
stroke: 'none',
|
||||
},
|
||||
cyanPath: {
|
||||
fill: '#00adee',
|
||||
stroke: 'none',
|
||||
},
|
||||
});
|
||||
|
||||
export const LogoBig = () => {
|
||||
const classes = useStyles();
|
||||
|
||||
return (
|
||||
<svg
|
||||
className={classes.svg}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 2079.95 850.05"
|
||||
>
|
||||
<g><path className={classes.cyanPath} d="M 923.5,136.5 C 928.809,135.565 932.309,137.565 934,142.5C 947.791,206.068 948.457,269.735 936,333.5C 923.041,396.634 891.874,448.468 842.5,489C 832.527,492.024 828.694,488.524 831,478.5C 881.237,437.163 911.904,384.163 923,319.5C 929.867,281.3 931.201,242.966 927,204.5C 849.896,286.335 757.063,343.169 648.5,375C 560.306,401.762 470.306,412.428 378.5,407C 374.251,402.199 374.584,397.866 379.5,394C 493.995,398.085 604.329,378.751 710.5,336C 794.01,301.471 865.51,250.305 925,182.5C 924.752,176.101 923.752,169.768 922,163.5C 879.994,213.56 830.16,253.726 772.5,284C 654.362,345.618 528.696,374.618 395.5,371C 379.341,370.192 363.674,367.026 348.5,361.5C 354.504,394.851 367.67,425.184 388,452.5C 394,459.833 400.667,466.5 408,472.5C 410.323,482.507 406.489,486.007 396.5,483C 371.585,461.039 353.751,434.206 343,402.5C 336.28,383.953 331.28,364.953 328,345.5C 324.768,332.813 328.601,322.98 339.5,316C 350.829,310.223 362.829,307.89 375.5,309C 380.313,313.041 380.646,317.375 376.5,322C 366.116,323.176 356.116,325.843 346.5,330C 340.55,336.251 341.55,341.251 349.5,345C 365.81,351.729 382.81,355.396 400.5,356C 530.3,358.299 652.633,329.299 767.5,269C 829.235,236.299 881.235,192.132 923.5,136.5 Z"/></g>
|
||||
<g><path className={classes.cyanPath} d="M 659.5,164.5 C 714.308,178.702 768.974,193.536 823.5,209C 826.394,211.909 827.227,215.409 826,219.5C 823.588,222.792 820.421,223.959 816.5,223C 767.741,209.802 719.075,196.302 670.5,182.5C 669.668,192.483 669.168,202.483 669,212.5C 663.851,217.996 659.184,217.663 655,211.5C 654.333,197.5 654.333,183.5 655,169.5C 656.025,167.313 657.525,165.646 659.5,164.5 Z"/></g>
|
||||
<g><path className={classes.bluePath} d="M 568.5,192.5 C 585.66,194.231 602.66,197.065 619.5,201C 635.167,211.333 650.833,221.667 666.5,232C 667.126,232.75 667.626,233.584 668,234.5C 668.667,257.167 668.667,279.833 668,302.5C 663.829,308.729 659.163,309.062 654,303.5C 653.833,282.825 653.333,262.159 652.5,241.5C 642.742,235.539 633.242,229.205 624,222.5C 623.667,253.833 623.333,285.167 623,316.5C 618.333,321.833 613.667,321.833 609,316.5C 608.5,282.502 608.333,248.502 608.5,214.5C 599.213,212.576 589.88,210.909 580.5,209.5C 580.667,248.168 580.5,286.835 580,325.5C 575.829,331.729 571.163,332.062 566,326.5C 565.667,288.833 565.333,251.167 565,213.5C 558.639,218.097 551.805,221.763 544.5,224.5C 537.956,223.089 535.789,219.089 538,212.5C 538.833,211.667 539.667,210.833 540.5,210C 550.066,204.392 559.399,198.559 568.5,192.5 Z"/></g>
|
||||
<g><path className={classes.cyanPath} d="M 490.5,212.5 C 504.026,214.715 517.36,217.881 530.5,222C 533.102,222.935 534.602,224.768 535,227.5C 535.667,263.167 535.667,298.833 535,334.5C 532.567,340.448 528.4,341.948 522.5,339C 521.107,337.829 520.273,336.329 520,334.5C 519.5,301.168 519.333,267.835 519.5,234.5C 512.102,232.981 504.769,231.481 497.5,230C 492.207,238.751 487.041,247.585 482,256.5C 481.667,285.833 481.333,315.167 481,344.5C 478.612,349.226 474.778,350.726 469.5,349C 468.667,348.167 467.833,347.333 467,346.5C 466.333,314.833 466.333,283.167 467,251.5C 474.342,238.14 482.175,225.14 490.5,212.5 Z"/></g>
|
||||
<g><path className={classes.bluePath} d="M 432.5,231.5 C 442.172,231.334 451.839,231.5 461.5,232C 467.259,235.892 467.926,240.559 463.5,246C 456.254,247.391 448.921,247.891 441.5,247.5C 441.667,280.502 441.5,313.502 441,346.5C 437.004,351.3 432.67,351.633 428,347.5C 426.346,310.585 426.013,273.585 427,236.5C 428.107,233.887 429.94,232.22 432.5,231.5 Z"/></g>
|
||||
<g><path className={classes.bluePath} d="M 392.5,265.5 C 400.507,265.334 408.507,265.5 416.5,266C 424.5,271 424.5,276 416.5,281C 411.511,281.499 406.511,281.666 401.5,281.5C 401.667,302.503 401.5,323.503 401,344.5C 396.333,349.833 391.667,349.833 387,344.5C 386.333,319.5 386.333,294.5 387,269.5C 388.5,267.531 390.333,266.198 392.5,265.5 Z"/></g>
|
||||
<g><path className={classes.bluePath} d="M 898.5,452.5 C 942.835,452.333 987.168,452.5 1031.5,453C 1034.37,454.393 1035.7,456.726 1035.5,460C 1035.7,463.274 1034.37,465.607 1031.5,467C 987.167,467.667 942.833,467.667 898.5,467C 895.634,465.607 894.301,463.274 894.5,460C 894.43,456.634 895.763,454.134 898.5,452.5 Z"/></g>
|
||||
<g><path className={classes.bluePath} d="M 243.5,464.5 C 285.501,464.333 327.501,464.5 369.5,465C 372.366,466.393 373.699,468.726 373.5,472C 373.699,475.274 372.366,477.607 369.5,479C 327.5,479.667 285.5,479.667 243.5,479C 240.484,477.471 239.151,474.971 239.5,471.5C 239.197,468.156 240.53,465.822 243.5,464.5 Z"/></g>
|
||||
<g><path className={classes.bluePath} d="M 925.5,479.5 C 944.503,479.333 963.503,479.5 982.5,480C 988.509,484.314 988.843,488.981 983.5,494C 963.5,494.667 943.5,494.667 923.5,494C 919.002,488.32 919.669,483.487 925.5,479.5 Z"/></g>
|
||||
<g><path className={classes.bluePath} d="M 235.5,490.5 C 260.502,490.333 285.502,490.5 310.5,491C 314.136,493.039 315.802,496.206 315.5,500.5C 404.501,500.333 493.501,500.5 582.5,501C 588.729,505.171 589.062,509.837 583.5,515C 490.167,515.667 396.833,515.667 303.5,515C 300.38,512.592 299.047,509.426 299.5,505.5C 277.831,505.667 256.164,505.5 234.5,505C 231.606,502.091 230.773,498.591 232,494.5C 232.69,492.65 233.856,491.316 235.5,490.5 Z"/></g>
|
||||
<g><path className={classes.bluePath} d="M 614.5,498.5 C 714.167,498.333 813.834,498.5 913.5,499C 918.535,502.013 919.702,506.18 917,511.5C 916.25,512.126 915.416,512.626 914.5,513C 814.167,513.667 713.833,513.667 613.5,513C 608.003,507.683 608.336,502.85 614.5,498.5 Z"/></g>
|
||||
<g><path className={classes.bluePath} d="M 566.5,526.5 C 588.169,526.333 609.836,526.5 631.5,527C 637.786,529.362 639.453,533.529 636.5,539.5C 635.335,540.584 634.002,541.417 632.5,542C 610.167,542.667 587.833,542.667 565.5,542C 560.774,539.612 559.274,535.778 561,530.5C 562.5,528.531 564.333,527.198 566.5,526.5 Z"/></g>
|
||||
<g><path className={classes.bluePath} d="M 276.5,527.5 C 314.501,527.333 352.501,527.5 390.5,528C 396.552,532.223 396.885,536.889 391.5,542C 353.167,542.667 314.833,542.667 276.5,542C 272.788,539.487 271.622,535.987 273,531.5C 274.376,530.295 275.542,528.962 276.5,527.5 Z"/></g>
|
||||
<g><path className={classes.bluePath} d="M 738.5,532.5 C 777.168,532.333 815.835,532.5 854.5,533C 860.062,538.163 859.729,542.829 853.5,547C 815.167,547.667 776.833,547.667 738.5,547C 735.634,545.607 734.301,543.274 734.5,540C 734.43,536.634 735.763,534.134 738.5,532.5 Z"/></g>
|
||||
<g><path className={classes.bluePath} d="M 355.5,556.5 C 438.167,556.333 520.834,556.5 603.5,557C 609.075,562.115 608.742,566.781 602.5,571C 520.5,571.667 438.5,571.667 356.5,571C 350.367,566.613 350.033,561.78 355.5,556.5 Z"/></g>
|
||||
<g><path className={classes.bluePath} d="M 844.5,558.5 C 880.502,558.333 916.502,558.5 952.5,559C 957.833,563.667 957.833,568.333 952.5,573C 916.833,573.667 881.167,573.667 845.5,573C 839.231,568.657 838.898,563.824 844.5,558.5 Z"/></g>
|
||||
<g><path className={classes.cyanPath} d="M 197.5,608.5 C 245.833,608.5 294.167,608.5 342.5,608.5C 342.5,622.5 342.5,636.5 342.5,650.5C 296.832,650.333 251.165,650.5 205.5,651C 192.103,652.4 183.603,659.566 180,672.5C 178,698.5 178,724.5 180,750.5C 183.451,762.947 191.618,770.113 204.5,772C 250.499,772.5 296.499,772.667 342.5,772.5C 342.5,786.5 342.5,800.5 342.5,814.5C 293.499,814.667 244.499,814.5 195.5,814C 161.97,812.133 142.803,794.633 138,761.5C 137.333,728.167 137.333,694.833 138,661.5C 140.962,636.219 154.462,619.719 178.5,612C 184.933,610.597 191.267,609.43 197.5,608.5 Z"/></g>
|
||||
<g><path className={classes.cyanPath} d="M 399.5,608.5 C 413.837,608.333 428.171,608.5 442.5,609C 483.022,655.888 523.189,703.054 563,750.5C 563.5,703.168 563.667,655.835 563.5,608.5C 577.5,608.5 591.5,608.5 605.5,608.5C 605.5,677.167 605.5,745.833 605.5,814.5C 591.163,814.667 576.829,814.5 562.5,814C 522.215,767.26 482.048,720.426 442,673.5C 441.5,720.499 441.333,767.499 441.5,814.5C 427.5,814.5 413.5,814.5 399.5,814.5C 399.5,745.833 399.5,677.167 399.5,608.5 Z"/></g>
|
||||
<g><path className={classes.cyanPath} d="M 722.5,608.5 C 753.86,608.071 785.193,608.571 816.5,610C 848.538,615.71 865.371,634.876 867,667.5C 867.667,696.833 867.667,726.167 867,755.5C 864.167,791.667 844.667,811.167 808.5,814C 778.833,814.667 749.167,814.667 719.5,814C 683.4,810.566 664.233,790.732 662,754.5C 661.333,725.833 661.333,697.167 662,668.5C 664.815,630.851 684.982,610.851 722.5,608.5 Z M 725.5,650.5 C 749.502,650.333 773.502,650.5 797.5,651C 813.546,652.713 822.712,661.546 825,677.5C 825.667,700.167 825.667,722.833 825,745.5C 822.833,761 814,769.833 798.5,772C 775.833,772.667 753.167,772.667 730.5,772C 714.546,769.712 705.713,760.546 704,744.5C 703.333,722.167 703.333,699.833 704,677.5C 704.565,662.953 711.732,653.953 725.5,650.5 Z"/></g>
|
||||
<g><path className={classes.cyanPath} d="M 924.5,608.5 C 992.833,608.5 1061.17,608.5 1129.5,608.5C 1129.5,622.5 1129.5,636.5 1129.5,650.5C 1061.17,650.5 992.833,650.5 924.5,650.5C 924.5,636.5 924.5,622.5 924.5,608.5 Z"/></g>
|
||||
<g><path className={classes.cyanPath} d="M 924.5,690.5 C 992.833,690.5 1061.17,690.5 1129.5,690.5C 1129.5,704.5 1129.5,718.5 1129.5,732.5C 1061.17,732.5 992.833,732.5 924.5,732.5C 924.5,718.5 924.5,704.5 924.5,690.5 Z"/></g>
|
||||
<g><path className={classes.cyanPath} d="M 924.5,772.5 C 992.833,772.5 1061.17,772.5 1129.5,772.5C 1129.5,786.5 1129.5,800.5 1129.5,814.5C 1061.17,814.5 992.833,814.5 924.5,814.5C 924.5,800.5 924.5,786.5 924.5,772.5 Z"/></g>
|
||||
</svg>
|
||||
);
|
||||
};
|
56
plugins/cnoe-ui/src/components/logos/LogoFull.tsx
Normal file
56
plugins/cnoe-ui/src/components/logos/LogoFull.tsx
Normal file
|
@ -0,0 +1,56 @@
|
|||
import React from 'react';
|
||||
import { makeStyles } from '@material-ui/core';
|
||||
|
||||
const useStyles = makeStyles({
|
||||
svg: {
|
||||
width: 'auto',
|
||||
height: 90,
|
||||
},
|
||||
whitePath: {
|
||||
fill: '#ffffff',
|
||||
stroke: 'none',
|
||||
},
|
||||
bluePath: {
|
||||
fill: '#00568c',
|
||||
stroke: 'none',
|
||||
},
|
||||
cyanPath: {
|
||||
fill: '#00adee',
|
||||
stroke: 'none',
|
||||
},
|
||||
});
|
||||
|
||||
export const LogoFull = () => {
|
||||
const classes = useStyles();
|
||||
|
||||
return (
|
||||
<svg
|
||||
className={classes.svg}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 2079.95 850.05"
|
||||
>
|
||||
<g><path className={classes.cyanPath} d="M 923.5,136.5 C 928.809,135.565 932.309,137.565 934,142.5C 947.791,206.068 948.457,269.735 936,333.5C 923.041,396.634 891.874,448.468 842.5,489C 832.527,492.024 828.694,488.524 831,478.5C 881.237,437.163 911.904,384.163 923,319.5C 929.867,281.3 931.201,242.966 927,204.5C 849.896,286.335 757.063,343.169 648.5,375C 560.306,401.762 470.306,412.428 378.5,407C 374.251,402.199 374.584,397.866 379.5,394C 493.995,398.085 604.329,378.751 710.5,336C 794.01,301.471 865.51,250.305 925,182.5C 924.752,176.101 923.752,169.768 922,163.5C 879.994,213.56 830.16,253.726 772.5,284C 654.362,345.618 528.696,374.618 395.5,371C 379.341,370.192 363.674,367.026 348.5,361.5C 354.504,394.851 367.67,425.184 388,452.5C 394,459.833 400.667,466.5 408,472.5C 410.323,482.507 406.489,486.007 396.5,483C 371.585,461.039 353.751,434.206 343,402.5C 336.28,383.953 331.28,364.953 328,345.5C 324.768,332.813 328.601,322.98 339.5,316C 350.829,310.223 362.829,307.89 375.5,309C 380.313,313.041 380.646,317.375 376.5,322C 366.116,323.176 356.116,325.843 346.5,330C 340.55,336.251 341.55,341.251 349.5,345C 365.81,351.729 382.81,355.396 400.5,356C 530.3,358.299 652.633,329.299 767.5,269C 829.235,236.299 881.235,192.132 923.5,136.5 Z"/></g>
|
||||
<g><path className={classes.cyanPath} d="M 659.5,164.5 C 714.308,178.702 768.974,193.536 823.5,209C 826.394,211.909 827.227,215.409 826,219.5C 823.588,222.792 820.421,223.959 816.5,223C 767.741,209.802 719.075,196.302 670.5,182.5C 669.668,192.483 669.168,202.483 669,212.5C 663.851,217.996 659.184,217.663 655,211.5C 654.333,197.5 654.333,183.5 655,169.5C 656.025,167.313 657.525,165.646 659.5,164.5 Z"/></g>
|
||||
<g><path className={classes.bluePath} d="M 568.5,192.5 C 585.66,194.231 602.66,197.065 619.5,201C 635.167,211.333 650.833,221.667 666.5,232C 667.126,232.75 667.626,233.584 668,234.5C 668.667,257.167 668.667,279.833 668,302.5C 663.829,308.729 659.163,309.062 654,303.5C 653.833,282.825 653.333,262.159 652.5,241.5C 642.742,235.539 633.242,229.205 624,222.5C 623.667,253.833 623.333,285.167 623,316.5C 618.333,321.833 613.667,321.833 609,316.5C 608.5,282.502 608.333,248.502 608.5,214.5C 599.213,212.576 589.88,210.909 580.5,209.5C 580.667,248.168 580.5,286.835 580,325.5C 575.829,331.729 571.163,332.062 566,326.5C 565.667,288.833 565.333,251.167 565,213.5C 558.639,218.097 551.805,221.763 544.5,224.5C 537.956,223.089 535.789,219.089 538,212.5C 538.833,211.667 539.667,210.833 540.5,210C 550.066,204.392 559.399,198.559 568.5,192.5 Z"/></g>
|
||||
<g><path className={classes.cyanPath} d="M 490.5,212.5 C 504.026,214.715 517.36,217.881 530.5,222C 533.102,222.935 534.602,224.768 535,227.5C 535.667,263.167 535.667,298.833 535,334.5C 532.567,340.448 528.4,341.948 522.5,339C 521.107,337.829 520.273,336.329 520,334.5C 519.5,301.168 519.333,267.835 519.5,234.5C 512.102,232.981 504.769,231.481 497.5,230C 492.207,238.751 487.041,247.585 482,256.5C 481.667,285.833 481.333,315.167 481,344.5C 478.612,349.226 474.778,350.726 469.5,349C 468.667,348.167 467.833,347.333 467,346.5C 466.333,314.833 466.333,283.167 467,251.5C 474.342,238.14 482.175,225.14 490.5,212.5 Z"/></g>
|
||||
<g><path className={classes.bluePath} d="M 432.5,231.5 C 442.172,231.334 451.839,231.5 461.5,232C 467.259,235.892 467.926,240.559 463.5,246C 456.254,247.391 448.921,247.891 441.5,247.5C 441.667,280.502 441.5,313.502 441,346.5C 437.004,351.3 432.67,351.633 428,347.5C 426.346,310.585 426.013,273.585 427,236.5C 428.107,233.887 429.94,232.22 432.5,231.5 Z"/></g>
|
||||
<g><path className={classes.bluePath} d="M 392.5,265.5 C 400.507,265.334 408.507,265.5 416.5,266C 424.5,271 424.5,276 416.5,281C 411.511,281.499 406.511,281.666 401.5,281.5C 401.667,302.503 401.5,323.503 401,344.5C 396.333,349.833 391.667,349.833 387,344.5C 386.333,319.5 386.333,294.5 387,269.5C 388.5,267.531 390.333,266.198 392.5,265.5 Z"/></g>
|
||||
<g><path className={classes.bluePath} d="M 898.5,452.5 C 942.835,452.333 987.168,452.5 1031.5,453C 1034.37,454.393 1035.7,456.726 1035.5,460C 1035.7,463.274 1034.37,465.607 1031.5,467C 987.167,467.667 942.833,467.667 898.5,467C 895.634,465.607 894.301,463.274 894.5,460C 894.43,456.634 895.763,454.134 898.5,452.5 Z"/></g>
|
||||
<g><path className={classes.bluePath} d="M 243.5,464.5 C 285.501,464.333 327.501,464.5 369.5,465C 372.366,466.393 373.699,468.726 373.5,472C 373.699,475.274 372.366,477.607 369.5,479C 327.5,479.667 285.5,479.667 243.5,479C 240.484,477.471 239.151,474.971 239.5,471.5C 239.197,468.156 240.53,465.822 243.5,464.5 Z"/></g>
|
||||
<g><path className={classes.bluePath} d="M 925.5,479.5 C 944.503,479.333 963.503,479.5 982.5,480C 988.509,484.314 988.843,488.981 983.5,494C 963.5,494.667 943.5,494.667 923.5,494C 919.002,488.32 919.669,483.487 925.5,479.5 Z"/></g>
|
||||
<g><path className={classes.bluePath} d="M 235.5,490.5 C 260.502,490.333 285.502,490.5 310.5,491C 314.136,493.039 315.802,496.206 315.5,500.5C 404.501,500.333 493.501,500.5 582.5,501C 588.729,505.171 589.062,509.837 583.5,515C 490.167,515.667 396.833,515.667 303.5,515C 300.38,512.592 299.047,509.426 299.5,505.5C 277.831,505.667 256.164,505.5 234.5,505C 231.606,502.091 230.773,498.591 232,494.5C 232.69,492.65 233.856,491.316 235.5,490.5 Z"/></g>
|
||||
<g><path className={classes.bluePath} d="M 614.5,498.5 C 714.167,498.333 813.834,498.5 913.5,499C 918.535,502.013 919.702,506.18 917,511.5C 916.25,512.126 915.416,512.626 914.5,513C 814.167,513.667 713.833,513.667 613.5,513C 608.003,507.683 608.336,502.85 614.5,498.5 Z"/></g>
|
||||
<g><path className={classes.bluePath} d="M 566.5,526.5 C 588.169,526.333 609.836,526.5 631.5,527C 637.786,529.362 639.453,533.529 636.5,539.5C 635.335,540.584 634.002,541.417 632.5,542C 610.167,542.667 587.833,542.667 565.5,542C 560.774,539.612 559.274,535.778 561,530.5C 562.5,528.531 564.333,527.198 566.5,526.5 Z"/></g>
|
||||
<g><path className={classes.bluePath} d="M 276.5,527.5 C 314.501,527.333 352.501,527.5 390.5,528C 396.552,532.223 396.885,536.889 391.5,542C 353.167,542.667 314.833,542.667 276.5,542C 272.788,539.487 271.622,535.987 273,531.5C 274.376,530.295 275.542,528.962 276.5,527.5 Z"/></g>
|
||||
<g><path className={classes.bluePath} d="M 738.5,532.5 C 777.168,532.333 815.835,532.5 854.5,533C 860.062,538.163 859.729,542.829 853.5,547C 815.167,547.667 776.833,547.667 738.5,547C 735.634,545.607 734.301,543.274 734.5,540C 734.43,536.634 735.763,534.134 738.5,532.5 Z"/></g>
|
||||
<g><path className={classes.bluePath} d="M 355.5,556.5 C 438.167,556.333 520.834,556.5 603.5,557C 609.075,562.115 608.742,566.781 602.5,571C 520.5,571.667 438.5,571.667 356.5,571C 350.367,566.613 350.033,561.78 355.5,556.5 Z"/></g>
|
||||
<g><path className={classes.bluePath} d="M 844.5,558.5 C 880.502,558.333 916.502,558.5 952.5,559C 957.833,563.667 957.833,568.333 952.5,573C 916.833,573.667 881.167,573.667 845.5,573C 839.231,568.657 838.898,563.824 844.5,558.5 Z"/></g>
|
||||
<g><path className={classes.cyanPath} d="M 197.5,608.5 C 245.833,608.5 294.167,608.5 342.5,608.5C 342.5,622.5 342.5,636.5 342.5,650.5C 296.832,650.333 251.165,650.5 205.5,651C 192.103,652.4 183.603,659.566 180,672.5C 178,698.5 178,724.5 180,750.5C 183.451,762.947 191.618,770.113 204.5,772C 250.499,772.5 296.499,772.667 342.5,772.5C 342.5,786.5 342.5,800.5 342.5,814.5C 293.499,814.667 244.499,814.5 195.5,814C 161.97,812.133 142.803,794.633 138,761.5C 137.333,728.167 137.333,694.833 138,661.5C 140.962,636.219 154.462,619.719 178.5,612C 184.933,610.597 191.267,609.43 197.5,608.5 Z"/></g>
|
||||
<g><path className={classes.cyanPath} d="M 399.5,608.5 C 413.837,608.333 428.171,608.5 442.5,609C 483.022,655.888 523.189,703.054 563,750.5C 563.5,703.168 563.667,655.835 563.5,608.5C 577.5,608.5 591.5,608.5 605.5,608.5C 605.5,677.167 605.5,745.833 605.5,814.5C 591.163,814.667 576.829,814.5 562.5,814C 522.215,767.26 482.048,720.426 442,673.5C 441.5,720.499 441.333,767.499 441.5,814.5C 427.5,814.5 413.5,814.5 399.5,814.5C 399.5,745.833 399.5,677.167 399.5,608.5 Z"/></g>
|
||||
<g><path className={classes.cyanPath} d="M 722.5,608.5 C 753.86,608.071 785.193,608.571 816.5,610C 848.538,615.71 865.371,634.876 867,667.5C 867.667,696.833 867.667,726.167 867,755.5C 864.167,791.667 844.667,811.167 808.5,814C 778.833,814.667 749.167,814.667 719.5,814C 683.4,810.566 664.233,790.732 662,754.5C 661.333,725.833 661.333,697.167 662,668.5C 664.815,630.851 684.982,610.851 722.5,608.5 Z M 725.5,650.5 C 749.502,650.333 773.502,650.5 797.5,651C 813.546,652.713 822.712,661.546 825,677.5C 825.667,700.167 825.667,722.833 825,745.5C 822.833,761 814,769.833 798.5,772C 775.833,772.667 753.167,772.667 730.5,772C 714.546,769.712 705.713,760.546 704,744.5C 703.333,722.167 703.333,699.833 704,677.5C 704.565,662.953 711.732,653.953 725.5,650.5 Z"/></g>
|
||||
<g><path className={classes.cyanPath} d="M 924.5,608.5 C 992.833,608.5 1061.17,608.5 1129.5,608.5C 1129.5,622.5 1129.5,636.5 1129.5,650.5C 1061.17,650.5 992.833,650.5 924.5,650.5C 924.5,636.5 924.5,622.5 924.5,608.5 Z"/></g>
|
||||
<g><path className={classes.cyanPath} d="M 924.5,690.5 C 992.833,690.5 1061.17,690.5 1129.5,690.5C 1129.5,704.5 1129.5,718.5 1129.5,732.5C 1061.17,732.5 992.833,732.5 924.5,732.5C 924.5,718.5 924.5,704.5 924.5,690.5 Z"/></g>
|
||||
<g><path className={classes.cyanPath} d="M 924.5,772.5 C 992.833,772.5 1061.17,772.5 1129.5,772.5C 1129.5,786.5 1129.5,800.5 1129.5,814.5C 1061.17,814.5 992.833,814.5 924.5,814.5C 924.5,800.5 924.5,786.5 924.5,772.5 Z"/></g>
|
||||
</svg>
|
||||
);
|
||||
};
|
44
plugins/cnoe-ui/src/components/logos/LogoIcon.tsx
Normal file
44
plugins/cnoe-ui/src/components/logos/LogoIcon.tsx
Normal file
|
@ -0,0 +1,44 @@
|
|||
import React from 'react';
|
||||
import { makeStyles } from '@material-ui/core';
|
||||
|
||||
const useStyles = makeStyles({
|
||||
svg: {
|
||||
width: 'auto',
|
||||
height: 28,
|
||||
},
|
||||
whitePath: {
|
||||
fill: '#ffffff',
|
||||
stroke: 'none',
|
||||
},
|
||||
bluePath: {
|
||||
fill: '#00568c',
|
||||
stroke: 'none',
|
||||
},
|
||||
cyanPath: {
|
||||
fill: '#00adee',
|
||||
stroke: 'none',
|
||||
},
|
||||
});
|
||||
|
||||
export const LogoIcon = () => {
|
||||
const classes = useStyles();
|
||||
|
||||
return (
|
||||
<svg
|
||||
className={classes.svg}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 337.46 428.5"
|
||||
>
|
||||
<g><path className={classes.cyanPath} d="M 923.5,136.5 C 928.809,135.565 932.309,137.565 934,142.5C 947.791,206.068 948.457,269.735 936,333.5C 923.041,396.634 891.874,448.468 842.5,489C 832.527,492.024 828.694,488.524 831,478.5C 881.237,437.163 911.904,384.163 923,319.5C 929.867,281.3 931.201,242.966 927,204.5C 849.896,286.335 757.063,343.169 648.5,375C 560.306,401.762 470.306,412.428 378.5,407C 374.251,402.199 374.584,397.866 379.5,394C 493.995,398.085 604.329,378.751 710.5,336C 794.01,301.471 865.51,250.305 925,182.5C 924.752,176.101 923.752,169.768 922,163.5C 879.994,213.56 830.16,253.726 772.5,284C 654.362,345.618 528.696,374.618 395.5,371C 379.341,370.192 363.674,367.026 348.5,361.5C 354.504,394.851 367.67,425.184 388,452.5C 394,459.833 400.667,466.5 408,472.5C 410.323,482.507 406.489,486.007 396.5,483C 371.585,461.039 353.751,434.206 343,402.5C 336.28,383.953 331.28,364.953 328,345.5C 324.768,332.813 328.601,322.98 339.5,316C 350.829,310.223 362.829,307.89 375.5,309C 380.313,313.041 380.646,317.375 376.5,322C 366.116,323.176 356.116,325.843 346.5,330C 340.55,336.251 341.55,341.251 349.5,345C 365.81,351.729 382.81,355.396 400.5,356C 530.3,358.299 652.633,329.299 767.5,269C 829.235,236.299 881.235,192.132 923.5,136.5 Z"/></g>
|
||||
<g><path className={classes.cyanPath} d="M 659.5,164.5 C 714.308,178.702 768.974,193.536 823.5,209C 826.394,211.909 827.227,215.409 826,219.5C 823.588,222.792 820.421,223.959 816.5,223C 767.741,209.802 719.075,196.302 670.5,182.5C 669.668,192.483 669.168,202.483 669,212.5C 663.851,217.996 659.184,217.663 655,211.5C 654.333,197.5 654.333,183.5 655,169.5C 656.025,167.313 657.525,165.646 659.5,164.5 Z"/></g>
|
||||
<g><path className={classes.bluePath} d="M 568.5,192.5 C 585.66,194.231 602.66,197.065 619.5,201C 635.167,211.333 650.833,221.667 666.5,232C 667.126,232.75 667.626,233.584 668,234.5C 668.667,257.167 668.667,279.833 668,302.5C 663.829,308.729 659.163,309.062 654,303.5C 653.833,282.825 653.333,262.159 652.5,241.5C 642.742,235.539 633.242,229.205 624,222.5C 623.667,253.833 623.333,285.167 623,316.5C 618.333,321.833 613.667,321.833 609,316.5C 608.5,282.502 608.333,248.502 608.5,214.5C 599.213,212.576 589.88,210.909 580.5,209.5C 580.667,248.168 580.5,286.835 580,325.5C 575.829,331.729 571.163,332.062 566,326.5C 565.667,288.833 565.333,251.167 565,213.5C 558.639,218.097 551.805,221.763 544.5,224.5C 537.956,223.089 535.789,219.089 538,212.5C 538.833,211.667 539.667,210.833 540.5,210C 550.066,204.392 559.399,198.559 568.5,192.5 Z"/></g>
|
||||
<g><path className={classes.cyanPath} d="M 490.5,212.5 C 504.026,214.715 517.36,217.881 530.5,222C 533.102,222.935 534.602,224.768 535,227.5C 535.667,263.167 535.667,298.833 535,334.5C 532.567,340.448 528.4,341.948 522.5,339C 521.107,337.829 520.273,336.329 520,334.5C 519.5,301.168 519.333,267.835 519.5,234.5C 512.102,232.981 504.769,231.481 497.5,230C 492.207,238.751 487.041,247.585 482,256.5C 481.667,285.833 481.333,315.167 481,344.5C 478.612,349.226 474.778,350.726 469.5,349C 468.667,348.167 467.833,347.333 467,346.5C 466.333,314.833 466.333,283.167 467,251.5C 474.342,238.14 482.175,225.14 490.5,212.5 Z"/></g>
|
||||
<g><path className={classes.bluePath} d="M 432.5,231.5 C 442.172,231.334 451.839,231.5 461.5,232C 467.259,235.892 467.926,240.559 463.5,246C 456.254,247.391 448.921,247.891 441.5,247.5C 441.667,280.502 441.5,313.502 441,346.5C 437.004,351.3 432.67,351.633 428,347.5C 426.346,310.585 426.013,273.585 427,236.5C 428.107,233.887 429.94,232.22 432.5,231.5 Z"/></g>
|
||||
<g><path className={classes.bluePath} d="M 392.5,265.5 C 400.507,265.334 408.507,265.5 416.5,266C 424.5,271 424.5,276 416.5,281C 411.511,281.499 406.511,281.666 401.5,281.5C 401.667,302.503 401.5,323.503 401,344.5C 396.333,349.833 391.667,349.833 387,344.5C 386.333,319.5 386.333,294.5 387,269.5C 388.5,267.531 390.333,266.198 392.5,265.5 Z"/></g>
|
||||
<g><path className={classes.bluePath} d="M 898.5,452.5 C 942.835,452.333 987.168,452.5 1031.5,453C 1034.37,454.393 1035.7,456.726 1035.5,460C 1035.7,463.274 1034.37,465.607 1031.5,467C 987.167,467.667 942.833,467.667 898.5,467C 895.634,465.607 894.301,463.274 894.5,460C 894.43,456.634 895.763,454.134 898.5,452.5 Z"/></g>
|
||||
<g><path className={classes.bluePath} d="M 243.5,464.5 C 285.501,464.333 327.501,464.5 369.5,465C 372.366,466.393 373.699,468.726 373.5,472C 373.699,475.274 372.366,477.607 369.5,479C 327.5,479.667 285.5,479.667 243.5,479C 240.484,477.471 239.151,474.971 239.5,471.5C 239.197,468.156 240.53,465.822 243.5,464.5 Z"/></g>
|
||||
<g><path className={classes.bluePath} d="M 925.5,479.5 C 944.503,479.333 963.503,479.5 982.5,480C 988.509,484.314 988.843,488.981 983.5,494C 963.5,494.667 943.5,494.667 923.5,494C 919.002,488.32 919.669,483.487 925.5,479.5 Z"/></g>
|
||||
<g><path className={classes.bluePath} d="M 235.5,490.5 C 260.502,490.333 285.502,490.5 310.5,491C 314.136,493.039 315.802,496.206 315.5,500.5C 404.501,500.333 493.501,500.5 582.5,501C 588.729,505.171 589.062,509.837 583.5,515C 490.167,515.667 396.833,515.667 303.5,515C 300.38,512.592 299.047,509.426 299.5,505.5C 277.831,505.667 256.164,505.5 234.5,505C 231.606,502.091 230.773,498.591 232,494.5C 232.69,492.65 233.856,491.316 235.5,490.5 Z"/></g>
|
||||
</svg>
|
||||
);
|
||||
};
|
3
plugins/cnoe-ui/src/components/logos/index.tsx
Normal file
3
plugins/cnoe-ui/src/components/logos/index.tsx
Normal file
|
@ -0,0 +1,3 @@
|
|||
export {LogoFull} from './LogoFull';
|
||||
export {LogoIcon} from './LogoIcon';
|
||||
export {LogoBig} from './LogoBig';
|
38
plugins/cnoe-ui/src/components/themes/dark-theme.ts
Normal file
38
plugins/cnoe-ui/src/components/themes/dark-theme.ts
Normal file
|
@ -0,0 +1,38 @@
|
|||
import {createTheme, darkTheme, genPageTheme, shapes} from '@backstage/theme';
|
||||
|
||||
export const cnoeDarkTheme = createTheme({
|
||||
palette: {
|
||||
...darkTheme.palette,
|
||||
primary: {
|
||||
main: '#25a0c2',
|
||||
},
|
||||
secondary: {
|
||||
main: '#00568c',
|
||||
},
|
||||
},
|
||||
defaultPageTheme: 'home',
|
||||
pageTheme: {
|
||||
home: genPageTheme({colors: ['#25a0c2', '#00568c'], shape: shapes.wave}),
|
||||
documentation: genPageTheme({
|
||||
colors: ['#25a0c2', '#00568c'],
|
||||
shape: shapes.wave2,
|
||||
}),
|
||||
tool: genPageTheme({colors: ['#25a0c2', '#00568c'], shape: shapes.round}),
|
||||
service: genPageTheme({
|
||||
colors: ['#25a0c2', '#00568c'],
|
||||
shape: shapes.wave,
|
||||
}),
|
||||
website: genPageTheme({
|
||||
colors: ['#25a0c2', '#00568c'],
|
||||
shape: shapes.wave,
|
||||
}),
|
||||
library: genPageTheme({
|
||||
colors: ['#25a0c2', '#00568c'],
|
||||
shape: shapes.wave,
|
||||
}),
|
||||
other: genPageTheme({colors: ['#25a0c2', '#00568c'], shape: shapes.wave}),
|
||||
app: genPageTheme({colors: ['#25a0c2', '#00568c'], shape: shapes.wave}),
|
||||
apis: genPageTheme({colors: ['#25a0c2', '#00568c'], shape: shapes.wave}),
|
||||
},
|
||||
});
|
||||
|
2
plugins/cnoe-ui/src/components/themes/index.ts
Normal file
2
plugins/cnoe-ui/src/components/themes/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
|||
export {cnoeDarkTheme} from './dark-theme';
|
||||
export {cnoeLightTheme} from './light-theme';
|
39
plugins/cnoe-ui/src/components/themes/light-theme.ts
Normal file
39
plugins/cnoe-ui/src/components/themes/light-theme.ts
Normal file
|
@ -0,0 +1,39 @@
|
|||
import {createTheme, lightTheme, genPageTheme, shapes} from '@backstage/theme';
|
||||
|
||||
export const cnoeLightTheme = createTheme({
|
||||
palette: {
|
||||
...lightTheme.palette,
|
||||
primary: {
|
||||
main: '#00568c',
|
||||
},
|
||||
secondary: {
|
||||
main: '#00adee',
|
||||
},
|
||||
},
|
||||
defaultPageTheme: 'home',
|
||||
pageTheme: {
|
||||
home: genPageTheme({colors: ['#00568c', '#00adee'], shape: shapes.wave}),
|
||||
documentation: genPageTheme({
|
||||
colors: ['#00568c', '#00adee'],
|
||||
shape: shapes.wave2,
|
||||
}),
|
||||
tool: genPageTheme({colors: ['#00568c', '#00adee'], shape: shapes.round}),
|
||||
service: genPageTheme({
|
||||
colors: ['#00568c', '#00adee'],
|
||||
shape: shapes.wave,
|
||||
}),
|
||||
website: genPageTheme({
|
||||
colors: ['#00568c', '#00adee'],
|
||||
shape: shapes.wave,
|
||||
}),
|
||||
library: genPageTheme({
|
||||
colors: ['#00568c', '#00adee'],
|
||||
shape: shapes.wave,
|
||||
}),
|
||||
other: genPageTheme({colors: ['#00568c', '#00adee'], shape: shapes.wave}),
|
||||
app: genPageTheme({colors: ['#00568c', '#00adee'], shape: shapes.wave}),
|
||||
apis: genPageTheme({colors: ['#00568c', '#00adee'], shape: shapes.wave}),
|
||||
},
|
||||
});
|
||||
|
||||
|
3
plugins/cnoe-ui/src/index.ts
Normal file
3
plugins/cnoe-ui/src/index.ts
Normal file
|
@ -0,0 +1,3 @@
|
|||
export * from './components/themes';
|
||||
export {LogoFull, LogoIcon} from './components/logos';
|
||||
export {CNOEHomepage} from './components/Homepage';
|
7
plugins/cnoe-ui/src/plugin.test.ts
Normal file
7
plugins/cnoe-ui/src/plugin.test.ts
Normal file
|
@ -0,0 +1,7 @@
|
|||
import { cnoeUiPlugin } from './plugin';
|
||||
|
||||
describe('cnoe-ui', () => {
|
||||
it('should export plugin', () => {
|
||||
expect(cnoeUiPlugin).toBeDefined();
|
||||
});
|
||||
});
|
36
plugins/cnoe-ui/src/plugin.ts
Normal file
36
plugins/cnoe-ui/src/plugin.ts
Normal file
|
@ -0,0 +1,36 @@
|
|||
import {createPlugin, createComponentExtension} from '@backstage/core-plugin-api';
|
||||
|
||||
import {rootRouteRef} from './routes';
|
||||
|
||||
export const cnoeFrontendPlugin = createPlugin({
|
||||
id: 'cnoe-ui-plugin',
|
||||
routes: {
|
||||
root: rootRouteRef,
|
||||
},
|
||||
});
|
||||
|
||||
export const AWSLogoFull = cnoeFrontendPlugin
|
||||
.provide(
|
||||
createComponentExtension({
|
||||
name: 'LogoFull',
|
||||
component: {lazy: () => import('./components/logos/LogoFull').then(m => m.LogoFull)},
|
||||
}),
|
||||
);
|
||||
|
||||
export const AWSLogoIcon = cnoeFrontendPlugin
|
||||
.provide(
|
||||
createComponentExtension({
|
||||
name: 'LogoIcon',
|
||||
component: {lazy: () => import('./components/logos/LogoIcon').then(m => m.LogoIcon)},
|
||||
}),
|
||||
);
|
||||
|
||||
export const CNOEHomepage = cnoeFrontendPlugin
|
||||
.provide(
|
||||
createComponentExtension({
|
||||
name: 'Homepage',
|
||||
component: {
|
||||
lazy: () => import('./components/Homepage').then(m => m.CNOEHomepage),
|
||||
},
|
||||
}),
|
||||
);
|
5
plugins/cnoe-ui/src/routes.ts
Normal file
5
plugins/cnoe-ui/src/routes.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
import {createRouteRef} from '@backstage/core-plugin-api';
|
||||
|
||||
export const rootRouteRef = createRouteRef({
|
||||
id: 'cnoe-ui',
|
||||
});
|
1
plugins/cnoe-ui/src/setupTests.ts
Normal file
1
plugins/cnoe-ui/src/setupTests.ts
Normal file
|
@ -0,0 +1 @@
|
|||
import '@testing-library/jest-dom';
|
Loading…
Reference in a new issue