From eab992b7355b6e58d417fcda2f85e9f091f9342f Mon Sep 17 00:00:00 2001 From: Shashank Sharma Date: Fri, 12 May 2023 19:00:54 +0530 Subject: [PATCH 01/30] Adding bff proxy changes to handle guest auth for playground environment. --- bff/src/auth/auth.ts | 108 ++++++++++++++- bff/src/config.ts | 1 + bff/src/index.ts | 14 +- bff/src/proxy/proxy.ts | 127 +++++++++++++++++- bff/src/utils/helper.ts | 10 +- .../components/argo-core/top-bar/top-bar.tsx | 69 ++++++---- .../pages/authorized/authorizedRouteNames.ts | 7 + web/src/pages/authorized/feature_toggle.tsx | 4 + web/src/router/PrivateRoute.tsx | 25 ++-- 9 files changed, 317 insertions(+), 48 deletions(-) diff --git a/bff/src/auth/auth.ts b/bff/src/auth/auth.ts index 077c76327..240d8b0f4 100644 --- a/bff/src/auth/auth.ts +++ b/bff/src/auth/auth.ts @@ -13,7 +13,7 @@ import * as express from "express"; import { auth, requiresAuth } from "express-openid-connect"; import * as session from "express-session"; import { ExpressOIDC } from "@okta/oidc-middleware"; -import * as crypto from 'crypto'; +import * as crypto from "crypto"; export async function getUser(username: string): Promise { try { @@ -34,6 +34,48 @@ export async function getUser(username: string): Promise { } } +export async function getPlaygroundUser(username: string): Promise { + try { + const user = await axios.get( + `${process.env.ZLIFECYCLE_API_URL}/v1/orgs/1/auth/users/${username}` + ); + + return user.data; + } catch (err) { + if (axios.isAxiosError(err)) { + // @ts-ignore + logger.error("get user error", { error: err.toJSON().message }); + } else { + logger.error("get user error", { error: { message: err.message } }); + } + + return null; + } +} + +export async function createPlaygroundUser(ipv4: string): Promise { + const url = `${process.env.ZLIFECYCLE_API_URL}/v1/orgs/1/auth/playground/users`; + console.log(url); + try { + const user = await axios.post(url, { + ipv4, + }); + + console.log(user); + + return user.data; + } catch (err) { + if (axios.isAxiosError(err)) { + // @ts-ignore + logger.error("get user error", { error: err.toJSON().message }); + } else { + logger.error("get user error", { error: { message: err.message } }); + } + + return null; + } +} + async function createUser( username: string, email: string, @@ -252,8 +294,57 @@ function getOktaAuthMW() { return oidc.router; } +export async function guestAuthMW(req, res, next) { + const ipv4 = getClientIP(req); + logger.info("GUEST AUTH MW", { + ipv4, + }); + if (!ipv4) { + res.status(500).send(); + return; + } + logger.info("Getting user for: ", { + ipv4, + }); + let user = await getPlaygroundUser(ipv4); + if (!user) { + logger.info("User not found for: ", { + ipv4, + }); + logger.info("Creating user for", { + ipv4, + }); + user = await createPlaygroundUser(ipv4); + } + if (user) { + // Setting the appsession + req.session.appSession = { + user, + organizations: user.organizations, + }; + } + logger.info("Current user info", { + user, + }); + next(); +} + export function setUpAuth(app: express.Express, authRouter: express.Router) { - if (helper.isOktaAuth()) { + if (helper.isGuestAuth()) { + const MemoryStore = require("memorystore")(session); + app.use( + session({ + secret: crypto.randomUUID(), + resave: false, + saveUninitialized: false, + cookie: { maxAge: 86400000 }, + store: new MemoryStore({ + checkPeriod: 86400000, + }), + }) + ); + app.use(guestAuthMW); + } else if (helper.isOktaAuth()) { const MemoryStore = require("memorystore")(session); app.use( session({ @@ -284,3 +375,16 @@ export function setUpAuth(app: express.Express, authRouter: express.Router) { authRouter.use(requiresAuth()); } } + +function getClientIP(req) { + if (!req.headers["x-forwarded-for"]) { + return null; + } + + // x-forwarded-for header returns the list of ips that our request has been forwarded by, + // first being clients and then it depends on the no. of proxies that have forwarded it. + const addresses = req.headers["x-forwarded-for"]; + + // Getting the first ip since that is where the request has originated from. + return addresses.split(",")[0]; +} diff --git a/bff/src/config.ts b/bff/src/config.ts index f2ee6aff6..cda2463c6 100644 --- a/bff/src/config.ts +++ b/bff/src/config.ts @@ -10,6 +10,7 @@ const config = { WEB_URL: process.env.SITE_URL, API_URL: `${process.env.ZLIFECYCLE_API_URL}/v1`, ARGOCD_URL: process.env.ARGO_CD_API_URL, + PLAYGROUND_APP: process.env.CK_PLAYGROUND, argoWFUrl: (orgName: string) => process.env.ARGO_WORKFLOW_API_URL.replaceAll(":org", orgName), stateMgrUrl: (orgName: string) => diff --git a/bff/src/index.ts b/bff/src/index.ts index 5f95dd32b..4cc22a04c 100644 --- a/bff/src/index.ts +++ b/bff/src/index.ts @@ -4,11 +4,19 @@ import * as cookieParser from "cookie-parser"; import * as cors from "cors"; import * as express from "express"; import * as correlator from "express-correlation-id"; -import { apiAuthMw, getUser, organizationMW, setUpAuth } from "./auth/auth"; +import { + apiAuthMw, + getUser, + organizationMW, + setUpAuth +} from "./auth/auth"; import zlConfig from "./config"; import AuthRoutes from "./controllers/auth.controller"; import { - externalApiRoutes, handlePublicRoutes, noOrgRoutes, orgRoutes + externalApiRoutes, + getOrgRoutes, + handlePublicRoutes, + noOrgRoutes } from "./proxy/proxy"; import helper, { oidcUser } from "./utils/helper"; import logger, { AuthRequestLogger, ErrorLogger } from "./utils/logger"; @@ -70,7 +78,7 @@ authRouter.use(AuthRequestLogger); authRouter.use(organizationMW); // checks for selectedOrg cookie, throws 401 if not present app.use("/auth", AuthRoutes(authRouter)); -app.use("/", orgRoutes(authRouter)); +app.use("/", getOrgRoutes(authRouter)) // replaces expresses default error handler app.use(ErrorLogger); diff --git a/bff/src/proxy/proxy.ts b/bff/src/proxy/proxy.ts index 73b8fe54e..0f17a0d2e 100644 --- a/bff/src/proxy/proxy.ts +++ b/bff/src/proxy/proxy.ts @@ -350,6 +350,123 @@ export function noOrgRoutes(router: express.Router) { return router; } +export function playgroundOrgRoutes(router: express.Router) { + + router.use("/wf", async (req: BFFRequest, res, next) => { + const org = await helper.orgFromReq(req); + + if (!org) { + helper.handleNoOrg(res); + return; + } + + return ( + createProxy(org, "/wf", { + target: config.argoWFUrl(org.name), + pathRewrite: pathRewrite("/wf", WF_MAPPINGS, { orgName: org.name }), + cookieDomainRewrite: "", + onProxyRes: enableCors, + changeOrigin: true, + }) as any + )(req, res, next); + }); + + router.use("/cd", + async (req: BFFRequest, res, next) => { + /* + Since http-proxy-middleware's are cached we need a way to inject ArgoCD tokens + into the cached request headers. Otherwise, the cached jwt, which has a 24h TTL, + would expire. + + Here, we set the `authorization` header and get a valid ArgoCD token on each call. + */ + const org = await helper.orgFromReq(req); + + if (!org) { + helper.handleNoOrg(res); + return; + } + const { authorization } = await getArgoCDAuthHeader(org.name); + + req.headers["authorization"] = authorization; + + next(); + }, + async (req: BFFRequest, res, next) => { + const org = await helper.orgFromReq(req); + + if (!org) { + helper.handleNoOrg(res); + return; + } + + return ( + createProxy(org, "/cd", { + target: config.ARGOCD_URL, + changeOrigin: true, + secure: true, + cookieDomainRewrite: "", + onProxyRes: enableCors, + pathRewrite: pathRewrite("/cd", CD_MAPPINGS, { + orgId: org.id, + orgName: org.name, + }), + }) as any + )(req, res, next); + } + ); + + router.use("/reconciliation", async (req: BFFRequest, res, next) => { + const org = await helper.orgFromReq(req); + + if (!org) { + helper.handleNoOrg(res); + return; + } + const user = helper.userFromReq(req); + + return ( + createProxy(org, "/reconciliation", { + target: process.env.ZLIFECYCLE_API_URL, + changeOrigin: true, + secure: true, + cookieDomainRewrite: "", + onProxyRes: enableCors, + pathRewrite: pathRewrite("/", AUDIT_MAPPINGS, { + orgId: org.id, + email: user.email, + }), + }) as any + )(req, res, next); + }); + + router.use("/api", async (req: BFFRequest, res, next) => { + const org = await helper.orgFromReq(req); + + if (!org) { + helper.handleNoOrg(res); + return; + } + + const { authorization } = await getArgoCDAuthHeader(org.name); + console.log(authorization); + req.headers["argo_cd_auth_header"] = authorization; + + return ( + createProxy(org, "/api", { + target: process.env.ZLIFECYCLE_API_URL, + changeOrigin: true, + secure: true, + cookieDomainRewrite: "", + onProxyRes: enableCors, + pathRewrite: pathRewrite("/", API_MAPPINGS, { orgId: org.id }), + }) as any + )(req, res, next); + }); + + return router; +} + export function orgRoutes(router: express.Router) { router.use("/wf", async (req: BFFRequest, res, next) => { const org = await helper.orgFromReq(req); @@ -370,8 +487,7 @@ export function orgRoutes(router: express.Router) { )(req, res, next); }); - router.use( - "/cd", + router.use("/cd", async (req: BFFRequest, res, next) => { /* Since http-proxy-middleware's are cached we need a way to inject ArgoCD tokens @@ -626,3 +742,10 @@ export function orgRoutes(router: express.Router) { return router; } + +export function getOrgRoutes(router) { + if (config.PLAYGROUND_APP) { + return playgroundOrgRoutes(router); + } + return orgRoutes(router); +} diff --git a/bff/src/utils/helper.ts b/bff/src/utils/helper.ts index 9df7b9ebf..1c3053e6b 100644 --- a/bff/src/utils/helper.ts +++ b/bff/src/utils/helper.ts @@ -152,8 +152,10 @@ const isOktaAuth = () => ckConfig.AUTH0_ISSUER_BASE_URL.includes("oktapreview.com") || ckConfig.AUTH0_ISSUER_BASE_URL.includes("okta.com"); +const isGuestAuth = () => true; //ckConfig.PLAYGROUND_APP === "true"; + export const appSession = (req: BFFRequest): any => { - if (isOktaAuth()) { + if (isOktaAuth() || isGuestAuth()) { return req.session.appSession; } return req.appSession; @@ -163,6 +165,9 @@ export const oidcUser = (req: BFFRequest) => { if (isOktaAuth()) { return req.session.passport.user; } + if (isGuestAuth()) { + return req.session.appSession.user; + } return req.oidc.user; }; @@ -175,5 +180,6 @@ export default { getOrg, syncWatcher, appSessionFromReq, - isOktaAuth + isOktaAuth, + isGuestAuth }; diff --git a/web/src/components/argo-core/top-bar/top-bar.tsx b/web/src/components/argo-core/top-bar/top-bar.tsx index 04abd7ef5..0b71d4560 100644 --- a/web/src/components/argo-core/top-bar/top-bar.tsx +++ b/web/src/components/argo-core/top-bar/top-bar.tsx @@ -7,6 +7,7 @@ import { NavItem } from 'models/nav-item.models'; import { TopNav } from 'components/organisms/top-nav/TopNav'; import { BradAdarshFeatureVisible, FeatureKeys, FeatureRoutes } from 'pages/authorized/feature_toggle'; import { useHistory } from 'react-router-dom'; +import { ENVIRONMENT_VARIABLES } from 'utils/environmentVariables'; require('./top-bar.scss'); @@ -56,12 +57,16 @@ const navItems: NavItem[] = [ }, ], }, - { title: 'Infra Components', path: '/all/all' }, - { title: 'Overview', path: '/overview', visible: () => BradAdarshFeatureVisible()}, + { title: 'Infra Components', path: '/all/all', visible: () => !ENVIRONMENT_VARIABLES.PLAYGROUND_APP }, + { title: 'Overview', path: '/overview', visible: () => BradAdarshFeatureVisible() }, { title: 'Dashboard', path: '/demo-dashboard', visible: () => BradAdarshFeatureVisible() }, { title: 'Builder', path: '/builder', visible: () => BradAdarshFeatureVisible() }, - { title: 'Settings', path: '/settings', visible: () => AuthStore.getUser()?.role === 'Admin' }, - { title: 'Quick Start', path: '/quick-start'}, + { + title: 'Settings', + path: '/settings', + visible: () => !ENVIRONMENT_VARIABLES.PLAYGROUND_APP && AuthStore.getUser()?.role === 'Admin', + }, + { title: 'Quick Start', path: '/quick-start', visible: () => !ENVIRONMENT_VARIABLES.PLAYGROUND_APP }, ]; export const TopBar = ({ title }: TopBarProps) => { @@ -73,9 +78,13 @@ export const TopBar = ({ title }: TopBarProps) => {
- { - history.push('/'); - }} style={{ width: '80px', marginRight: '30px', cursor: 'pointer' }} className="top-bar__logo" /> + { + history.push('/'); + }} + style={{ width: '80px', marginRight: '30px', cursor: 'pointer' }} + className="top-bar__logo" + />
@@ -84,28 +93,30 @@ export const TopBar = ({ title }: TopBarProps) => {
-
- setShowDropDown(!showDropdown)} - /> - ({ - text: org.name || '', - action: async () => { - await AuthStore.selectOrganization(org.name); - }, - selected: AuthStore.getUser()?.selectedOrg.name === org.name - })), - { text: '', jsx: Log Out, action: () => true }, - ]} - /> -
+ {!ENVIRONMENT_VARIABLES.PLAYGROUND_APP && ( +
+ setShowDropDown(!showDropdown)} + /> + ({ + text: org.name || '', + action: async () => { + await AuthStore.selectOrganization(org.name); + }, + selected: AuthStore.getUser()?.selectedOrg.name === org.name, + })), + { text: '', jsx: Log Out, action: () => true }, + ]} + /> +
+ )}
); diff --git a/web/src/pages/authorized/authorizedRouteNames.ts b/web/src/pages/authorized/authorizedRouteNames.ts index 180383aa1..db8843c5e 100644 --- a/web/src/pages/authorized/authorizedRouteNames.ts +++ b/web/src/pages/authorized/authorizedRouteNames.ts @@ -11,6 +11,7 @@ import { Overview } from './overview/Overview'; import { Profile } from './profile/Profile'; import { Teams } from './teams/Teams'; import { TermsAndConditions } from './terms-and-conditions/TermsAndConditons'; +import { ENVIRONMENT_VARIABLES } from 'utils/environmentVariables'; export const PROJECTS_URL = '/dashboard'; const DASHBOARD_URL = '/demo-dashboard'; @@ -38,6 +39,12 @@ const urls = [ { key: 'RESOURCE_VIEW_URL', value: RESOURCE_VIEW_URL }, ]; +if (ENVIRONMENT_VARIABLES.PLAYGROUND_APP) { + ['ORG_REGISTRATION', 'OVERVIEW_URL', 'QUICK_START_URL', 'ENVIRONMENT_BUILDER_URL', 'PROFILE_URL', 'RESOURCE_VIEW_URL'].forEach(e => { + urls.splice(urls.findIndex(u => e === u.key), 1); + }) +} + Reflect.ownKeys(FeatureRoutes).forEach(key => { if (Reflect.get(FeatureRoutes, key) === false) { switch (key) { diff --git a/web/src/pages/authorized/feature_toggle.tsx b/web/src/pages/authorized/feature_toggle.tsx index 0295b76c3..b8f2924d0 100644 --- a/web/src/pages/authorized/feature_toggle.tsx +++ b/web/src/pages/authorized/feature_toggle.tsx @@ -1,4 +1,5 @@ import AuthStore from "auth/AuthStore"; +import { ENVIRONMENT_VARIABLES } from "utils/environmentVariables"; const showFeatures = (process.env.REACT_APP_ENABLED_FEATURE_FLAGS || '') .toString() @@ -52,6 +53,9 @@ export const featureToggled = (featureKey: string, userBased: boolean = false) = } export function BradAdarshFeatureVisible() : boolean { + if (ENVIRONMENT_VARIABLES.PLAYGROUND_APP) { + return false; + } const user = AuthStore.getUser(); // sometimes life hands you lemons... diff --git a/web/src/router/PrivateRoute.tsx b/web/src/router/PrivateRoute.tsx index 231a27ba3..c625c00cc 100644 --- a/web/src/router/PrivateRoute.tsx +++ b/web/src/router/PrivateRoute.tsx @@ -6,6 +6,7 @@ import { QuickStart } from 'pages/authorized/quick-start/QuickStart'; import { TermsAndConditions } from 'pages/authorized/terms-and-conditions/TermsAndConditons'; import React, { ElementType, FC, ReactNode, useEffect } from 'react'; import { Redirect, Route, RouteComponentProps, RouteProps } from 'react-router-dom'; +import { ENVIRONMENT_VARIABLES } from 'utils/environmentVariables'; interface PrivateRouteProps extends Omit { component: ElementType; @@ -20,21 +21,25 @@ const PrivateRoute: FC = ({ component: Component, ...rest }: render={(props: RouteComponentProps): ReactNode => { const user = AuthStore.getUser(); if (user) { - if (user.role !== 'Admin' && rest.location?.pathname?.includes('settings')) { - return - } + if (!ENVIRONMENT_VARIABLES.PLAYGROUND_APP) { + if (user.role !== 'Admin' && rest.location?.pathname?.includes('settings')) { + return ; + } - if ((user.selectedOrg && !user.selectedOrg.githubRepo) || rest.location?.pathname === QUICK_START_URL) { - return ; - } + if ( + (user.selectedOrg && !user.selectedOrg.githubRepo) || + rest.location?.pathname === QUICK_START_URL + ) { + return ; + } - if (user.organizations.length === 0 || user.selectedOrg?.provisioned !== true) { - return ; + if (user.organizations.length === 0 || user.selectedOrg?.provisioned !== true) { + return ; + } } - + return ; } - return ; }} /> From 7b1e6c42584aa54a6ad627fd31014abb1f29ab32 Mon Sep 17 00:00:00 2001 From: Shashank Sharma Date: Fri, 12 May 2023 19:36:48 +0530 Subject: [PATCH 02/30] Pushing code for web and bff to support login and restricting features for playground app. --- api/src/environment/environment.module.ts | 3 +- api/src/organization/organization.module.ts | 1 + api/src/organization/organization.service.ts | 5 ++ api/src/typeorm/User.entity.ts | 6 +++ .../1683899743907-UserIpForPlayground.js | 9 ++++ api/src/users/User.dto.ts | 5 ++ api/src/users/users.controller.ts | 18 ++++++- api/src/users/users.module.ts | 6 ++- api/src/users/users.service.ts | 50 ++++++++++++++++++- bff/src/auth/auth.ts | 5 +- bff/src/config.ts | 2 +- helm-charts/bff/templates/deployment.yaml | 2 + helm-charts/bff/values.yaml | 1 + helm-charts/web/templates/deployment.yaml | 2 + helm-charts/web/values.yaml | 1 + web/src/utils/environmentVariables.ts | 3 +- 16 files changed, 108 insertions(+), 11 deletions(-) create mode 100644 api/src/typeorm/migrations/1683899743907-UserIpForPlayground.js diff --git a/api/src/environment/environment.module.ts b/api/src/environment/environment.module.ts index bd4d3ea3d..da5b065fe 100644 --- a/api/src/environment/environment.module.ts +++ b/api/src/environment/environment.module.ts @@ -36,8 +36,9 @@ import { SystemService } from 'src/system/system.service'; EnvironmentService, TeamService, ReconciliationService, - SystemService + SystemService, ], + exports: [EnvironmentService], }) export class EnvironmentModule implements NestModule { configure(consumer: MiddlewareConsumer) { diff --git a/api/src/organization/organization.module.ts b/api/src/organization/organization.module.ts index d667295e3..fba577ed3 100644 --- a/api/src/organization/organization.module.ts +++ b/api/src/organization/organization.module.ts @@ -15,6 +15,7 @@ import { OrganizationService } from './organization.service'; imports: [TypeOrmModule.forFeature([Organization, User])], controllers: [OrganizationController], providers: [OrganizationService, UsersService], + exports: [OrganizationService], }) export class OrganizationModule implements NestModule { configure(consumer: MiddlewareConsumer) { diff --git a/api/src/organization/organization.service.ts b/api/src/organization/organization.service.ts index 74384220b..a08ab9fd3 100644 --- a/api/src/organization/organization.service.ts +++ b/api/src/organization/organization.service.ts @@ -142,4 +142,9 @@ export class OrganizationService { id: org.id, }); } + + async getEmptyOrg() { + const orgs = await this.getOrganizations(); + return orgs.find(org => org.users.length === 0); + } } diff --git a/api/src/typeorm/User.entity.ts b/api/src/typeorm/User.entity.ts index def3de8e1..c8b6cafd1 100644 --- a/api/src/typeorm/User.entity.ts +++ b/api/src/typeorm/User.entity.ts @@ -42,6 +42,12 @@ export class User { }) archived: boolean; + @Column({ + default: null, + unique: true + }) + ipv4: string; + @ManyToMany(() => Organization, (org) => org.users) organizations: Organization[]; diff --git a/api/src/typeorm/migrations/1683899743907-UserIpForPlayground.js b/api/src/typeorm/migrations/1683899743907-UserIpForPlayground.js new file mode 100644 index 000000000..d24c3b76b --- /dev/null +++ b/api/src/typeorm/migrations/1683899743907-UserIpForPlayground.js @@ -0,0 +1,9 @@ +module.exports = class UserIpForPlayground1683899743907 { + async up(queryRunner) { + await queryRunner.query( + 'ALTER TABLE `USERS` ADD COLUMN `ipv4` varchar(255) UNIQUE default null' + ); + } + + async down(queryRunner) {} +}; diff --git a/api/src/users/User.dto.ts b/api/src/users/User.dto.ts index 3367539d9..50e920fe2 100644 --- a/api/src/users/User.dto.ts +++ b/api/src/users/User.dto.ts @@ -16,6 +16,11 @@ export class CreateUserDto { name: string; } +export class CreatePlaygroundUserDto { + @ApiProperty() + ipv4: string; +} + export class PatchUserDto { @ApiProperty({ default: null, diff --git a/api/src/users/users.controller.ts b/api/src/users/users.controller.ts index 4b377d4e2..a7751004a 100644 --- a/api/src/users/users.controller.ts +++ b/api/src/users/users.controller.ts @@ -10,7 +10,7 @@ import { import { ApiTags } from '@nestjs/swagger'; import { AuthController } from 'src/auth/auth.controller'; import { User } from 'src/typeorm/User.entity'; -import { CreateUserDto } from './User.dto'; +import { CreatePlaygroundUserDto, CreateUserDto } from './User.dto'; import { UsersService } from './users.service'; @Controller({ @@ -33,10 +33,26 @@ export class UsersController { return user; } + @Get('/playground/:username') + public async getPlaygroundUser(@Param('username') username: string): Promise { + const user = await this.userService.getPlaygroundUser(username); + + if (!user) { + throw new NotFoundException(); + } + + return user; + } + @Post() public async createUser(@Body() body: CreateUserDto): Promise { const user = await this.userService.create(body); return user; } + + @Post('/playground') + public async createPlaygroundUser(@Body() user: CreatePlaygroundUserDto): Promise { + return this.userService.createPlaygroundUser(user); + } } diff --git a/api/src/users/users.module.ts b/api/src/users/users.module.ts index f05e099bc..1d83d2cef 100644 --- a/api/src/users/users.module.ts +++ b/api/src/users/users.module.ts @@ -3,11 +3,13 @@ import { TypeOrmModule } from '@nestjs/typeorm'; import { User } from 'src/typeorm/User.entity'; import { UsersController } from './users.controller'; import { UsersService } from './users.service'; +import { OrganizationService } from 'src/organization/organization.service'; +import { Organization } from 'src/typeorm'; @Module({ - imports: [TypeOrmModule.forFeature([User])], + imports: [TypeOrmModule.forFeature([User, Organization])], controllers: [UsersController], - providers: [UsersService], + providers: [UsersService, OrganizationService], exports: [UsersService], }) export class UsersModule {} diff --git a/api/src/users/users.service.ts b/api/src/users/users.service.ts index 0e29037af..c2261dac4 100644 --- a/api/src/users/users.service.ts +++ b/api/src/users/users.service.ts @@ -1,8 +1,10 @@ -import { BadRequestException, Injectable, Logger } from '@nestjs/common'; +import { BadRequestException, Injectable, Logger, NotFoundException } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; +import { randomUUID } from 'crypto'; import { User } from 'src/typeorm'; import { Repository } from 'typeorm'; -import { CreateUserDto } from './User.dto'; +import { CreatePlaygroundUserDto, CreateUserDto } from './User.dto'; +// import { OrganizationService } from 'src/organization/organization.service'; @Injectable() export class UsersService { @@ -19,6 +21,15 @@ export class UsersService { }); } + async getPlaygroundUser(ipv4: string): Promise { + return this.userRepo.findOne({ + where: { ipv4 }, + relations: { + organizations: true, + }, + }); + } + async getUserById(userId: number): Promise { return this.userRepo.findOne({ where: { id: userId }, @@ -51,4 +62,39 @@ export class UsersService { return user; } + + public async createPlaygroundUser(user: CreatePlaygroundUserDto) { + if (!user.ipv4) { + throw new BadRequestException('rquest does not have a valid ip address'); + } + + const currentUser = await this.userRepo.findOne({ + where: { + ipv4: user.ipv4, + }, + }); + + if (currentUser) { + throw new BadRequestException('User already exists'); + } + + // Get an org that is not associated to any user + + const org = null; //await this.orgSvc.getEmptyOrg(); + + if (!org) { + throw new NotFoundException("No Organization is present at the moment."); + } + + const uuid = `guest-${randomUUID()}`; + // Create user + const newUser = new User(); + newUser.email = `${uuid}@cloudknit.io`; + newUser.name = uuid; + newUser.username = uuid; + newUser.role = 'Guest'; + newUser.organizations = [org]; + + return this.userRepo.save(newUser); + } } diff --git a/bff/src/auth/auth.ts b/bff/src/auth/auth.ts index 240d8b0f4..c5e467eea 100644 --- a/bff/src/auth/auth.ts +++ b/bff/src/auth/auth.ts @@ -37,7 +37,7 @@ export async function getUser(username: string): Promise { export async function getPlaygroundUser(username: string): Promise { try { const user = await axios.get( - `${process.env.ZLIFECYCLE_API_URL}/v1/orgs/1/auth/users/${username}` + `${process.env.ZLIFECYCLE_API_URL}/v1/users/playground/${username}` ); return user.data; @@ -54,8 +54,7 @@ export async function getPlaygroundUser(username: string): Promise { } export async function createPlaygroundUser(ipv4: string): Promise { - const url = `${process.env.ZLIFECYCLE_API_URL}/v1/orgs/1/auth/playground/users`; - console.log(url); + const url = `${process.env.ZLIFECYCLE_API_URL}/v1/users/playground`; try { const user = await axios.post(url, { ipv4, diff --git a/bff/src/config.ts b/bff/src/config.ts index cda2463c6..354935304 100644 --- a/bff/src/config.ts +++ b/bff/src/config.ts @@ -10,7 +10,7 @@ const config = { WEB_URL: process.env.SITE_URL, API_URL: `${process.env.ZLIFECYCLE_API_URL}/v1`, ARGOCD_URL: process.env.ARGO_CD_API_URL, - PLAYGROUND_APP: process.env.CK_PLAYGROUND, + PLAYGROUND_APP: process.env.CK_PLAYGROUND == 'true', argoWFUrl: (orgName: string) => process.env.ARGO_WORKFLOW_API_URL.replaceAll(":org", orgName), stateMgrUrl: (orgName: string) => diff --git a/helm-charts/bff/templates/deployment.yaml b/helm-charts/bff/templates/deployment.yaml index f11b67fce..88e6e38a2 100644 --- a/helm-charts/bff/templates/deployment.yaml +++ b/helm-charts/bff/templates/deployment.yaml @@ -27,6 +27,8 @@ spec: value: {{ .Values.domain | quote }} - name: COOKIE_SECRET value: "test" + - name: CK_PLAYGROUND + value: {{ .Values.playground }} - name: ARGO_WORKFLOW_API_URL value: {{ .Values.argoWorkflowApiUrl | quote }} - name: ARGO_CD_API_URL diff --git a/helm-charts/bff/values.yaml b/helm-charts/bff/values.yaml index 965bb7aa3..72543b4c2 100644 --- a/helm-charts/bff/values.yaml +++ b/helm-charts/bff/values.yaml @@ -29,6 +29,7 @@ ingressPaths: - "/api" - "/ext" protocol: +playground: true auth0: issuerBaseUrl: web: diff --git a/helm-charts/web/templates/deployment.yaml b/helm-charts/web/templates/deployment.yaml index 9903cbb1d..b52557b78 100644 --- a/helm-charts/web/templates/deployment.yaml +++ b/helm-charts/web/templates/deployment.yaml @@ -21,6 +21,8 @@ spec: value: "3000" - name: HOST value: {{ .Values.domain }} + - name: PLAYGROUND_APP + value: {{ .Values.playground }} - name: REACT_APP_STREAM_URL value: {{ .Values.bff.urlWithProtocol }} - name: REACT_APP_BASE_URL diff --git a/helm-charts/web/values.yaml b/helm-charts/web/values.yaml index aeb3f2948..1d47a3dd5 100644 --- a/helm-charts/web/values.yaml +++ b/helm-charts/web/values.yaml @@ -8,6 +8,7 @@ domain: zcustomer.zlifecycle.com environment: enabledFeatureFlags: adminDomain: zcustomer-admin.zlifecycle.com +playground: true replicas: 1 bff: urlWithProtocol: diff --git a/web/src/utils/environmentVariables.ts b/web/src/utils/environmentVariables.ts index c14908503..1cdb444f6 100644 --- a/web/src/utils/environmentVariables.ts +++ b/web/src/utils/environmentVariables.ts @@ -1,4 +1,5 @@ export const ENVIRONMENT_VARIABLES = { REACT_APP_CUSTOMER_NAME: `${process.env.REACT_APP_CUSTOMER_NAME}`, - REACT_APP_BASE_URL: `${process.env.REACT_APP_BASE_URL}` + REACT_APP_BASE_URL: `${process.env.REACT_APP_BASE_URL}`, + PLAYGROUND_APP: `${process.env.PLAYGROUND_APP}` == 'true' } \ No newline at end of file From c38944c75ebbea1c0877a8f0f1978eb80d079edd Mon Sep 17 00:00:00 2001 From: Shashank Sharma Date: Fri, 12 May 2023 20:58:40 +0530 Subject: [PATCH 03/30] Fixing user flow. --- api/src/organization/organization.service.ts | 5 --- api/src/users/users.controller.ts | 7 ++-- api/src/users/users.module.ts | 3 +- api/src/users/users.service.ts | 35 ++++++++++++-------- bff/src/auth/auth.ts | 3 +- 5 files changed, 29 insertions(+), 24 deletions(-) diff --git a/api/src/organization/organization.service.ts b/api/src/organization/organization.service.ts index a08ab9fd3..74384220b 100644 --- a/api/src/organization/organization.service.ts +++ b/api/src/organization/organization.service.ts @@ -142,9 +142,4 @@ export class OrganizationService { id: org.id, }); } - - async getEmptyOrg() { - const orgs = await this.getOrganizations(); - return orgs.find(org => org.users.length === 0); - } } diff --git a/api/src/users/users.controller.ts b/api/src/users/users.controller.ts index a7751004a..57d23acf2 100644 --- a/api/src/users/users.controller.ts +++ b/api/src/users/users.controller.ts @@ -6,6 +6,7 @@ import { NotFoundException, Param, Post, + Query, } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; import { AuthController } from 'src/auth/auth.controller'; @@ -33,9 +34,9 @@ export class UsersController { return user; } - @Get('/playground/:username') - public async getPlaygroundUser(@Param('username') username: string): Promise { - const user = await this.userService.getPlaygroundUser(username); + @Get('/playground') + public async getPlaygroundUser(@Query('ipv4') ipv4: string): Promise { + const user = await this.userService.getPlaygroundUser(ipv4); if (!user) { throw new NotFoundException(); diff --git a/api/src/users/users.module.ts b/api/src/users/users.module.ts index 1d83d2cef..e30886a18 100644 --- a/api/src/users/users.module.ts +++ b/api/src/users/users.module.ts @@ -3,13 +3,12 @@ import { TypeOrmModule } from '@nestjs/typeorm'; import { User } from 'src/typeorm/User.entity'; import { UsersController } from './users.controller'; import { UsersService } from './users.service'; -import { OrganizationService } from 'src/organization/organization.service'; import { Organization } from 'src/typeorm'; @Module({ imports: [TypeOrmModule.forFeature([User, Organization])], controllers: [UsersController], - providers: [UsersService, OrganizationService], + providers: [UsersService], exports: [UsersService], }) export class UsersModule {} diff --git a/api/src/users/users.service.ts b/api/src/users/users.service.ts index c2261dac4..0fc5a2a7d 100644 --- a/api/src/users/users.service.ts +++ b/api/src/users/users.service.ts @@ -1,16 +1,23 @@ -import { BadRequestException, Injectable, Logger, NotFoundException } from '@nestjs/common'; +import { + BadRequestException, + Injectable, + Logger, + NotFoundException, +} from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { randomUUID } from 'crypto'; -import { User } from 'src/typeorm'; +import { Organization, User } from 'src/typeorm'; import { Repository } from 'typeorm'; import { CreatePlaygroundUserDto, CreateUserDto } from './User.dto'; -// import { OrganizationService } from 'src/organization/organization.service'; @Injectable() export class UsersService { private readonly logger = new Logger(UsersService.name); - constructor(@InjectRepository(User) private userRepo: Repository) {} + constructor( + @InjectRepository(User) private userRepo: Repository, + @InjectRepository(Organization) private orgRepo: Repository + ) {} async getUser(username: string): Promise { return this.userRepo.findOne({ @@ -65,14 +72,10 @@ export class UsersService { public async createPlaygroundUser(user: CreatePlaygroundUserDto) { if (!user.ipv4) { - throw new BadRequestException('rquest does not have a valid ip address'); + throw new BadRequestException('request does not have a valid ip address'); } - const currentUser = await this.userRepo.findOne({ - where: { - ipv4: user.ipv4, - }, - }); + const currentUser = await this.getPlaygroundUser(user.ipv4); if (currentUser) { throw new BadRequestException('User already exists'); @@ -80,12 +83,18 @@ export class UsersService { // Get an org that is not associated to any user - const org = null; //await this.orgSvc.getEmptyOrg(); + const orgs = await this.orgRepo.find({ + relations: { + users: true + }, + }); + const org = orgs.find((org) => org.users.length === 0); + if (!org) { - throw new NotFoundException("No Organization is present at the moment."); + throw new NotFoundException('No Organization is present at the moment.'); } - + const uuid = `guest-${randomUUID()}`; // Create user const newUser = new User(); diff --git a/bff/src/auth/auth.ts b/bff/src/auth/auth.ts index c5e467eea..a5d813f27 100644 --- a/bff/src/auth/auth.ts +++ b/bff/src/auth/auth.ts @@ -37,7 +37,7 @@ export async function getUser(username: string): Promise { export async function getPlaygroundUser(username: string): Promise { try { const user = await axios.get( - `${process.env.ZLIFECYCLE_API_URL}/v1/users/playground/${username}` + `${process.env.ZLIFECYCLE_API_URL}/v1/users/playground?ipv4=${username}` ); return user.data; @@ -376,6 +376,7 @@ export function setUpAuth(app: express.Express, authRouter: express.Router) { } function getClientIP(req) { + return '127.0.0.1'; if (!req.headers["x-forwarded-for"]) { return null; } From fdc626615d63dcc80a8e4f9003fa206d79ef6711 Mon Sep 17 00:00:00 2001 From: Shashank Sharma Date: Fri, 12 May 2023 21:51:48 +0530 Subject: [PATCH 04/30] Github API Changes * commit implementation --- api/src/app.module.ts | 22 +++--- api/src/config.ts | 10 +++ .../github-api/github-api.controller.spec.ts | 18 +++++ api/src/github-api/github-api.controller.ts | 27 ++++++++ api/src/github-api/github-api.module.ts | 21 ++++++ api/src/github-api/github-api.service.ts | 69 +++++++++++++++++++ api/src/routes.ts | 12 +++- 7 files changed, 166 insertions(+), 13 deletions(-) create mode 100644 api/src/github-api/github-api.controller.spec.ts create mode 100644 api/src/github-api/github-api.controller.ts create mode 100644 api/src/github-api/github-api.module.ts create mode 100644 api/src/github-api/github-api.service.ts diff --git a/api/src/app.module.ts b/api/src/app.module.ts index 800fda83d..fa92378d5 100644 --- a/api/src/app.module.ts +++ b/api/src/app.module.ts @@ -1,25 +1,26 @@ import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import { RouterModule } from '@nestjs/core'; +import { EventEmitterModule } from '@nestjs/event-emitter'; import { TypeOrmModule, TypeOrmModuleOptions } from '@nestjs/typeorm'; import { AuthModule } from './auth/auth.module'; +import { CachingModule } from './caching/caching.module'; +import { CachingService } from './caching/caching.service'; +import { ComponentModule } from './component/component.module'; +import { EnvironmentModule } from './environment/environment.module'; +import { ErrorsModule } from './errors/errors.module'; +import { GithubApiModule } from './github-api/github-api.module'; +import { AppLoggerMiddleware } from './middleware/logger.middle'; +import { OperationsModule } from './operations/operations.module'; import { OrganizationModule } from './organization/organization.module'; import { ReconciliationModule } from './reconciliation/reconciliation.module'; import { appRoutes } from './routes'; import { SecretsModule } from './secrets/secrets.module'; -import { UsersModule } from './users/users.module'; +import { StreamModule } from './stream/stream.module'; import { SystemModule } from './system/system.module'; -import { OperationsModule } from './operations/operations.module'; -import { AppLoggerMiddleware } from './middleware/logger.middle'; import { TeamModule } from './team/team.module'; -import { EnvironmentModule } from './environment/environment.module'; -import { ComponentModule } from './component/component.module'; -import { StreamModule } from './stream/stream.module'; -import { CachingService } from './caching/caching.service'; -import { CachingModule } from './caching/caching.module'; import { dbConfig } from './typeorm'; -import { EventEmitterModule } from '@nestjs/event-emitter'; -import { ErrorsModule } from './errors/errors.module'; +import { UsersModule } from './users/users.module'; @Module({ imports: [ @@ -42,6 +43,7 @@ import { ErrorsModule } from './errors/errors.module'; StreamModule, CachingModule, ErrorsModule, + GithubApiModule, ], controllers: [], providers: [CachingService], diff --git a/api/src/config.ts b/api/src/config.ts index cef88608f..fe7b4d8ac 100644 --- a/api/src/config.ts +++ b/api/src/config.ts @@ -9,6 +9,11 @@ export type ApiConfig = { database: string; }; port: number; + github: { + personalAccessToken: string, + owner: string, + repo: string, + } AWS: { accessKeyId: string; secretAccessKey: string; @@ -62,6 +67,11 @@ export function init() { database: getEnvVarOrFail('TYPEORM_DATABASE'), }, port: parseInt(process.env.APP_PORT) || 3000, + github: { + personalAccessToken: getEnvVarOrDefault('PERSONAL_ACCESS_TOKEN','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'), + owner: getEnvVarOrDefault('OWNER', 'zlab-tech'), + repo: getEnvVarOrDefault('REPO', 'checkout-config'), + }, AWS: { accessKeyId: getEnvVarOrFail('AWS_ACCESS_KEY_ID'), secretAccessKey: getEnvVarOrFail('AWS_SECRET_ACCESS_KEY'), diff --git a/api/src/github-api/github-api.controller.spec.ts b/api/src/github-api/github-api.controller.spec.ts new file mode 100644 index 000000000..8ffcfcea9 --- /dev/null +++ b/api/src/github-api/github-api.controller.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { GithubApiController } from './github-api.controller'; + +describe('GithubApiController', () => { + let controller: GithubApiController; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [GithubApiController], + }).compile(); + + controller = module.get(GithubApiController); + }); + + it('should be defined', () => { + expect(controller).toBeDefined(); + }); +}); diff --git a/api/src/github-api/github-api.controller.ts b/api/src/github-api/github-api.controller.ts new file mode 100644 index 000000000..892137668 --- /dev/null +++ b/api/src/github-api/github-api.controller.ts @@ -0,0 +1,27 @@ +import { Controller, Post, Request } from '@nestjs/common'; +import { ApiTags } from '@nestjs/swagger'; +import { EnvironmentService } from 'src/environment/environment.service'; +import { APIRequest, EnvironmentApiParam } from 'src/types'; +import { GithubApiService } from './github-api.service'; +import { get } from 'src/config'; + +@Controller({ + version: '1', +}) +@ApiTags('github-api') +export class GithubApiController { + constructor( + private readonly envSvc: EnvironmentService, + private readonly gitSvc: GithubApiService + ) {} + + @Post('/:environmentId') + @EnvironmentApiParam() + async gitCommit(@Request() req: APIRequest) { + const { org, team, env } = req; + const environment = await this.envSvc.findById(org, env.id); + if (environment) { + return this.gitSvc.gitCommit(get().github.owner, get().github.repo, `${env.name}/env.yaml`); + } + } +} diff --git a/api/src/github-api/github-api.module.ts b/api/src/github-api/github-api.module.ts new file mode 100644 index 000000000..80e7c5e0b --- /dev/null +++ b/api/src/github-api/github-api.module.ts @@ -0,0 +1,21 @@ +import { MiddlewareConsumer, Module, RequestMethod } from '@nestjs/common'; +import { GithubApiController } from './github-api.controller'; +import { EnvironmentService } from 'src/environment/environment.service'; +import { EnvironmentMiddleware } from 'src/middleware/environment.middle'; +import { GithubApiService } from './github-api.service'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { Environment } from 'src/typeorm/environment.entity'; + +@Module({ + imports: [TypeOrmModule.forFeature([Environment])], + controllers: [GithubApiController], + providers: [EnvironmentService, GithubApiService], +}) +export class GithubApiModule { + configure(consumer: MiddlewareConsumer) { + consumer.apply(EnvironmentMiddleware).forRoutes({ + path: '*/github/:environmentId*', + method: RequestMethod.ALL, + }); + } +} diff --git a/api/src/github-api/github-api.service.ts b/api/src/github-api/github-api.service.ts new file mode 100644 index 000000000..8d81df362 --- /dev/null +++ b/api/src/github-api/github-api.service.ts @@ -0,0 +1,69 @@ +import { Injectable, InternalServerErrorException } from '@nestjs/common'; +import axios from 'axios'; +import { get } from 'src/config'; + +@Injectable() +export class GithubApiService { + private readonly baseUri: string = 'https://api.github.com/repos'; + private readonly headers = { + Accept: 'application/vnd.github+json', + Authorization: `Bearer ${get().github.personalAccessToken}`, + 'X-GitHub-Api-Version': '2022-11-28', + }; + + private getURL(owner: string, repo: string, filePath: string) { + return `${this.baseUri}/${owner}/${repo}/contents/${filePath}`; + } + + private async getFileSHA(owner: string, repo: string, filePath: string) { + const url = this.getURL(owner, repo, filePath); + const { data } = await axios.get<{ sha: string; content: string }>(url, { + headers: this.headers, + }); + return data; + } + + public setAlternateFlag(yamlString: string) { + const decodedYaml = Buffer.from(yamlString, 'base64').toString(); + console.log(decodedYaml); + if (decodedYaml.includes('teardown: true')) { + return Buffer.from( + decodedYaml.replace('teardown: true', 'teardown: false') + ).toString('base64'); + } + return Buffer.from( + decodedYaml.replace('teardown: false', 'teardown: true') + ).toString('base64'); + } + + public async gitCommit(owner: string, repo: string, filePath: string) { + const { sha, content } = await this.getFileSHA(owner, repo, filePath); + const payload = { + message: 'api testing for playground ', + committer: { name: 'playground', email: 'playground@cloudknit.io' }, + content: this.setAlternateFlag(content), + sha, + }; + + try { + const { data } = await axios.put( + this.getURL(owner, repo, filePath), + payload, + { + headers: this.headers, + } + ); + + const { commit } = data; + const { html_url } = commit; + return { + status: 'success', + html_url, + }; + } catch (err) { + throw new InternalServerErrorException( + 'There was an error while pushing the commit to git' + ); + } + } +} diff --git a/api/src/routes.ts b/api/src/routes.ts index 134606ed4..6a28ea8b8 100644 --- a/api/src/routes.ts +++ b/api/src/routes.ts @@ -11,6 +11,7 @@ import { EnvironmentModule } from './environment/environment.module'; import { ComponentModule } from './component/component.module'; import { StreamModule } from './stream/stream.module'; import { ErrorsModule } from './errors/errors.module'; +import { GithubApiModule } from './github-api/github-api.module'; export const appRoutes: Routes = [ { @@ -58,10 +59,15 @@ export const appRoutes: Routes = [ module: ComponentModule, }, ], - }, { + }, + { + path: '/:teamId/github', + module: GithubApiModule, + }, + { path: '/:teamId/errors', - module: ErrorsModule - } + module: ErrorsModule, + }, ], }, ], From 2c1899533dcc3f52fb725e1ce86571e7f763bf5d Mon Sep 17 00:00:00 2001 From: Shashank Sharma Date: Fri, 12 May 2023 21:53:06 +0530 Subject: [PATCH 05/30] Api Config changes for github --- api/src/config.ts | 6 +++--- helm-charts/api/templates/deployment.yaml | 6 ++++++ helm-charts/api/values.yaml | 4 ++++ 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/api/src/config.ts b/api/src/config.ts index fe7b4d8ac..23b96c472 100644 --- a/api/src/config.ts +++ b/api/src/config.ts @@ -68,9 +68,9 @@ export function init() { }, port: parseInt(process.env.APP_PORT) || 3000, github: { - personalAccessToken: getEnvVarOrDefault('PERSONAL_ACCESS_TOKEN','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'), - owner: getEnvVarOrDefault('OWNER', 'zlab-tech'), - repo: getEnvVarOrDefault('REPO', 'checkout-config'), + personalAccessToken: getEnvVarOrDefault('GIT_PERSONAL_ACCESS_TOKEN','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'), + owner: getEnvVarOrDefault('GIT_OWNER', 'zlab-tech'), + repo: getEnvVarOrDefault('GIT_REPO', 'checkout-config'), }, AWS: { accessKeyId: getEnvVarOrFail('AWS_ACCESS_KEY_ID'), diff --git a/helm-charts/api/templates/deployment.yaml b/helm-charts/api/templates/deployment.yaml index b416c1486..df9eef42e 100644 --- a/helm-charts/api/templates/deployment.yaml +++ b/helm-charts/api/templates/deployment.yaml @@ -46,6 +46,12 @@ spec: value: {{ .Values.awsCredentials.sessionToken}} - name: AWS_REGION value: {{ .Values.awsCredentials.region}} + - name: GIT_PERSONAL_ACCESS_TOKEN + value: {{ .Values.github.personalAccessToken}} + - name: GIT_OWNER + value: {{ .Values.github.owner}} + - name: GIT_REPO + value: {{ .Values.github.repo}} image: {{ .Values.image.repository }}/{{ .Values.image.name }}:{{ .Values.image.tag }} ports: - containerPort: 3000 diff --git a/helm-charts/api/values.yaml b/helm-charts/api/values.yaml index ddd13dc8d..318476910 100644 --- a/helm-charts/api/values.yaml +++ b/helm-charts/api/values.yaml @@ -3,6 +3,10 @@ image: tag: latest repository: replicas: 1 +github: + personalAccessToken: "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" + owner: + repo: database: host: username: From 38196b13ba9a5a0ea9cf57fc544eb04b11ecdc6c Mon Sep 17 00:00:00 2001 From: Shashank Sharma Date: Fri, 12 May 2023 22:28:11 +0530 Subject: [PATCH 06/30] Adding modal popup to UI * will only be show when environment is destroyed --- bff/src/proxy/pathMappings.ts | 4 ++ .../molecules/modal/ZModalPopup.tsx | 22 ++++++++ web/src/components/molecules/modal/style.scss | 46 +++++++++++++++++ .../authorized/environments/Environments.tsx | 51 +++++++++++++++++-- web/src/services/entity/entity.service.tsx | 16 +++++- 5 files changed, 135 insertions(+), 4 deletions(-) create mode 100644 web/src/components/molecules/modal/ZModalPopup.tsx create mode 100644 web/src/components/molecules/modal/style.scss diff --git a/bff/src/proxy/pathMappings.ts b/bff/src/proxy/pathMappings.ts index 4728bd865..164940f4e 100644 --- a/bff/src/proxy/pathMappings.ts +++ b/bff/src/proxy/pathMappings.ts @@ -196,6 +196,10 @@ const API_PATH_MAPPINGS = [ path: "/api/teams/:teamId/environments/:envId", newPath: (params: any) => `v1/orgs/${params.orgId}/teams/${params.teamId}/environments/${params.envId}`, }, + { + path: "/api/teams/:teamId/gitCommit/:envId", + newPath: (params: any) => `v1/orgs/${params.orgId}/teams/${params.teamId}/github-api/${params.envId}`, + }, { path: "/api/teams/:teamId/environments/:envId/components", newPath: (params: any) => `v1/orgs/${params.orgId}/teams/${params.teamId}/environments/${params.envId}/components`, diff --git a/web/src/components/molecules/modal/ZModalPopup.tsx b/web/src/components/molecules/modal/ZModalPopup.tsx new file mode 100644 index 000000000..9334878bf --- /dev/null +++ b/web/src/components/molecules/modal/ZModalPopup.tsx @@ -0,0 +1,22 @@ +import './style.scss'; + +import classNames from 'classnames'; +import { FC, PropsWithChildren } from 'react'; + +interface Props extends PropsWithChildren { + className?: string; + isShown: boolean; + header: JSX.Element | string; + onClose: () => void; +} + +export const ZModalPopup: FC = ({ className = '', isShown, header, onClose, children }: Props) => { + return ( +
+
+
{header}
+
{children}
+
+
+ ); +}; diff --git a/web/src/components/molecules/modal/style.scss b/web/src/components/molecules/modal/style.scss new file mode 100644 index 000000000..bd5825911 --- /dev/null +++ b/web/src/components/molecules/modal/style.scss @@ -0,0 +1,46 @@ +@import '../../../assets/styles/colors'; + +.zlifecycle-modal-popup-overlay { + height: 100vh; + width: 100vw; + background-color: rgba(255, 255, 255, 0.3); + backdrop-filter: blur(10px); + position: fixed; + top: 0px; + left: 0px; + z-index: 2; + display: none; + .zlifecycle-modal-popup { + display: flex; + opacity: 0; + position: fixed; + height: fit-content; + top: -75vh; + width: 50vw; + left: 25vw; + z-index: 2; + box-shadow: 0 0 10px #aaa; + background-color: white; + border-radius: 5px; + transition: all 0.5s ease-in-out; + overflow-y: scroll; + flex-direction: column; + &--header { + background-color: $zlifecycle-navy; + color: white; + padding: 5px 10px; + font-size: 1.5em; + font-family: 'DM Sans'; + } + &--content { + padding: 5px 10px; + } + } + &--active { + display: block; + .zlifecycle-modal-popup { + top: 25vh; + opacity: 1; + } + } +} diff --git a/web/src/pages/authorized/environments/Environments.tsx b/web/src/pages/authorized/environments/Environments.tsx index 1586c989d..e812d8e66 100644 --- a/web/src/pages/authorized/environments/Environments.tsx +++ b/web/src/pages/authorized/environments/Environments.tsx @@ -1,7 +1,9 @@ import { DiffEditor } from '@monaco-editor/react'; -import { NotificationType } from 'components/argo-core'; +import { NotificationType, NotificationsApi } from 'components/argo-core'; import { ZLoaderCover } from 'components/atoms/loader/LoaderCover'; import { EnvironmentCards } from 'components/molecules/cards/EnvironmentCards'; +import { ZModalPopup } from 'components/molecules/modal/ZModalPopup'; +import { SmallText } from 'components/organisms/workflow-diagram/WorkflowDiagram'; import { Context } from 'context/argo/ArgoUi'; import { ZEnvSyncStatus } from 'models/argo.models'; import { EntityStore } from 'models/entity.store'; @@ -12,11 +14,12 @@ import { getCheckBoxFilters, mockModifiedYaml, mockOriginalYaml, - renderSyncStatusItems + renderSyncStatusItems, } from 'pages/authorized/environments/helpers'; import React, { useEffect, useMemo, useState } from 'react'; import { useParams } from 'react-router-dom'; import { Subscription } from 'rxjs'; +import { EntityService } from 'services/entity/entity.service'; import { usePageHeader } from '../contexts/EnvironmentHeaderContext'; type CompareEnv = { @@ -37,6 +40,7 @@ export const Environments: React.FC = () => { const [loading, setLoading] = useState(true); const [environments, setEnvironments] = useState([]); const [viewType, setViewType] = useState(''); + const [pushingCommit, setPushingCommit] = useState(null); const [checkBoxFilters, setCheckBoxFilters] = useState(<>); const [filterItems, setFilterItems] = useState JSX.Element>>([]); const { pageHeaderObservable, breadcrumbObservable } = usePageHeader(); @@ -46,6 +50,7 @@ export const Environments: React.FC = () => { a: null, b: null, }); + const nm = React.useContext(Context)?.notifications as NotificationsApi; const breadcrumbItems = [ { @@ -205,7 +210,7 @@ export const Environments: React.FC = () => { return (
- +
{viewType === 'list' ? (
@@ -222,6 +227,46 @@ export const Environments: React.FC = () => { )} {compareEnvs.a?.env && compareEnvs.b?.env ? compareMode && renderDiffEditor() : null}
+ Provison an Environment
} + isShown={ + !loading && + environments?.length > 0 && + environments[0].status === ZEnvSyncStatus.Destroyed && + pushingCommit === null + } + onClose={() => {}}> +
+ +
+ + ); diff --git a/web/src/services/entity/entity.service.tsx b/web/src/services/entity/entity.service.tsx index 4265d741a..954b7ed09 100644 --- a/web/src/services/entity/entity.service.tsx +++ b/web/src/services/entity/entity.service.tsx @@ -44,7 +44,7 @@ export class EntityService extends BaseService { const url = this.constructUri(EntitytUriType.environment(teamId, envId)); try { const { data } = await ApiClient.patch(url, { - isReconcile: true + isReconcile: true, }); return data; } catch (err) { @@ -64,6 +64,19 @@ export class EntityService extends BaseService { } } + async gitCommit(teamId: number, envId: number) { + const url = this.constructUri(EntitytUriType.gitCommit(teamId, envId)); + try { + const { data } = await ApiClient.post(url); + return data; + } catch (err) { + console.error(err); + return { + status: 'error', + }; + } + } + stream(eventList: string[]) { const ec = new EventClient(this.constructUri(EntitytUriType.stream()), eventList); return ec.listen(); @@ -74,6 +87,7 @@ class EntitytUriType { static teams = (withComps: boolean) => `teams?withCost=true&withEnvironments=true&withComponents=${withComps}`; static environments = (teamId: number) => `teams/${teamId}/environments`; static environment = (teamId: number, envId: number) => `teams/${teamId}/environments/${envId}`; + static gitCommit = (teamId: number, envId: number) => `teams/${teamId}/gitCommit/${envId}`; static components = (teamId: number, envId: number, withLastAuditStatus: boolean) => `teams/${teamId}/environments/${envId}/components?withLastAuditStatus=${withLastAuditStatus}`; static stream = () => `stream`; From ae47ad56d698b36d60ccb3fa1cace02cce6f8ba1 Mon Sep 17 00:00:00 2001 From: Shashank Sharma Date: Mon, 15 May 2023 21:31:46 +0530 Subject: [PATCH 07/30] Removing github access token from code --- api/src/config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/config.ts b/api/src/config.ts index 23b96c472..954906727 100644 --- a/api/src/config.ts +++ b/api/src/config.ts @@ -68,7 +68,7 @@ export function init() { }, port: parseInt(process.env.APP_PORT) || 3000, github: { - personalAccessToken: getEnvVarOrDefault('GIT_PERSONAL_ACCESS_TOKEN','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'), + personalAccessToken: getEnvVarOrFail('GIT_PERSONAL_ACCESS_TOKEN'), owner: getEnvVarOrDefault('GIT_OWNER', 'zlab-tech'), repo: getEnvVarOrDefault('GIT_REPO', 'checkout-config'), }, From 208802c5fb8dd25ce41b918f9fff714eed579113 Mon Sep 17 00:00:00 2001 From: Shashank Sharma Date: Tue, 16 May 2023 21:36:44 +0530 Subject: [PATCH 08/30] updates chart version for bff and web --- helm-charts/bff/Chart.yaml | 2 +- helm-charts/web/Chart.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/helm-charts/bff/Chart.yaml b/helm-charts/bff/Chart.yaml index e593a6b30..386041bbd 100644 --- a/helm-charts/bff/Chart.yaml +++ b/helm-charts/bff/Chart.yaml @@ -3,4 +3,4 @@ name: zlifecycle-web-bff description: A Helm chart for Kubernetes type: application version: 0.35.0 -appVersion: "1.16.0" +appVersion: "1.17.0" diff --git a/helm-charts/web/Chart.yaml b/helm-charts/web/Chart.yaml index fd1b723f2..84c4e02b6 100644 --- a/helm-charts/web/Chart.yaml +++ b/helm-charts/web/Chart.yaml @@ -3,4 +3,4 @@ name: zlifecycle-web description: A Helm chart for Kubernetes type: application version: 0.8.0 -appVersion: "1.16.0" +appVersion: "1.17.0" From 1a35341f1cfa72a79f8a454544c84c28535908e5 Mon Sep 17 00:00:00 2001 From: Shashank Sharma Date: Tue, 16 May 2023 21:38:20 +0530 Subject: [PATCH 09/30] Updates correct version and reverts wrong change --- helm-charts/bff/Chart.yaml | 4 ++-- helm-charts/web/Chart.yaml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/helm-charts/bff/Chart.yaml b/helm-charts/bff/Chart.yaml index 386041bbd..be399ba0f 100644 --- a/helm-charts/bff/Chart.yaml +++ b/helm-charts/bff/Chart.yaml @@ -2,5 +2,5 @@ apiVersion: v1 name: zlifecycle-web-bff description: A Helm chart for Kubernetes type: application -version: 0.35.0 -appVersion: "1.17.0" +version: 0.36.0 +appVersion: "1.16.0" diff --git a/helm-charts/web/Chart.yaml b/helm-charts/web/Chart.yaml index 84c4e02b6..661698d69 100644 --- a/helm-charts/web/Chart.yaml +++ b/helm-charts/web/Chart.yaml @@ -2,5 +2,5 @@ apiVersion: v1 name: zlifecycle-web description: A Helm chart for Kubernetes type: application -version: 0.8.0 -appVersion: "1.17.0" +version: 0.9.0 +appVersion: "1.16.0" From 21aed7a595580a170be97e322951a0f872c02d7e Mon Sep 17 00:00:00 2001 From: Shashank Sharma Date: Tue, 16 May 2023 21:49:43 +0530 Subject: [PATCH 10/30] hardcodes playground and other env vars for testing. --- api/src/config.ts | 2 +- bff/src/config.ts | 2 +- web/src/utils/environmentVariables.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/api/src/config.ts b/api/src/config.ts index 58a614a63..79e179471 100644 --- a/api/src/config.ts +++ b/api/src/config.ts @@ -68,7 +68,7 @@ export function init() { }, port: parseInt(process.env.APP_PORT) || 3000, github: { - personalAccessToken: getEnvVarOrFail('GIT_PERSONAL_ACCESS_TOKEN'), + personalAccessToken: getEnvVarOrDefault('GIT_PERSONAL_ACCESS_TOKEN', ''), //getEnvVarOrFail('GIT_PERSONAL_ACCESS_TOKEN'), owner: getEnvVarOrDefault('GIT_OWNER', 'zlab-tech'), repo: getEnvVarOrDefault('GIT_REPO', 'checkout-config'), }, diff --git a/bff/src/config.ts b/bff/src/config.ts index 354935304..ab9d2919b 100644 --- a/bff/src/config.ts +++ b/bff/src/config.ts @@ -10,7 +10,7 @@ const config = { WEB_URL: process.env.SITE_URL, API_URL: `${process.env.ZLIFECYCLE_API_URL}/v1`, ARGOCD_URL: process.env.ARGO_CD_API_URL, - PLAYGROUND_APP: process.env.CK_PLAYGROUND == 'true', + PLAYGROUND_APP: true, //process.env.CK_PLAYGROUND == 'true', argoWFUrl: (orgName: string) => process.env.ARGO_WORKFLOW_API_URL.replaceAll(":org", orgName), stateMgrUrl: (orgName: string) => diff --git a/web/src/utils/environmentVariables.ts b/web/src/utils/environmentVariables.ts index 1cdb444f6..2584df076 100644 --- a/web/src/utils/environmentVariables.ts +++ b/web/src/utils/environmentVariables.ts @@ -1,5 +1,5 @@ export const ENVIRONMENT_VARIABLES = { REACT_APP_CUSTOMER_NAME: `${process.env.REACT_APP_CUSTOMER_NAME}`, REACT_APP_BASE_URL: `${process.env.REACT_APP_BASE_URL}`, - PLAYGROUND_APP: `${process.env.PLAYGROUND_APP}` == 'true' + PLAYGROUND_APP: true, //`${process.env.PLAYGROUND_APP}` == 'true' } \ No newline at end of file From 1bc84a11fad789678aecfff793a31171082974e7 Mon Sep 17 00:00:00 2001 From: Shashank Sharma Date: Tue, 16 May 2023 23:07:07 +0530 Subject: [PATCH 11/30] adds logic to extract ip for user --- bff/src/auth/auth.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/bff/src/auth/auth.ts b/bff/src/auth/auth.ts index a5d813f27..7644cd65d 100644 --- a/bff/src/auth/auth.ts +++ b/bff/src/auth/auth.ts @@ -376,7 +376,6 @@ export function setUpAuth(app: express.Express, authRouter: express.Router) { } function getClientIP(req) { - return '127.0.0.1'; if (!req.headers["x-forwarded-for"]) { return null; } From 8a57becaae71cb2300cbe28cb4e7bf051e276682 Mon Sep 17 00:00:00 2001 From: Shashank Sharma Date: Wed, 17 May 2023 18:58:45 +0530 Subject: [PATCH 12/30] Adding ipv4 to save user call --- api/src/users/users.service.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/api/src/users/users.service.ts b/api/src/users/users.service.ts index 0fc5a2a7d..77d35f480 100644 --- a/api/src/users/users.service.ts +++ b/api/src/users/users.service.ts @@ -102,6 +102,7 @@ export class UsersService { newUser.name = uuid; newUser.username = uuid; newUser.role = 'Guest'; + newUser.ipv4 = user.ipv4; newUser.organizations = [org]; return this.userRepo.save(newUser); From d47c0be85c65ad2428f148f0f6ceb378f1eb8e3c Mon Sep 17 00:00:00 2001 From: Shashank Sharma Date: Wed, 17 May 2023 19:19:13 +0530 Subject: [PATCH 13/30] Using correct controller route to fetch user using ip --- api/src/users/users.controller.ts | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/api/src/users/users.controller.ts b/api/src/users/users.controller.ts index 57d23acf2..702629ea2 100644 --- a/api/src/users/users.controller.ts +++ b/api/src/users/users.controller.ts @@ -34,20 +34,19 @@ export class UsersController { return user; } - @Get('/playground') - public async getPlaygroundUser(@Query('ipv4') ipv4: string): Promise { - const user = await this.userService.getPlaygroundUser(ipv4); - - if (!user) { - throw new NotFoundException(); - } + @Post() + public async createUser(@Body() body: CreateUserDto): Promise { + const user = await this.userService.create(body); return user; } - @Post() - public async createUser(@Body() body: CreateUserDto): Promise { - const user = await this.userService.create(body); + @Get('/playground/:ipv4') + public async getPlaygroundUser(@Param('ipv4') ipv4: string): Promise { + const user = await this.userService.getPlaygroundUser(ipv4); + if (!user) { + throw new NotFoundException(); + } return user; } From 4141933232d18d242ca555e8e69c4c7f5661f97c Mon Sep 17 00:00:00 2001 From: Shashank Sharma Date: Wed, 17 May 2023 19:51:52 +0530 Subject: [PATCH 14/30] sending ip param to api --- bff/src/auth/auth.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bff/src/auth/auth.ts b/bff/src/auth/auth.ts index 7644cd65d..c5e467eea 100644 --- a/bff/src/auth/auth.ts +++ b/bff/src/auth/auth.ts @@ -37,7 +37,7 @@ export async function getUser(username: string): Promise { export async function getPlaygroundUser(username: string): Promise { try { const user = await axios.get( - `${process.env.ZLIFECYCLE_API_URL}/v1/users/playground?ipv4=${username}` + `${process.env.ZLIFECYCLE_API_URL}/v1/users/playground/${username}` ); return user.data; From 62734d6f00141a4f2daf21faf9f6d00ce9a6d4fd Mon Sep 17 00:00:00 2001 From: Shashank Sharma Date: Thu, 18 May 2023 20:09:57 +0530 Subject: [PATCH 15/30] Removing error api calls from UI --- web/src/models/entity.store.ts | 1 - .../services/error/error-state.service.tsx | 68 ------------- web/src/services/error/error.service.tsx | 99 ------------------- 3 files changed, 168 deletions(-) delete mode 100644 web/src/services/error/error-state.service.tsx delete mode 100644 web/src/services/error/error.service.tsx diff --git a/web/src/models/entity.store.ts b/web/src/models/entity.store.ts index 6abdb98ad..572daaccc 100644 --- a/web/src/models/entity.store.ts +++ b/web/src/models/entity.store.ts @@ -39,7 +39,6 @@ export class EntityStore { } private constructor() { - ErrorStateService.getInstance(); this.generateEmitterMap(); Promise.resolve(this.getTeams()); this.startStreaming(); diff --git a/web/src/services/error/error-state.service.tsx b/web/src/services/error/error-state.service.tsx deleted file mode 100644 index c1569a7bc..000000000 --- a/web/src/services/error/error-state.service.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import { EnvironmentStatus, ErrorEvent, ErrorStatus, EventMessage } from 'models/error.model'; -import { ErrorService } from './error.service'; -import { Subject } from 'rxjs'; - -export class ErrorStateService { - private static instance: ErrorStateService | null = null; - private errorStateEnvironment: Map = new Map(); - public updates: Subject = new Subject(); - - private constructor() { - const errorInstance = ErrorService.getInstance(); - errorInstance.subscribeToErrorStream().subscribe((data: EnvironmentStatus) => this.errorStateData(data)); - errorInstance.getEventData(); - } - - static getInstance() { - if (!ErrorStateService.instance) { - ErrorStateService.instance = new ErrorStateService(); - } - return ErrorStateService.instance; - } - - private getTimestamp(e: EnvironmentStatus, err: string) { - const errorEvent = e.events.find(ev => (ev.payload || []).includes(err)); - return errorEvent?.createdAt ? new Date(errorEvent?.createdAt).toUTCString() : null; - } - - private errorMapper(e: EnvironmentStatus, err: string): EventMessage { - // @ts-ignore - return { - company: e.company, - environment: e.environment, - team: e.team, - message: err, - timestamp: e.events.length > 0 ? e.events[0].createdAt.toLocaleString() : 'N/A' - }; - } - - errorStateData(e: EnvironmentStatus) { - this.errorStateEnvironment.set(e.environment, e); - this.updates.next(); - } - - errorsInEnvironment(environmentId: string) { - const field = this.errorStateEnvironment.get(environmentId); - if (!field) { - return []; - } - return (field.errors || []).map(e => this.errorMapper(field, e)); - } - - public get Errors(): EventMessage[] { - const msgs: EventMessage[] = []; - - const errors = [...this.errorStateEnvironment.values()]; - const filtered = errors.filter(e => e.errors?.length > 0); - - filtered.forEach(error => { - msgs.push(...error.errors.map(er => this.errorMapper(error, er))); - }); - - return msgs; - } - - public get ErrorsEnvs(): EnvironmentStatus[] { - return [...this.errorStateEnvironment.values()].filter(e => e.errors?.length > 0); - } -} diff --git a/web/src/services/error/error.service.tsx b/web/src/services/error/error.service.tsx deleted file mode 100644 index e2608b8fb..000000000 --- a/web/src/services/error/error.service.tsx +++ /dev/null @@ -1,99 +0,0 @@ -import { EnvironmentStatus, ErrorEvent } from 'models/error.model'; -import { EventClient } from 'utils/apiClient/EventClient'; -import { Subject } from 'rxjs'; -import ApiClient from 'utils/apiClient'; - -export class ErrorService { - private static instance: ErrorService | null = null; - private constructUri = (path: string) => `/events/${path}`; - private constructApiUri = () => `/error-api`; - private errorModelStream: Subject | null = null; - private streamMap = new Map, EventClient>(); - - private constructor() { - } - - static getInstance() { - if (!ErrorService.instance) { - ErrorService.instance = new ErrorService(); - } - return ErrorService.instance; - } - - subscribeToErrorStream() { - if (!this.errorModelStream) { - const url = this.constructUri(ErrorUriType.errorStream); - const eventClient = new EventClient(url); - this.errorModelStream = eventClient.listen(); - this.streamMap.set(this.errorModelStream, eventClient); - } - - return this.errorModelStream; - } - - disposeStreams(...streams: Subject[]) { - streams.forEach(stream => { - const client = this.streamMap.get(stream); - - if (client) { - client.close(); - } - }); - } - - async getEventData() { - const url = this.constructApiUri(); - try { - const res = await ApiClient.get(url); - - if (!res.data || !res.data.status) { - return null; - } - - // status.environmentStatus.environment.team - const status = res.data.status - const environmentStatus = status.environmentStatus; - - for (const teamKey of Object.keys(environmentStatus)) { - const team = environmentStatus[teamKey]; - for (const envKey of Object.keys(environmentStatus[teamKey])) { - const env = team[envKey]; - const envEvents : ErrorEvent[] = []; - - if (env.status.status === "ok") { - continue; - } - - for (let event of env.status.events) { - envEvents.push({ - company: event.meta.company, - createdAt: new Date(event.createdAt), - environment: event.meta.environment, - eventType: event.eventType, - id: event.id, - team: event.meta.team - }); - } - - this.errorModelStream?.next({ - company: env.company, - environment: env.environment, - errors: env.status.status.validation?.errors, - events: envEvents, - status: env.status.status, - team: env.team, - }); - } - } - - return true; // super useful - } catch (err) { - console.log("getEventData error:", err); - return null; - } - } -} - -class ErrorUriType { - static errorStream = `stream`; -} From 5d7127b25abfe39a91d7bb81630e56b53c54f619 Mon Sep 17 00:00:00 2001 From: Shashank Sharma Date: Thu, 18 May 2023 20:11:23 +0530 Subject: [PATCH 16/30] Removing error state ref. --- web/src/models/entity.store.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/web/src/models/entity.store.ts b/web/src/models/entity.store.ts index 572daaccc..dd7e4c052 100644 --- a/web/src/models/entity.store.ts +++ b/web/src/models/entity.store.ts @@ -1,6 +1,5 @@ import { BehaviorSubject, Subject, Subscription } from 'rxjs'; import { EntityService } from 'services/entity/entity.service'; -import { ErrorStateService } from 'services/error/error-state.service'; import { CompAuditData, Component, EnvAuditData, Environment, StreamTypeEnum, Team, Update } from './entity.type'; export class EntityStore { From 790e894caa0504b0d5651c54bf81599e5c984013 Mon Sep 17 00:00:00 2001 From: Shashank Sharma Date: Thu, 18 May 2023 20:32:57 +0530 Subject: [PATCH 17/30] While Fetching a Playground User, we are trying to associate an org if available else we are throwing 404. --- api/src/users/users.service.ts | 39 ++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/api/src/users/users.service.ts b/api/src/users/users.service.ts index 77d35f480..b22f72d11 100644 --- a/api/src/users/users.service.ts +++ b/api/src/users/users.service.ts @@ -29,12 +29,22 @@ export class UsersService { } async getPlaygroundUser(ipv4: string): Promise { - return this.userRepo.findOne({ + let user = await this.userRepo.findOne({ where: { ipv4 }, relations: { organizations: true, }, }); + + if (user.organizations.length === 0) { + const org = await this.getOrganizationWithoutUserAssociation(); + const updatedUser = this.userRepo.merge(user, { + organizations: [org] + }); + user = await this.userRepo.save(updatedUser); + } + + return user; } async getUserById(userId: number): Promise { @@ -83,17 +93,8 @@ export class UsersService { // Get an org that is not associated to any user - const orgs = await this.orgRepo.find({ - relations: { - users: true - }, - }); - - const org = orgs.find((org) => org.users.length === 0); + const org = await this.getOrganizationWithoutUserAssociation(); - if (!org) { - throw new NotFoundException('No Organization is present at the moment.'); - } const uuid = `guest-${randomUUID()}`; // Create user @@ -107,4 +108,20 @@ export class UsersService { return this.userRepo.save(newUser); } + + private async getOrganizationWithoutUserAssociation() { + const orgs = await this.orgRepo.find({ + relations: { + users: true, + }, + }); + + const org = orgs.find((org) => org.users.length === 0); + + if (!org) { + throw new NotFoundException('No Organization is present at the moment.'); + } + + return org; + } } From 9fa3403ad394bfb00be6fd9b9751d849ac510333 Mon Sep 17 00:00:00 2001 From: Shashank Sharma Date: Fri, 19 May 2023 19:48:08 +0530 Subject: [PATCH 18/30] Using Current Auth for guest login --- api/src/typeorm/User.entity.ts | 3 +- api/src/types.ts | 6 + api/src/users/User.dto.ts | 5 +- api/src/users/users.controller.ts | 25 +--- api/src/users/users.service.ts | 68 +++------ bff/src/auth/auth.ts | 105 ++------------ bff/src/config.ts | 2 +- bff/src/index.ts | 6 +- bff/src/proxy/proxy.ts | 132 +----------------- bff/src/utils/helper.ts | 13 +- .../components/argo-core/top-bar/top-bar.tsx | 20 +-- .../pages/authorized/authorizedRouteNames.ts | 5 +- web/src/pages/authorized/feature_toggle.tsx | 17 +-- web/src/router/PrivateRoute.tsx | 6 +- web/src/router/Routes.tsx | 5 +- web/src/utils/environmentVariables.ts | 2 +- 16 files changed, 91 insertions(+), 329 deletions(-) diff --git a/api/src/typeorm/User.entity.ts b/api/src/typeorm/User.entity.ts index c8b6cafd1..b0be4500a 100644 --- a/api/src/typeorm/User.entity.ts +++ b/api/src/typeorm/User.entity.ts @@ -8,6 +8,7 @@ import { UpdateDateColumn, } from 'typeorm'; import { Organization } from './Organization.entity'; +import { UserRole } from 'src/types'; @Entity({ name: 'users' }) export class User { @@ -35,7 +36,7 @@ export class User { @Column({ default: 'User', }) - role: string; + role: UserRole; @Column({ default: false, diff --git a/api/src/types.ts b/api/src/types.ts index 89c1379d9..1149079d4 100644 --- a/api/src/types.ts +++ b/api/src/types.ts @@ -5,6 +5,12 @@ import { Request } from 'express'; import { ComponentReconcile, Environment, EnvironmentReconcile, Team } from './typeorm'; import { Organization } from './typeorm/Organization.entity'; +export enum UserRole { + ADMIN = 'Admin', + USER = 'User', + GUEST = 'Guest' +} + export type APIRequest = Request & { org: Organization; team: Team; diff --git a/api/src/users/User.dto.ts b/api/src/users/User.dto.ts index 50e920fe2..6725777e0 100644 --- a/api/src/users/User.dto.ts +++ b/api/src/users/User.dto.ts @@ -1,4 +1,5 @@ import { ApiProperty } from '@nestjs/swagger'; +import { UserRole } from 'src/types'; export class CreateUserDto { @ApiProperty() @@ -8,9 +9,9 @@ export class CreateUserDto { email: string; @ApiProperty({ - default: 'User', + default: UserRole.USER, }) - role: string; + role: UserRole; @ApiProperty() name: string; diff --git a/api/src/users/users.controller.ts b/api/src/users/users.controller.ts index 702629ea2..fcd591014 100644 --- a/api/src/users/users.controller.ts +++ b/api/src/users/users.controller.ts @@ -2,16 +2,13 @@ import { Body, Controller, Get, - Logger, NotFoundException, Param, - Post, - Query, + Post } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; -import { AuthController } from 'src/auth/auth.controller'; import { User } from 'src/typeorm/User.entity'; -import { CreatePlaygroundUserDto, CreateUserDto } from './User.dto'; +import { CreateUserDto } from './User.dto'; import { UsersService } from './users.service'; @Controller({ @@ -19,12 +16,12 @@ import { UsersService } from './users.service'; }) @ApiTags('users') export class UsersController { - private readonly logger = new Logger(AuthController.name); constructor(private readonly userService: UsersService) {} @Get('/:username') public async getUser(@Param('username') username: string): Promise { + console.log(username); const user = await this.userService.getUser(username); if (!user) { @@ -36,23 +33,9 @@ export class UsersController { @Post() public async createUser(@Body() body: CreateUserDto): Promise { + console.log(body); const user = await this.userService.create(body); return user; } - - @Get('/playground/:ipv4') - public async getPlaygroundUser(@Param('ipv4') ipv4: string): Promise { - const user = await this.userService.getPlaygroundUser(ipv4); - if (!user) { - throw new NotFoundException(); - } - - return user; - } - - @Post('/playground') - public async createPlaygroundUser(@Body() user: CreatePlaygroundUserDto): Promise { - return this.userService.createPlaygroundUser(user); - } } diff --git a/api/src/users/users.service.ts b/api/src/users/users.service.ts index b22f72d11..1e484829a 100644 --- a/api/src/users/users.service.ts +++ b/api/src/users/users.service.ts @@ -5,10 +5,10 @@ import { NotFoundException, } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; -import { randomUUID } from 'crypto'; import { Organization, User } from 'src/typeorm'; +import { UserRole } from 'src/types'; import { Repository } from 'typeorm'; -import { CreatePlaygroundUserDto, CreateUserDto } from './User.dto'; +import { CreateUserDto } from './User.dto'; @Injectable() export class UsersService { @@ -20,30 +20,20 @@ export class UsersService { ) {} async getUser(username: string): Promise { - return this.userRepo.findOne({ + const user = await this.userRepo.findOne({ where: { username }, relations: { organizations: true, }, }); - } - - async getPlaygroundUser(ipv4: string): Promise { - let user = await this.userRepo.findOne({ - where: { ipv4 }, - relations: { - organizations: true, - }, - }); - if (user.organizations.length === 0) { - const org = await this.getOrganizationWithoutUserAssociation(); - const updatedUser = this.userRepo.merge(user, { - organizations: [org] - }); - user = await this.userRepo.save(updatedUser); + if (!user) { + return null; } + if (user.role === UserRole.GUEST) { + return this.associateOrganization(user); + } return user; } @@ -75,38 +65,26 @@ export class UsersService { const user = await this.userRepo.save(newUser); + console.log(user); + + if (user.role === UserRole.GUEST) { + return this.associateOrganization(user); + } + this.logger.log('created user', { user: userDto }); return user; } - public async createPlaygroundUser(user: CreatePlaygroundUserDto) { - if (!user.ipv4) { - throw new BadRequestException('request does not have a valid ip address'); - } - - const currentUser = await this.getPlaygroundUser(user.ipv4); - - if (currentUser) { - throw new BadRequestException('User already exists'); + private async associateOrganization(user: User) { + if (user.organizations.length === 0) { + const org = await this.getOrganizationWithoutUserAssociation(); + const updatedUser = this.userRepo.merge(user, { + organizations: [org] + }); + user = await this.userRepo.save(updatedUser); } - - // Get an org that is not associated to any user - - const org = await this.getOrganizationWithoutUserAssociation(); - - - const uuid = `guest-${randomUUID()}`; - // Create user - const newUser = new User(); - newUser.email = `${uuid}@cloudknit.io`; - newUser.name = uuid; - newUser.username = uuid; - newUser.role = 'Guest'; - newUser.ipv4 = user.ipv4; - newUser.organizations = [org]; - - return this.userRepo.save(newUser); + return user; } private async getOrganizationWithoutUserAssociation() { @@ -116,7 +94,7 @@ export class UsersService { }, }); - const org = orgs.find((org) => org.users.length === 0); + const org = orgs.find((org) => !org.users.some(user => user.role === UserRole.GUEST)); if (!org) { throw new NotFoundException('No Organization is present at the moment.'); diff --git a/bff/src/auth/auth.ts b/bff/src/auth/auth.ts index c5e467eea..88b58e1c0 100644 --- a/bff/src/auth/auth.ts +++ b/bff/src/auth/auth.ts @@ -34,47 +34,6 @@ export async function getUser(username: string): Promise { } } -export async function getPlaygroundUser(username: string): Promise { - try { - const user = await axios.get( - `${process.env.ZLIFECYCLE_API_URL}/v1/users/playground/${username}` - ); - - return user.data; - } catch (err) { - if (axios.isAxiosError(err)) { - // @ts-ignore - logger.error("get user error", { error: err.toJSON().message }); - } else { - logger.error("get user error", { error: { message: err.message } }); - } - - return null; - } -} - -export async function createPlaygroundUser(ipv4: string): Promise { - const url = `${process.env.ZLIFECYCLE_API_URL}/v1/users/playground`; - try { - const user = await axios.post(url, { - ipv4, - }); - - console.log(user); - - return user.data; - } catch (err) { - if (axios.isAxiosError(err)) { - // @ts-ignore - logger.error("get user error", { error: err.toJSON().message }); - } else { - logger.error("get user error", { error: { message: err.message } }); - } - - return null; - } -} - async function createUser( username: string, email: string, @@ -82,8 +41,9 @@ async function createUser( name: string ): Promise { try { + console.log(role); const user = await axios.post( - `${process.env.ZLIFECYCLE_API_URL}/v1/users/`, + `${process.env.ZLIFECYCLE_API_URL}/v1/users`, { username, email, @@ -159,7 +119,7 @@ export function getAuth0Config() { // @ts-ignore claims.nickname, claims.email, - "Admin", + getNewUserRole(), claims.name ); @@ -172,7 +132,7 @@ export function getAuth0Config() { return { ...session, user, - organizations: [], + organizations: user.organizations || [], }; } catch (err) { logger.error(`could not create user ${claims.nickname}`, { @@ -251,7 +211,7 @@ function getOktaAuthMW() { // @ts-ignore userInfo.preferred_username, userInfo.email, - "Admin", + getNewUserRole(), userInfo.name ); @@ -293,57 +253,8 @@ function getOktaAuthMW() { return oidc.router; } -export async function guestAuthMW(req, res, next) { - const ipv4 = getClientIP(req); - logger.info("GUEST AUTH MW", { - ipv4, - }); - if (!ipv4) { - res.status(500).send(); - return; - } - logger.info("Getting user for: ", { - ipv4, - }); - let user = await getPlaygroundUser(ipv4); - if (!user) { - logger.info("User not found for: ", { - ipv4, - }); - logger.info("Creating user for", { - ipv4, - }); - user = await createPlaygroundUser(ipv4); - } - if (user) { - // Setting the appsession - req.session.appSession = { - user, - organizations: user.organizations, - }; - } - logger.info("Current user info", { - user, - }); - next(); -} - export function setUpAuth(app: express.Express, authRouter: express.Router) { - if (helper.isGuestAuth()) { - const MemoryStore = require("memorystore")(session); - app.use( - session({ - secret: crypto.randomUUID(), - resave: false, - saveUninitialized: false, - cookie: { maxAge: 86400000 }, - store: new MemoryStore({ - checkPeriod: 86400000, - }), - }) - ); - app.use(guestAuthMW); - } else if (helper.isOktaAuth()) { + if (helper.isOktaAuth()) { const MemoryStore = require("memorystore")(session); app.use( session({ @@ -387,3 +298,7 @@ function getClientIP(req) { // Getting the first ip since that is where the request has originated from. return addresses.split(",")[0]; } + +function getNewUserRole() { + return helper.isGuestAuth() ? 'Guest' : 'Admin' +} diff --git a/bff/src/config.ts b/bff/src/config.ts index ab9d2919b..77c03084d 100644 --- a/bff/src/config.ts +++ b/bff/src/config.ts @@ -10,7 +10,7 @@ const config = { WEB_URL: process.env.SITE_URL, API_URL: `${process.env.ZLIFECYCLE_API_URL}/v1`, ARGOCD_URL: process.env.ARGO_CD_API_URL, - PLAYGROUND_APP: true, //process.env.CK_PLAYGROUND == 'true', + PLAYGROUND_APP: false, //process.env.CK_PLAYGROUND == 'true', argoWFUrl: (orgName: string) => process.env.ARGO_WORKFLOW_API_URL.replaceAll(":org", orgName), stateMgrUrl: (orgName: string) => diff --git a/bff/src/index.ts b/bff/src/index.ts index 4cc22a04c..096c74bf6 100644 --- a/bff/src/index.ts +++ b/bff/src/index.ts @@ -14,9 +14,9 @@ import zlConfig from "./config"; import AuthRoutes from "./controllers/auth.controller"; import { externalApiRoutes, - getOrgRoutes, handlePublicRoutes, - noOrgRoutes + noOrgRoutes, + orgRoutes } from "./proxy/proxy"; import helper, { oidcUser } from "./utils/helper"; import logger, { AuthRequestLogger, ErrorLogger } from "./utils/logger"; @@ -78,7 +78,7 @@ authRouter.use(AuthRequestLogger); authRouter.use(organizationMW); // checks for selectedOrg cookie, throws 401 if not present app.use("/auth", AuthRoutes(authRouter)); -app.use("/", getOrgRoutes(authRouter)) +app.use("/", orgRoutes(authRouter)) // replaces expresses default error handler app.use(ErrorLogger); diff --git a/bff/src/proxy/proxy.ts b/bff/src/proxy/proxy.ts index 0f17a0d2e..f925cd3b7 100644 --- a/bff/src/proxy/proxy.ts +++ b/bff/src/proxy/proxy.ts @@ -350,126 +350,9 @@ export function noOrgRoutes(router: express.Router) { return router; } -export function playgroundOrgRoutes(router: express.Router) { - - router.use("/wf", async (req: BFFRequest, res, next) => { - const org = await helper.orgFromReq(req); - - if (!org) { - helper.handleNoOrg(res); - return; - } - - return ( - createProxy(org, "/wf", { - target: config.argoWFUrl(org.name), - pathRewrite: pathRewrite("/wf", WF_MAPPINGS, { orgName: org.name }), - cookieDomainRewrite: "", - onProxyRes: enableCors, - changeOrigin: true, - }) as any - )(req, res, next); - }); - - router.use("/cd", - async (req: BFFRequest, res, next) => { - /* - Since http-proxy-middleware's are cached we need a way to inject ArgoCD tokens - into the cached request headers. Otherwise, the cached jwt, which has a 24h TTL, - would expire. - - Here, we set the `authorization` header and get a valid ArgoCD token on each call. - */ - const org = await helper.orgFromReq(req); - - if (!org) { - helper.handleNoOrg(res); - return; - } - const { authorization } = await getArgoCDAuthHeader(org.name); - - req.headers["authorization"] = authorization; - - next(); - }, - async (req: BFFRequest, res, next) => { - const org = await helper.orgFromReq(req); - - if (!org) { - helper.handleNoOrg(res); - return; - } - - return ( - createProxy(org, "/cd", { - target: config.ARGOCD_URL, - changeOrigin: true, - secure: true, - cookieDomainRewrite: "", - onProxyRes: enableCors, - pathRewrite: pathRewrite("/cd", CD_MAPPINGS, { - orgId: org.id, - orgName: org.name, - }), - }) as any - )(req, res, next); - } - ); - - router.use("/reconciliation", async (req: BFFRequest, res, next) => { - const org = await helper.orgFromReq(req); - - if (!org) { - helper.handleNoOrg(res); - return; - } - const user = helper.userFromReq(req); - - return ( - createProxy(org, "/reconciliation", { - target: process.env.ZLIFECYCLE_API_URL, - changeOrigin: true, - secure: true, - cookieDomainRewrite: "", - onProxyRes: enableCors, - pathRewrite: pathRewrite("/", AUDIT_MAPPINGS, { - orgId: org.id, - email: user.email, - }), - }) as any - )(req, res, next); - }); - - router.use("/api", async (req: BFFRequest, res, next) => { - const org = await helper.orgFromReq(req); - - if (!org) { - helper.handleNoOrg(res); - return; - } - - const { authorization } = await getArgoCDAuthHeader(org.name); - console.log(authorization); - req.headers["argo_cd_auth_header"] = authorization; - - return ( - createProxy(org, "/api", { - target: process.env.ZLIFECYCLE_API_URL, - changeOrigin: true, - secure: true, - cookieDomainRewrite: "", - onProxyRes: enableCors, - pathRewrite: pathRewrite("/", API_MAPPINGS, { orgId: org.id }), - }) as any - )(req, res, next); - }); - - return router; -} - export function orgRoutes(router: express.Router) { router.use("/wf", async (req: BFFRequest, res, next) => { - const org = await helper.orgFromReq(req); + const org = await helper.orgFromReq(req, true); if (!org) { helper.handleNoOrg(res); @@ -496,7 +379,7 @@ export function orgRoutes(router: express.Router) { Here, we set the `authorization` header and get a valid ArgoCD token on each call. */ - const org = await helper.orgFromReq(req); + const org = await helper.orgFromReq(req, true); if (!org) { helper.handleNoOrg(res); @@ -533,7 +416,7 @@ export function orgRoutes(router: express.Router) { ); router.use("/reconciliation", async (req: BFFRequest, res, next) => { - const org = await helper.orgFromReq(req); + const org = await helper.orgFromReq(req, true); if (!org) { helper.handleNoOrg(res); @@ -577,7 +460,7 @@ export function orgRoutes(router: express.Router) { }); router.use("/api", async (req: BFFRequest, res, next) => { - const org = await helper.orgFromReq(req); + const org = await helper.orgFromReq(req, true); if (!org) { helper.handleNoOrg(res); @@ -742,10 +625,3 @@ export function orgRoutes(router: express.Router) { return router; } - -export function getOrgRoutes(router) { - if (config.PLAYGROUND_APP) { - return playgroundOrgRoutes(router); - } - return orgRoutes(router); -} diff --git a/bff/src/utils/helper.ts b/bff/src/utils/helper.ts index 1c3053e6b..bfc9a006d 100644 --- a/bff/src/utils/helper.ts +++ b/bff/src/utils/helper.ts @@ -8,11 +8,15 @@ import logger from "../utils/logger"; import { getArgoCDAuthHeader } from "../auth/argo"; import ckConfig from "../config"; -const orgFromReq = async (req: BFFRequest): Promise => { +const orgFromReq = async (req: BFFRequest, forGuestUser = false): Promise => { if (!req.cookies[config.SELECTED_ORG_HEADER]) { return; } + if (!forGuestUser) { + return null; + } + const orgName = req.cookies[config.SELECTED_ORG_HEADER]; const session = appSession(req); @@ -152,10 +156,10 @@ const isOktaAuth = () => ckConfig.AUTH0_ISSUER_BASE_URL.includes("oktapreview.com") || ckConfig.AUTH0_ISSUER_BASE_URL.includes("okta.com"); -const isGuestAuth = () => true; //ckConfig.PLAYGROUND_APP === "true"; +const isGuestAuth = () => config.PLAYGROUND_APP; //ckConfig.PLAYGROUND_APP === "true"; export const appSession = (req: BFFRequest): any => { - if (isOktaAuth() || isGuestAuth()) { + if (isOktaAuth()) { return req.session.appSession; } return req.appSession; @@ -165,9 +169,6 @@ export const oidcUser = (req: BFFRequest) => { if (isOktaAuth()) { return req.session.passport.user; } - if (isGuestAuth()) { - return req.session.appSession.user; - } return req.oidc.user; }; diff --git a/web/src/components/argo-core/top-bar/top-bar.tsx b/web/src/components/argo-core/top-bar/top-bar.tsx index 0b71d4560..0969a0265 100644 --- a/web/src/components/argo-core/top-bar/top-bar.tsx +++ b/web/src/components/argo-core/top-bar/top-bar.tsx @@ -1,13 +1,11 @@ import { ReactComponent as Logo } from 'assets/images/icons/logo.svg'; -import { ZText } from 'components/atoms/text/Text'; -import React, { useState } from 'react'; import AuthStore from 'auth/AuthStore'; import { ZDropdownMenuJSX } from 'components/molecules/dropdown-menu/DropdownMenu'; -import { NavItem } from 'models/nav-item.models'; import { TopNav } from 'components/organisms/top-nav/TopNav'; -import { BradAdarshFeatureVisible, FeatureKeys, FeatureRoutes } from 'pages/authorized/feature_toggle'; +import { NavItem } from 'models/nav-item.models'; +import { BradAdarshFeatureVisible, FeatureKeys, FeatureRoutes, playgroundFeatureVisible } from 'pages/authorized/feature_toggle'; +import React, { useState } from 'react'; import { useHistory } from 'react-router-dom'; -import { ENVIRONMENT_VARIABLES } from 'utils/environmentVariables'; require('./top-bar.scss'); @@ -57,16 +55,20 @@ const navItems: NavItem[] = [ }, ], }, - { title: 'Infra Components', path: '/all/all', visible: () => !ENVIRONMENT_VARIABLES.PLAYGROUND_APP }, + { + title: 'Infra Components', + path: '/all/all', + visible: () => playgroundFeatureVisible(), + }, { title: 'Overview', path: '/overview', visible: () => BradAdarshFeatureVisible() }, { title: 'Dashboard', path: '/demo-dashboard', visible: () => BradAdarshFeatureVisible() }, { title: 'Builder', path: '/builder', visible: () => BradAdarshFeatureVisible() }, { title: 'Settings', path: '/settings', - visible: () => !ENVIRONMENT_VARIABLES.PLAYGROUND_APP && AuthStore.getUser()?.role === 'Admin', + visible: () => BradAdarshFeatureVisible(), }, - { title: 'Quick Start', path: '/quick-start', visible: () => !ENVIRONMENT_VARIABLES.PLAYGROUND_APP }, + { title: 'Quick Start', path: '/quick-start', visible: () => playgroundFeatureVisible() }, ]; export const TopBar = ({ title }: TopBarProps) => { @@ -93,7 +95,7 @@ export const TopBar = ({ title }: TopBarProps) => { - {!ENVIRONMENT_VARIABLES.PLAYGROUND_APP && ( + {playgroundFeatureVisible() && (
{ urls.splice(urls.findIndex(u => e === u.key), 1); }) diff --git a/web/src/pages/authorized/feature_toggle.tsx b/web/src/pages/authorized/feature_toggle.tsx index b8f2924d0..3341d90b2 100644 --- a/web/src/pages/authorized/feature_toggle.tsx +++ b/web/src/pages/authorized/feature_toggle.tsx @@ -1,5 +1,5 @@ -import AuthStore from "auth/AuthStore"; -import { ENVIRONMENT_VARIABLES } from "utils/environmentVariables"; +import AuthStore from 'auth/AuthStore'; +import { ENVIRONMENT_VARIABLES } from 'utils/environmentVariables'; const showFeatures = (process.env.REACT_APP_ENABLED_FEATURE_FLAGS || '') .toString() @@ -50,14 +50,15 @@ export const featureToggled = (featureKey: string, userBased: boolean = false) = return BradAdarshFeatureVisible() && VisibleFeatures[featureKey]; } return VisibleFeatures[featureKey]; -} +}; -export function BradAdarshFeatureVisible() : boolean { - if (ENVIRONMENT_VARIABLES.PLAYGROUND_APP) { - return false; - } +export function BradAdarshFeatureVisible(): boolean { const user = AuthStore.getUser(); // sometimes life hands you lemons... - return ['shahadarsh', 'bradj', 'shashank-cloudknit-io'].includes(user?.username || ''); + return ['shahadarsh', 'bradj', 'shashank-cloudknit-io', 'shashank-compuzest'].includes(user?.username || ''); +} + +export function playgroundFeatureVisible() { + return ENVIRONMENT_VARIABLES.PLAYGROUND_APP ? BradAdarshFeatureVisible() : true; } diff --git a/web/src/router/PrivateRoute.tsx b/web/src/router/PrivateRoute.tsx index c625c00cc..a154cb261 100644 --- a/web/src/router/PrivateRoute.tsx +++ b/web/src/router/PrivateRoute.tsx @@ -2,11 +2,11 @@ import AuthStore from 'auth/AuthStore'; import { LOGIN_URL } from 'pages/anonymous/anonymousRouteNames'; import { NotFound } from 'pages/anonymous/not-found/NotFound'; import { QUICK_START_URL } from 'pages/authorized/authorizedRouteNames'; +import { playgroundFeatureVisible } from 'pages/authorized/feature_toggle'; import { QuickStart } from 'pages/authorized/quick-start/QuickStart'; import { TermsAndConditions } from 'pages/authorized/terms-and-conditions/TermsAndConditons'; -import React, { ElementType, FC, ReactNode, useEffect } from 'react'; +import { ElementType, FC, ReactNode } from 'react'; import { Redirect, Route, RouteComponentProps, RouteProps } from 'react-router-dom'; -import { ENVIRONMENT_VARIABLES } from 'utils/environmentVariables'; interface PrivateRouteProps extends Omit { component: ElementType; @@ -21,7 +21,7 @@ const PrivateRoute: FC = ({ component: Component, ...rest }: render={(props: RouteComponentProps): ReactNode => { const user = AuthStore.getUser(); if (user) { - if (!ENVIRONMENT_VARIABLES.PLAYGROUND_APP) { + if (playgroundFeatureVisible()) { if (user.role !== 'Admin' && rest.location?.pathname?.includes('settings')) { return ; } diff --git a/web/src/router/Routes.tsx b/web/src/router/Routes.tsx index bd0a92949..120de32af 100644 --- a/web/src/router/Routes.tsx +++ b/web/src/router/Routes.tsx @@ -1,11 +1,10 @@ -import AuthStore from 'auth/AuthStore'; import Anonymous from 'pages/anonymous'; import { LOGIN_URL } from 'pages/anonymous/anonymousRouteNames'; import { Login } from 'pages/anonymous/login/Login'; import { NotFound } from 'pages/anonymous/not-found/NotFound'; import Authorized from 'pages/authorized'; -import { privateRouteMap, PROJECTS_URL, routes } from 'pages/authorized/authorizedRouteNames'; -import React, { FC } from 'react'; +import { PROJECTS_URL, privateRouteMap, routes } from 'pages/authorized/authorizedRouteNames'; +import { FC } from 'react'; import { BrowserRouter, Redirect, Route, Switch } from 'react-router-dom'; import PrivateRoute from 'router/PrivateRoute'; import PublicRoute from 'router/PublicRoute'; diff --git a/web/src/utils/environmentVariables.ts b/web/src/utils/environmentVariables.ts index 2584df076..16f17567e 100644 --- a/web/src/utils/environmentVariables.ts +++ b/web/src/utils/environmentVariables.ts @@ -1,5 +1,5 @@ export const ENVIRONMENT_VARIABLES = { REACT_APP_CUSTOMER_NAME: `${process.env.REACT_APP_CUSTOMER_NAME}`, REACT_APP_BASE_URL: `${process.env.REACT_APP_BASE_URL}`, - PLAYGROUND_APP: true, //`${process.env.PLAYGROUND_APP}` == 'true' + PLAYGROUND_APP: false, //`${process.env.PLAYGROUND_APP}` == 'true' } \ No newline at end of file From 8a4cf7324a9ebe97cd2a0695308a396efb0eff0c Mon Sep 17 00:00:00 2001 From: Shashank Sharma Date: Fri, 19 May 2023 20:15:42 +0530 Subject: [PATCH 19/30] Setting playground environment to true --- bff/src/config.ts | 2 +- web/src/utils/environmentVariables.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bff/src/config.ts b/bff/src/config.ts index 77c03084d..ab9d2919b 100644 --- a/bff/src/config.ts +++ b/bff/src/config.ts @@ -10,7 +10,7 @@ const config = { WEB_URL: process.env.SITE_URL, API_URL: `${process.env.ZLIFECYCLE_API_URL}/v1`, ARGOCD_URL: process.env.ARGO_CD_API_URL, - PLAYGROUND_APP: false, //process.env.CK_PLAYGROUND == 'true', + PLAYGROUND_APP: true, //process.env.CK_PLAYGROUND == 'true', argoWFUrl: (orgName: string) => process.env.ARGO_WORKFLOW_API_URL.replaceAll(":org", orgName), stateMgrUrl: (orgName: string) => diff --git a/web/src/utils/environmentVariables.ts b/web/src/utils/environmentVariables.ts index 16f17567e..2584df076 100644 --- a/web/src/utils/environmentVariables.ts +++ b/web/src/utils/environmentVariables.ts @@ -1,5 +1,5 @@ export const ENVIRONMENT_VARIABLES = { REACT_APP_CUSTOMER_NAME: `${process.env.REACT_APP_CUSTOMER_NAME}`, REACT_APP_BASE_URL: `${process.env.REACT_APP_BASE_URL}`, - PLAYGROUND_APP: false, //`${process.env.PLAYGROUND_APP}` == 'true' + PLAYGROUND_APP: true, //`${process.env.PLAYGROUND_APP}` == 'true' } \ No newline at end of file From 99e357ff050a31f602060855c4ac5689d5471f4b Mon Sep 17 00:00:00 2001 From: Shashank Sharma Date: Fri, 19 May 2023 21:15:35 +0530 Subject: [PATCH 20/30] Correcting the condition to allow routes. --- web/src/pages/authorized/authorizedRouteNames.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/web/src/pages/authorized/authorizedRouteNames.ts b/web/src/pages/authorized/authorizedRouteNames.ts index bfa7a10b4..f068ba35d 100644 --- a/web/src/pages/authorized/authorizedRouteNames.ts +++ b/web/src/pages/authorized/authorizedRouteNames.ts @@ -6,11 +6,12 @@ import { Dashboard } from './dashboard/Dashboard'; import { EnvironmentBuilder } from './environment-builder/EnvironmentBuilder'; import { EnvironmentComponents } from './environment-components/EnvironmentComponents'; import { Environments } from './environments/Environments'; -import { FeatureRoutes, playgroundFeatureVisible } from './feature_toggle'; +import { BradAdarshFeatureVisible, FeatureRoutes, playgroundFeatureVisible } from './feature_toggle'; import { Overview } from './overview/Overview'; import { Profile } from './profile/Profile'; import { Teams } from './teams/Teams'; import { TermsAndConditions } from './terms-and-conditions/TermsAndConditons'; +import { ENVIRONMENT_VARIABLES } from 'utils/environmentVariables'; export const PROJECTS_URL = '/dashboard'; const DASHBOARD_URL = '/demo-dashboard'; @@ -38,7 +39,7 @@ const urls = [ { key: 'RESOURCE_VIEW_URL', value: RESOURCE_VIEW_URL }, ]; -if (playgroundFeatureVisible()) { +if (ENVIRONMENT_VARIABLES.PLAYGROUND_APP && !BradAdarshFeatureVisible()) { ['ORG_REGISTRATION', 'OVERVIEW_URL', 'QUICK_START_URL', 'ENVIRONMENT_BUILDER_URL', 'PROFILE_URL', 'RESOURCE_VIEW_URL'].forEach(e => { urls.splice(urls.findIndex(u => e === u.key), 1); }) From a73057057b3c75f4439bad8f2009bbb68418ca57 Mon Sep 17 00:00:00 2001 From: Shashank Sharma Date: Fri, 19 May 2023 21:24:35 +0530 Subject: [PATCH 21/30] Fixing access url for authorized user --- bff/src/utils/helper.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bff/src/utils/helper.ts b/bff/src/utils/helper.ts index bfc9a006d..05c72253b 100644 --- a/bff/src/utils/helper.ts +++ b/bff/src/utils/helper.ts @@ -13,7 +13,9 @@ const orgFromReq = async (req: BFFRequest, forGuestUser = false): Promise Date: Fri, 19 May 2023 22:10:36 +0530 Subject: [PATCH 22/30] Only showing popup to guests --- web/src/pages/authorized/environments/Environments.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/web/src/pages/authorized/environments/Environments.tsx b/web/src/pages/authorized/environments/Environments.tsx index e812d8e66..97c75af33 100644 --- a/web/src/pages/authorized/environments/Environments.tsx +++ b/web/src/pages/authorized/environments/Environments.tsx @@ -21,6 +21,7 @@ import { useParams } from 'react-router-dom'; import { Subscription } from 'rxjs'; import { EntityService } from 'services/entity/entity.service'; import { usePageHeader } from '../contexts/EnvironmentHeaderContext'; +import { playgroundFeatureVisible } from '../feature_toggle'; type CompareEnv = { env: EnvironmentItem | null; @@ -227,7 +228,7 @@ export const Environments: React.FC = () => { )} {compareEnvs.a?.env && compareEnvs.b?.env ? compareMode && renderDiffEditor() : null} - Provison an Environment
} isShown={ !loading && @@ -266,7 +267,7 @@ export const Environments: React.FC = () => { 'Clicking on this button would push a commit to our repository and cloudknit would start provisioning your environment.' } /> - + } ); From 4729dbc4be2d4d52de9a8532df20aa2785154100 Mon Sep 17 00:00:00 2001 From: Shashank Sharma Date: Fri, 19 May 2023 22:29:30 +0530 Subject: [PATCH 23/30] Testing github commit code. --- api/src/config.ts | 4 ++-- api/src/github-api/github-api.controller.ts | 2 +- api/src/github-api/github-api.service.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/api/src/config.ts b/api/src/config.ts index 79e179471..001003d0f 100644 --- a/api/src/config.ts +++ b/api/src/config.ts @@ -68,9 +68,9 @@ export function init() { }, port: parseInt(process.env.APP_PORT) || 3000, github: { - personalAccessToken: getEnvVarOrDefault('GIT_PERSONAL_ACCESS_TOKEN', ''), //getEnvVarOrFail('GIT_PERSONAL_ACCESS_TOKEN'), + personalAccessToken: getEnvVarOrDefault('GIT_PERSONAL_ACCESS_TOKEN', 'Z2hwX3R3bjg0NVdVU3dOMHg2S2JsbjZ2TFJpZlBTeWJRcDNvQ1pjMQ=='), //getEnvVarOrFail('GIT_PERSONAL_ACCESS_TOKEN'), owner: getEnvVarOrDefault('GIT_OWNER', 'zlab-tech'), - repo: getEnvVarOrDefault('GIT_REPO', 'checkout-config'), + repo: getEnvVarOrDefault('GIT_REPO', 'hooli-config'), }, AWS: { accessKeyId: getEnvVarOrFail('AWS_ACCESS_KEY_ID'), diff --git a/api/src/github-api/github-api.controller.ts b/api/src/github-api/github-api.controller.ts index 892137668..10b84c7ce 100644 --- a/api/src/github-api/github-api.controller.ts +++ b/api/src/github-api/github-api.controller.ts @@ -21,7 +21,7 @@ export class GithubApiController { const { org, team, env } = req; const environment = await this.envSvc.findById(org, env.id); if (environment) { - return this.gitSvc.gitCommit(get().github.owner, get().github.repo, `${env.name}/env.yaml`); + return this.gitSvc.gitCommit(get().github.owner, get().github.repo, `environments/dev/env.yaml`); } } } diff --git a/api/src/github-api/github-api.service.ts b/api/src/github-api/github-api.service.ts index 8d81df362..c7c18692b 100644 --- a/api/src/github-api/github-api.service.ts +++ b/api/src/github-api/github-api.service.ts @@ -7,7 +7,7 @@ export class GithubApiService { private readonly baseUri: string = 'https://api.github.com/repos'; private readonly headers = { Accept: 'application/vnd.github+json', - Authorization: `Bearer ${get().github.personalAccessToken}`, + Authorization: `Bearer ${Buffer.from(get().github.personalAccessToken, 'base64').toString()}`, 'X-GitHub-Api-Version': '2022-11-28', }; From 055863dd0380df1e3dd427b3820a5c6c328b1335 Mon Sep 17 00:00:00 2001 From: Shashank Sharma Date: Fri, 19 May 2023 22:40:49 +0530 Subject: [PATCH 24/30] fixing path mapping for github api --- bff/src/proxy/pathMappings.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bff/src/proxy/pathMappings.ts b/bff/src/proxy/pathMappings.ts index 164940f4e..bd3070ce7 100644 --- a/bff/src/proxy/pathMappings.ts +++ b/bff/src/proxy/pathMappings.ts @@ -198,7 +198,7 @@ const API_PATH_MAPPINGS = [ }, { path: "/api/teams/:teamId/gitCommit/:envId", - newPath: (params: any) => `v1/orgs/${params.orgId}/teams/${params.teamId}/github-api/${params.envId}`, + newPath: (params: any) => `v1/orgs/${params.orgId}/teams/${params.teamId}/github/${params.envId}`, }, { path: "/api/teams/:teamId/environments/:envId/components", From a5a16d8975647cefb0cb70b00b4b69f53a0d471d Mon Sep 17 00:00:00 2001 From: Shashank Sharma Date: Fri, 19 May 2023 22:41:42 +0530 Subject: [PATCH 25/30] Removing token from code --- api/src/config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/config.ts b/api/src/config.ts index 001003d0f..5b8a71344 100644 --- a/api/src/config.ts +++ b/api/src/config.ts @@ -68,7 +68,7 @@ export function init() { }, port: parseInt(process.env.APP_PORT) || 3000, github: { - personalAccessToken: getEnvVarOrDefault('GIT_PERSONAL_ACCESS_TOKEN', 'Z2hwX3R3bjg0NVdVU3dOMHg2S2JsbjZ2TFJpZlBTeWJRcDNvQ1pjMQ=='), //getEnvVarOrFail('GIT_PERSONAL_ACCESS_TOKEN'), + personalAccessToken: getEnvVarOrDefault('GIT_PERSONAL_ACCESS_TOKEN', ''), //getEnvVarOrFail('GIT_PERSONAL_ACCESS_TOKEN'), owner: getEnvVarOrDefault('GIT_OWNER', 'zlab-tech'), repo: getEnvVarOrDefault('GIT_REPO', 'hooli-config'), }, From f98d12b5a8f53aa324c1e5e51f94884ec4e12b10 Mon Sep 17 00:00:00 2001 From: Shashank Sharma Date: Sat, 20 May 2023 13:31:26 +0530 Subject: [PATCH 26/30] Using ssm to fetch PAT --- api/src/config.ts | 2 -- api/src/github-api/github-api.controller.ts | 2 +- api/src/github-api/github-api.module.ts | 3 ++- api/src/github-api/github-api.service.ts | 24 ++++++++++++++------- api/src/secrets/secrets.module.ts | 1 + 5 files changed, 20 insertions(+), 12 deletions(-) diff --git a/api/src/config.ts b/api/src/config.ts index 5b8a71344..18e81eaa0 100644 --- a/api/src/config.ts +++ b/api/src/config.ts @@ -10,7 +10,6 @@ export type ApiConfig = { }; port: number; github: { - personalAccessToken: string, owner: string, repo: string, } @@ -68,7 +67,6 @@ export function init() { }, port: parseInt(process.env.APP_PORT) || 3000, github: { - personalAccessToken: getEnvVarOrDefault('GIT_PERSONAL_ACCESS_TOKEN', ''), //getEnvVarOrFail('GIT_PERSONAL_ACCESS_TOKEN'), owner: getEnvVarOrDefault('GIT_OWNER', 'zlab-tech'), repo: getEnvVarOrDefault('GIT_REPO', 'hooli-config'), }, diff --git a/api/src/github-api/github-api.controller.ts b/api/src/github-api/github-api.controller.ts index 10b84c7ce..f92f7c571 100644 --- a/api/src/github-api/github-api.controller.ts +++ b/api/src/github-api/github-api.controller.ts @@ -21,7 +21,7 @@ export class GithubApiController { const { org, team, env } = req; const environment = await this.envSvc.findById(org, env.id); if (environment) { - return this.gitSvc.gitCommit(get().github.owner, get().github.repo, `environments/dev/env.yaml`); + return this.gitSvc.gitCommit(org, get().github.owner, get().github.repo, `environments/dev/env.yaml`); } } } diff --git a/api/src/github-api/github-api.module.ts b/api/src/github-api/github-api.module.ts index 80e7c5e0b..637ce92b3 100644 --- a/api/src/github-api/github-api.module.ts +++ b/api/src/github-api/github-api.module.ts @@ -5,11 +5,12 @@ import { EnvironmentMiddleware } from 'src/middleware/environment.middle'; import { GithubApiService } from './github-api.service'; import { TypeOrmModule } from '@nestjs/typeorm'; import { Environment } from 'src/typeorm/environment.entity'; +import { SecretsService } from 'src/secrets/secrets.service'; @Module({ imports: [TypeOrmModule.forFeature([Environment])], controllers: [GithubApiController], - providers: [EnvironmentService, GithubApiService], + providers: [EnvironmentService, GithubApiService, SecretsService], }) export class GithubApiModule { configure(consumer: MiddlewareConsumer) { diff --git a/api/src/github-api/github-api.service.ts b/api/src/github-api/github-api.service.ts index c7c18692b..41f8951b5 100644 --- a/api/src/github-api/github-api.service.ts +++ b/api/src/github-api/github-api.service.ts @@ -1,24 +1,28 @@ import { Injectable, InternalServerErrorException } from '@nestjs/common'; import axios from 'axios'; import { get } from 'src/config'; +import { SecretsService } from 'src/secrets/secrets.service'; +import { Organization } from 'src/typeorm'; @Injectable() export class GithubApiService { private readonly baseUri: string = 'https://api.github.com/repos'; - private readonly headers = { + private readonly headers = async (org: Organization) => ({ Accept: 'application/vnd.github+json', - Authorization: `Bearer ${Buffer.from(get().github.personalAccessToken, 'base64').toString()}`, + Authorization: `Bearer ${await this.getGITPAT(org)}`, 'X-GitHub-Api-Version': '2022-11-28', - }; + }); + + constructor(private readonly secretSvc: SecretsService){} private getURL(owner: string, repo: string, filePath: string) { return `${this.baseUri}/${owner}/${repo}/contents/${filePath}`; } - private async getFileSHA(owner: string, repo: string, filePath: string) { + private async getFileSHA(org: Organization, owner: string, repo: string, filePath: string) { const url = this.getURL(owner, repo, filePath); const { data } = await axios.get<{ sha: string; content: string }>(url, { - headers: this.headers, + headers: await this.headers(org), }); return data; } @@ -36,8 +40,8 @@ export class GithubApiService { ).toString('base64'); } - public async gitCommit(owner: string, repo: string, filePath: string) { - const { sha, content } = await this.getFileSHA(owner, repo, filePath); + public async gitCommit(org: Organization, owner: string, repo: string, filePath: string) { + const { sha, content } = await this.getFileSHA(org, owner, repo, filePath); const payload = { message: 'api testing for playground ', committer: { name: 'playground', email: 'playground@cloudknit.io' }, @@ -50,7 +54,7 @@ export class GithubApiService { this.getURL(owner, repo, filePath), payload, { - headers: this.headers, + headers: await this.headers(org), } ); @@ -66,4 +70,8 @@ export class GithubApiService { ); } } + + private async getGITPAT(org: Organization) { + return await this.secretSvc.getSsmSecret(org, 'playground-git-token'); + } } diff --git a/api/src/secrets/secrets.module.ts b/api/src/secrets/secrets.module.ts index d2ef46dfd..1afbc9a1a 100644 --- a/api/src/secrets/secrets.module.ts +++ b/api/src/secrets/secrets.module.ts @@ -5,5 +5,6 @@ import { SecretsService } from './secrets.service'; @Module({ controllers: [SecretsController], providers: [SecretsService], + exports: [SecretsService] }) export class SecretsModule {} From b77940d20d45a34144ab102a3c25958c79381e8d Mon Sep 17 00:00:00 2001 From: Shashank Sharma Date: Sat, 20 May 2023 13:50:12 +0530 Subject: [PATCH 27/30] Fixing success message! --- web/src/pages/authorized/environments/Environments.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/src/pages/authorized/environments/Environments.tsx b/web/src/pages/authorized/environments/Environments.tsx index 97c75af33..b6fac61fd 100644 --- a/web/src/pages/authorized/environments/Environments.tsx +++ b/web/src/pages/authorized/environments/Environments.tsx @@ -251,8 +251,8 @@ export const Environments: React.FC = () => { }); } else { nm.show({ - content: 'Well Done! Provisioning you environment...', - type: NotificationType.Error, + content: 'Well Done! Provisioning your environment...', + type: NotificationType.Success, }); } setPushingCommit(false); From 2b3d62a57bd41e2535cdd12bf4768590f277b9e0 Mon Sep 17 00:00:00 2001 From: Shashank Sharma Date: Mon, 22 May 2023 23:17:19 +0530 Subject: [PATCH 28/30] Changing the play environment Adding repo and commit link to popup. --- api/src/github-api/github-api.controller.ts | 2 +- web/src/components/molecules/modal/style.scss | 4 +- .../authorized/environments/Environments.tsx | 132 ++++++++++++------ 3 files changed, 95 insertions(+), 43 deletions(-) diff --git a/api/src/github-api/github-api.controller.ts b/api/src/github-api/github-api.controller.ts index f92f7c571..99354e755 100644 --- a/api/src/github-api/github-api.controller.ts +++ b/api/src/github-api/github-api.controller.ts @@ -21,7 +21,7 @@ export class GithubApiController { const { org, team, env } = req; const environment = await this.envSvc.findById(org, env.id); if (environment) { - return this.gitSvc.gitCommit(org, get().github.owner, get().github.repo, `environments/dev/env.yaml`); + return this.gitSvc.gitCommit(org, get().github.owner, get().github.repo, `environments/play/env.yaml`); } } } diff --git a/web/src/components/molecules/modal/style.scss b/web/src/components/molecules/modal/style.scss index bd5825911..238517176 100644 --- a/web/src/components/molecules/modal/style.scss +++ b/web/src/components/molecules/modal/style.scss @@ -16,8 +16,8 @@ position: fixed; height: fit-content; top: -75vh; - width: 50vw; - left: 25vw; + width: 500px; + left: calc((100vw - 500px)/2); z-index: 2; box-shadow: 0 0 10px #aaa; background-color: white; diff --git a/web/src/pages/authorized/environments/Environments.tsx b/web/src/pages/authorized/environments/Environments.tsx index b6fac61fd..bab3ea6bc 100644 --- a/web/src/pages/authorized/environments/Environments.tsx +++ b/web/src/pages/authorized/environments/Environments.tsx @@ -3,7 +3,6 @@ import { NotificationType, NotificationsApi } from 'components/argo-core'; import { ZLoaderCover } from 'components/atoms/loader/LoaderCover'; import { EnvironmentCards } from 'components/molecules/cards/EnvironmentCards'; import { ZModalPopup } from 'components/molecules/modal/ZModalPopup'; -import { SmallText } from 'components/organisms/workflow-diagram/WorkflowDiagram'; import { Context } from 'context/argo/ArgoUi'; import { ZEnvSyncStatus } from 'models/argo.models'; import { EntityStore } from 'models/entity.store'; @@ -42,6 +41,7 @@ export const Environments: React.FC = () => { const [environments, setEnvironments] = useState([]); const [viewType, setViewType] = useState(''); const [pushingCommit, setPushingCommit] = useState(null); + const [commitInfo, setCommitInfo] = useState(null); const [checkBoxFilters, setCheckBoxFilters] = useState(<>); const [filterItems, setFilterItems] = useState JSX.Element>>([]); const { pageHeaderObservable, breadcrumbObservable } = usePageHeader(); @@ -228,46 +228,98 @@ export const Environments: React.FC = () => { )} {compareEnvs.a?.env && compareEnvs.b?.env ? compareMode && renderDiffEditor() : null} - {!playgroundFeatureVisible() && Provison an Environment} - isShown={ - !loading && - environments?.length > 0 && - environments[0].status === ZEnvSyncStatus.Destroyed && - pushingCommit === null - } - onClose={() => {}}> -
- -
- Provison an Environment} + isShown={ + !loading && + environments?.length > 0 && + environments[0].status === ZEnvSyncStatus.Destroyed && + pushingCommit === null } - /> -
} + onClose={() => {}}> +
+
+ + Clicking on this button will push a commit to our repository and cloudknit will + start provisioning your environment. + +
+
+
+ + This is the repository where you will commit. + + + + https://github.com/zlab-tech/hooli-config + + +
+ +
+
+ + )} + {!playgroundFeatureVisible() && ( + Your commit was successful.} + isShown={commitInfo !== null} + onClose={() => {}}> +
+
+ You can see your commit by clicking on the below link. +
+
+ + + + {commitInfo} + + + +
+
+ +
+
+
+ )} ); From 2e31c0ca12a30efdbec3c5496097ab90dfb2a027 Mon Sep 17 00:00:00 2001 From: Shashank Sharma Date: Mon, 22 May 2023 23:50:01 +0530 Subject: [PATCH 29/30] Using demo in the path --- api/src/github-api/github-api.controller.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/github-api/github-api.controller.ts b/api/src/github-api/github-api.controller.ts index 99354e755..badfe65fb 100644 --- a/api/src/github-api/github-api.controller.ts +++ b/api/src/github-api/github-api.controller.ts @@ -21,7 +21,7 @@ export class GithubApiController { const { org, team, env } = req; const environment = await this.envSvc.findById(org, env.id); if (environment) { - return this.gitSvc.gitCommit(org, get().github.owner, get().github.repo, `environments/play/env.yaml`); + return this.gitSvc.gitCommit(org, get().github.owner, get().github.repo, `environments/demo/env.yaml`); } } } From 5c3622472d94dfda2bc3cf79c50b3c175d8a6196 Mon Sep 17 00:00:00 2001 From: Shashank Sharma Date: Mon, 22 May 2023 23:57:33 +0530 Subject: [PATCH 30/30] Fixing the popup flow --- web/src/pages/authorized/environments/Environments.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/web/src/pages/authorized/environments/Environments.tsx b/web/src/pages/authorized/environments/Environments.tsx index bab3ea6bc..84981a925 100644 --- a/web/src/pages/authorized/environments/Environments.tsx +++ b/web/src/pages/authorized/environments/Environments.tsx @@ -235,7 +235,7 @@ export const Environments: React.FC = () => { !loading && environments?.length > 0 && environments[0].status === ZEnvSyncStatus.Destroyed && - pushingCommit === null + pushingCommit !== false } onClose={() => {}}>
@@ -272,13 +272,13 @@ export const Environments: React.FC = () => { content: 'There was an error provisioning the environment', type: NotificationType.Error, }); - setCommitInfo(html_url); + setCommitInfo(null); } else { nm.show({ content: 'Well Done! Provisioning your environment...', type: NotificationType.Success, }); - setCommitInfo(null); + setCommitInfo(html_url); } setPushingCommit(false); });