From 26d4bed3a9724aeb8bfe72daef42f402d059b598 Mon Sep 17 00:00:00 2001 From: rajdip-b Date: Tue, 17 Dec 2024 18:45:33 +0530 Subject: [PATCH 01/19] chore(ci): Update web deployment to push to ACR --- .github/workflows/deploy-web.yml | 76 +++++++++++++++----------------- 1 file changed, 36 insertions(+), 40 deletions(-) diff --git a/.github/workflows/deploy-web.yml b/.github/workflows/deploy-web.yml index 6af7f7f8..a459ca0b 100644 --- a/.github/workflows/deploy-web.yml +++ b/.github/workflows/deploy-web.yml @@ -5,64 +5,60 @@ on: push: branches: - main + - develop paths: ['apps/web/**', '.github/workflows/deploy-web.yml', 'package.json'] jobs: build: runs-on: ubuntu-latest - environment: alpha + environment: ${{ github.ref == 'refs/heads/main' && 'beta' || 'staging' }} name: Build and push Web docker image for release steps: - name: Checkout uses: actions/checkout@v4 - - name: Configure AWS credentials - uses: aws-actions/configure-aws-credentials@v4 + - name: Docker Login + uses: azure/docker-login@v2 with: - aws-access-key-id: ${{ secrets.ACCESS_KEY }} - aws-secret-access-key: ${{ secrets.SECRET_KEY }} - aws-region: ap-south-1 - - - name: Login to Amazon ECR - id: login-ecr - uses: aws-actions/amazon-ecr-login@v2 + login-server: ${{ vars.ACR_REGISTRY_URL }} + username: ${{ secrets.ACR_USERNAME }} + password: ${{ secrets.ACR_PASSWORD }} - name: Build Docker image id: build env: - ECR_REGISTRY: ${{ vars.ECR_REGISTRY }} - ECR_REPOSITORY: ${{ vars.ECR_WEB_REPOSITORY }} + ACR_REGISTRY_URL: ${{ vars.ACR_REGISTRY_URL }} + REPOSITORY_NAME: web run: | - # Build a docker container and push it to ECR - aws ecr get-login-password --region ap-south-1 | docker login --username AWS --password-stdin $ECR_REGISTRY - docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:${GITHUB_SHA::6} -t $ECR_REGISTRY/$ECR_REPOSITORY:latest -f ./apps/web/Dockerfile . - echo "Pushing image to ECR..." - docker push $ECR_REGISTRY/$ECR_REPOSITORY:latest - docker push $ECR_REGISTRY/$ECR_REPOSITORY:${GITHUB_SHA::6} - echo "name=image::$ECR_REGISTRY/$ECR_REPOSITORY:latest" >> $GITHUB_OUTPUT + # Build a docker container and push it to ACR + docker build -t $ACR_REGISTRY_URL/$REPOSITORY_NAME:${GITHUB_SHA::6} -t $ACR_REGISTRY_URL/$REPOSITORY_NAME:latest -f ./apps/web/Dockerfile . + echo "Pushing image to ACR..." + docker push $ACR_REGISTRY_URL/$REPOSITORY_NAME:latest + docker push $ACR_REGISTRY_URL/$REPOSITORY_NAME:${GITHUB_SHA::6} + echo "name=image::$ACR_REGISTRY_URL/$REPOSITORY_NAME:latest" >> $GITHUB_OUTPUT - deploy: - needs: build - runs-on: ubuntu-latest - environment: alpha - name: Deploy Web docker image for release + # deploy: + # needs: build + # runs-on: ubuntu-latest + # environment: alpha + # name: Deploy Web docker image for release - steps: - - name: Checkout - uses: actions/checkout@v4 + # steps: + # - name: Checkout + # uses: actions/checkout@v4 - - name: Configure AWS credentials - uses: aws-actions/configure-aws-credentials@v4 - with: - aws-access-key-id: ${{ secrets.ACCESS_KEY }} - aws-secret-access-key: ${{ secrets.SECRET_KEY }} - aws-region: ap-south-1 + # - name: Configure AWS credentials + # uses: aws-actions/configure-aws-credentials@v4 + # with: + # aws-access-key-id: ${{ secrets.ACCESS_KEY }} + # aws-secret-access-key: ${{ secrets.SECRET_KEY }} + # aws-region: ap-south-1 - - name: Force re-deploy task in service - id: force-redeploy - env: - ECS_CLUSTER: ${{ vars.ECS_CLUSTER }} - ECS_SERVICE: ${{ vars.ECS_WEB_SERVICE }} - run: | - aws ecs update-service --cluster $ECS_CLUSTER --service $ECS_SERVICE --force-new-deployment + # - name: Force re-deploy task in service + # id: force-redeploy + # env: + # ECS_CLUSTER: ${{ vars.ECS_CLUSTER }} + # ECS_SERVICE: ${{ vars.ECS_WEB_SERVICE }} + # run: | + # aws ecs update-service --cluster $ECS_CLUSTER --service $ECS_SERVICE --force-new-deployment From 172c348ff10941da5a60fa0927254d4f87a89a4a Mon Sep 17 00:00:00 2001 From: rajdip-b Date: Tue, 17 Dec 2024 18:47:44 +0530 Subject: [PATCH 02/19] chore(ci): Fixed environment name --- .github/workflows/deploy-web.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy-web.yml b/.github/workflows/deploy-web.yml index a459ca0b..9cb2d184 100644 --- a/.github/workflows/deploy-web.yml +++ b/.github/workflows/deploy-web.yml @@ -11,7 +11,7 @@ on: jobs: build: runs-on: ubuntu-latest - environment: ${{ github.ref == 'refs/heads/main' && 'beta' || 'staging' }} + environment: ${{ github.ref == 'refs/heads/main' && 'beta' || 'stage' }} name: Build and push Web docker image for release steps: From 5f79dd74c25b8e9685fb605714a40bd0fa64a1ea Mon Sep 17 00:00:00 2001 From: rajdip-b Date: Tue, 17 Dec 2024 18:53:40 +0530 Subject: [PATCH 03/19] chore(ci): Push docker images of platform and API to ACR --- .github/workflows/deploy-api.yml | 44 ++++++++++++--------------- .github/workflows/deploy-platform.yml | 44 ++++++++++++--------------- 2 files changed, 40 insertions(+), 48 deletions(-) diff --git a/.github/workflows/deploy-api.yml b/.github/workflows/deploy-api.yml index 0271908f..280e8c8c 100644 --- a/.github/workflows/deploy-api.yml +++ b/.github/workflows/deploy-api.yml @@ -5,42 +5,38 @@ on: push: branches: - main + - develop paths: ['apps/api/**', '.github/workflows/deploy-api.yml', 'package.json'] jobs: build: runs-on: ubuntu-latest - environment: alpha + environment: ${{ github.ref == 'refs/heads/main' && 'beta' || 'stage' }} name: Build and push API docker image for release steps: - name: Checkout uses: actions/checkout@v4 - # - name: Configure AWS credentials - # uses: aws-actions/configure-aws-credentials@v4 - # with: - # aws-access-key-id: ${{ secrets.ACCESS_KEY }} - # aws-secret-access-key: ${{ secrets.SECRET_KEY }} - # aws-region: ap-south-1 - - # - name: Login to Amazon ECR - # id: login-ecr - # uses: aws-actions/amazon-ecr-login@v2 + - name: Docker Login + uses: azure/docker-login@v2 + with: + login-server: ${{ vars.ACR_REGISTRY_URL }} + username: ${{ secrets.ACR_USERNAME }} + password: ${{ secrets.ACR_PASSWORD }} - # - name: Build Docker image - # id: build - # env: - # ECR_REGISTRY: ${{ vars.ECR_REGISTRY }} - # ECR_REPOSITORY: ${{ vars.ECR_API_REPOSITORY }} - # run: | - # # Build a docker container and push it to ECR - # aws ecr get-login-password --region ap-south-1 | docker login --username AWS --password-stdin $ECR_REGISTRY - # docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:${GITHUB_SHA::6} -t $ECR_REGISTRY/$ECR_REPOSITORY:latest -f ./apps/api/Dockerfile . - # echo "Pushing image to ECR..." - # docker push $ECR_REGISTRY/$ECR_REPOSITORY:latest - # docker push $ECR_REGISTRY/$ECR_REPOSITORY:${GITHUB_SHA::6} - # echo "name=image::$ECR_REGISTRY/$ECR_REPOSITORY:latest" >> $GITHUB_OUTPUT + - name: Build Docker image + id: build + env: + ACR_REGISTRY_URL: ${{ vars.ACR_REGISTRY_URL }} + REPOSITORY_NAME: api + run: | + # Build a docker container and push it to ACR + docker build -t $ACR_REGISTRY_URL/$REPOSITORY_NAME:${GITHUB_SHA::6} -t $ACR_REGISTRY_URL/$REPOSITORY_NAME:latest -f ./apps/web/Dockerfile . + echo "Pushing image to ACR..." + docker push $ACR_REGISTRY_URL/$REPOSITORY_NAME:latest + docker push $ACR_REGISTRY_URL/$REPOSITORY_NAME:${GITHUB_SHA::6} + echo "name=image::$ACR_REGISTRY_URL/$REPOSITORY_NAME:latest" >> $GITHUB_OUTPUT # setup-database: # needs: build diff --git a/.github/workflows/deploy-platform.yml b/.github/workflows/deploy-platform.yml index 8ac2eeb5..53d98b55 100644 --- a/.github/workflows/deploy-platform.yml +++ b/.github/workflows/deploy-platform.yml @@ -5,6 +5,7 @@ on: push: branches: - main + - develop paths: [ 'apps/platform/**', @@ -15,37 +16,32 @@ on: jobs: build: runs-on: ubuntu-latest - environment: alpha + environment: ${{ github.ref == 'refs/heads/main' && 'beta' || 'stage' }} name: Build and push Platform docker image for release steps: - name: Checkout uses: actions/checkout@v4 - # - name: Configure AWS credentials - # uses: aws-actions/configure-aws-credentials@v4 - # with: - # aws-access-key-id: ${{ secrets.ACCESS_KEY }} - # aws-secret-access-key: ${{ secrets.SECRET_KEY }} - # aws-region: ap-south-1 - - # - name: Login to Amazon ECR - # id: login-ecr - # uses: aws-actions/amazon-ecr-login@v2 + - name: Docker Login + uses: azure/docker-login@v2 + with: + login-server: ${{ vars.ACR_REGISTRY_URL }} + username: ${{ secrets.ACR_USERNAME }} + password: ${{ secrets.ACR_PASSWORD }} - # - name: Build Docker image - # id: build - # env: - # ECR_REGISTRY: ${{ vars.ECR_REGISTRY }} - # ECR_REPOSITORY: ${{ vars.ECR_PLATFORM_REPOSITORY }} - # run: | - # # Build a docker container and push it to ECR - # aws ecr get-login-password --region ap-south-1 | docker login --username AWS --password-stdin $ECR_REGISTRY - # docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:${GITHUB_SHA::6} -t $ECR_REGISTRY/$ECR_REPOSITORY:latest -f ./apps/platform/Dockerfile . - # echo "Pushing image to ECR..." - # docker push $ECR_REGISTRY/$ECR_REPOSITORY:latest - # docker push $ECR_REGISTRY/$ECR_REPOSITORY:${GITHUB_SHA::6} - # echo "name=image::$ECR_REGISTRY/$ECR_REPOSITORY:latest" >> $GITHUB_OUTPUT + - name: Build Docker image + id: build + env: + ACR_REGISTRY_URL: ${{ vars.ACR_REGISTRY_URL }} + REPOSITORY_NAME: platform + run: | + # Build a docker container and push it to ACR + docker build -t $ACR_REGISTRY_URL/$REPOSITORY_NAME:${GITHUB_SHA::6} -t $ACR_REGISTRY_URL/$REPOSITORY_NAME:latest -f ./apps/platform/Dockerfile . + echo "Pushing image to ACR..." + docker push $ACR_REGISTRY_URL/$REPOSITORY_NAME:latest + docker push $ACR_REGISTRY_URL/$REPOSITORY_NAME:${GITHUB_SHA::6} + echo "name=image::$ACR_REGISTRY_URL/$REPOSITORY_NAME:latest" >> $GITHUB_OUTPUT # deploy: # needs: build From 40ef3e24933315c1ec4e264d900148228f170432 Mon Sep 17 00:00:00 2001 From: rajdip-b Date: Tue, 17 Dec 2024 19:06:40 +0530 Subject: [PATCH 04/19] fix(docker): Update build script --- apps/platform/Dockerfile | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/apps/platform/Dockerfile b/apps/platform/Dockerfile index 65d06a48..4dee7895 100644 --- a/apps/platform/Dockerfile +++ b/apps/platform/Dockerfile @@ -1,6 +1,5 @@ FROM node:20-alpine AS base - FROM base AS builder RUN apk add --no-cache libc6-compat RUN apk update && apk upgrade @@ -27,8 +26,7 @@ RUN pnpm install --no-cache --ignore-scripts COPY --from=builder /app/out/full/ . COPY turbo.json turbo.json - -RUN pnpm turbo build --filter=platform... +RUN pnpm build:platform FROM base AS runner WORKDIR /app From c600db731099665c590cb0c47369530020f5a4c2 Mon Sep 17 00:00:00 2001 From: Sameer Poswal <106386145+poswalsameer@users.noreply.github.com> Date: Tue, 17 Dec 2024 22:04:10 +0530 Subject: [PATCH 05/19] chore(platoform): Swapped all legacy API calls with `@keyshade/api-client` (#584) Co-authored-by: rajdip-b --- apps/platform/.eslintrc.cjs | 11 +- apps/platform/Dockerfile | 4 +- apps/platform/src/app/(main)/page.tsx | 28 +- .../(main)/project/[project]/@secret/page.tsx | 63 +- .../app/(main)/project/[project]/layout.tsx | 24 +- .../src/app/(main)/settings/@profile/page.tsx | 79 +-- .../src/components/shared/navbar/index.tsx | 2 +- .../src/components/shared/sidebar/index.tsx | 2 +- apps/platform/src/components/ui/combobox.tsx | 105 ++- apps/platform/src/lib/api-client.ts | 93 --- apps/platform/src/lib/controller-instance.ts | 84 ++- apps/platform/src/lib/workspace-storage.ts | 1 - erDetails(userData) | 664 ++++++++++++++++++ packages/api-client/src/index.ts | 4 +- packages/schema/src/secret/index.ts | 20 +- packages/schema/tests/secret.spec.ts | 21 +- t updateSelf = useCallback(async () => { | 664 ++++++++++++++++++ ...ccess('User details updated successfully') | 258 +++++++ 18 files changed, 1833 insertions(+), 294 deletions(-) delete mode 100644 apps/platform/src/lib/api-client.ts create mode 100644 erDetails(userData) create mode 100644 t updateSelf = useCallback(async () => { create mode 100644 t.success('User details updated successfully') diff --git a/apps/platform/.eslintrc.cjs b/apps/platform/.eslintrc.cjs index 28a8b27a..e67976a0 100644 --- a/apps/platform/.eslintrc.cjs +++ b/apps/platform/.eslintrc.cjs @@ -7,24 +7,17 @@ module.exports = { }, rules: { 'import/no-extraneous-dependencies': 0, - '@typescript-eslint/interface-name-prefix': 'off', '@typescript-eslint/explicit-function-return-type': 'off', '@typescript-eslint/explicit-module-boundary-types': 'off', - '@typescript-eslint/no-explicit-any': 'off', '@typescript-eslint/no-unused-vars': ['warn'], - '@typescript-eslint/no-unsafe-call': 'off', - '@typescript-eslint/no-unsafe-return': 'off', '@typescript-eslint/space-before-function-paren': 'off', '@typescript-eslint/strict-boolean-expressions': 'off', - '@typescript-eslint/prefer-nullish-coalescing': 'off', - 'space-before-function-paren': 'off', - '@typescript-eslint/member-delimiter-style': 'off', '@typescript-eslint/no-confusing-void-expression': 'off', '@typescript-eslint/no-floating-promises': 'off', '@typescript-eslint/no-misused-promises': 'off', '@typescript-eslint/no-unsafe-assignment': 'off', + '@typescript-eslint/no-unsafe-call': 'off', '@typescript-eslint/no-unsafe-member-access': 'off', - '@typescript-eslint/no-unsafe-argument': 'off', - '@typescript-eslint/no-unnecessary-condition': 'off' + '@typescript-eslint/no-unsafe-return': 'off' } } diff --git a/apps/platform/Dockerfile b/apps/platform/Dockerfile index 4dee7895..69f9c239 100644 --- a/apps/platform/Dockerfile +++ b/apps/platform/Dockerfile @@ -36,13 +36,11 @@ RUN addgroup --system --gid 1001 nodejs RUN adduser --system --uid 1001 nextjs USER nextjs -COPY --from=installer /app/apps/platform/next.config.mjs . COPY --from=installer /app/apps/platform/package.json . # Automatically leverage output traces to reduce image size # https://nextjs.org/docs/advanced-features/output-file-tracing -COPY --from=installer --chown=nextjs:nodejs /app/apps/platform/.next/standalone ./ -COPY --from=installer --chown=nextjs:nodejs /app/apps/platform/.next/static ./apps/platform/.next/static +COPY --from=installer --chown=nextjs:nodejs /app/apps/platform/.next ./apps/platform/.next COPY --from=installer --chown=nextjs:nodejs /app/apps/platform/public ./apps/platform/public diff --git a/apps/platform/src/app/(main)/page.tsx b/apps/platform/src/app/(main)/page.tsx index fe8fa2aa..9a9cb2ac 100644 --- a/apps/platform/src/app/(main)/page.tsx +++ b/apps/platform/src/app/(main)/page.tsx @@ -6,7 +6,6 @@ import type { ProjectWithCount, Workspace } from '@keyshade/schema' -import { ProjectController } from '@keyshade/api-client' import { AddSVG } from '@public/svg/shared' import { FolderSVG } from '@public/svg/dashboard' import ProjectCard from '@/components/dashboard/projectCard' @@ -38,6 +37,7 @@ import { DialogHeader, DialogTrigger } from '@/components/ui/dialog' +import ControllerInstance from '@/lib/controller-instance' export default function Index(): JSX.Element { const [isSheetOpen, setIsSheetOpen] = useState(false) @@ -76,16 +76,13 @@ export default function Index(): JSX.Element { // If a workspace is selected, we want to fetch all the projects // under that workspace and display it in the dashboard. useEffect(() => { - const projectController = new ProjectController( - process.env.NEXT_PUBLIC_BACKEND_URL - ) - async function getAllProjects() { if (currentWorkspace) { - const { success, error, data } = await projectController.getAllProjects( - { workspaceSlug: currentWorkspace.slug }, - {} - ) + const { success, error, data } = + await ControllerInstance.getInstance().projectController.getAllProjects( + { workspaceSlug: currentWorkspace.slug }, + {} + ) if (success && data) { setProjects(data.items) @@ -105,16 +102,13 @@ export default function Index(): JSX.Element { // Function to create a new project const createNewProject = useCallback(async () => { if (currentWorkspace) { - const projectController = new ProjectController( - process.env.NEXT_PUBLIC_BACKEND_URL - ) - newProjectData.workspaceSlug = currentWorkspace.slug - const { data, error, success } = await projectController.createProject( - newProjectData, - {} - ) + const { data, error, success } = + await ControllerInstance.getInstance().projectController.createProject( + newProjectData, + {} + ) if (success && data) { setProjects([ diff --git a/apps/platform/src/app/(main)/project/[project]/@secret/page.tsx b/apps/platform/src/app/(main)/project/[project]/@secret/page.tsx index 5061bf8e..e1bd8e63 100644 --- a/apps/platform/src/app/(main)/project/[project]/@secret/page.tsx +++ b/apps/platform/src/app/(main)/project/[project]/@secret/page.tsx @@ -5,6 +5,7 @@ import { usePathname } from 'next/navigation' import dayjs, { extend } from 'dayjs' import relativeTime from 'dayjs/plugin/relativeTime' import { NoteIconSVG } from '@public/svg/secret' +import type { GetAllSecretsOfProjectResponse } from '@keyshade/schema' import { Accordion, AccordionContent, @@ -19,8 +20,6 @@ import { TableHeader, TableRow } from '@/components/ui/table' -import { Secrets } from '@/lib/api-functions/secrets' -import type { Secret } from '@/types' import { ScrollArea } from '@/components/ui/scroll-area' import { Tooltip, @@ -29,35 +28,45 @@ import { TooltipTrigger } from '@/components/ui/tooltip' import { Skeleton } from '@/components/ui/skeleton' +import ControllerInstance from '@/lib/controller-instance' extend(relativeTime) function SecretPage(): React.JSX.Element { - const [allSecrets, setAllSecrets] = useState() + const [allSecrets, setAllSecrets] = + useState() const [isLoading, setIsLoading] = useState(true) const pathname = usePathname() useEffect(() => { setIsLoading(true) - Secrets.getAllSecretbyProjectId(pathname.split('/')[2]) - .then((data) => { - setAllSecrets(data) - }) - .catch((error) => { + + async function getAllSecretsByProjectSlug() { + const { success, error, data } = + await ControllerInstance.getInstance().secretController.getAllSecretsOfProject( + { projectSlug: pathname.split('/')[2] }, + {} + ) + + if (success && data) { + setAllSecrets(data.items) + } else { // eslint-disable-next-line no-console -- we need to log the error console.error(error) - }) - .finally(() => { - setIsLoading(false) - }) + } + } + + getAllSecretsByProjectSlug() + + setIsLoading(false) }, [pathname]) if (isLoading) { return (
- - - + + +
) } @@ -69,37 +78,35 @@ function SecretPage(): React.JSX.Element { collapsible type="single" > - {allSecrets?.map((secret) => { + {allSecrets?.map(({ secret, values }) => { return ( - {dayjs(secret.secret.updatedAt).toNow(true)} ago by{' '} - - {secret.secret.lastUpdatedBy.name} - + {dayjs(secret.updatedAt).toNow(true)} ago by{' '} + {secret.lastUpdatedById} } >
{/* */} - {secret.secret.name} + {secret.name}
- {secret.secret.note ? ( + {secret.note ? ( -

{secret.secret.note}

+

{secret.note}

@@ -115,10 +122,10 @@ function SecretPage(): React.JSX.Element { - {secret.values.map((value) => { + {values.map((value) => { return ( - {value.environment.name} + {value.environment.slug} {value.value} @@ -136,7 +143,7 @@ function SecretPage(): React.JSX.Element { ) } -function SerectLoader(): React.JSX.Element { +function SecretLoader(): React.JSX.Element { return (
diff --git a/apps/platform/src/app/(main)/project/[project]/layout.tsx b/apps/platform/src/app/(main)/project/[project]/layout.tsx index 8a747ac4..9edfb0bb 100644 --- a/apps/platform/src/app/(main)/project/[project]/layout.tsx +++ b/apps/platform/src/app/(main)/project/[project]/layout.tsx @@ -2,6 +2,7 @@ import { useEffect, useState } from 'react' import { useSearchParams } from 'next/navigation' import { AddSVG } from '@public/svg/shared' +import type { Project } from '@keyshade/schema' import { Button } from '@/components/ui/button' import { Dialog, @@ -13,8 +14,7 @@ import { } from '@/components/ui/dialog' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' -import type { Project } from '@/types' -import { Projects } from '@/lib/api-functions/projects' +import ControllerInstance from '@/lib/controller-instance' interface DetailedProjectPageProps { params: { project: string } @@ -38,14 +38,22 @@ function DetailedProjectPage({ const tab = searchParams.get('tab') ?? 'rollup-details' useEffect(() => { - Projects.getProjectbyID(params.project) - .then((project) => { - setCurrentProject(project) - }) - .catch((error) => { + async function getProjectBySlug() { + const { success, error, data } = + await ControllerInstance.getInstance().projectController.getProject( + { projectSlug: params.project }, + {} + ) + + if (success && data) { + setCurrentProject(data) + } else { // eslint-disable-next-line no-console -- we need to log the error console.error(error) - }) + } + } + + getProjectBySlug() }, [params.project]) return ( diff --git a/apps/platform/src/app/(main)/settings/@profile/page.tsx b/apps/platform/src/app/(main)/settings/@profile/page.tsx index 21de60b3..ac8060c0 100644 --- a/apps/platform/src/app/(main)/settings/@profile/page.tsx +++ b/apps/platform/src/app/(main)/settings/@profile/page.tsx @@ -1,54 +1,52 @@ 'use client' -import React, { useEffect, useState } from 'react' +import React, { useCallback, useEffect, useState } from 'react' import { toast } from 'sonner' -import type { User } from '@keyshade/schema' import InputLoading from './loading' import { Input } from '@/components/ui/input' import { Separator } from '@/components/ui/separator' +import ControllerInstance from '@/lib/controller-instance' import { Button } from '@/components/ui/button' -import { apiClient } from '@/lib/api-client' - -type UserData = Omit< - User, - 'id' | 'isActive' | 'isOnboardingFinished' | 'isAdmin' | 'authProvider' -> -async function getUserDetails(): Promise { - try { - return await apiClient.get('/user') - } catch (error) { - // eslint-disable-next-line no-console -- we need to log the error - console.error(error) - } -} - -async function updateUserDetails(userData: UserData): Promise { - try { - await apiClient.put('/user', userData) - } catch (error) { - // eslint-disable-next-line no-console -- we need to log the error - console.error(error) - } -} function ProfilePage(): React.JSX.Element { const [isLoading, setIsLoading] = useState(true) - const [userData, setUserData] = useState({ + const [userData, setUserData] = useState({ email: '', name: '', profilePictureUrl: '' }) const [isModified, setIsModified] = useState(false) + const updateSelf = useCallback(async () => { + try { + await ControllerInstance.getInstance().userController.updateSelf( + { + name: userData.name, + email: userData.email + }, + {} + ) + toast.success('Profile updated successfully') + } catch (error) { + // eslint-disable-next-line no-console -- we need to log the error + console.error(error) + } + setIsModified(false) + }, [userData]) + useEffect(() => { - getUserDetails() - .then((data) => { - if (data) { + ControllerInstance.getInstance() + .userController.getSelf() + .then(({ data, success, error }) => { + if (success && data) { setUserData({ email: data.email, - name: data.name ?? '', - profilePictureUrl: data.profilePictureUrl + name: data.name, + profilePictureUrl: data.profilePictureUrl || '' }) setIsLoading(false) + } else { + // eslint-disable-next-line no-console -- we need to log the error + console.error(error) } }) .catch((error) => { @@ -67,7 +65,7 @@ function ProfilePage(): React.JSX.Element { Upload a picture to change your avatar across Keyshade.
-
{' '} +
{/* //! This is will be replaced by an image tag */}
{/* Name */} @@ -87,7 +85,7 @@ function ProfilePage(): React.JSX.Element { setUserData((prev) => ({ ...prev, name: e.target.value })) }} placeholder="name" - value={userData.name ?? ''} + value={userData.name || ''} /> )}
@@ -114,20 +112,7 @@ function ProfilePage(): React.JSX.Element { )}
-
diff --git a/apps/platform/src/components/shared/navbar/index.tsx b/apps/platform/src/components/shared/navbar/index.tsx index 693aa8bd..224e0ed7 100644 --- a/apps/platform/src/components/shared/navbar/index.tsx +++ b/apps/platform/src/components/shared/navbar/index.tsx @@ -34,7 +34,7 @@ async function fetchNameImage(): Promise { ) const data: User = (await response.json()) as User return { - name: data.name?.split(' ')[0] ?? data.email.split('@')[0], + name: data.name.split(' ')[0] ?? data.email.split('@')[0], image: data.profilePictureUrl } } catch (error) { diff --git a/apps/platform/src/components/shared/sidebar/index.tsx b/apps/platform/src/components/shared/sidebar/index.tsx index ee7cd9b4..7da768b3 100644 --- a/apps/platform/src/components/shared/sidebar/index.tsx +++ b/apps/platform/src/components/shared/sidebar/index.tsx @@ -6,8 +6,8 @@ import { SettingsSVG, TeamSVG } from '@public/svg/shared' -import { Combobox } from '@/components/ui/combobox' import SidebarTab from './sidebarTab' +import { Combobox } from '@/components/ui/combobox' function Sidebar(): JSX.Element { const sidebarTabData = [ diff --git a/apps/platform/src/components/ui/combobox.tsx b/apps/platform/src/components/ui/combobox.tsx index b3107dc1..b252056c 100644 --- a/apps/platform/src/components/ui/combobox.tsx +++ b/apps/platform/src/components/ui/combobox.tsx @@ -5,27 +5,6 @@ import { useEffect, useState } from 'react' import { useRouter } from 'next/navigation' import { toast } from 'sonner' import { AddSVG } from '@public/svg/shared' -import { cn } from '@/lib/utils' -import { - Command, - CommandEmpty, - CommandInput, - CommandItem, - CommandList -} from '@/components/ui/command' -import { - Popover, - PopoverContent, - PopoverTrigger -} from '@/components/ui/popover' -import { apiClient } from '@/lib/api-client' -// import type { Workspace } from '@/types' -import { zWorkspace } from '@/types' -import { - getCurrentWorkspace, - setCurrentWorkspace, - setWorkspace -} from '@/lib/workspace-storage' import type { Workspace } from '@keyshade/schema' import { Input } from './input' import { Label } from './label' @@ -38,39 +17,43 @@ import { DialogTrigger } from './dialog' import { Button } from './button' -import { WorkspaceSchema } from '@keyshade/schema/schemas' - -interface WorkspaceResponse { - items: Workspace[] - metadata: { - page: number - perPage: number - pageCount: number - totalCount: number - links: { - self: string - first: string - previous: string | null - next: string | null - last: string - } - } -} +import { + getCurrentWorkspace, + setCurrentWorkspace, + setWorkspace +} from '@/lib/workspace-storage' +import { cn } from '@/lib/utils' +import { + Popover, + PopoverContent, + PopoverTrigger +} from '@/components/ui/popover' +import { + Command, + CommandEmpty, + CommandInput, + CommandItem, + CommandList +} from '@/components/ui/command' +import ControllerInstance from '@/lib/controller-instance' async function getAllWorkspace(): Promise { try { - const workspaceData: WorkspaceResponse = - await apiClient.get('/workspace') + const { data, success, error } = + await ControllerInstance.getInstance().workspaceController.getWorkspacesOfUser( + {}, + {} + ) - // TODO: We are getting error here from the success flag, need to see this again - // const { success, data } = WorkspaceSchema.array().safeParse(workspaceData.items) - // if (!success) { - // throw new Error('Invalid data') - // } + if (error) { + // eslint-disable-next-line no-console -- we need to log the error + console.error(error) + return undefined + } - return workspaceData.items - // return data - // return workspaceData; + if (success && data) { + return data.items + } } catch (error) { // eslint-disable-next-line no-console -- we need to log the error console.error(error) @@ -92,11 +75,25 @@ export function Combobox(): React.JSX.Element { } setIsNameEmpty(false) try { - const response = await apiClient.post('/workspace', { - name - }) - setCurrentWorkspace(response) - setOpen(false) + const { data, error, success } = + await ControllerInstance.getInstance().workspaceController.createWorkspace( + { + name + }, + {} + ) + + if (error) { + // eslint-disable-next-line no-console -- we need to log the error + console.error(error) + return + } + + if (success && data) { + toast.success('Workspace created successfully') + setCurrentWorkspace(data) + setOpen(false) + } } catch (error) { // eslint-disable-next-line no-console -- we need to log the error console.error(error) diff --git a/apps/platform/src/lib/api-client.ts b/apps/platform/src/lib/api-client.ts deleted file mode 100644 index 410f1864..00000000 --- a/apps/platform/src/lib/api-client.ts +++ /dev/null @@ -1,93 +0,0 @@ -interface ErrorWithResponse extends Error { - status: number - response: Record -} - -class APIClient { - private baseUrl: string - constructor(baseUrl: string) { - this.baseUrl = baseUrl - } - - async request(url: string, options: RequestInit): Promise { - const response = await fetch(`${this.baseUrl}${url}`, options) - if (!response.ok) { - const error = new Error(response.statusText) as ErrorWithResponse - error.status = response.status - error.response = (await response.json()) as Record // Add type annotation here - throw error - } - return response.json() as Promise - } - - /** - * Sends a GET request to the specified URL and returns a Promise that resolves to the response data. - * @param url - The URL to send the GET request to. - * @returns A Promise that resolves to the response data. - */ - get(url: string): Promise { - return this.request(url, { - method: 'GET', - headers: { - 'Content-Type': 'application/json' - }, - credentials: 'include' - }) - } - - /** - * Sends a POST request to the specified URL with the provided data. - * - * @param url - The URL to send the request to. - * @param data - The data to send in the request body. - * @returns A Promise that resolves to the response data. - */ - post(url: string, data: Record): Promise { - return this.request(url, { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify(data), - credentials: 'include' - }) - } - - /** - * Sends a PUT request to the specified URL with the provided data. - * - * @param url - The URL to send the request to. - * @param data - The data to be sent in the request body. - * @returns A Promise that resolves to the response data. - */ - put(url: string, data: Record): Promise { - return this.request(url, { - method: 'PUT', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify(data), - credentials: 'include' - }) - } - - /** - * Sends a DELETE request to the specified URL and returns a Promise that resolves to the response data. - * - * @param url - The URL to send the DELETE request to. - * @returns A Promise that resolves to the response data. - */ - delete(url: string): Promise { - return this.request(url, { - method: 'DELETE', - headers: { - 'Content-Type': 'application/json' - }, - credentials: 'include' - }) - } -} - -export const apiClient = new APIClient( - `${process.env.NEXT_PUBLIC_BACKEND_URL}/api` -) diff --git a/apps/platform/src/lib/controller-instance.ts b/apps/platform/src/lib/controller-instance.ts index fe432b7b..a766038f 100644 --- a/apps/platform/src/lib/controller-instance.ts +++ b/apps/platform/src/lib/controller-instance.ts @@ -1,30 +1,90 @@ -import { AuthController } from '@keyshade/api-client' +import { + AuthController, + EnvironmentController, + ProjectController, + SecretController, + UserController, + VariableController, + WorkspaceController, + WorkspaceMembershipController, + WorkspaceRoleController +} from '@keyshade/api-client' export default class ControllerInstance { private static instance: ControllerInstance | null - private _authController: AuthController | null = null + private _authController: AuthController + private _userController: UserController + private _workspaceController: WorkspaceController + private _workspaceMembershipController: WorkspaceMembershipController + private _workspaceRoleController: WorkspaceRoleController + private _projectController: ProjectController + private _environmentController: EnvironmentController + private _secretController: SecretController + private _variableController: VariableController get authController(): AuthController { - if (!this._authController) { - throw new Error('ControllerInstance not initialized') - } return this._authController } - static initialize(baseUrl: string): void { - if (!ControllerInstance.instance) { - const instance = new ControllerInstance() + get workspaceController(): WorkspaceController { + return this._workspaceController + } + + get workspaceMembershipController(): WorkspaceMembershipController { + return this._workspaceMembershipController + } - instance._authController = new AuthController(baseUrl) + get workspaceRoleController(): WorkspaceRoleController { + return this._workspaceRoleController + } - ControllerInstance.instance = instance - } + get projectController(): ProjectController { + return this._projectController + } + + get environmentController(): EnvironmentController { + return this._environmentController + } + + get secretController(): SecretController { + return this._secretController + } + + get variableController(): VariableController { + return this._variableController + } + + get userController(): UserController { + return this._userController } static getInstance(): ControllerInstance { if (!ControllerInstance.instance) { - throw new Error('ControllerInstance not initialized') + ControllerInstance.instance = new ControllerInstance() + ControllerInstance.instance._authController = new AuthController( + process.env.NEXT_PUBLIC_BACKEND_URL + ) + ControllerInstance.instance._userController = new UserController( + process.env.NEXT_PUBLIC_BACKEND_URL + ) + ControllerInstance.instance._workspaceController = + new WorkspaceController(process.env.NEXT_PUBLIC_BACKEND_URL) + ControllerInstance.instance._workspaceMembershipController = + new WorkspaceMembershipController(process.env.NEXT_PUBLIC_BACKEND_URL) + ControllerInstance.instance._workspaceRoleController = + new WorkspaceRoleController(process.env.NEXT_PUBLIC_BACKEND_URL) + ControllerInstance.instance._projectController = new ProjectController( + process.env.NEXT_PUBLIC_BACKEND_URL + ) + ControllerInstance.instance._environmentController = + new EnvironmentController(process.env.NEXT_PUBLIC_BACKEND_URL) + ControllerInstance.instance._secretController = new SecretController( + process.env.NEXT_PUBLIC_BACKEND_URL + ) + ControllerInstance.instance._variableController = new VariableController( + process.env.NEXT_PUBLIC_BACKEND_URL + ) } return ControllerInstance.instance } diff --git a/apps/platform/src/lib/workspace-storage.ts b/apps/platform/src/lib/workspace-storage.ts index 9d09dafb..7da1685e 100644 --- a/apps/platform/src/lib/workspace-storage.ts +++ b/apps/platform/src/lib/workspace-storage.ts @@ -1,4 +1,3 @@ -// import type { Workspace } from '@/types' import type { Workspace } from '@keyshade/schema' export function setWorkspace(workspaceData: Workspace[]): void { diff --git a/erDetails(userData) b/erDetails(userData) new file mode 100644 index 00000000..95c798b9 --- /dev/null +++ b/erDetails(userData) @@ -0,0 +1,664 @@ +diff --git a/apps/platform/.eslintrc.cjs b/apps/platform/.eslintrc.cjs +index 28a8b27..fa9233a 100644 +--- a/apps/platform/.eslintrc.cjs ++++ b/apps/platform/.eslintrc.cjs +@@ -7,24 +7,13 @@ module.exports = { + }, + rules: { + 'import/no-extraneous-dependencies': 0, +- '@typescript-eslint/interface-name-prefix': 'off', + '@typescript-eslint/explicit-function-return-type': 'off', + '@typescript-eslint/explicit-module-boundary-types': 'off', +- '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/no-unused-vars': ['warn'], +- '@typescript-eslint/no-unsafe-call': 'off', +- '@typescript-eslint/no-unsafe-return': 'off', + '@typescript-eslint/space-before-function-paren': 'off', + '@typescript-eslint/strict-boolean-expressions': 'off', +- '@typescript-eslint/prefer-nullish-coalescing': 'off', +- 'space-before-function-paren': 'off', +- '@typescript-eslint/member-delimiter-style': 'off', + '@typescript-eslint/no-confusing-void-expression': 'off', + '@typescript-eslint/no-floating-promises': 'off', +- '@typescript-eslint/no-misused-promises': 'off', +- '@typescript-eslint/no-unsafe-assignment': 'off', +- '@typescript-eslint/no-unsafe-member-access': 'off', +- '@typescript-eslint/no-unsafe-argument': 'off', +- '@typescript-eslint/no-unnecessary-condition': 'off' ++ '@typescript-eslint/no-misused-promises': 'off' + } + } +diff --git a/apps/platform/src/app/(main)/page.tsx b/apps/platform/src/app/(main)/page.tsx +index fe8fa2a..9a9cb2a 100644 +--- a/apps/platform/src/app/(main)/page.tsx ++++ b/apps/platform/src/app/(main)/page.tsx +@@ -6,7 +6,6 @@ import type { + ProjectWithCount, + Workspace + } from '@keyshade/schema' +-import { ProjectController } from '@keyshade/api-client' + import { AddSVG } from '@public/svg/shared' + import { FolderSVG } from '@public/svg/dashboard' + import ProjectCard from '@/components/dashboard/projectCard' +@@ -38,6 +37,7 @@ import { + DialogHeader, + DialogTrigger + } from '@/components/ui/dialog' ++import ControllerInstance from '@/lib/controller-instance' +  + export default function Index(): JSX.Element { + const [isSheetOpen, setIsSheetOpen] = useState(false) +@@ -76,16 +76,13 @@ export default function Index(): JSX.Element { + // If a workspace is selected, we want to fetch all the projects + // under that workspace and display it in the dashboard. + useEffect(() => { +- const projectController = new ProjectController( +- process.env.NEXT_PUBLIC_BACKEND_URL +- ) +- + async function getAllProjects() { + if (currentWorkspace) { +- const { success, error, data } = await projectController.getAllProjects( +- { workspaceSlug: currentWorkspace.slug }, +- {} +- ) ++ const { success, error, data } = ++ await ControllerInstance.getInstance().projectController.getAllProjects( ++ { workspaceSlug: currentWorkspace.slug }, ++ {} ++ ) +  + if (success && data) { + setProjects(data.items) +@@ -105,16 +102,13 @@ export default function Index(): JSX.Element { + // Function to create a new project + const createNewProject = useCallback(async () => { + if (currentWorkspace) { +- const projectController = new ProjectController( +- process.env.NEXT_PUBLIC_BACKEND_URL +- ) +- + newProjectData.workspaceSlug = currentWorkspace.slug +  +- const { data, error, success } = await projectController.createProject( +- newProjectData, +- {} +- ) ++ const { data, error, success } = ++ await ControllerInstance.getInstance().projectController.createProject( ++ newProjectData, ++ {} ++ ) +  + if (success && data) { + setProjects([ +diff --git a/apps/platform/src/app/(main)/project/[project]/@secret/page.tsx b/apps/platform/src/app/(main)/project/[project]/@secret/page.tsx +index 18134ab..e1bd8e6 100644 +--- a/apps/platform/src/app/(main)/project/[project]/@secret/page.tsx ++++ b/apps/platform/src/app/(main)/project/[project]/@secret/page.tsx +@@ -5,6 +5,7 @@ import { usePathname } from 'next/navigation' + import dayjs, { extend } from 'dayjs' + import relativeTime from 'dayjs/plugin/relativeTime' + import { NoteIconSVG } from '@public/svg/secret' ++import type { GetAllSecretsOfProjectResponse } from '@keyshade/schema' + import { + Accordion, + AccordionContent, +@@ -19,8 +20,6 @@ import { + TableHeader, + TableRow + } from '@/components/ui/table' +-import type { Secret } from '@keyshade/schema' +-import { SecretController } from '@keyshade/api-client' + import { ScrollArea } from '@/components/ui/scroll-area' + import { + Tooltip, +@@ -29,31 +28,28 @@ import { + TooltipTrigger + } from '@/components/ui/tooltip' + import { Skeleton } from '@/components/ui/skeleton' ++import ControllerInstance from '@/lib/controller-instance' +  + extend(relativeTime) +  + function SecretPage(): React.JSX.Element { +- const [allSecrets, setAllSecrets] = useState() ++ const [allSecrets, setAllSecrets] = ++ useState() + const [isLoading, setIsLoading] = useState(true) + const pathname = usePathname() +  + useEffect(() => { + setIsLoading(true) +  +- const secretController = new SecretController( +- process.env.NEXT_PUBLIC_BACKEND_URL +- ) +- + async function getAllSecretsByProjectSlug() { + const { success, error, data } = +- await secretController.getAllSecretsOfProject( ++ await ControllerInstance.getInstance().secretController.getAllSecretsOfProject( + { projectSlug: pathname.split('/')[2] }, + {} + ) +  + if (success && data) { +- //@ts-ignore +- setAllSecrets(data) ++ setAllSecrets(data.items) + } else { + // eslint-disable-next-line no-console -- we need to log the error + console.error(error) +@@ -68,9 +64,9 @@ function SecretPage(): React.JSX.Element { + if (isLoading) { + return ( +
 +-  +-  +-  ++  ++  ++  +
 + ) + } +@@ -82,7 +78,7 @@ function SecretPage(): React.JSX.Element { + collapsible + type="single" + > +- {allSecrets?.map((secret) => { ++ {allSecrets?.map(({ secret, values }) => { + return ( +  +  +  +- {secret.versions.map((value) => { ++ {values.map((value) => { + return ( +  + {value.environment.slug} +@@ -147,7 +143,7 @@ function SecretPage(): React.JSX.Element { + ) + } +  +-function SerectLoader(): React.JSX.Element { ++function SecretLoader(): React.JSX.Element { + return ( +
 +
 +diff --git a/apps/platform/src/app/(main)/project/[project]/layout.tsx b/apps/platform/src/app/(main)/project/[project]/layout.tsx +index 1eb511b..9edfb0b 100644 +--- a/apps/platform/src/app/(main)/project/[project]/layout.tsx ++++ b/apps/platform/src/app/(main)/project/[project]/layout.tsx +@@ -2,6 +2,7 @@ + import { useEffect, useState } from 'react' + import { useSearchParams } from 'next/navigation' + import { AddSVG } from '@public/svg/shared' ++import type { Project } from '@keyshade/schema' + import { Button } from '@/components/ui/button' + import { + Dialog, +@@ -13,8 +14,7 @@ import { + } from '@/components/ui/dialog' + import { Input } from '@/components/ui/input' + import { Label } from '@/components/ui/label' +-import { ProjectController } from '@keyshade/api-client' +-import type { Project } from '@keyshade/schema' ++import ControllerInstance from '@/lib/controller-instance' +  + interface DetailedProjectPageProps { + params: { project: string } +@@ -38,29 +38,22 @@ function DetailedProjectPage({ + const tab = searchParams.get('tab') ?? 'rollup-details' +  + useEffect(() => { ++ async function getProjectBySlug() { ++ const { success, error, data } = ++ await ControllerInstance.getInstance().projectController.getProject( ++ { projectSlug: params.project }, ++ {} ++ ) +  +- const projectController = new ProjectController( +- process.env.NEXT_PUBLIC_BACKEND_URL +- ) +- +- async function getProjectBySlug(){ +- const {success, error, data} = await projectController.getProject( +- {projectSlug: params.project}, +- {} +- ) +- +- if( success && data ){ +- //@ts-ignore ++ if (success && data) { + setCurrentProject(data) +- } +- else{ ++ } else { + // eslint-disable-next-line no-console -- we need to log the error + console.error(error) + } + } +  + getProjectBySlug() +- + }, [params.project]) +  + return ( +diff --git a/apps/platform/src/app/(main)/settings/@profile/page.tsx b/apps/platform/src/app/(main)/settings/@profile/page.tsx +index 21de60b..4559ec6 100644 +--- a/apps/platform/src/app/(main)/settings/@profile/page.tsx ++++ b/apps/platform/src/app/(main)/settings/@profile/page.tsx +@@ -1,54 +1,52 @@ + 'use client' +-import React, { useEffect, useState } from 'react' ++import React, { useCallback, useEffect, useState } from 'react' + import { toast } from 'sonner' +-import type { User } from '@keyshade/schema' + import InputLoading from './loading' + import { Input } from '@/components/ui/input' + import { Separator } from '@/components/ui/separator' ++import ControllerInstance from '@/lib/controller-instance' + import { Button } from '@/components/ui/button' +-import { apiClient } from '@/lib/api-client' +- +-type UserData = Omit< +- User, +- 'id' | 'isActive' | 'isOnboardingFinished' | 'isAdmin' | 'authProvider' +-> +-async function getUserDetails(): Promise { +- try { +- return await apiClient.get('/user') +- } catch (error) { +- // eslint-disable-next-line no-console -- we need to log the error +- console.error(error) +- } +-} +- +-async function updateUserDetails(userData: UserData): Promise { +- try { +- await apiClient.put('/user', userData) +- } catch (error) { +- // eslint-disable-next-line no-console -- we need to log the error +- console.error(error) +- } +-} +  + function ProfilePage(): React.JSX.Element { + const [isLoading, setIsLoading] = useState(true) +- const [userData, setUserData] = useState({ ++ const [userData, setUserData] = useState({ + email: '', + name: '', + profilePictureUrl: '' + }) + const [isModified, setIsModified] = useState(false) +  ++ const updateSelf = useCallback(async () => { ++ try { ++ await ControllerInstance.getInstance().userController.updateSelf( ++ { ++ name: userData.name, ++ email: userData.email ++ }, ++ {} ++ ) ++ toast.success('Profile updated successfully') ++ } catch (error) { ++ // eslint-disable-next-line no-console -- we need to log the error ++ console.error(error) ++ } ++ setIsModified(false) ++ }, [userData]) ++ + useEffect(() => { +- getUserDetails() +- .then((data) => { +- if (data) { ++ ControllerInstance.getInstance() ++ .userController.getSelf() ++ .then(({ data, success, error }) => { ++ if (success && data) { + setUserData({ + email: data.email, +- name: data.name ?? '', +- profilePictureUrl: data.profilePictureUrl ++ name: data.name, ++ profilePictureUrl: data.profilePictureUrl || '' + }) + setIsLoading(false) ++ } else { ++ // eslint-disable-next-line no-console -- we need to log the error ++ console.error(error) + } + }) + .catch((error) => { +@@ -67,7 +65,7 @@ function ProfilePage(): React.JSX.Element { + Upload a picture to change your avatar across Keyshade. +  +
 +-
{' '} ++
 + {/* //! This is will be replaced by an image tag */} +
 + {/* Name */} +@@ -114,20 +112,7 @@ function ProfilePage(): React.JSX.Element { + )} +
 +
 +- { +- updateUserDetails(userData) +- .then(() => { +- toast.success('User details updated successfully') +- }) +- .catch(() => { +- toast.error('Failed to update user details') +- }) +- setIsModified(false) +- }} +- variant="secondary" +- > ++  +
 +diff --git a/apps/platform/src/components/shared/sidebar/index.tsx b/apps/platform/src/components/shared/sidebar/index.tsx +index ee7cd9b..7da768b 100644 +--- a/apps/platform/src/components/shared/sidebar/index.tsx ++++ b/apps/platform/src/components/shared/sidebar/index.tsx +@@ -6,8 +6,8 @@ import { + SettingsSVG, + TeamSVG + } from '@public/svg/shared' +-import { Combobox } from '@/components/ui/combobox' + import SidebarTab from './sidebarTab' ++import { Combobox } from '@/components/ui/combobox' +  + function Sidebar(): JSX.Element { + const sidebarTabData = [ +diff --git a/apps/platform/src/components/ui/combobox.tsx b/apps/platform/src/components/ui/combobox.tsx +index b3107dc..8414098 100644 +--- a/apps/platform/src/components/ui/combobox.tsx ++++ b/apps/platform/src/components/ui/combobox.tsx +@@ -5,27 +5,6 @@ import { useEffect, useState } from 'react' + import { useRouter } from 'next/navigation' + import { toast } from 'sonner' + import { AddSVG } from '@public/svg/shared' +-import { cn } from '@/lib/utils' +-import { +- Command, +- CommandEmpty, +- CommandInput, +- CommandItem, +- CommandList +-} from '@/components/ui/command' +-import { +- Popover, +- PopoverContent, +- PopoverTrigger +-} from '@/components/ui/popover' +-import { apiClient } from '@/lib/api-client' +-// import type { Workspace } from '@/types' +-import { zWorkspace } from '@/types' +-import { +- getCurrentWorkspace, +- setCurrentWorkspace, +- setWorkspace +-} from '@/lib/workspace-storage' + import type { Workspace } from '@keyshade/schema' + import { Input } from './input' + import { Label } from './label' +@@ -38,39 +17,43 @@ import { + DialogTrigger + } from './dialog' + import { Button } from './button' +-import { WorkspaceSchema } from '@keyshade/schema/schemas' +- +-interface WorkspaceResponse { +- items: Workspace[] +- metadata: { +- page: number +- perPage: number +- pageCount: number +- totalCount: number +- links: { +- self: string +- first: string +- previous: string | null +- next: string | null +- last: string +- } +- } +-} ++import { ++ getCurrentWorkspace, ++ setCurrentWorkspace, ++ setWorkspace ++} from '@/lib/workspace-storage' ++import { cn } from '@/lib/utils' ++import { ++ Popover, ++ PopoverContent, ++ PopoverTrigger ++} from '@/components/ui/popover' ++import { ++ Command, ++ CommandEmpty, ++ CommandInput, ++ CommandItem, ++ CommandList ++} from '@/components/ui/command' ++import ControllerInstance from '@/lib/controller-instance' +  + async function getAllWorkspace(): Promise { + try { +- const workspaceData: WorkspaceResponse = +- await apiClient.get('/workspace') ++ const { data, success, error } = ++ await ControllerInstance.getInstance().workspaceController.getWorkspacesOfUser( ++ {}, ++ {} ++ ) +  +- // TODO: We are getting error here from the success flag, need to see this again +- // const { success, data } = WorkspaceSchema.array().safeParse(workspaceData.items) +- // if (!success) { +- // throw new Error('Invalid data') +- // } ++ if (error) { ++ // eslint-disable-next-line no-console -- we need to log the error ++ console.error(error) ++ return undefined ++ } +  +- return workspaceData.items +- // return data +- // return workspaceData; ++ if (success && data) { ++ return data.items ++ } + } catch (error) { + // eslint-disable-next-line no-console -- we need to log the error + console.error(error) +@@ -92,9 +75,13 @@ export function Combobox(): React.JSX.Element { + } + setIsNameEmpty(false) + try { +- const response = await apiClient.post('/workspace', { +- name +- }) ++ const response = ++ await ControllerInstance.getInstance().workspaceController.createWorkspace( ++ { ++ name ++ }, ++ {} ++ ) + setCurrentWorkspace(response) + setOpen(false) + } catch (error) { +diff --git a/apps/platform/src/lib/controller-instance.ts b/apps/platform/src/lib/controller-instance.ts +index fe432b7..a766038 100644 +--- a/apps/platform/src/lib/controller-instance.ts ++++ b/apps/platform/src/lib/controller-instance.ts +@@ -1,30 +1,90 @@ +-import { AuthController } from '@keyshade/api-client' ++import { ++ AuthController, ++ EnvironmentController, ++ ProjectController, ++ SecretController, ++ UserController, ++ VariableController, ++ WorkspaceController, ++ WorkspaceMembershipController, ++ WorkspaceRoleController ++} from '@keyshade/api-client' +  + export default class ControllerInstance { + private static instance: ControllerInstance | null +  +- private _authController: AuthController | null = null ++ private _authController: AuthController ++ private _userController: UserController ++ private _workspaceController: WorkspaceController ++ private _workspaceMembershipController: WorkspaceMembershipController ++ private _workspaceRoleController: WorkspaceRoleController ++ private _projectController: ProjectController ++ private _environmentController: EnvironmentController ++ private _secretController: SecretController ++ private _variableController: VariableController +  + get authController(): AuthController { +- if (!this._authController) { +- throw new Error('ControllerInstance not initialized') +- } + return this._authController + } +  +- static initialize(baseUrl: string): void { +- if (!ControllerInstance.instance) { +- const instance = new ControllerInstance() ++ get workspaceController(): WorkspaceController { ++ return this._workspaceController ++ } ++ ++ get workspaceMembershipController(): WorkspaceMembershipController { ++ return this._workspaceMembershipController ++ } +  +- instance._authController = new AuthController(baseUrl) ++ get workspaceRoleController(): WorkspaceRoleController { ++ return this._workspaceRoleController ++ } +  +- ControllerInstance.instance = instance +- } ++ get projectController(): ProjectController { ++ return this._projectController ++ } ++ ++ get environmentController(): EnvironmentController { ++ return this._environmentController ++ } ++ ++ get secretController(): SecretController { ++ return this._secretController ++ } ++ ++ get variableController(): VariableController { ++ return this._variableController ++ } ++ ++ get userController(): UserController { ++ return this._userController + } +  + static getInstance(): ControllerInstance { + if (!ControllerInstance.instance) { +- throw new Error('ControllerInstance not initialized') ++ ControllerInstance.instance = new ControllerInstance() ++ ControllerInstance.instance._authController = new AuthController( ++ process.env.NEXT_PUBLIC_BACKEND_URL ++ ) ++ ControllerInstance.instance._userController = new UserController( ++ process.env.NEXT_PUBLIC_BACKEND_URL ++ ) ++ ControllerInstance.instance._workspaceController = ++ new WorkspaceController(process.env.NEXT_PUBLIC_BACKEND_URL) ++ ControllerInstance.instance._workspaceMembershipController = ++ new WorkspaceMembershipController(process.env.NEXT_PUBLIC_BACKEND_URL) ++ ControllerInstance.instance._workspaceRoleController = ++ new WorkspaceRoleController(process.env.NEXT_PUBLIC_BACKEND_URL) ++ ControllerInstance.instance._projectController = new ProjectController( ++ process.env.NEXT_PUBLIC_BACKEND_URL ++ ) ++ ControllerInstance.instance._environmentController = ++ new EnvironmentController(process.env.NEXT_PUBLIC_BACKEND_URL) ++ ControllerInstance.instance._secretController = new SecretController( ++ process.env.NEXT_PUBLIC_BACKEND_URL ++ ) ++ ControllerInstance.instance._variableController = new VariableController( ++ process.env.NEXT_PUBLIC_BACKEND_URL ++ ) + } + return ControllerInstance.instance + } +diff --git a/packages/api-client/src/index.ts b/packages/api-client/src/index.ts +index 79ab5f6..edd344f 100644 +--- a/packages/api-client/src/index.ts ++++ b/packages/api-client/src/index.ts +@@ -8,6 +8,7 @@ import WorkspaceController from '@api-client/controllers/workspace' + import WorkspaceRoleController from '@api-client/controllers/workspace-role' + import WorkspaceMembershipController from '@api-client/controllers/workspace-membership' + import AuthController from '@api-client/controllers/auth' ++import UserController from '@api-client/controllers/user' + export { + EnvironmentController, + SecretController, +@@ -18,5 +19,6 @@ export { + WorkspaceController, + WorkspaceRoleController, + WorkspaceMembershipController, +- AuthController ++ AuthController, ++ UserController + } +diff --git a/packages/schema/src/secret/index.ts b/packages/schema/src/secret/index.ts +index ff5b499..4277679 100644 +--- a/packages/schema/src/secret/index.ts ++++ b/packages/schema/src/secret/index.ts +@@ -100,15 +100,17 @@ export const GetAllSecretsOfProjectResponseSchema = PageResponseSchema( + name: z.string() + }) + }), +- values: z.object({ +- environment: z.object({ +- id: z.string(), +- name: z.string(), +- slug: z.string() +- }), +- value: z.string(), +- version: z.number() +- }) ++ values: z.array( ++ z.object({ ++ environment: z.object({ ++ id: z.string(), ++ name: z.string(), ++ slug: z.string() ++ }), ++ value: z.string(), ++ version: z.number() ++ }) ++ ) + }) + ) +  diff --git a/packages/api-client/src/index.ts b/packages/api-client/src/index.ts index 79ab5f6d..edd344fb 100644 --- a/packages/api-client/src/index.ts +++ b/packages/api-client/src/index.ts @@ -8,6 +8,7 @@ import WorkspaceController from '@api-client/controllers/workspace' import WorkspaceRoleController from '@api-client/controllers/workspace-role' import WorkspaceMembershipController from '@api-client/controllers/workspace-membership' import AuthController from '@api-client/controllers/auth' +import UserController from '@api-client/controllers/user' export { EnvironmentController, SecretController, @@ -18,5 +19,6 @@ export { WorkspaceController, WorkspaceRoleController, WorkspaceMembershipController, - AuthController + AuthController, + UserController } diff --git a/packages/schema/src/secret/index.ts b/packages/schema/src/secret/index.ts index ff5b499e..4277679f 100644 --- a/packages/schema/src/secret/index.ts +++ b/packages/schema/src/secret/index.ts @@ -100,15 +100,17 @@ export const GetAllSecretsOfProjectResponseSchema = PageResponseSchema( name: z.string() }) }), - values: z.object({ - environment: z.object({ - id: z.string(), - name: z.string(), - slug: z.string() - }), - value: z.string(), - version: z.number() - }) + values: z.array( + z.object({ + environment: z.object({ + id: z.string(), + name: z.string(), + slug: z.string() + }), + value: z.string(), + version: z.number() + }) + ) }) ) diff --git a/packages/schema/tests/secret.spec.ts b/packages/schema/tests/secret.spec.ts index 823d471d..6d30c099 100644 --- a/packages/schema/tests/secret.spec.ts +++ b/packages/schema/tests/secret.spec.ts @@ -16,7 +16,6 @@ import { GetRevisionsOfSecretResponseSchema } from '@/secret' import { rotateAfterEnum } from '@/enums' -import { env, versions } from 'process' describe('Secret Schema Tests', () => { describe('SecretSchema Tests', () => { @@ -350,15 +349,17 @@ describe('Secret Schema Tests', () => { name: 'John Doe' } }, - values: { - environment: { - id: 'env123', - name: 'Development', - slug: 'development' - }, - value: 'secret-value', - version: 1 - } + values: [ + { + environment: { + id: 'env123', + name: 'Development', + slug: 'development' + }, + value: 'secret-value', + version: 1 + } + ] } ], metadata: { diff --git a/t updateSelf = useCallback(async () => { b/t updateSelf = useCallback(async () => { new file mode 100644 index 00000000..95c798b9 --- /dev/null +++ b/t updateSelf = useCallback(async () => { @@ -0,0 +1,664 @@ +diff --git a/apps/platform/.eslintrc.cjs b/apps/platform/.eslintrc.cjs +index 28a8b27..fa9233a 100644 +--- a/apps/platform/.eslintrc.cjs ++++ b/apps/platform/.eslintrc.cjs +@@ -7,24 +7,13 @@ module.exports = { + }, + rules: { + 'import/no-extraneous-dependencies': 0, +- '@typescript-eslint/interface-name-prefix': 'off', + '@typescript-eslint/explicit-function-return-type': 'off', + '@typescript-eslint/explicit-module-boundary-types': 'off', +- '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/no-unused-vars': ['warn'], +- '@typescript-eslint/no-unsafe-call': 'off', +- '@typescript-eslint/no-unsafe-return': 'off', + '@typescript-eslint/space-before-function-paren': 'off', + '@typescript-eslint/strict-boolean-expressions': 'off', +- '@typescript-eslint/prefer-nullish-coalescing': 'off', +- 'space-before-function-paren': 'off', +- '@typescript-eslint/member-delimiter-style': 'off', + '@typescript-eslint/no-confusing-void-expression': 'off', + '@typescript-eslint/no-floating-promises': 'off', +- '@typescript-eslint/no-misused-promises': 'off', +- '@typescript-eslint/no-unsafe-assignment': 'off', +- '@typescript-eslint/no-unsafe-member-access': 'off', +- '@typescript-eslint/no-unsafe-argument': 'off', +- '@typescript-eslint/no-unnecessary-condition': 'off' ++ '@typescript-eslint/no-misused-promises': 'off' + } + } +diff --git a/apps/platform/src/app/(main)/page.tsx b/apps/platform/src/app/(main)/page.tsx +index fe8fa2a..9a9cb2a 100644 +--- a/apps/platform/src/app/(main)/page.tsx ++++ b/apps/platform/src/app/(main)/page.tsx +@@ -6,7 +6,6 @@ import type { + ProjectWithCount, + Workspace + } from '@keyshade/schema' +-import { ProjectController } from '@keyshade/api-client' + import { AddSVG } from '@public/svg/shared' + import { FolderSVG } from '@public/svg/dashboard' + import ProjectCard from '@/components/dashboard/projectCard' +@@ -38,6 +37,7 @@ import { + DialogHeader, + DialogTrigger + } from '@/components/ui/dialog' ++import ControllerInstance from '@/lib/controller-instance' +  + export default function Index(): JSX.Element { + const [isSheetOpen, setIsSheetOpen] = useState(false) +@@ -76,16 +76,13 @@ export default function Index(): JSX.Element { + // If a workspace is selected, we want to fetch all the projects + // under that workspace and display it in the dashboard. + useEffect(() => { +- const projectController = new ProjectController( +- process.env.NEXT_PUBLIC_BACKEND_URL +- ) +- + async function getAllProjects() { + if (currentWorkspace) { +- const { success, error, data } = await projectController.getAllProjects( +- { workspaceSlug: currentWorkspace.slug }, +- {} +- ) ++ const { success, error, data } = ++ await ControllerInstance.getInstance().projectController.getAllProjects( ++ { workspaceSlug: currentWorkspace.slug }, ++ {} ++ ) +  + if (success && data) { + setProjects(data.items) +@@ -105,16 +102,13 @@ export default function Index(): JSX.Element { + // Function to create a new project + const createNewProject = useCallback(async () => { + if (currentWorkspace) { +- const projectController = new ProjectController( +- process.env.NEXT_PUBLIC_BACKEND_URL +- ) +- + newProjectData.workspaceSlug = currentWorkspace.slug +  +- const { data, error, success } = await projectController.createProject( +- newProjectData, +- {} +- ) ++ const { data, error, success } = ++ await ControllerInstance.getInstance().projectController.createProject( ++ newProjectData, ++ {} ++ ) +  + if (success && data) { + setProjects([ +diff --git a/apps/platform/src/app/(main)/project/[project]/@secret/page.tsx b/apps/platform/src/app/(main)/project/[project]/@secret/page.tsx +index 18134ab..e1bd8e6 100644 +--- a/apps/platform/src/app/(main)/project/[project]/@secret/page.tsx ++++ b/apps/platform/src/app/(main)/project/[project]/@secret/page.tsx +@@ -5,6 +5,7 @@ import { usePathname } from 'next/navigation' + import dayjs, { extend } from 'dayjs' + import relativeTime from 'dayjs/plugin/relativeTime' + import { NoteIconSVG } from '@public/svg/secret' ++import type { GetAllSecretsOfProjectResponse } from '@keyshade/schema' + import { + Accordion, + AccordionContent, +@@ -19,8 +20,6 @@ import { + TableHeader, + TableRow + } from '@/components/ui/table' +-import type { Secret } from '@keyshade/schema' +-import { SecretController } from '@keyshade/api-client' + import { ScrollArea } from '@/components/ui/scroll-area' + import { + Tooltip, +@@ -29,31 +28,28 @@ import { + TooltipTrigger + } from '@/components/ui/tooltip' + import { Skeleton } from '@/components/ui/skeleton' ++import ControllerInstance from '@/lib/controller-instance' +  + extend(relativeTime) +  + function SecretPage(): React.JSX.Element { +- const [allSecrets, setAllSecrets] = useState() ++ const [allSecrets, setAllSecrets] = ++ useState() + const [isLoading, setIsLoading] = useState(true) + const pathname = usePathname() +  + useEffect(() => { + setIsLoading(true) +  +- const secretController = new SecretController( +- process.env.NEXT_PUBLIC_BACKEND_URL +- ) +- + async function getAllSecretsByProjectSlug() { + const { success, error, data } = +- await secretController.getAllSecretsOfProject( ++ await ControllerInstance.getInstance().secretController.getAllSecretsOfProject( + { projectSlug: pathname.split('/')[2] }, + {} + ) +  + if (success && data) { +- //@ts-ignore +- setAllSecrets(data) ++ setAllSecrets(data.items) + } else { + // eslint-disable-next-line no-console -- we need to log the error + console.error(error) +@@ -68,9 +64,9 @@ function SecretPage(): React.JSX.Element { + if (isLoading) { + return ( +
 +-  +-  +-  ++  ++  ++  +
 + ) + } +@@ -82,7 +78,7 @@ function SecretPage(): React.JSX.Element { + collapsible + type="single" + > +- {allSecrets?.map((secret) => { ++ {allSecrets?.map(({ secret, values }) => { + return ( +  +  +  +- {secret.versions.map((value) => { ++ {values.map((value) => { + return ( +  + {value.environment.slug} +@@ -147,7 +143,7 @@ function SecretPage(): React.JSX.Element { + ) + } +  +-function SerectLoader(): React.JSX.Element { ++function SecretLoader(): React.JSX.Element { + return ( +
 +
 +diff --git a/apps/platform/src/app/(main)/project/[project]/layout.tsx b/apps/platform/src/app/(main)/project/[project]/layout.tsx +index 1eb511b..9edfb0b 100644 +--- a/apps/platform/src/app/(main)/project/[project]/layout.tsx ++++ b/apps/platform/src/app/(main)/project/[project]/layout.tsx +@@ -2,6 +2,7 @@ + import { useEffect, useState } from 'react' + import { useSearchParams } from 'next/navigation' + import { AddSVG } from '@public/svg/shared' ++import type { Project } from '@keyshade/schema' + import { Button } from '@/components/ui/button' + import { + Dialog, +@@ -13,8 +14,7 @@ import { + } from '@/components/ui/dialog' + import { Input } from '@/components/ui/input' + import { Label } from '@/components/ui/label' +-import { ProjectController } from '@keyshade/api-client' +-import type { Project } from '@keyshade/schema' ++import ControllerInstance from '@/lib/controller-instance' +  + interface DetailedProjectPageProps { + params: { project: string } +@@ -38,29 +38,22 @@ function DetailedProjectPage({ + const tab = searchParams.get('tab') ?? 'rollup-details' +  + useEffect(() => { ++ async function getProjectBySlug() { ++ const { success, error, data } = ++ await ControllerInstance.getInstance().projectController.getProject( ++ { projectSlug: params.project }, ++ {} ++ ) +  +- const projectController = new ProjectController( +- process.env.NEXT_PUBLIC_BACKEND_URL +- ) +- +- async function getProjectBySlug(){ +- const {success, error, data} = await projectController.getProject( +- {projectSlug: params.project}, +- {} +- ) +- +- if( success && data ){ +- //@ts-ignore ++ if (success && data) { + setCurrentProject(data) +- } +- else{ ++ } else { + // eslint-disable-next-line no-console -- we need to log the error + console.error(error) + } + } +  + getProjectBySlug() +- + }, [params.project]) +  + return ( +diff --git a/apps/platform/src/app/(main)/settings/@profile/page.tsx b/apps/platform/src/app/(main)/settings/@profile/page.tsx +index 21de60b..4559ec6 100644 +--- a/apps/platform/src/app/(main)/settings/@profile/page.tsx ++++ b/apps/platform/src/app/(main)/settings/@profile/page.tsx +@@ -1,54 +1,52 @@ + 'use client' +-import React, { useEffect, useState } from 'react' ++import React, { useCallback, useEffect, useState } from 'react' + import { toast } from 'sonner' +-import type { User } from '@keyshade/schema' + import InputLoading from './loading' + import { Input } from '@/components/ui/input' + import { Separator } from '@/components/ui/separator' ++import ControllerInstance from '@/lib/controller-instance' + import { Button } from '@/components/ui/button' +-import { apiClient } from '@/lib/api-client' +- +-type UserData = Omit< +- User, +- 'id' | 'isActive' | 'isOnboardingFinished' | 'isAdmin' | 'authProvider' +-> +-async function getUserDetails(): Promise { +- try { +- return await apiClient.get('/user') +- } catch (error) { +- // eslint-disable-next-line no-console -- we need to log the error +- console.error(error) +- } +-} +- +-async function updateUserDetails(userData: UserData): Promise { +- try { +- await apiClient.put('/user', userData) +- } catch (error) { +- // eslint-disable-next-line no-console -- we need to log the error +- console.error(error) +- } +-} +  + function ProfilePage(): React.JSX.Element { + const [isLoading, setIsLoading] = useState(true) +- const [userData, setUserData] = useState({ ++ const [userData, setUserData] = useState({ + email: '', + name: '', + profilePictureUrl: '' + }) + const [isModified, setIsModified] = useState(false) +  ++ const updateSelf = useCallback(async () => { ++ try { ++ await ControllerInstance.getInstance().userController.updateSelf( ++ { ++ name: userData.name, ++ email: userData.email ++ }, ++ {} ++ ) ++ toast.success('Profile updated successfully') ++ } catch (error) { ++ // eslint-disable-next-line no-console -- we need to log the error ++ console.error(error) ++ } ++ setIsModified(false) ++ }, [userData]) ++ + useEffect(() => { +- getUserDetails() +- .then((data) => { +- if (data) { ++ ControllerInstance.getInstance() ++ .userController.getSelf() ++ .then(({ data, success, error }) => { ++ if (success && data) { + setUserData({ + email: data.email, +- name: data.name ?? '', +- profilePictureUrl: data.profilePictureUrl ++ name: data.name, ++ profilePictureUrl: data.profilePictureUrl || '' + }) + setIsLoading(false) ++ } else { ++ // eslint-disable-next-line no-console -- we need to log the error ++ console.error(error) + } + }) + .catch((error) => { +@@ -67,7 +65,7 @@ function ProfilePage(): React.JSX.Element { + Upload a picture to change your avatar across Keyshade. +  +
 +-
{' '} ++
 + {/* //! This is will be replaced by an image tag */} +
 + {/* Name */} +@@ -114,20 +112,7 @@ function ProfilePage(): React.JSX.Element { + )} +
 +
 +- { +- updateUserDetails(userData) +- .then(() => { +- toast.success('User details updated successfully') +- }) +- .catch(() => { +- toast.error('Failed to update user details') +- }) +- setIsModified(false) +- }} +- variant="secondary" +- > ++  +
 +diff --git a/apps/platform/src/components/shared/sidebar/index.tsx b/apps/platform/src/components/shared/sidebar/index.tsx +index ee7cd9b..7da768b 100644 +--- a/apps/platform/src/components/shared/sidebar/index.tsx ++++ b/apps/platform/src/components/shared/sidebar/index.tsx +@@ -6,8 +6,8 @@ import { + SettingsSVG, + TeamSVG + } from '@public/svg/shared' +-import { Combobox } from '@/components/ui/combobox' + import SidebarTab from './sidebarTab' ++import { Combobox } from '@/components/ui/combobox' +  + function Sidebar(): JSX.Element { + const sidebarTabData = [ +diff --git a/apps/platform/src/components/ui/combobox.tsx b/apps/platform/src/components/ui/combobox.tsx +index b3107dc..8414098 100644 +--- a/apps/platform/src/components/ui/combobox.tsx ++++ b/apps/platform/src/components/ui/combobox.tsx +@@ -5,27 +5,6 @@ import { useEffect, useState } from 'react' + import { useRouter } from 'next/navigation' + import { toast } from 'sonner' + import { AddSVG } from '@public/svg/shared' +-import { cn } from '@/lib/utils' +-import { +- Command, +- CommandEmpty, +- CommandInput, +- CommandItem, +- CommandList +-} from '@/components/ui/command' +-import { +- Popover, +- PopoverContent, +- PopoverTrigger +-} from '@/components/ui/popover' +-import { apiClient } from '@/lib/api-client' +-// import type { Workspace } from '@/types' +-import { zWorkspace } from '@/types' +-import { +- getCurrentWorkspace, +- setCurrentWorkspace, +- setWorkspace +-} from '@/lib/workspace-storage' + import type { Workspace } from '@keyshade/schema' + import { Input } from './input' + import { Label } from './label' +@@ -38,39 +17,43 @@ import { + DialogTrigger + } from './dialog' + import { Button } from './button' +-import { WorkspaceSchema } from '@keyshade/schema/schemas' +- +-interface WorkspaceResponse { +- items: Workspace[] +- metadata: { +- page: number +- perPage: number +- pageCount: number +- totalCount: number +- links: { +- self: string +- first: string +- previous: string | null +- next: string | null +- last: string +- } +- } +-} ++import { ++ getCurrentWorkspace, ++ setCurrentWorkspace, ++ setWorkspace ++} from '@/lib/workspace-storage' ++import { cn } from '@/lib/utils' ++import { ++ Popover, ++ PopoverContent, ++ PopoverTrigger ++} from '@/components/ui/popover' ++import { ++ Command, ++ CommandEmpty, ++ CommandInput, ++ CommandItem, ++ CommandList ++} from '@/components/ui/command' ++import ControllerInstance from '@/lib/controller-instance' +  + async function getAllWorkspace(): Promise { + try { +- const workspaceData: WorkspaceResponse = +- await apiClient.get('/workspace') ++ const { data, success, error } = ++ await ControllerInstance.getInstance().workspaceController.getWorkspacesOfUser( ++ {}, ++ {} ++ ) +  +- // TODO: We are getting error here from the success flag, need to see this again +- // const { success, data } = WorkspaceSchema.array().safeParse(workspaceData.items) +- // if (!success) { +- // throw new Error('Invalid data') +- // } ++ if (error) { ++ // eslint-disable-next-line no-console -- we need to log the error ++ console.error(error) ++ return undefined ++ } +  +- return workspaceData.items +- // return data +- // return workspaceData; ++ if (success && data) { ++ return data.items ++ } + } catch (error) { + // eslint-disable-next-line no-console -- we need to log the error + console.error(error) +@@ -92,9 +75,13 @@ export function Combobox(): React.JSX.Element { + } + setIsNameEmpty(false) + try { +- const response = await apiClient.post('/workspace', { +- name +- }) ++ const response = ++ await ControllerInstance.getInstance().workspaceController.createWorkspace( ++ { ++ name ++ }, ++ {} ++ ) + setCurrentWorkspace(response) + setOpen(false) + } catch (error) { +diff --git a/apps/platform/src/lib/controller-instance.ts b/apps/platform/src/lib/controller-instance.ts +index fe432b7..a766038 100644 +--- a/apps/platform/src/lib/controller-instance.ts ++++ b/apps/platform/src/lib/controller-instance.ts +@@ -1,30 +1,90 @@ +-import { AuthController } from '@keyshade/api-client' ++import { ++ AuthController, ++ EnvironmentController, ++ ProjectController, ++ SecretController, ++ UserController, ++ VariableController, ++ WorkspaceController, ++ WorkspaceMembershipController, ++ WorkspaceRoleController ++} from '@keyshade/api-client' +  + export default class ControllerInstance { + private static instance: ControllerInstance | null +  +- private _authController: AuthController | null = null ++ private _authController: AuthController ++ private _userController: UserController ++ private _workspaceController: WorkspaceController ++ private _workspaceMembershipController: WorkspaceMembershipController ++ private _workspaceRoleController: WorkspaceRoleController ++ private _projectController: ProjectController ++ private _environmentController: EnvironmentController ++ private _secretController: SecretController ++ private _variableController: VariableController +  + get authController(): AuthController { +- if (!this._authController) { +- throw new Error('ControllerInstance not initialized') +- } + return this._authController + } +  +- static initialize(baseUrl: string): void { +- if (!ControllerInstance.instance) { +- const instance = new ControllerInstance() ++ get workspaceController(): WorkspaceController { ++ return this._workspaceController ++ } ++ ++ get workspaceMembershipController(): WorkspaceMembershipController { ++ return this._workspaceMembershipController ++ } +  +- instance._authController = new AuthController(baseUrl) ++ get workspaceRoleController(): WorkspaceRoleController { ++ return this._workspaceRoleController ++ } +  +- ControllerInstance.instance = instance +- } ++ get projectController(): ProjectController { ++ return this._projectController ++ } ++ ++ get environmentController(): EnvironmentController { ++ return this._environmentController ++ } ++ ++ get secretController(): SecretController { ++ return this._secretController ++ } ++ ++ get variableController(): VariableController { ++ return this._variableController ++ } ++ ++ get userController(): UserController { ++ return this._userController + } +  + static getInstance(): ControllerInstance { + if (!ControllerInstance.instance) { +- throw new Error('ControllerInstance not initialized') ++ ControllerInstance.instance = new ControllerInstance() ++ ControllerInstance.instance._authController = new AuthController( ++ process.env.NEXT_PUBLIC_BACKEND_URL ++ ) ++ ControllerInstance.instance._userController = new UserController( ++ process.env.NEXT_PUBLIC_BACKEND_URL ++ ) ++ ControllerInstance.instance._workspaceController = ++ new WorkspaceController(process.env.NEXT_PUBLIC_BACKEND_URL) ++ ControllerInstance.instance._workspaceMembershipController = ++ new WorkspaceMembershipController(process.env.NEXT_PUBLIC_BACKEND_URL) ++ ControllerInstance.instance._workspaceRoleController = ++ new WorkspaceRoleController(process.env.NEXT_PUBLIC_BACKEND_URL) ++ ControllerInstance.instance._projectController = new ProjectController( ++ process.env.NEXT_PUBLIC_BACKEND_URL ++ ) ++ ControllerInstance.instance._environmentController = ++ new EnvironmentController(process.env.NEXT_PUBLIC_BACKEND_URL) ++ ControllerInstance.instance._secretController = new SecretController( ++ process.env.NEXT_PUBLIC_BACKEND_URL ++ ) ++ ControllerInstance.instance._variableController = new VariableController( ++ process.env.NEXT_PUBLIC_BACKEND_URL ++ ) + } + return ControllerInstance.instance + } +diff --git a/packages/api-client/src/index.ts b/packages/api-client/src/index.ts +index 79ab5f6..edd344f 100644 +--- a/packages/api-client/src/index.ts ++++ b/packages/api-client/src/index.ts +@@ -8,6 +8,7 @@ import WorkspaceController from '@api-client/controllers/workspace' + import WorkspaceRoleController from '@api-client/controllers/workspace-role' + import WorkspaceMembershipController from '@api-client/controllers/workspace-membership' + import AuthController from '@api-client/controllers/auth' ++import UserController from '@api-client/controllers/user' + export { + EnvironmentController, + SecretController, +@@ -18,5 +19,6 @@ export { + WorkspaceController, + WorkspaceRoleController, + WorkspaceMembershipController, +- AuthController ++ AuthController, ++ UserController + } +diff --git a/packages/schema/src/secret/index.ts b/packages/schema/src/secret/index.ts +index ff5b499..4277679 100644 +--- a/packages/schema/src/secret/index.ts ++++ b/packages/schema/src/secret/index.ts +@@ -100,15 +100,17 @@ export const GetAllSecretsOfProjectResponseSchema = PageResponseSchema( + name: z.string() + }) + }), +- values: z.object({ +- environment: z.object({ +- id: z.string(), +- name: z.string(), +- slug: z.string() +- }), +- value: z.string(), +- version: z.number() +- }) ++ values: z.array( ++ z.object({ ++ environment: z.object({ ++ id: z.string(), ++ name: z.string(), ++ slug: z.string() ++ }), ++ value: z.string(), ++ version: z.number() ++ }) ++ ) + }) + ) +  diff --git a/t.success('User details updated successfully') b/t.success('User details updated successfully') new file mode 100644 index 00000000..333a0b57 --- /dev/null +++ b/t.success('User details updated successfully') @@ -0,0 +1,258 @@ + + SSUUMMMMAARRYY OOFF LLEESSSS CCOOMMMMAANNDDSS + + Commands marked with * may be preceded by a number, _N. + Notes in parentheses indicate the behavior if _N is given. + A key preceded by a caret indicates the Ctrl key; thus ^K is ctrl-K. + + h H Display this help. + q :q Q :Q ZZ Exit. + --------------------------------------------------------------------------- + + MMOOVVIINNGG + + e ^E j ^N CR * Forward one line (or _N lines). + y ^Y k ^K ^P * Backward one line (or _N lines). + f ^F ^V SPACE * Forward one window (or _N lines). + b ^B ESC-v * Backward one window (or _N lines). + z * Forward one window (and set window to _N). + w * Backward one window (and set window to _N). + ESC-SPACE * Forward one window, but don't stop at end-of-file. + d ^D * Forward one half-window (and set half-window to _N). + u ^U * Backward one half-window (and set half-window to _N). + ESC-) RightArrow * Right one half screen width (or _N positions). + ESC-( LeftArrow * Left one half screen width (or _N positions). + ESC-} ^RightArrow Right to last column displayed. + ESC-{ ^LeftArrow Left to first column. + F Forward forever; like "tail -f". + ESC-F Like F but stop when search pattern is found. + r ^R ^L Repaint screen. + R Repaint screen, discarding buffered input. + --------------------------------------------------- + Default "window" is the screen height. + Default "half-window" is half of the screen height. + --------------------------------------------------------------------------- + + SSEEAARRCCHHIINNGG + + /_p_a_t_t_e_r_n * Search forward for (_N-th) matching line. + ?_p_a_t_t_e_r_n * Search backward for (_N-th) matching line. + n * Repeat previous search (for _N-th occurrence). + N * Repeat previous search in reverse direction. + ESC-n * Repeat previous search, spanning files. + ESC-N * Repeat previous search, reverse dir. & spanning files. + ESC-u Undo (toggle) search highlighting. + ESC-U Clear search highlighting. + &_p_a_t_t_e_r_n * Display only matching lines. + --------------------------------------------------- + A search pattern may begin with one or more of: + ^N or ! Search for NON-matching lines. + ^E or * Search multiple files (pass thru END OF FILE). + ^F or @ Start search at FIRST file (for /) or last file (for ?). + ^K Highlight matches, but don't move (KEEP position). + ^R Don't use REGULAR EXPRESSIONS. + ^W WRAP search if no match found. + --------------------------------------------------------------------------- + + JJUUMMPPIINNGG + + g < ESC-< * Go to first line in file (or line _N). + G > ESC-> * Go to last line in file (or line _N). + p % * Go to beginning of file (or _N percent into file). + t * Go to the (_N-th) next tag. + T * Go to the (_N-th) previous tag. + { ( [ * Find close bracket } ) ]. + } ) ] * Find open bracket { ( [. + ESC-^F _<_c_1_> _<_c_2_> * Find close bracket _<_c_2_>. + ESC-^B _<_c_1_> _<_c_2_> * Find open bracket _<_c_1_>. + --------------------------------------------------- + Each "find close bracket" command goes forward to the close bracket + matching the (_N-th) open bracket in the top line. + Each "find open bracket" command goes backward to the open bracket + matching the (_N-th) close bracket in the bottom line. + + m_<_l_e_t_t_e_r_> Mark the current top line with . + M_<_l_e_t_t_e_r_> Mark the current bottom line with . + '_<_l_e_t_t_e_r_> Go to a previously marked position. + '' Go to the previous position. + ^X^X Same as '. + ESC-M_<_l_e_t_t_e_r_> Clear a mark. + --------------------------------------------------- + A mark is any upper-case or lower-case letter. + Certain marks are predefined: + ^ means beginning of the file + $ means end of the file + --------------------------------------------------------------------------- + + CCHHAANNGGIINNGG FFIILLEESS + + :e [_f_i_l_e] Examine a new file. + ^X^V Same as :e. + :n * Examine the (_N-th) next file from the command line. + :p * Examine the (_N-th) previous file from the command line. + :x * Examine the first (or _N-th) file from the command line. + :d Delete the current file from the command line list. + = ^G :f Print current file name. + --------------------------------------------------------------------------- + + MMIISSCCEELLLLAANNEEOOUUSS CCOOMMMMAANNDDSS + + -_<_f_l_a_g_> Toggle a command line option [see OPTIONS below]. + --_<_n_a_m_e_> Toggle a command line option, by name. + __<_f_l_a_g_> Display the setting of a command line option. + ___<_n_a_m_e_> Display the setting of an option, by name. + +_c_m_d Execute the less cmd each time a new file is examined. + + !_c_o_m_m_a_n_d Execute the shell command with $SHELL. + |XX_c_o_m_m_a_n_d Pipe file between current pos & mark XX to shell command. + s _f_i_l_e Save input to a file. + v Edit the current file with $VISUAL or $EDITOR. + V Print version number of "less". + --------------------------------------------------------------------------- + + OOPPTTIIOONNSS + + Most options may be changed either on the command line, + or from within less by using the - or -- command. + Options may be given in one of two forms: either a single + character preceded by a -, or a name preceded by --. + + -? ........ --help + Display help (from command line). + -a ........ --search-skip-screen + Search skips current screen. + -A ........ --SEARCH-SKIP-SCREEN + Search starts just after target line. + -b [_N] .... --buffers=[_N] + Number of buffers. + -B ........ --auto-buffers + Don't automatically allocate buffers for pipes. + -c ........ --clear-screen + Repaint by clearing rather than scrolling. + -d ........ --dumb + Dumb terminal. + -D xx_c_o_l_o_r . --color=xx_c_o_l_o_r + Set screen colors. + -e -E .... --quit-at-eof --QUIT-AT-EOF + Quit at end of file. + -f ........ --force + Force open non-regular files. + -F ........ --quit-if-one-screen + Quit if entire file fits on first screen. + -g ........ --hilite-search + Highlight only last match for searches. + -G ........ --HILITE-SEARCH + Don't highlight any matches for searches. + -h [_N] .... --max-back-scroll=[_N] + Backward scroll limit. + -i ........ --ignore-case + Ignore case in searches that do not contain uppercase. + -I ........ --IGNORE-CASE + Ignore case in all searches. + -j [_N] .... --jump-target=[_N] + Screen position of target lines. + -J ........ --status-column + Display a status column at left edge of screen. + -k [_f_i_l_e] . --lesskey-file=[_f_i_l_e] + Use a lesskey file. + -K ........ --quit-on-intr + Exit less in response to ctrl-C. + -L ........ --no-lessopen + Ignore the LESSOPEN environment variable. + -m -M .... --long-prompt --LONG-PROMPT + Set prompt style. + -n -N .... --line-numbers --LINE-NUMBERS + Don't use line numbers. + -o [_f_i_l_e] . --log-file=[_f_i_l_e] + Copy to log file (standard input only). + -O [_f_i_l_e] . --LOG-FILE=[_f_i_l_e] + Copy to log file (unconditionally overwrite). + -p [_p_a_t_t_e_r_n] --pattern=[_p_a_t_t_e_r_n] + Start at pattern (from command line). + -P [_p_r_o_m_p_t] --prompt=[_p_r_o_m_p_t] + Define new prompt. + -q -Q .... --quiet --QUIET --silent --SILENT + Quiet the terminal bell. + -r -R .... --raw-control-chars --RAW-CONTROL-CHARS + Output "raw" control characters. + -s ........ --squeeze-blank-lines + Squeeze multiple blank lines. + -S ........ --chop-long-lines + Chop (truncate) long lines rather than wrapping. + -t [_t_a_g] .. --tag=[_t_a_g] + Find a tag. + -T [_t_a_g_s_f_i_l_e] --tag-file=[_t_a_g_s_f_i_l_e] + Use an alternate tags file. + -u -U .... --underline-special --UNDERLINE-SPECIAL + Change handling of backspaces. + -V ........ --version + Display the version number of "less". + -w ........ --hilite-unread + Highlight first new line after forward-screen. + -W ........ --HILITE-UNREAD + Highlight first new line after any forward movement. + -x [_N[,...]] --tabs=[_N[,...]] + Set tab stops. + -X ........ --no-init + Don't use termcap init/deinit strings. + -y [_N] .... --max-forw-scroll=[_N] + Forward scroll limit. + -z [_N] .... --window=[_N] + Set size of window. + -" [_c[_c]] . --quotes=[_c[_c]] + Set shell quote characters. + -~ ........ --tilde + Don't display tildes after end of file. + -# [_N] .... --shift=[_N] + Set horizontal scroll amount (0 = one half screen width). + --file-size + Automatically determine the size of the input file. + --follow-name + The F command changes files if the input file is renamed. + --incsearch + Search file as each pattern character is typed in. + --line-num-width=N + Set the width of the -N line number field to N characters. + --mouse + Enable mouse input. + --no-keypad + Don't send termcap keypad init/deinit strings. + --no-histdups + Remove duplicates from command history. + --rscroll=C + Set the character used to mark truncated lines. + --save-marks + Retain marks across invocations of less. + --status-col-width=N + Set the width of the -J status column to N characters. + --use-backslash + Subsequent options use backslash as escape char. + --use-color + Enables colored text. + --wheel-lines=N + Each click of the mouse wheel moves N lines. + + + --------------------------------------------------------------------------- + + LLIINNEE EEDDIITTIINNGG + + These keys can be used to edit text being entered + on the "command line" at the bottom of the screen. + + RightArrow ..................... ESC-l ... Move cursor right one character. + LeftArrow ...................... ESC-h ... Move cursor left one character. + ctrl-RightArrow ESC-RightArrow ESC-w ... Move cursor right one word. + ctrl-LeftArrow ESC-LeftArrow ESC-b ... Move cursor left one word. + HOME ........................... ESC-0 ... Move cursor to start of line. + END ............................ ESC-$ ... Move cursor to end of line. + BACKSPACE ................................ Delete char to left of cursor. + DELETE ......................... ESC-x ... Delete char under cursor. + ctrl-BACKSPACE ESC-BACKSPACE ........... Delete word to left of cursor. + ctrl-DELETE .... ESC-DELETE .... ESC-X ... Delete word under cursor. + ctrl-U ......... ESC (MS-DOS only) ....... Delete entire line. + UpArrow ........................ ESC-k ... Retrieve previous command line. + DownArrow ...................... ESC-j ... Retrieve next command line. + TAB ...................................... Complete filename & cycle. + SHIFT-TAB ...................... ESC-TAB Complete filename & reverse cycle. + ctrl-L ................................... Complete filename, list all. From 83a18511a732163e72e9b9a4e6bbe199136fe707 Mon Sep 17 00:00:00 2001 From: rajdip-b Date: Tue, 17 Dec 2024 22:07:13 +0530 Subject: [PATCH 06/19] chore: Update platform build command --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 11c6ed91..3608c233 100644 --- a/package.json +++ b/package.json @@ -106,7 +106,7 @@ "build": "turbo run build", "build:api": "pnpm db:generate-types && turbo run build --filter=api", "build:web": "pnpm build:api-client && turbo run build --filter=web", - "build:platform": "turbo run build --filter=platform", + "build:platform": "pnpm build:api-client && turbo run build --filter=platform", "build:cli": "pnpm build:secret-scan && pnpm build:api-client && turbo run build --filter=cli", "build:api-client": "pnpm build:schema && turbo run --filter=@keyshade/api-client build", "build:schema": "turbo run build --filter=@keyshade/schema", From a9fc39ee4ae1f2b3869c1bc8addfa20958049022 Mon Sep 17 00:00:00 2001 From: Muntasir Mallik <73852736+muntaxir4@users.noreply.github.com> Date: Thu, 19 Dec 2024 11:25:23 +0530 Subject: [PATCH 07/19] chore(ci): Add internal package dependencies to existing workflows (#592) --- .github/workflows/validate-api-client.yaml | 12 ++++++++++-- .github/workflows/validate-api.yaml | 4 ++-- .github/workflows/validate-cli.yaml | 12 ++++++++---- .github/workflows/validate-platform.yaml | 12 +++++++----- .github/workflows/validate-schema.yaml | 4 ++-- .github/workflows/validate-secret-scan.yaml | 4 ++-- .github/workflows/validate-web.yaml | 8 ++++---- package.json | 2 +- 8 files changed, 36 insertions(+), 22 deletions(-) diff --git a/.github/workflows/validate-api-client.yaml b/.github/workflows/validate-api-client.yaml index 4d336299..6c1f229e 100644 --- a/.github/workflows/validate-api-client.yaml +++ b/.github/workflows/validate-api-client.yaml @@ -6,10 +6,18 @@ on: - '!develop' - '!main' paths: - ['packages/api-client/**', '.github/workflows/validate-api-client.yml'] + [ + 'packages/schema/**', + 'packages/api-client/**', + '.github/workflows/validate-api-client.yaml' + ] pull_request: paths: - ['packages/api-client/**', '.github/workflows/validate-api-client.yml'] + [ + 'packages/schema/**', + 'packages/api-client/**', + '.github/workflows/validate-api-client.yaml' + ] jobs: validate: diff --git a/.github/workflows/validate-api.yaml b/.github/workflows/validate-api.yaml index ae187a18..8243e60f 100644 --- a/.github/workflows/validate-api.yaml +++ b/.github/workflows/validate-api.yaml @@ -5,9 +5,9 @@ on: branches: - '!develop' - '!main' - paths: ['apps/api/**', '.github/workflows/validate-api.yml'] + paths: ['apps/api/**', '.github/workflows/validate-api.yaml'] pull_request: - paths: ['apps/api/**', '.github/workflows/validate-api.yml'] + paths: ['apps/api/**', '.github/workflows/validate-api.yaml'] jobs: validate: diff --git a/.github/workflows/validate-cli.yaml b/.github/workflows/validate-cli.yaml index 806f3901..7666d987 100644 --- a/.github/workflows/validate-cli.yaml +++ b/.github/workflows/validate-cli.yaml @@ -7,16 +7,20 @@ on: - '!main' paths: [ + 'packages/api-client/**', + 'packages/secret-scan/**', 'apps/cli/**', - '.github/workflows/validate-cli.yml', - '.github/workflows/deploy-cli.yml' + '.github/workflows/validate-cli.yaml', + '.github/workflows/deploy-cli.yaml' ] pull_request: paths: [ + 'packages/api-client/**', + 'packages/secret-scan/**', 'apps/cli/**', - '.github/workflows/deploy-cli.yml', - '.github/workflows/validate-cli.yml' + '.github/workflows/deploy-cli.yaml', + '.github/workflows/validate-cli.yaml' ] jobs: diff --git a/.github/workflows/validate-platform.yaml b/.github/workflows/validate-platform.yaml index c29b2b67..01e4e3e3 100644 --- a/.github/workflows/validate-platform.yaml +++ b/.github/workflows/validate-platform.yaml @@ -7,16 +7,18 @@ on: - '!main' paths: [ + 'packages/api-client/**', 'apps/platform/**', - '.github/workflows/validate-platform.yml', - '.github/workflows/deploy-platform.yml' + '.github/workflows/validate-platform.yaml', + '.github/workflows/deploy-platform.yaml' ] pull_request: paths: [ + 'packages/api-client/**', 'apps/platform/**', - '.github/workflows/deploy-platform.yml', - '.github/workflows/validate-platform.yml' + '.github/workflows/deploy-platform.yaml', + '.github/workflows/validate-platform.yaml' ] jobs: @@ -58,4 +60,4 @@ jobs: - name: Lint run: | - pnpm run lint:platform \ No newline at end of file + pnpm run lint:platform diff --git a/.github/workflows/validate-schema.yaml b/.github/workflows/validate-schema.yaml index e46b437c..9719e7e3 100644 --- a/.github/workflows/validate-schema.yaml +++ b/.github/workflows/validate-schema.yaml @@ -5,9 +5,9 @@ on: branches: - '!develop' - '!main' - paths: ['packages/schema/**', '.github/workflows/validate-schema.yml'] + paths: ['packages/schema/**', '.github/workflows/validate-schema.yaml'] pull_request: - paths: ['packages/schema/**', '.github/workflows/validate-schema.yml'] + paths: ['packages/schema/**', '.github/workflows/validate-schema.yaml'] jobs: validate: diff --git a/.github/workflows/validate-secret-scan.yaml b/.github/workflows/validate-secret-scan.yaml index dacc5ce0..83fea21d 100644 --- a/.github/workflows/validate-secret-scan.yaml +++ b/.github/workflows/validate-secret-scan.yaml @@ -6,10 +6,10 @@ on: - '!develop' - '!main' paths: - ['packages/secret-scan/**', '.github/workflows/validate-secret-scan.yml'] + ['packages/secret-scan/**', '.github/workflows/validate-secret-scan.yaml'] pull_request: paths: - ['packages/secret-scan/**', '.github/workflows/validate-secret-scan.yml'] + ['packages/secret-scan/**', '.github/workflows/validate-secret-scan.yaml'] jobs: validate: diff --git a/.github/workflows/validate-web.yaml b/.github/workflows/validate-web.yaml index f396b437..73ab67ff 100644 --- a/.github/workflows/validate-web.yaml +++ b/.github/workflows/validate-web.yaml @@ -8,15 +8,15 @@ on: paths: [ 'apps/web/**', - '.github/workflows/validate-web.yml', - '.github/workflows/deploy-web.yml' + '.github/workflows/validate-web.yaml', + '.github/workflows/deploy-web.yaml' ] pull_request: paths: [ 'apps/web/**', - '.github/workflows/deploy-web.yml', - '.github/workflows/validate-web.yml' + '.github/workflows/deploy-web.yaml', + '.github/workflows/validate-web.yaml' ] jobs: diff --git a/package.json b/package.json index 3608c233..f0926c15 100644 --- a/package.json +++ b/package.json @@ -101,7 +101,7 @@ "lint:platform": "turbo run lint --filter=platform", "lint:cli": "turbo run lint --filter=cli", "lint:api-client": "turbo run lint --filter=@keyshade/api-client", - "lint:secret-scan": "turbo run lint --filter=secret-scan", + "lint:secret-scan": "turbo run lint --filter=@keyshade/secret-scan", "lint:schema": "turbo run lint --filter=@keyshade/schema", "build": "turbo run build", "build:api": "pnpm db:generate-types && turbo run build --filter=api", From 1882f33aaca5017de2ab01dd618f82bdb9e1501c Mon Sep 17 00:00:00 2001 From: Sameer Poswal <106386145+poswalsameer@users.noreply.github.com> Date: Fri, 20 Dec 2024 00:34:00 +0530 Subject: [PATCH 08/19] Delete t.success('User details updated successfully') --- ...ccess('User details updated successfully') | 258 ------------------ 1 file changed, 258 deletions(-) delete mode 100644 t.success('User details updated successfully') diff --git a/t.success('User details updated successfully') b/t.success('User details updated successfully') deleted file mode 100644 index 333a0b57..00000000 --- a/t.success('User details updated successfully') +++ /dev/null @@ -1,258 +0,0 @@ - - SSUUMMMMAARRYY OOFF LLEESSSS CCOOMMMMAANNDDSS - - Commands marked with * may be preceded by a number, _N. - Notes in parentheses indicate the behavior if _N is given. - A key preceded by a caret indicates the Ctrl key; thus ^K is ctrl-K. - - h H Display this help. - q :q Q :Q ZZ Exit. - --------------------------------------------------------------------------- - - MMOOVVIINNGG - - e ^E j ^N CR * Forward one line (or _N lines). - y ^Y k ^K ^P * Backward one line (or _N lines). - f ^F ^V SPACE * Forward one window (or _N lines). - b ^B ESC-v * Backward one window (or _N lines). - z * Forward one window (and set window to _N). - w * Backward one window (and set window to _N). - ESC-SPACE * Forward one window, but don't stop at end-of-file. - d ^D * Forward one half-window (and set half-window to _N). - u ^U * Backward one half-window (and set half-window to _N). - ESC-) RightArrow * Right one half screen width (or _N positions). - ESC-( LeftArrow * Left one half screen width (or _N positions). - ESC-} ^RightArrow Right to last column displayed. - ESC-{ ^LeftArrow Left to first column. - F Forward forever; like "tail -f". - ESC-F Like F but stop when search pattern is found. - r ^R ^L Repaint screen. - R Repaint screen, discarding buffered input. - --------------------------------------------------- - Default "window" is the screen height. - Default "half-window" is half of the screen height. - --------------------------------------------------------------------------- - - SSEEAARRCCHHIINNGG - - /_p_a_t_t_e_r_n * Search forward for (_N-th) matching line. - ?_p_a_t_t_e_r_n * Search backward for (_N-th) matching line. - n * Repeat previous search (for _N-th occurrence). - N * Repeat previous search in reverse direction. - ESC-n * Repeat previous search, spanning files. - ESC-N * Repeat previous search, reverse dir. & spanning files. - ESC-u Undo (toggle) search highlighting. - ESC-U Clear search highlighting. - &_p_a_t_t_e_r_n * Display only matching lines. - --------------------------------------------------- - A search pattern may begin with one or more of: - ^N or ! Search for NON-matching lines. - ^E or * Search multiple files (pass thru END OF FILE). - ^F or @ Start search at FIRST file (for /) or last file (for ?). - ^K Highlight matches, but don't move (KEEP position). - ^R Don't use REGULAR EXPRESSIONS. - ^W WRAP search if no match found. - --------------------------------------------------------------------------- - - JJUUMMPPIINNGG - - g < ESC-< * Go to first line in file (or line _N). - G > ESC-> * Go to last line in file (or line _N). - p % * Go to beginning of file (or _N percent into file). - t * Go to the (_N-th) next tag. - T * Go to the (_N-th) previous tag. - { ( [ * Find close bracket } ) ]. - } ) ] * Find open bracket { ( [. - ESC-^F _<_c_1_> _<_c_2_> * Find close bracket _<_c_2_>. - ESC-^B _<_c_1_> _<_c_2_> * Find open bracket _<_c_1_>. - --------------------------------------------------- - Each "find close bracket" command goes forward to the close bracket - matching the (_N-th) open bracket in the top line. - Each "find open bracket" command goes backward to the open bracket - matching the (_N-th) close bracket in the bottom line. - - m_<_l_e_t_t_e_r_> Mark the current top line with . - M_<_l_e_t_t_e_r_> Mark the current bottom line with . - '_<_l_e_t_t_e_r_> Go to a previously marked position. - '' Go to the previous position. - ^X^X Same as '. - ESC-M_<_l_e_t_t_e_r_> Clear a mark. - --------------------------------------------------- - A mark is any upper-case or lower-case letter. - Certain marks are predefined: - ^ means beginning of the file - $ means end of the file - --------------------------------------------------------------------------- - - CCHHAANNGGIINNGG FFIILLEESS - - :e [_f_i_l_e] Examine a new file. - ^X^V Same as :e. - :n * Examine the (_N-th) next file from the command line. - :p * Examine the (_N-th) previous file from the command line. - :x * Examine the first (or _N-th) file from the command line. - :d Delete the current file from the command line list. - = ^G :f Print current file name. - --------------------------------------------------------------------------- - - MMIISSCCEELLLLAANNEEOOUUSS CCOOMMMMAANNDDSS - - -_<_f_l_a_g_> Toggle a command line option [see OPTIONS below]. - --_<_n_a_m_e_> Toggle a command line option, by name. - __<_f_l_a_g_> Display the setting of a command line option. - ___<_n_a_m_e_> Display the setting of an option, by name. - +_c_m_d Execute the less cmd each time a new file is examined. - - !_c_o_m_m_a_n_d Execute the shell command with $SHELL. - |XX_c_o_m_m_a_n_d Pipe file between current pos & mark XX to shell command. - s _f_i_l_e Save input to a file. - v Edit the current file with $VISUAL or $EDITOR. - V Print version number of "less". - --------------------------------------------------------------------------- - - OOPPTTIIOONNSS - - Most options may be changed either on the command line, - or from within less by using the - or -- command. - Options may be given in one of two forms: either a single - character preceded by a -, or a name preceded by --. - - -? ........ --help - Display help (from command line). - -a ........ --search-skip-screen - Search skips current screen. - -A ........ --SEARCH-SKIP-SCREEN - Search starts just after target line. - -b [_N] .... --buffers=[_N] - Number of buffers. - -B ........ --auto-buffers - Don't automatically allocate buffers for pipes. - -c ........ --clear-screen - Repaint by clearing rather than scrolling. - -d ........ --dumb - Dumb terminal. - -D xx_c_o_l_o_r . --color=xx_c_o_l_o_r - Set screen colors. - -e -E .... --quit-at-eof --QUIT-AT-EOF - Quit at end of file. - -f ........ --force - Force open non-regular files. - -F ........ --quit-if-one-screen - Quit if entire file fits on first screen. - -g ........ --hilite-search - Highlight only last match for searches. - -G ........ --HILITE-SEARCH - Don't highlight any matches for searches. - -h [_N] .... --max-back-scroll=[_N] - Backward scroll limit. - -i ........ --ignore-case - Ignore case in searches that do not contain uppercase. - -I ........ --IGNORE-CASE - Ignore case in all searches. - -j [_N] .... --jump-target=[_N] - Screen position of target lines. - -J ........ --status-column - Display a status column at left edge of screen. - -k [_f_i_l_e] . --lesskey-file=[_f_i_l_e] - Use a lesskey file. - -K ........ --quit-on-intr - Exit less in response to ctrl-C. - -L ........ --no-lessopen - Ignore the LESSOPEN environment variable. - -m -M .... --long-prompt --LONG-PROMPT - Set prompt style. - -n -N .... --line-numbers --LINE-NUMBERS - Don't use line numbers. - -o [_f_i_l_e] . --log-file=[_f_i_l_e] - Copy to log file (standard input only). - -O [_f_i_l_e] . --LOG-FILE=[_f_i_l_e] - Copy to log file (unconditionally overwrite). - -p [_p_a_t_t_e_r_n] --pattern=[_p_a_t_t_e_r_n] - Start at pattern (from command line). - -P [_p_r_o_m_p_t] --prompt=[_p_r_o_m_p_t] - Define new prompt. - -q -Q .... --quiet --QUIET --silent --SILENT - Quiet the terminal bell. - -r -R .... --raw-control-chars --RAW-CONTROL-CHARS - Output "raw" control characters. - -s ........ --squeeze-blank-lines - Squeeze multiple blank lines. - -S ........ --chop-long-lines - Chop (truncate) long lines rather than wrapping. - -t [_t_a_g] .. --tag=[_t_a_g] - Find a tag. - -T [_t_a_g_s_f_i_l_e] --tag-file=[_t_a_g_s_f_i_l_e] - Use an alternate tags file. - -u -U .... --underline-special --UNDERLINE-SPECIAL - Change handling of backspaces. - -V ........ --version - Display the version number of "less". - -w ........ --hilite-unread - Highlight first new line after forward-screen. - -W ........ --HILITE-UNREAD - Highlight first new line after any forward movement. - -x [_N[,...]] --tabs=[_N[,...]] - Set tab stops. - -X ........ --no-init - Don't use termcap init/deinit strings. - -y [_N] .... --max-forw-scroll=[_N] - Forward scroll limit. - -z [_N] .... --window=[_N] - Set size of window. - -" [_c[_c]] . --quotes=[_c[_c]] - Set shell quote characters. - -~ ........ --tilde - Don't display tildes after end of file. - -# [_N] .... --shift=[_N] - Set horizontal scroll amount (0 = one half screen width). - --file-size - Automatically determine the size of the input file. - --follow-name - The F command changes files if the input file is renamed. - --incsearch - Search file as each pattern character is typed in. - --line-num-width=N - Set the width of the -N line number field to N characters. - --mouse - Enable mouse input. - --no-keypad - Don't send termcap keypad init/deinit strings. - --no-histdups - Remove duplicates from command history. - --rscroll=C - Set the character used to mark truncated lines. - --save-marks - Retain marks across invocations of less. - --status-col-width=N - Set the width of the -J status column to N characters. - --use-backslash - Subsequent options use backslash as escape char. - --use-color - Enables colored text. - --wheel-lines=N - Each click of the mouse wheel moves N lines. - - - --------------------------------------------------------------------------- - - LLIINNEE EEDDIITTIINNGG - - These keys can be used to edit text being entered - on the "command line" at the bottom of the screen. - - RightArrow ..................... ESC-l ... Move cursor right one character. - LeftArrow ...................... ESC-h ... Move cursor left one character. - ctrl-RightArrow ESC-RightArrow ESC-w ... Move cursor right one word. - ctrl-LeftArrow ESC-LeftArrow ESC-b ... Move cursor left one word. - HOME ........................... ESC-0 ... Move cursor to start of line. - END ............................ ESC-$ ... Move cursor to end of line. - BACKSPACE ................................ Delete char to left of cursor. - DELETE ......................... ESC-x ... Delete char under cursor. - ctrl-BACKSPACE ESC-BACKSPACE ........... Delete word to left of cursor. - ctrl-DELETE .... ESC-DELETE .... ESC-X ... Delete word under cursor. - ctrl-U ......... ESC (MS-DOS only) ....... Delete entire line. - UpArrow ........................ ESC-k ... Retrieve previous command line. - DownArrow ...................... ESC-j ... Retrieve next command line. - TAB ...................................... Complete filename & cycle. - SHIFT-TAB ...................... ESC-TAB Complete filename & reverse cycle. - ctrl-L ................................... Complete filename, list all. From 675f6e4db1bb461f102b33d687a45a29971b298b Mon Sep 17 00:00:00 2001 From: Sameer Poswal <106386145+poswalsameer@users.noreply.github.com> Date: Fri, 20 Dec 2024 00:34:20 +0530 Subject: [PATCH 09/19] Delete t updateSelf = useCallback(async () => { --- t updateSelf = useCallback(async () => { | 664 ----------------------- 1 file changed, 664 deletions(-) delete mode 100644 t updateSelf = useCallback(async () => { diff --git a/t updateSelf = useCallback(async () => { b/t updateSelf = useCallback(async () => { deleted file mode 100644 index 95c798b9..00000000 --- a/t updateSelf = useCallback(async () => { +++ /dev/null @@ -1,664 +0,0 @@ -diff --git a/apps/platform/.eslintrc.cjs b/apps/platform/.eslintrc.cjs -index 28a8b27..fa9233a 100644 ---- a/apps/platform/.eslintrc.cjs -+++ b/apps/platform/.eslintrc.cjs -@@ -7,24 +7,13 @@ module.exports = { - }, - rules: { - 'import/no-extraneous-dependencies': 0, -- '@typescript-eslint/interface-name-prefix': 'off', - '@typescript-eslint/explicit-function-return-type': 'off', - '@typescript-eslint/explicit-module-boundary-types': 'off', -- '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/no-unused-vars': ['warn'], -- '@typescript-eslint/no-unsafe-call': 'off', -- '@typescript-eslint/no-unsafe-return': 'off', - '@typescript-eslint/space-before-function-paren': 'off', - '@typescript-eslint/strict-boolean-expressions': 'off', -- '@typescript-eslint/prefer-nullish-coalescing': 'off', -- 'space-before-function-paren': 'off', -- '@typescript-eslint/member-delimiter-style': 'off', - '@typescript-eslint/no-confusing-void-expression': 'off', - '@typescript-eslint/no-floating-promises': 'off', -- '@typescript-eslint/no-misused-promises': 'off', -- '@typescript-eslint/no-unsafe-assignment': 'off', -- '@typescript-eslint/no-unsafe-member-access': 'off', -- '@typescript-eslint/no-unsafe-argument': 'off', -- '@typescript-eslint/no-unnecessary-condition': 'off' -+ '@typescript-eslint/no-misused-promises': 'off' - } - } -diff --git a/apps/platform/src/app/(main)/page.tsx b/apps/platform/src/app/(main)/page.tsx -index fe8fa2a..9a9cb2a 100644 ---- a/apps/platform/src/app/(main)/page.tsx -+++ b/apps/platform/src/app/(main)/page.tsx -@@ -6,7 +6,6 @@ import type { - ProjectWithCount, - Workspace - } from '@keyshade/schema' --import { ProjectController } from '@keyshade/api-client' - import { AddSVG } from '@public/svg/shared' - import { FolderSVG } from '@public/svg/dashboard' - import ProjectCard from '@/components/dashboard/projectCard' -@@ -38,6 +37,7 @@ import { - DialogHeader, - DialogTrigger - } from '@/components/ui/dialog' -+import ControllerInstance from '@/lib/controller-instance' -  - export default function Index(): JSX.Element { - const [isSheetOpen, setIsSheetOpen] = useState(false) -@@ -76,16 +76,13 @@ export default function Index(): JSX.Element { - // If a workspace is selected, we want to fetch all the projects - // under that workspace and display it in the dashboard. - useEffect(() => { -- const projectController = new ProjectController( -- process.env.NEXT_PUBLIC_BACKEND_URL -- ) -- - async function getAllProjects() { - if (currentWorkspace) { -- const { success, error, data } = await projectController.getAllProjects( -- { workspaceSlug: currentWorkspace.slug }, -- {} -- ) -+ const { success, error, data } = -+ await ControllerInstance.getInstance().projectController.getAllProjects( -+ { workspaceSlug: currentWorkspace.slug }, -+ {} -+ ) -  - if (success && data) { - setProjects(data.items) -@@ -105,16 +102,13 @@ export default function Index(): JSX.Element { - // Function to create a new project - const createNewProject = useCallback(async () => { - if (currentWorkspace) { -- const projectController = new ProjectController( -- process.env.NEXT_PUBLIC_BACKEND_URL -- ) -- - newProjectData.workspaceSlug = currentWorkspace.slug -  -- const { data, error, success } = await projectController.createProject( -- newProjectData, -- {} -- ) -+ const { data, error, success } = -+ await ControllerInstance.getInstance().projectController.createProject( -+ newProjectData, -+ {} -+ ) -  - if (success && data) { - setProjects([ -diff --git a/apps/platform/src/app/(main)/project/[project]/@secret/page.tsx b/apps/platform/src/app/(main)/project/[project]/@secret/page.tsx -index 18134ab..e1bd8e6 100644 ---- a/apps/platform/src/app/(main)/project/[project]/@secret/page.tsx -+++ b/apps/platform/src/app/(main)/project/[project]/@secret/page.tsx -@@ -5,6 +5,7 @@ import { usePathname } from 'next/navigation' - import dayjs, { extend } from 'dayjs' - import relativeTime from 'dayjs/plugin/relativeTime' - import { NoteIconSVG } from '@public/svg/secret' -+import type { GetAllSecretsOfProjectResponse } from '@keyshade/schema' - import { - Accordion, - AccordionContent, -@@ -19,8 +20,6 @@ import { - TableHeader, - TableRow - } from '@/components/ui/table' --import type { Secret } from '@keyshade/schema' --import { SecretController } from '@keyshade/api-client' - import { ScrollArea } from '@/components/ui/scroll-area' - import { - Tooltip, -@@ -29,31 +28,28 @@ import { - TooltipTrigger - } from '@/components/ui/tooltip' - import { Skeleton } from '@/components/ui/skeleton' -+import ControllerInstance from '@/lib/controller-instance' -  - extend(relativeTime) -  - function SecretPage(): React.JSX.Element { -- const [allSecrets, setAllSecrets] = useState() -+ const [allSecrets, setAllSecrets] = -+ useState() - const [isLoading, setIsLoading] = useState(true) - const pathname = usePathname() -  - useEffect(() => { - setIsLoading(true) -  -- const secretController = new SecretController( -- process.env.NEXT_PUBLIC_BACKEND_URL -- ) -- - async function getAllSecretsByProjectSlug() { - const { success, error, data } = -- await secretController.getAllSecretsOfProject( -+ await ControllerInstance.getInstance().secretController.getAllSecretsOfProject( - { projectSlug: pathname.split('/')[2] }, - {} - ) -  - if (success && data) { -- //@ts-ignore -- setAllSecrets(data) -+ setAllSecrets(data.items) - } else { - // eslint-disable-next-line no-console -- we need to log the error - console.error(error) -@@ -68,9 +64,9 @@ function SecretPage(): React.JSX.Element { - if (isLoading) { - return ( -
 --  --  --  -+  -+  -+  -
 - ) - } -@@ -82,7 +78,7 @@ function SecretPage(): React.JSX.Element { - collapsible - type="single" - > -- {allSecrets?.map((secret) => { -+ {allSecrets?.map(({ secret, values }) => { - return ( -  -  -  -- {secret.versions.map((value) => { -+ {values.map((value) => { - return ( -  - {value.environment.slug} -@@ -147,7 +143,7 @@ function SecretPage(): React.JSX.Element { - ) - } -  --function SerectLoader(): React.JSX.Element { -+function SecretLoader(): React.JSX.Element { - return ( -
 -
 -diff --git a/apps/platform/src/app/(main)/project/[project]/layout.tsx b/apps/platform/src/app/(main)/project/[project]/layout.tsx -index 1eb511b..9edfb0b 100644 ---- a/apps/platform/src/app/(main)/project/[project]/layout.tsx -+++ b/apps/platform/src/app/(main)/project/[project]/layout.tsx -@@ -2,6 +2,7 @@ - import { useEffect, useState } from 'react' - import { useSearchParams } from 'next/navigation' - import { AddSVG } from '@public/svg/shared' -+import type { Project } from '@keyshade/schema' - import { Button } from '@/components/ui/button' - import { - Dialog, -@@ -13,8 +14,7 @@ import { - } from '@/components/ui/dialog' - import { Input } from '@/components/ui/input' - import { Label } from '@/components/ui/label' --import { ProjectController } from '@keyshade/api-client' --import type { Project } from '@keyshade/schema' -+import ControllerInstance from '@/lib/controller-instance' -  - interface DetailedProjectPageProps { - params: { project: string } -@@ -38,29 +38,22 @@ function DetailedProjectPage({ - const tab = searchParams.get('tab') ?? 'rollup-details' -  - useEffect(() => { -+ async function getProjectBySlug() { -+ const { success, error, data } = -+ await ControllerInstance.getInstance().projectController.getProject( -+ { projectSlug: params.project }, -+ {} -+ ) -  -- const projectController = new ProjectController( -- process.env.NEXT_PUBLIC_BACKEND_URL -- ) -- -- async function getProjectBySlug(){ -- const {success, error, data} = await projectController.getProject( -- {projectSlug: params.project}, -- {} -- ) -- -- if( success && data ){ -- //@ts-ignore -+ if (success && data) { - setCurrentProject(data) -- } -- else{ -+ } else { - // eslint-disable-next-line no-console -- we need to log the error - console.error(error) - } - } -  - getProjectBySlug() -- - }, [params.project]) -  - return ( -diff --git a/apps/platform/src/app/(main)/settings/@profile/page.tsx b/apps/platform/src/app/(main)/settings/@profile/page.tsx -index 21de60b..4559ec6 100644 ---- a/apps/platform/src/app/(main)/settings/@profile/page.tsx -+++ b/apps/platform/src/app/(main)/settings/@profile/page.tsx -@@ -1,54 +1,52 @@ - 'use client' --import React, { useEffect, useState } from 'react' -+import React, { useCallback, useEffect, useState } from 'react' - import { toast } from 'sonner' --import type { User } from '@keyshade/schema' - import InputLoading from './loading' - import { Input } from '@/components/ui/input' - import { Separator } from '@/components/ui/separator' -+import ControllerInstance from '@/lib/controller-instance' - import { Button } from '@/components/ui/button' --import { apiClient } from '@/lib/api-client' -- --type UserData = Omit< -- User, -- 'id' | 'isActive' | 'isOnboardingFinished' | 'isAdmin' | 'authProvider' --> --async function getUserDetails(): Promise { -- try { -- return await apiClient.get('/user') -- } catch (error) { -- // eslint-disable-next-line no-console -- we need to log the error -- console.error(error) -- } --} -- --async function updateUserDetails(userData: UserData): Promise { -- try { -- await apiClient.put('/user', userData) -- } catch (error) { -- // eslint-disable-next-line no-console -- we need to log the error -- console.error(error) -- } --} -  - function ProfilePage(): React.JSX.Element { - const [isLoading, setIsLoading] = useState(true) -- const [userData, setUserData] = useState({ -+ const [userData, setUserData] = useState({ - email: '', - name: '', - profilePictureUrl: '' - }) - const [isModified, setIsModified] = useState(false) -  -+ const updateSelf = useCallback(async () => { -+ try { -+ await ControllerInstance.getInstance().userController.updateSelf( -+ { -+ name: userData.name, -+ email: userData.email -+ }, -+ {} -+ ) -+ toast.success('Profile updated successfully') -+ } catch (error) { -+ // eslint-disable-next-line no-console -- we need to log the error -+ console.error(error) -+ } -+ setIsModified(false) -+ }, [userData]) -+ - useEffect(() => { -- getUserDetails() -- .then((data) => { -- if (data) { -+ ControllerInstance.getInstance() -+ .userController.getSelf() -+ .then(({ data, success, error }) => { -+ if (success && data) { - setUserData({ - email: data.email, -- name: data.name ?? '', -- profilePictureUrl: data.profilePictureUrl -+ name: data.name, -+ profilePictureUrl: data.profilePictureUrl || '' - }) - setIsLoading(false) -+ } else { -+ // eslint-disable-next-line no-console -- we need to log the error -+ console.error(error) - } - }) - .catch((error) => { -@@ -67,7 +65,7 @@ function ProfilePage(): React.JSX.Element { - Upload a picture to change your avatar across Keyshade. -  -
 --
{' '} -+
 - {/* //! This is will be replaced by an image tag */} -
 - {/* Name */} -@@ -114,20 +112,7 @@ function ProfilePage(): React.JSX.Element { - )} -
 -
 -- { -- updateUserDetails(userData) -- .then(() => { -- toast.success('User details updated successfully') -- }) -- .catch(() => { -- toast.error('Failed to update user details') -- }) -- setIsModified(false) -- }} -- variant="secondary" -- > -+  -
 -diff --git a/apps/platform/src/components/shared/sidebar/index.tsx b/apps/platform/src/components/shared/sidebar/index.tsx -index ee7cd9b..7da768b 100644 ---- a/apps/platform/src/components/shared/sidebar/index.tsx -+++ b/apps/platform/src/components/shared/sidebar/index.tsx -@@ -6,8 +6,8 @@ import { - SettingsSVG, - TeamSVG - } from '@public/svg/shared' --import { Combobox } from '@/components/ui/combobox' - import SidebarTab from './sidebarTab' -+import { Combobox } from '@/components/ui/combobox' -  - function Sidebar(): JSX.Element { - const sidebarTabData = [ -diff --git a/apps/platform/src/components/ui/combobox.tsx b/apps/platform/src/components/ui/combobox.tsx -index b3107dc..8414098 100644 ---- a/apps/platform/src/components/ui/combobox.tsx -+++ b/apps/platform/src/components/ui/combobox.tsx -@@ -5,27 +5,6 @@ import { useEffect, useState } from 'react' - import { useRouter } from 'next/navigation' - import { toast } from 'sonner' - import { AddSVG } from '@public/svg/shared' --import { cn } from '@/lib/utils' --import { -- Command, -- CommandEmpty, -- CommandInput, -- CommandItem, -- CommandList --} from '@/components/ui/command' --import { -- Popover, -- PopoverContent, -- PopoverTrigger --} from '@/components/ui/popover' --import { apiClient } from '@/lib/api-client' --// import type { Workspace } from '@/types' --import { zWorkspace } from '@/types' --import { -- getCurrentWorkspace, -- setCurrentWorkspace, -- setWorkspace --} from '@/lib/workspace-storage' - import type { Workspace } from '@keyshade/schema' - import { Input } from './input' - import { Label } from './label' -@@ -38,39 +17,43 @@ import { - DialogTrigger - } from './dialog' - import { Button } from './button' --import { WorkspaceSchema } from '@keyshade/schema/schemas' -- --interface WorkspaceResponse { -- items: Workspace[] -- metadata: { -- page: number -- perPage: number -- pageCount: number -- totalCount: number -- links: { -- self: string -- first: string -- previous: string | null -- next: string | null -- last: string -- } -- } --} -+import { -+ getCurrentWorkspace, -+ setCurrentWorkspace, -+ setWorkspace -+} from '@/lib/workspace-storage' -+import { cn } from '@/lib/utils' -+import { -+ Popover, -+ PopoverContent, -+ PopoverTrigger -+} from '@/components/ui/popover' -+import { -+ Command, -+ CommandEmpty, -+ CommandInput, -+ CommandItem, -+ CommandList -+} from '@/components/ui/command' -+import ControllerInstance from '@/lib/controller-instance' -  - async function getAllWorkspace(): Promise { - try { -- const workspaceData: WorkspaceResponse = -- await apiClient.get('/workspace') -+ const { data, success, error } = -+ await ControllerInstance.getInstance().workspaceController.getWorkspacesOfUser( -+ {}, -+ {} -+ ) -  -- // TODO: We are getting error here from the success flag, need to see this again -- // const { success, data } = WorkspaceSchema.array().safeParse(workspaceData.items) -- // if (!success) { -- // throw new Error('Invalid data') -- // } -+ if (error) { -+ // eslint-disable-next-line no-console -- we need to log the error -+ console.error(error) -+ return undefined -+ } -  -- return workspaceData.items -- // return data -- // return workspaceData; -+ if (success && data) { -+ return data.items -+ } - } catch (error) { - // eslint-disable-next-line no-console -- we need to log the error - console.error(error) -@@ -92,9 +75,13 @@ export function Combobox(): React.JSX.Element { - } - setIsNameEmpty(false) - try { -- const response = await apiClient.post('/workspace', { -- name -- }) -+ const response = -+ await ControllerInstance.getInstance().workspaceController.createWorkspace( -+ { -+ name -+ }, -+ {} -+ ) - setCurrentWorkspace(response) - setOpen(false) - } catch (error) { -diff --git a/apps/platform/src/lib/controller-instance.ts b/apps/platform/src/lib/controller-instance.ts -index fe432b7..a766038 100644 ---- a/apps/platform/src/lib/controller-instance.ts -+++ b/apps/platform/src/lib/controller-instance.ts -@@ -1,30 +1,90 @@ --import { AuthController } from '@keyshade/api-client' -+import { -+ AuthController, -+ EnvironmentController, -+ ProjectController, -+ SecretController, -+ UserController, -+ VariableController, -+ WorkspaceController, -+ WorkspaceMembershipController, -+ WorkspaceRoleController -+} from '@keyshade/api-client' -  - export default class ControllerInstance { - private static instance: ControllerInstance | null -  -- private _authController: AuthController | null = null -+ private _authController: AuthController -+ private _userController: UserController -+ private _workspaceController: WorkspaceController -+ private _workspaceMembershipController: WorkspaceMembershipController -+ private _workspaceRoleController: WorkspaceRoleController -+ private _projectController: ProjectController -+ private _environmentController: EnvironmentController -+ private _secretController: SecretController -+ private _variableController: VariableController -  - get authController(): AuthController { -- if (!this._authController) { -- throw new Error('ControllerInstance not initialized') -- } - return this._authController - } -  -- static initialize(baseUrl: string): void { -- if (!ControllerInstance.instance) { -- const instance = new ControllerInstance() -+ get workspaceController(): WorkspaceController { -+ return this._workspaceController -+ } -+ -+ get workspaceMembershipController(): WorkspaceMembershipController { -+ return this._workspaceMembershipController -+ } -  -- instance._authController = new AuthController(baseUrl) -+ get workspaceRoleController(): WorkspaceRoleController { -+ return this._workspaceRoleController -+ } -  -- ControllerInstance.instance = instance -- } -+ get projectController(): ProjectController { -+ return this._projectController -+ } -+ -+ get environmentController(): EnvironmentController { -+ return this._environmentController -+ } -+ -+ get secretController(): SecretController { -+ return this._secretController -+ } -+ -+ get variableController(): VariableController { -+ return this._variableController -+ } -+ -+ get userController(): UserController { -+ return this._userController - } -  - static getInstance(): ControllerInstance { - if (!ControllerInstance.instance) { -- throw new Error('ControllerInstance not initialized') -+ ControllerInstance.instance = new ControllerInstance() -+ ControllerInstance.instance._authController = new AuthController( -+ process.env.NEXT_PUBLIC_BACKEND_URL -+ ) -+ ControllerInstance.instance._userController = new UserController( -+ process.env.NEXT_PUBLIC_BACKEND_URL -+ ) -+ ControllerInstance.instance._workspaceController = -+ new WorkspaceController(process.env.NEXT_PUBLIC_BACKEND_URL) -+ ControllerInstance.instance._workspaceMembershipController = -+ new WorkspaceMembershipController(process.env.NEXT_PUBLIC_BACKEND_URL) -+ ControllerInstance.instance._workspaceRoleController = -+ new WorkspaceRoleController(process.env.NEXT_PUBLIC_BACKEND_URL) -+ ControllerInstance.instance._projectController = new ProjectController( -+ process.env.NEXT_PUBLIC_BACKEND_URL -+ ) -+ ControllerInstance.instance._environmentController = -+ new EnvironmentController(process.env.NEXT_PUBLIC_BACKEND_URL) -+ ControllerInstance.instance._secretController = new SecretController( -+ process.env.NEXT_PUBLIC_BACKEND_URL -+ ) -+ ControllerInstance.instance._variableController = new VariableController( -+ process.env.NEXT_PUBLIC_BACKEND_URL -+ ) - } - return ControllerInstance.instance - } -diff --git a/packages/api-client/src/index.ts b/packages/api-client/src/index.ts -index 79ab5f6..edd344f 100644 ---- a/packages/api-client/src/index.ts -+++ b/packages/api-client/src/index.ts -@@ -8,6 +8,7 @@ import WorkspaceController from '@api-client/controllers/workspace' - import WorkspaceRoleController from '@api-client/controllers/workspace-role' - import WorkspaceMembershipController from '@api-client/controllers/workspace-membership' - import AuthController from '@api-client/controllers/auth' -+import UserController from '@api-client/controllers/user' - export { - EnvironmentController, - SecretController, -@@ -18,5 +19,6 @@ export { - WorkspaceController, - WorkspaceRoleController, - WorkspaceMembershipController, -- AuthController -+ AuthController, -+ UserController - } -diff --git a/packages/schema/src/secret/index.ts b/packages/schema/src/secret/index.ts -index ff5b499..4277679 100644 ---- a/packages/schema/src/secret/index.ts -+++ b/packages/schema/src/secret/index.ts -@@ -100,15 +100,17 @@ export const GetAllSecretsOfProjectResponseSchema = PageResponseSchema( - name: z.string() - }) - }), -- values: z.object({ -- environment: z.object({ -- id: z.string(), -- name: z.string(), -- slug: z.string() -- }), -- value: z.string(), -- version: z.number() -- }) -+ values: z.array( -+ z.object({ -+ environment: z.object({ -+ id: z.string(), -+ name: z.string(), -+ slug: z.string() -+ }), -+ value: z.string(), -+ version: z.number() -+ }) -+ ) - }) - ) -  From 73d0888d9aea2633541e515e151f0411c685a26a Mon Sep 17 00:00:00 2001 From: Sameer Poswal <106386145+poswalsameer@users.noreply.github.com> Date: Fri, 20 Dec 2024 00:35:40 +0530 Subject: [PATCH 10/19] Delete erDetails(userData) --- erDetails(userData) | 664 -------------------------------------------- 1 file changed, 664 deletions(-) delete mode 100644 erDetails(userData) diff --git a/erDetails(userData) b/erDetails(userData) deleted file mode 100644 index 95c798b9..00000000 --- a/erDetails(userData) +++ /dev/null @@ -1,664 +0,0 @@ -diff --git a/apps/platform/.eslintrc.cjs b/apps/platform/.eslintrc.cjs -index 28a8b27..fa9233a 100644 ---- a/apps/platform/.eslintrc.cjs -+++ b/apps/platform/.eslintrc.cjs -@@ -7,24 +7,13 @@ module.exports = { - }, - rules: { - 'import/no-extraneous-dependencies': 0, -- '@typescript-eslint/interface-name-prefix': 'off', - '@typescript-eslint/explicit-function-return-type': 'off', - '@typescript-eslint/explicit-module-boundary-types': 'off', -- '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/no-unused-vars': ['warn'], -- '@typescript-eslint/no-unsafe-call': 'off', -- '@typescript-eslint/no-unsafe-return': 'off', - '@typescript-eslint/space-before-function-paren': 'off', - '@typescript-eslint/strict-boolean-expressions': 'off', -- '@typescript-eslint/prefer-nullish-coalescing': 'off', -- 'space-before-function-paren': 'off', -- '@typescript-eslint/member-delimiter-style': 'off', - '@typescript-eslint/no-confusing-void-expression': 'off', - '@typescript-eslint/no-floating-promises': 'off', -- '@typescript-eslint/no-misused-promises': 'off', -- '@typescript-eslint/no-unsafe-assignment': 'off', -- '@typescript-eslint/no-unsafe-member-access': 'off', -- '@typescript-eslint/no-unsafe-argument': 'off', -- '@typescript-eslint/no-unnecessary-condition': 'off' -+ '@typescript-eslint/no-misused-promises': 'off' - } - } -diff --git a/apps/platform/src/app/(main)/page.tsx b/apps/platform/src/app/(main)/page.tsx -index fe8fa2a..9a9cb2a 100644 ---- a/apps/platform/src/app/(main)/page.tsx -+++ b/apps/platform/src/app/(main)/page.tsx -@@ -6,7 +6,6 @@ import type { - ProjectWithCount, - Workspace - } from '@keyshade/schema' --import { ProjectController } from '@keyshade/api-client' - import { AddSVG } from '@public/svg/shared' - import { FolderSVG } from '@public/svg/dashboard' - import ProjectCard from '@/components/dashboard/projectCard' -@@ -38,6 +37,7 @@ import { - DialogHeader, - DialogTrigger - } from '@/components/ui/dialog' -+import ControllerInstance from '@/lib/controller-instance' -  - export default function Index(): JSX.Element { - const [isSheetOpen, setIsSheetOpen] = useState(false) -@@ -76,16 +76,13 @@ export default function Index(): JSX.Element { - // If a workspace is selected, we want to fetch all the projects - // under that workspace and display it in the dashboard. - useEffect(() => { -- const projectController = new ProjectController( -- process.env.NEXT_PUBLIC_BACKEND_URL -- ) -- - async function getAllProjects() { - if (currentWorkspace) { -- const { success, error, data } = await projectController.getAllProjects( -- { workspaceSlug: currentWorkspace.slug }, -- {} -- ) -+ const { success, error, data } = -+ await ControllerInstance.getInstance().projectController.getAllProjects( -+ { workspaceSlug: currentWorkspace.slug }, -+ {} -+ ) -  - if (success && data) { - setProjects(data.items) -@@ -105,16 +102,13 @@ export default function Index(): JSX.Element { - // Function to create a new project - const createNewProject = useCallback(async () => { - if (currentWorkspace) { -- const projectController = new ProjectController( -- process.env.NEXT_PUBLIC_BACKEND_URL -- ) -- - newProjectData.workspaceSlug = currentWorkspace.slug -  -- const { data, error, success } = await projectController.createProject( -- newProjectData, -- {} -- ) -+ const { data, error, success } = -+ await ControllerInstance.getInstance().projectController.createProject( -+ newProjectData, -+ {} -+ ) -  - if (success && data) { - setProjects([ -diff --git a/apps/platform/src/app/(main)/project/[project]/@secret/page.tsx b/apps/platform/src/app/(main)/project/[project]/@secret/page.tsx -index 18134ab..e1bd8e6 100644 ---- a/apps/platform/src/app/(main)/project/[project]/@secret/page.tsx -+++ b/apps/platform/src/app/(main)/project/[project]/@secret/page.tsx -@@ -5,6 +5,7 @@ import { usePathname } from 'next/navigation' - import dayjs, { extend } from 'dayjs' - import relativeTime from 'dayjs/plugin/relativeTime' - import { NoteIconSVG } from '@public/svg/secret' -+import type { GetAllSecretsOfProjectResponse } from '@keyshade/schema' - import { - Accordion, - AccordionContent, -@@ -19,8 +20,6 @@ import { - TableHeader, - TableRow - } from '@/components/ui/table' --import type { Secret } from '@keyshade/schema' --import { SecretController } from '@keyshade/api-client' - import { ScrollArea } from '@/components/ui/scroll-area' - import { - Tooltip, -@@ -29,31 +28,28 @@ import { - TooltipTrigger - } from '@/components/ui/tooltip' - import { Skeleton } from '@/components/ui/skeleton' -+import ControllerInstance from '@/lib/controller-instance' -  - extend(relativeTime) -  - function SecretPage(): React.JSX.Element { -- const [allSecrets, setAllSecrets] = useState() -+ const [allSecrets, setAllSecrets] = -+ useState() - const [isLoading, setIsLoading] = useState(true) - const pathname = usePathname() -  - useEffect(() => { - setIsLoading(true) -  -- const secretController = new SecretController( -- process.env.NEXT_PUBLIC_BACKEND_URL -- ) -- - async function getAllSecretsByProjectSlug() { - const { success, error, data } = -- await secretController.getAllSecretsOfProject( -+ await ControllerInstance.getInstance().secretController.getAllSecretsOfProject( - { projectSlug: pathname.split('/')[2] }, - {} - ) -  - if (success && data) { -- //@ts-ignore -- setAllSecrets(data) -+ setAllSecrets(data.items) - } else { - // eslint-disable-next-line no-console -- we need to log the error - console.error(error) -@@ -68,9 +64,9 @@ function SecretPage(): React.JSX.Element { - if (isLoading) { - return ( -
 --  --  --  -+  -+  -+  -
 - ) - } -@@ -82,7 +78,7 @@ function SecretPage(): React.JSX.Element { - collapsible - type="single" - > -- {allSecrets?.map((secret) => { -+ {allSecrets?.map(({ secret, values }) => { - return ( -  -  -  -- {secret.versions.map((value) => { -+ {values.map((value) => { - return ( -  - {value.environment.slug} -@@ -147,7 +143,7 @@ function SecretPage(): React.JSX.Element { - ) - } -  --function SerectLoader(): React.JSX.Element { -+function SecretLoader(): React.JSX.Element { - return ( -
 -
 -diff --git a/apps/platform/src/app/(main)/project/[project]/layout.tsx b/apps/platform/src/app/(main)/project/[project]/layout.tsx -index 1eb511b..9edfb0b 100644 ---- a/apps/platform/src/app/(main)/project/[project]/layout.tsx -+++ b/apps/platform/src/app/(main)/project/[project]/layout.tsx -@@ -2,6 +2,7 @@ - import { useEffect, useState } from 'react' - import { useSearchParams } from 'next/navigation' - import { AddSVG } from '@public/svg/shared' -+import type { Project } from '@keyshade/schema' - import { Button } from '@/components/ui/button' - import { - Dialog, -@@ -13,8 +14,7 @@ import { - } from '@/components/ui/dialog' - import { Input } from '@/components/ui/input' - import { Label } from '@/components/ui/label' --import { ProjectController } from '@keyshade/api-client' --import type { Project } from '@keyshade/schema' -+import ControllerInstance from '@/lib/controller-instance' -  - interface DetailedProjectPageProps { - params: { project: string } -@@ -38,29 +38,22 @@ function DetailedProjectPage({ - const tab = searchParams.get('tab') ?? 'rollup-details' -  - useEffect(() => { -+ async function getProjectBySlug() { -+ const { success, error, data } = -+ await ControllerInstance.getInstance().projectController.getProject( -+ { projectSlug: params.project }, -+ {} -+ ) -  -- const projectController = new ProjectController( -- process.env.NEXT_PUBLIC_BACKEND_URL -- ) -- -- async function getProjectBySlug(){ -- const {success, error, data} = await projectController.getProject( -- {projectSlug: params.project}, -- {} -- ) -- -- if( success && data ){ -- //@ts-ignore -+ if (success && data) { - setCurrentProject(data) -- } -- else{ -+ } else { - // eslint-disable-next-line no-console -- we need to log the error - console.error(error) - } - } -  - getProjectBySlug() -- - }, [params.project]) -  - return ( -diff --git a/apps/platform/src/app/(main)/settings/@profile/page.tsx b/apps/platform/src/app/(main)/settings/@profile/page.tsx -index 21de60b..4559ec6 100644 ---- a/apps/platform/src/app/(main)/settings/@profile/page.tsx -+++ b/apps/platform/src/app/(main)/settings/@profile/page.tsx -@@ -1,54 +1,52 @@ - 'use client' --import React, { useEffect, useState } from 'react' -+import React, { useCallback, useEffect, useState } from 'react' - import { toast } from 'sonner' --import type { User } from '@keyshade/schema' - import InputLoading from './loading' - import { Input } from '@/components/ui/input' - import { Separator } from '@/components/ui/separator' -+import ControllerInstance from '@/lib/controller-instance' - import { Button } from '@/components/ui/button' --import { apiClient } from '@/lib/api-client' -- --type UserData = Omit< -- User, -- 'id' | 'isActive' | 'isOnboardingFinished' | 'isAdmin' | 'authProvider' --> --async function getUserDetails(): Promise { -- try { -- return await apiClient.get('/user') -- } catch (error) { -- // eslint-disable-next-line no-console -- we need to log the error -- console.error(error) -- } --} -- --async function updateUserDetails(userData: UserData): Promise { -- try { -- await apiClient.put('/user', userData) -- } catch (error) { -- // eslint-disable-next-line no-console -- we need to log the error -- console.error(error) -- } --} -  - function ProfilePage(): React.JSX.Element { - const [isLoading, setIsLoading] = useState(true) -- const [userData, setUserData] = useState({ -+ const [userData, setUserData] = useState({ - email: '', - name: '', - profilePictureUrl: '' - }) - const [isModified, setIsModified] = useState(false) -  -+ const updateSelf = useCallback(async () => { -+ try { -+ await ControllerInstance.getInstance().userController.updateSelf( -+ { -+ name: userData.name, -+ email: userData.email -+ }, -+ {} -+ ) -+ toast.success('Profile updated successfully') -+ } catch (error) { -+ // eslint-disable-next-line no-console -- we need to log the error -+ console.error(error) -+ } -+ setIsModified(false) -+ }, [userData]) -+ - useEffect(() => { -- getUserDetails() -- .then((data) => { -- if (data) { -+ ControllerInstance.getInstance() -+ .userController.getSelf() -+ .then(({ data, success, error }) => { -+ if (success && data) { - setUserData({ - email: data.email, -- name: data.name ?? '', -- profilePictureUrl: data.profilePictureUrl -+ name: data.name, -+ profilePictureUrl: data.profilePictureUrl || '' - }) - setIsLoading(false) -+ } else { -+ // eslint-disable-next-line no-console -- we need to log the error -+ console.error(error) - } - }) - .catch((error) => { -@@ -67,7 +65,7 @@ function ProfilePage(): React.JSX.Element { - Upload a picture to change your avatar across Keyshade. -  -
 --
{' '} -+
 - {/* //! This is will be replaced by an image tag */} -
 - {/* Name */} -@@ -114,20 +112,7 @@ function ProfilePage(): React.JSX.Element { - )} -
 -
 -- { -- updateUserDetails(userData) -- .then(() => { -- toast.success('User details updated successfully') -- }) -- .catch(() => { -- toast.error('Failed to update user details') -- }) -- setIsModified(false) -- }} -- variant="secondary" -- > -+  -
 -diff --git a/apps/platform/src/components/shared/sidebar/index.tsx b/apps/platform/src/components/shared/sidebar/index.tsx -index ee7cd9b..7da768b 100644 ---- a/apps/platform/src/components/shared/sidebar/index.tsx -+++ b/apps/platform/src/components/shared/sidebar/index.tsx -@@ -6,8 +6,8 @@ import { - SettingsSVG, - TeamSVG - } from '@public/svg/shared' --import { Combobox } from '@/components/ui/combobox' - import SidebarTab from './sidebarTab' -+import { Combobox } from '@/components/ui/combobox' -  - function Sidebar(): JSX.Element { - const sidebarTabData = [ -diff --git a/apps/platform/src/components/ui/combobox.tsx b/apps/platform/src/components/ui/combobox.tsx -index b3107dc..8414098 100644 ---- a/apps/platform/src/components/ui/combobox.tsx -+++ b/apps/platform/src/components/ui/combobox.tsx -@@ -5,27 +5,6 @@ import { useEffect, useState } from 'react' - import { useRouter } from 'next/navigation' - import { toast } from 'sonner' - import { AddSVG } from '@public/svg/shared' --import { cn } from '@/lib/utils' --import { -- Command, -- CommandEmpty, -- CommandInput, -- CommandItem, -- CommandList --} from '@/components/ui/command' --import { -- Popover, -- PopoverContent, -- PopoverTrigger --} from '@/components/ui/popover' --import { apiClient } from '@/lib/api-client' --// import type { Workspace } from '@/types' --import { zWorkspace } from '@/types' --import { -- getCurrentWorkspace, -- setCurrentWorkspace, -- setWorkspace --} from '@/lib/workspace-storage' - import type { Workspace } from '@keyshade/schema' - import { Input } from './input' - import { Label } from './label' -@@ -38,39 +17,43 @@ import { - DialogTrigger - } from './dialog' - import { Button } from './button' --import { WorkspaceSchema } from '@keyshade/schema/schemas' -- --interface WorkspaceResponse { -- items: Workspace[] -- metadata: { -- page: number -- perPage: number -- pageCount: number -- totalCount: number -- links: { -- self: string -- first: string -- previous: string | null -- next: string | null -- last: string -- } -- } --} -+import { -+ getCurrentWorkspace, -+ setCurrentWorkspace, -+ setWorkspace -+} from '@/lib/workspace-storage' -+import { cn } from '@/lib/utils' -+import { -+ Popover, -+ PopoverContent, -+ PopoverTrigger -+} from '@/components/ui/popover' -+import { -+ Command, -+ CommandEmpty, -+ CommandInput, -+ CommandItem, -+ CommandList -+} from '@/components/ui/command' -+import ControllerInstance from '@/lib/controller-instance' -  - async function getAllWorkspace(): Promise { - try { -- const workspaceData: WorkspaceResponse = -- await apiClient.get('/workspace') -+ const { data, success, error } = -+ await ControllerInstance.getInstance().workspaceController.getWorkspacesOfUser( -+ {}, -+ {} -+ ) -  -- // TODO: We are getting error here from the success flag, need to see this again -- // const { success, data } = WorkspaceSchema.array().safeParse(workspaceData.items) -- // if (!success) { -- // throw new Error('Invalid data') -- // } -+ if (error) { -+ // eslint-disable-next-line no-console -- we need to log the error -+ console.error(error) -+ return undefined -+ } -  -- return workspaceData.items -- // return data -- // return workspaceData; -+ if (success && data) { -+ return data.items -+ } - } catch (error) { - // eslint-disable-next-line no-console -- we need to log the error - console.error(error) -@@ -92,9 +75,13 @@ export function Combobox(): React.JSX.Element { - } - setIsNameEmpty(false) - try { -- const response = await apiClient.post('/workspace', { -- name -- }) -+ const response = -+ await ControllerInstance.getInstance().workspaceController.createWorkspace( -+ { -+ name -+ }, -+ {} -+ ) - setCurrentWorkspace(response) - setOpen(false) - } catch (error) { -diff --git a/apps/platform/src/lib/controller-instance.ts b/apps/platform/src/lib/controller-instance.ts -index fe432b7..a766038 100644 ---- a/apps/platform/src/lib/controller-instance.ts -+++ b/apps/platform/src/lib/controller-instance.ts -@@ -1,30 +1,90 @@ --import { AuthController } from '@keyshade/api-client' -+import { -+ AuthController, -+ EnvironmentController, -+ ProjectController, -+ SecretController, -+ UserController, -+ VariableController, -+ WorkspaceController, -+ WorkspaceMembershipController, -+ WorkspaceRoleController -+} from '@keyshade/api-client' -  - export default class ControllerInstance { - private static instance: ControllerInstance | null -  -- private _authController: AuthController | null = null -+ private _authController: AuthController -+ private _userController: UserController -+ private _workspaceController: WorkspaceController -+ private _workspaceMembershipController: WorkspaceMembershipController -+ private _workspaceRoleController: WorkspaceRoleController -+ private _projectController: ProjectController -+ private _environmentController: EnvironmentController -+ private _secretController: SecretController -+ private _variableController: VariableController -  - get authController(): AuthController { -- if (!this._authController) { -- throw new Error('ControllerInstance not initialized') -- } - return this._authController - } -  -- static initialize(baseUrl: string): void { -- if (!ControllerInstance.instance) { -- const instance = new ControllerInstance() -+ get workspaceController(): WorkspaceController { -+ return this._workspaceController -+ } -+ -+ get workspaceMembershipController(): WorkspaceMembershipController { -+ return this._workspaceMembershipController -+ } -  -- instance._authController = new AuthController(baseUrl) -+ get workspaceRoleController(): WorkspaceRoleController { -+ return this._workspaceRoleController -+ } -  -- ControllerInstance.instance = instance -- } -+ get projectController(): ProjectController { -+ return this._projectController -+ } -+ -+ get environmentController(): EnvironmentController { -+ return this._environmentController -+ } -+ -+ get secretController(): SecretController { -+ return this._secretController -+ } -+ -+ get variableController(): VariableController { -+ return this._variableController -+ } -+ -+ get userController(): UserController { -+ return this._userController - } -  - static getInstance(): ControllerInstance { - if (!ControllerInstance.instance) { -- throw new Error('ControllerInstance not initialized') -+ ControllerInstance.instance = new ControllerInstance() -+ ControllerInstance.instance._authController = new AuthController( -+ process.env.NEXT_PUBLIC_BACKEND_URL -+ ) -+ ControllerInstance.instance._userController = new UserController( -+ process.env.NEXT_PUBLIC_BACKEND_URL -+ ) -+ ControllerInstance.instance._workspaceController = -+ new WorkspaceController(process.env.NEXT_PUBLIC_BACKEND_URL) -+ ControllerInstance.instance._workspaceMembershipController = -+ new WorkspaceMembershipController(process.env.NEXT_PUBLIC_BACKEND_URL) -+ ControllerInstance.instance._workspaceRoleController = -+ new WorkspaceRoleController(process.env.NEXT_PUBLIC_BACKEND_URL) -+ ControllerInstance.instance._projectController = new ProjectController( -+ process.env.NEXT_PUBLIC_BACKEND_URL -+ ) -+ ControllerInstance.instance._environmentController = -+ new EnvironmentController(process.env.NEXT_PUBLIC_BACKEND_URL) -+ ControllerInstance.instance._secretController = new SecretController( -+ process.env.NEXT_PUBLIC_BACKEND_URL -+ ) -+ ControllerInstance.instance._variableController = new VariableController( -+ process.env.NEXT_PUBLIC_BACKEND_URL -+ ) - } - return ControllerInstance.instance - } -diff --git a/packages/api-client/src/index.ts b/packages/api-client/src/index.ts -index 79ab5f6..edd344f 100644 ---- a/packages/api-client/src/index.ts -+++ b/packages/api-client/src/index.ts -@@ -8,6 +8,7 @@ import WorkspaceController from '@api-client/controllers/workspace' - import WorkspaceRoleController from '@api-client/controllers/workspace-role' - import WorkspaceMembershipController from '@api-client/controllers/workspace-membership' - import AuthController from '@api-client/controllers/auth' -+import UserController from '@api-client/controllers/user' - export { - EnvironmentController, - SecretController, -@@ -18,5 +19,6 @@ export { - WorkspaceController, - WorkspaceRoleController, - WorkspaceMembershipController, -- AuthController -+ AuthController, -+ UserController - } -diff --git a/packages/schema/src/secret/index.ts b/packages/schema/src/secret/index.ts -index ff5b499..4277679 100644 ---- a/packages/schema/src/secret/index.ts -+++ b/packages/schema/src/secret/index.ts -@@ -100,15 +100,17 @@ export const GetAllSecretsOfProjectResponseSchema = PageResponseSchema( - name: z.string() - }) - }), -- values: z.object({ -- environment: z.object({ -- id: z.string(), -- name: z.string(), -- slug: z.string() -- }), -- value: z.string(), -- version: z.number() -- }) -+ values: z.array( -+ z.object({ -+ environment: z.object({ -+ id: z.string(), -+ name: z.string(), -+ slug: z.string() -+ }), -+ value: z.string(), -+ version: z.number() -+ }) -+ ) - }) - ) -  From 4c4e96e2ca31307ea3a79f58b5d81a1fc74c072a Mon Sep 17 00:00:00 2001 From: poswalsameer Date: Fri, 20 Dec 2024 23:14:29 +0530 Subject: [PATCH 11/19] refactored the code for adding new variables to a project --- .../app/(main)/project/[project]/layout.tsx | 295 +++++++++++++++--- .../dashboard/projectCard/index.tsx | 3 +- 2 files changed, 249 insertions(+), 49 deletions(-) diff --git a/apps/platform/src/app/(main)/project/[project]/layout.tsx b/apps/platform/src/app/(main)/project/[project]/layout.tsx index 9edfb0bb..bdd48eef 100644 --- a/apps/platform/src/app/(main)/project/[project]/layout.tsx +++ b/apps/platform/src/app/(main)/project/[project]/layout.tsx @@ -2,7 +2,13 @@ import { useEffect, useState } from 'react' import { useSearchParams } from 'next/navigation' import { AddSVG } from '@public/svg/shared' -import type { Project } from '@keyshade/schema' +import type { + ClientResponse, + CreateVariableRequest, + Environment, + GetAllEnvironmentsOfProjectResponse, + Project +} from '@keyshade/schema' import { Button } from '@/components/ui/button' import { Dialog, @@ -15,6 +21,13 @@ import { import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import ControllerInstance from '@/lib/controller-instance' +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue +} from '@/components/ui/select' interface DetailedProjectPageProps { params: { project: string } @@ -31,12 +44,49 @@ function DetailedProjectPage({ const [key, setKey] = useState('') // eslint-disable-next-line @typescript-eslint/no-unused-vars -- will be used later const [value, setValue] = useState('') - const [currentProject, setCurrentProject] = useState() + const [isOpen, setIsOpen] = useState(false) + const [newVariableData, setNewVariableData] = useState({ + variableName: '', + note: '', + environmentName: '', + environmentValue: '' + }) + const [availableEnvironments, setAvailableEnvironments] = useState([]) const searchParams = useSearchParams() const tab = searchParams.get('tab') ?? 'rollup-details' + const addVariable = async (e: any) => { + e.preventDefault() + + const request: CreateVariableRequest = { + name: newVariableData.variableName, + projectSlug: currentProject?.slug as string, + entries: newVariableData.environmentValue + ? [ + { + value: newVariableData.environmentValue, + environmentSlug: newVariableData.environmentName + } + ] + : undefined, + note: newVariableData.note + } + + const { success, error, data } = await ControllerInstance.getInstance().variableController.createVariable( + request, + {} + ) + + if(error){ + // eslint-disable-next-line no-console -- we need to log the error + console.error(error) + } + + setIsOpen(false) + } + useEffect(() => { async function getProjectBySlug() { const { success, error, data } = @@ -46,6 +96,7 @@ function DetailedProjectPage({ ) if (success && data) { + //@ts-ignore setCurrentProject(data) } else { // eslint-disable-next-line no-console -- we need to log the error @@ -56,60 +107,208 @@ function DetailedProjectPage({ getProjectBySlug() }, [params.project]) + useEffect(() => { + const getAllEnvironments = async () => { + const { success, error, data }: ClientResponse = await ControllerInstance.getInstance().environmentController.getAllEnvironmentsOfProject( + { projectSlug: currentProject!.slug }, + {} + ) + + if (success && data) { + setAvailableEnvironments(data.items) + } else { + // eslint-disable-next-line no-console -- we need to log the error + console.error(error) + } + } + + getAllEnvironments() + }, [currentProject]) + return (
{currentProject?.name}
- - - - - - - Add a new secret - - Add a new secret to the project. This secret will be encrypted - and stored securely. - - -
-
-
- - { - setKey(e.target.value) - }} - placeholder="Enter the name of the secret" - /> + {tab === 'secret' && ( + + + + + + + Add a new secret + + Add a new secret to the project. This secret will be encrypted + and stored securely. + + +
+
+
+ + { + setKey(e.target.value) + }} + placeholder="Enter the name of the secret" + /> +
+
+ + { + setValue(e.target.value) + }} + placeholder="Enter the value of the secret" + /> +
-
- - { - setValue(e.target.value) - }} - placeholder="Enter the value of the secret" - /> +
+
-
- + +
+ )} + {tab === 'variable' && ( + + + + + + + +
+

+ Add a new variable +

+

+ Add a new variable to the project +

+
+
+
+ +
+
+
+ + + setNewVariableData({ + ...newVariableData, + variableName: e.target.value + }) + } + className="h-[2.75rem] w-[20rem] border-0 bg-[#2a2a2a] text-gray-300 placeholder:text-gray-500" + /> +
+ +
+ + + setNewVariableData({ + ...newVariableData, + note: e.target.value + }) + } + className="h-[2.75rem] w-[20rem] border-0 bg-[#2a2a2a] text-gray-300 placeholder:text-gray-500" + /> +
+ +
+
+ + +
+ +
+ + + setNewVariableData({ + ...newVariableData, + environmentValue: e.target.value + }) + } + className="h-[2.75rem] w-[13.5rem] border-0 bg-[#2a2a2a] text-gray-300 placeholder:text-gray-500" + /> +
+
+ +
+ +
+
-
- -
+ + + )}
{tab === 'secret' && secret} diff --git a/apps/platform/src/components/dashboard/projectCard/index.tsx b/apps/platform/src/components/dashboard/projectCard/index.tsx index 4cd0ad52..1818c51f 100644 --- a/apps/platform/src/components/dashboard/projectCard/index.tsx +++ b/apps/platform/src/components/dashboard/projectCard/index.tsx @@ -25,6 +25,7 @@ function ProjectCard({ }: ProjectCardProps): JSX.Element { const { id, + slug, name, description, environmentCount, @@ -69,7 +70,7 @@ function ProjectCard({
From 5a01926d890aba8811f400426697df44f6bb346b Mon Sep 17 00:00:00 2001 From: kriptonian1 Date: Sat, 21 Dec 2024 16:58:19 +0530 Subject: [PATCH 12/19] fix: conditionally render project creation button based on project state --- apps/platform/src/app/(main)/page.tsx | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/apps/platform/src/app/(main)/page.tsx b/apps/platform/src/app/(main)/page.tsx index 9a9cb2ac..62c9c92c 100644 --- a/apps/platform/src/app/(main)/page.tsx +++ b/apps/platform/src/app/(main)/page.tsx @@ -144,10 +144,12 @@ export default function Index(): JSX.Element { - + {isProjectEmpty ? null : ( + + )}
@@ -334,7 +336,9 @@ export default function Index(): JSX.Element {
Create a file and start setting up your environment and secret keys
- +
)} From 64c0d84b59086e079c1f60c62baaf1db5be6acbf Mon Sep 17 00:00:00 2001 From: kriptonian1 Date: Sat, 21 Dec 2024 17:49:03 +0530 Subject: [PATCH 13/19] fix: cast response as ClientResponse type for better type safety --- packages/api-client/src/core/response-parser.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/api-client/src/core/response-parser.ts b/packages/api-client/src/core/response-parser.ts index 0d616d7d..866beb33 100644 --- a/packages/api-client/src/core/response-parser.ts +++ b/packages/api-client/src/core/response-parser.ts @@ -17,7 +17,7 @@ export async function parseResponse( success: false, data: null, error - } as ClientResponse + } as unknown as ClientResponse } let data: any = null @@ -30,5 +30,5 @@ export async function parseResponse( success: true, data, error: null - } as ClientResponse + } as unknown as ClientResponse } From e79093414bc3496dc2b06a2b33340ee9a8b96d4d Mon Sep 17 00:00:00 2001 From: kriptonian1 Date: Sat, 21 Dec 2024 18:21:12 +0530 Subject: [PATCH 14/19] feat: add reusable Textarea component with customizable styles --- apps/platform/src/components/ui/textarea.tsx | 22 ++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 apps/platform/src/components/ui/textarea.tsx diff --git a/apps/platform/src/components/ui/textarea.tsx b/apps/platform/src/components/ui/textarea.tsx new file mode 100644 index 00000000..2a10e86f --- /dev/null +++ b/apps/platform/src/components/ui/textarea.tsx @@ -0,0 +1,22 @@ +import * as React from 'react' + +import { cn } from '@/lib/utils' + +const Textarea = React.forwardRef< + HTMLTextAreaElement, + React.ComponentProps<'textarea'> +>(({ className, ...props }, ref) => { + return ( +