mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
Merge branch 'main' of https://github.com/Infisical/infisical into fix/folder-sorting
This commit is contained in:
@@ -1,17 +1,16 @@
|
||||
import z from "zod";
|
||||
|
||||
import { readLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||
import {
|
||||
CreateChefConnectionSchema,
|
||||
SanitizedChefConnectionSchema,
|
||||
UpdateChefConnectionSchema
|
||||
} from "@app/services/app-connection/chef";
|
||||
} from "@app/ee/services/app-connections/chef";
|
||||
import { readLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { registerAppConnectionEndpoints } from "@app/server/routes/v1/app-connection-routers/app-connection-endpoints";
|
||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
import { registerAppConnectionEndpoints } from "./app-connection-endpoints";
|
||||
|
||||
export const registerChefConnectionRouter = async (server: FastifyZodProvider) => {
|
||||
registerAppConnectionEndpoints({
|
||||
app: AppConnection.Chef,
|
||||
@@ -1,8 +1,7 @@
|
||||
import { ChefSyncSchema, CreateChefSyncSchema, UpdateChefSyncSchema } from "@app/services/secret-sync/chef";
|
||||
import { ChefSyncSchema, CreateChefSyncSchema, UpdateChefSyncSchema } from "@app/ee/services/secret-sync/chef";
|
||||
import { registerSyncSecretsEndpoints } from "@app/server/routes/v1/secret-sync-routers/secret-sync-endpoints";
|
||||
import { SecretSync } from "@app/services/secret-sync/secret-sync-enums";
|
||||
|
||||
import { registerSyncSecretsEndpoints } from "./secret-sync-endpoints";
|
||||
|
||||
export const registerChefSyncRouter = async (server: FastifyZodProvider) =>
|
||||
registerSyncSecretsEndpoints({
|
||||
destination: SecretSync.Chef,
|
||||
@@ -5,10 +5,10 @@ import { request } from "@app/lib/config/request";
|
||||
import { BadRequestError } from "@app/lib/errors";
|
||||
import { removeTrailingSlash } from "@app/lib/fn";
|
||||
import { blockLocalAndPrivateIpAddresses } from "@app/lib/validator";
|
||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||
import { IntegrationUrls } from "@app/services/integration-auth/integration-list";
|
||||
|
||||
import { TChefDataBagItemContent } from "../../secret-sync/chef/chef-sync-types";
|
||||
import { AppConnection } from "../app-connection-enums";
|
||||
import { ChefConnectionMethod } from "./chef-connection-enums";
|
||||
import {
|
||||
TChefConnection,
|
||||
@@ -1,7 +1,8 @@
|
||||
import { ForbiddenRequestError } from "@app/lib/errors";
|
||||
import { BadRequestError, ForbiddenRequestError } from "@app/lib/errors";
|
||||
import { OrgServiceActor } from "@app/lib/types";
|
||||
|
||||
import { AppConnection } from "../app-connection-enums";
|
||||
import { AppConnection } from "../../../../services/app-connection/app-connection-enums";
|
||||
import { TLicenseServiceFactory } from "../../license/license-service";
|
||||
import { listChefDataBagItems, listChefDataBags } from "./chef-connection-fns";
|
||||
import { TChefConnection } from "./chef-connection-types";
|
||||
|
||||
@@ -11,8 +12,23 @@ type TGetAppConnectionFunc = (
|
||||
actor: OrgServiceActor
|
||||
) => Promise<TChefConnection>;
|
||||
|
||||
export const chefConnectionService = (getAppConnection: TGetAppConnectionFunc) => {
|
||||
// Enterprise check
|
||||
export const checkPlan = async (licenseService: Pick<TLicenseServiceFactory, "getPlan">, orgId: string) => {
|
||||
const plan = await licenseService.getPlan(orgId);
|
||||
if (!plan.enterpriseAppConnections)
|
||||
throw new BadRequestError({
|
||||
message:
|
||||
"Failed to use app connection due to plan restriction. Upgrade plan to access enterprise app connections."
|
||||
});
|
||||
};
|
||||
|
||||
export const chefConnectionService = (
|
||||
getAppConnection: TGetAppConnectionFunc,
|
||||
licenseService: Pick<TLicenseServiceFactory, "getPlan">
|
||||
) => {
|
||||
const listDataBags = async (appConnectionId: string, actor: OrgServiceActor) => {
|
||||
await checkPlan(licenseService, actor.orgId);
|
||||
|
||||
const appConnection = await getAppConnection(AppConnection.Chef, appConnectionId, actor);
|
||||
|
||||
if (!appConnection) {
|
||||
@@ -23,6 +39,8 @@ export const chefConnectionService = (getAppConnection: TGetAppConnectionFunc) =
|
||||
};
|
||||
|
||||
const listDataBagItems = async (appConnectionId: string, dataBagName: string, actor: OrgServiceActor) => {
|
||||
await checkPlan(licenseService, actor.orgId);
|
||||
|
||||
const appConnection = await getAppConnection(AppConnection.Chef, appConnectionId, actor);
|
||||
|
||||
if (!appConnection) {
|
||||
@@ -1,9 +1,9 @@
|
||||
import z from "zod";
|
||||
|
||||
import { TChefDataBagItemContent } from "@app/ee/services/secret-sync/chef";
|
||||
import { DiscriminativePick } from "@app/lib/types";
|
||||
import { TChefDataBagItemContent } from "@app/services/secret-sync/chef";
|
||||
|
||||
import { AppConnection } from "../app-connection-enums";
|
||||
import { AppConnection } from "../../../../services/app-connection/app-connection-enums";
|
||||
import {
|
||||
ChefConnectionSchema,
|
||||
CreateChefConnectionSchema,
|
||||
@@ -3,7 +3,7 @@ import net from "node:net";
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
import * as x509 from "@peculiar/x509";
|
||||
|
||||
import { OrganizationActionScope, OrgMembershipRole, TRelays } from "@app/db/schemas";
|
||||
import { OrganizationActionScope, OrgMembershipRole, OrgMembershipStatus, TRelays } from "@app/db/schemas";
|
||||
import { PgSqlLock } from "@app/keystore/keystore";
|
||||
import { crypto } from "@app/lib/crypto";
|
||||
import { DatabaseErrorCode } from "@app/lib/error-codes";
|
||||
@@ -909,7 +909,9 @@ export const gatewayV2ServiceFactory = ({
|
||||
|
||||
for await (const [orgId, gateways] of Object.entries(gatewaysByOrg)) {
|
||||
try {
|
||||
const admins = await orgDAL.findOrgMembersByRole(orgId, OrgMembershipRole.Admin);
|
||||
const admins = (await orgDAL.findOrgMembersByRole(orgId, OrgMembershipRole.Admin)).filter(
|
||||
(admin) => admin.status !== OrgMembershipStatus.Invited
|
||||
);
|
||||
if (admins.length === 0) {
|
||||
logger.warn({ orgId }, "Organization has no admins to notify about unhealthy gateway.");
|
||||
// eslint-disable-next-line no-continue
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import knex from "knex";
|
||||
import mysql, { Connection } from "mysql2/promise";
|
||||
import * as pg from "pg";
|
||||
import tls, { PeerCertificate } from "tls";
|
||||
|
||||
import { verifyHostInputValidity } from "@app/ee/services/dynamic-secret/dynamic-secret-fns";
|
||||
@@ -97,7 +96,7 @@ const makeSqlConnection = (
|
||||
try {
|
||||
await client.raw(SIMPLE_QUERY);
|
||||
} catch (error) {
|
||||
if (error instanceof pg.DatabaseError) {
|
||||
if (error instanceof Error) {
|
||||
// Hacky way to know if we successfully hit the database.
|
||||
// TODO: potentially two approaches to solve the problem.
|
||||
// 1. change the work flow, add account first then resource
|
||||
|
||||
@@ -3,7 +3,7 @@ import { isIP } from "node:net";
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
import * as x509 from "@peculiar/x509";
|
||||
|
||||
import { OrganizationActionScope, OrgMembershipRole, TRelays } from "@app/db/schemas";
|
||||
import { OrganizationActionScope, OrgMembershipRole, OrgMembershipStatus, TRelays } from "@app/db/schemas";
|
||||
import { PgSqlLock } from "@app/keystore/keystore";
|
||||
import { crypto } from "@app/lib/crypto";
|
||||
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
||||
@@ -1248,7 +1248,9 @@ export const relayServiceFactory = ({
|
||||
});
|
||||
}
|
||||
} else {
|
||||
const admins = await orgDAL.findOrgMembersByRole(orgId, OrgMembershipRole.Admin);
|
||||
const admins = (await orgDAL.findOrgMembersByRole(orgId, OrgMembershipRole.Admin)).filter(
|
||||
(admin) => admin.status !== OrgMembershipStatus.Invited
|
||||
);
|
||||
if (admins.length === 0) {
|
||||
// eslint-disable-next-line no-continue
|
||||
continue;
|
||||
|
||||
@@ -6,5 +6,6 @@ export const CHEF_SYNC_LIST_OPTION: TSecretSyncListItem = {
|
||||
name: "Chef",
|
||||
destination: SecretSync.Chef,
|
||||
connection: AppConnection.Chef,
|
||||
canImportSecrets: true
|
||||
canImportSecrets: true,
|
||||
enterprise: true
|
||||
};
|
||||
@@ -1,4 +1,4 @@
|
||||
import { getChefDataBagItem, updateChefDataBagItem } from "@app/services/app-connection/chef";
|
||||
import { getChefDataBagItem, updateChefDataBagItem } from "@app/ee/services/app-connections/chef";
|
||||
import { matchesSchema } from "@app/services/secret-sync/secret-sync-fns";
|
||||
import { TSecretMap } from "@app/services/secret-sync/secret-sync-types";
|
||||
|
||||
@@ -42,5 +42,6 @@ export const ChefSyncListItemSchema = z.object({
|
||||
name: z.literal("Chef"),
|
||||
connection: z.literal(AppConnection.Chef),
|
||||
destination: z.literal(SecretSync.Chef),
|
||||
canImportSecrets: z.literal(true)
|
||||
canImportSecrets: z.literal(true),
|
||||
enterprise: z.boolean()
|
||||
});
|
||||
@@ -1,6 +1,6 @@
|
||||
import z from "zod";
|
||||
|
||||
import { TChefConnection } from "@app/services/app-connection/chef";
|
||||
import { TChefConnection } from "@app/ee/services/app-connections/chef";
|
||||
|
||||
import { ChefSyncListItemSchema, ChefSyncSchema, CreateChefSyncSchema } from "./chef-sync-schemas";
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { ProjectType } from "@app/db/schemas";
|
||||
import { ChefConnectionListItemSchema, SanitizedChefConnectionSchema } from "@app/ee/services/app-connections/chef";
|
||||
import { OCIConnectionListItemSchema, SanitizedOCIConnectionSchema } from "@app/ee/services/app-connections/oci";
|
||||
import {
|
||||
OracleDBConnectionListItemSchema,
|
||||
@@ -48,7 +49,6 @@ import {
|
||||
ChecklyConnectionListItemSchema,
|
||||
SanitizedChecklyConnectionSchema
|
||||
} from "@app/services/app-connection/checkly";
|
||||
import { ChefConnectionListItemSchema, SanitizedChefConnectionSchema } from "@app/services/app-connection/chef";
|
||||
import {
|
||||
CloudflareConnectionListItemSchema,
|
||||
SanitizedCloudflareConnectionSchema
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { registerChefConnectionRouter } from "@app/ee/routes/v1/app-connection-routers/chef-connection-router";
|
||||
import { registerOCIConnectionRouter } from "@app/ee/routes/v1/app-connection-routers/oci-connection-router";
|
||||
import { registerOracleDBConnectionRouter } from "@app/ee/routes/v1/app-connection-routers/oracledb-connection-router";
|
||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||
@@ -13,7 +14,6 @@ import { registerAzureKeyVaultConnectionRouter } from "./azure-key-vault-connect
|
||||
import { registerBitbucketConnectionRouter } from "./bitbucket-connection-router";
|
||||
import { registerCamundaConnectionRouter } from "./camunda-connection-router";
|
||||
import { registerChecklyConnectionRouter } from "./checkly-connection-router";
|
||||
import { registerChefConnectionRouter } from "./chef-connection-router";
|
||||
import { registerCloudflareConnectionRouter } from "./cloudflare-connection-router";
|
||||
import { registerDatabricksConnectionRouter } from "./databricks-connection-router";
|
||||
import { registerDigitalOceanConnectionRouter } from "./digital-ocean-connection-router";
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { registerChefSyncRouter } from "@app/ee/routes/v1/secret-sync-routers/chef-sync-router";
|
||||
import { registerOCIVaultSyncRouter } from "@app/ee/routes/v1/secret-sync-routers/oci-vault-sync-router";
|
||||
import { SecretSync } from "@app/services/secret-sync/secret-sync-enums";
|
||||
|
||||
@@ -10,7 +11,6 @@ import { registerAzureKeyVaultSyncRouter } from "./azure-key-vault-sync-router";
|
||||
import { registerBitbucketSyncRouter } from "./bitbucket-sync-router";
|
||||
import { registerCamundaSyncRouter } from "./camunda-sync-router";
|
||||
import { registerChecklySyncRouter } from "./checkly-sync-router";
|
||||
import { registerChefSyncRouter } from "./chef-sync-router";
|
||||
import { registerCloudflarePagesSyncRouter } from "./cloudflare-pages-sync-router";
|
||||
import { registerCloudflareWorkersSyncRouter } from "./cloudflare-workers-sync-router";
|
||||
import { registerDatabricksSyncRouter } from "./databricks-sync-router";
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||
import { ChefSyncListItemSchema, ChefSyncSchema } from "@app/ee/services/secret-sync/chef";
|
||||
import { OCIVaultSyncListItemSchema, OCIVaultSyncSchema } from "@app/ee/services/secret-sync/oci-vault";
|
||||
import { ApiDocsTags, SecretSyncs } from "@app/lib/api-docs";
|
||||
import { readLimit } from "@app/server/config/rateLimiter";
|
||||
@@ -24,7 +25,6 @@ import { AzureKeyVaultSyncListItemSchema, AzureKeyVaultSyncSchema } from "@app/s
|
||||
import { BitbucketSyncListItemSchema, BitbucketSyncSchema } from "@app/services/secret-sync/bitbucket";
|
||||
import { CamundaSyncListItemSchema, CamundaSyncSchema } from "@app/services/secret-sync/camunda";
|
||||
import { ChecklySyncListItemSchema, ChecklySyncSchema } from "@app/services/secret-sync/checkly/checkly-sync-schemas";
|
||||
import { ChefSyncListItemSchema, ChefSyncSchema } from "@app/services/secret-sync/chef";
|
||||
import {
|
||||
CloudflarePagesSyncListItemSchema,
|
||||
CloudflarePagesSyncSchema
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
import { ProjectType } from "@app/db/schemas";
|
||||
import { TAppConnections } from "@app/db/schemas/app-connections";
|
||||
import {
|
||||
ChefConnectionMethod,
|
||||
getChefConnectionListItem,
|
||||
validateChefConnectionCredentials
|
||||
} from "@app/ee/services/app-connections/chef";
|
||||
import {
|
||||
getOCIConnectionListItem,
|
||||
OCIConnectionMethod,
|
||||
@@ -68,7 +73,6 @@ import {
|
||||
} from "./bitbucket";
|
||||
import { CamundaConnectionMethod, getCamundaConnectionListItem, validateCamundaConnectionCredentials } from "./camunda";
|
||||
import { ChecklyConnectionMethod, getChecklyConnectionListItem, validateChecklyConnectionCredentials } from "./checkly";
|
||||
import { ChefConnectionMethod, getChefConnectionListItem, validateChefConnectionCredentials } from "./chef";
|
||||
import { CloudflareConnectionMethod } from "./cloudflare/cloudflare-connection-enum";
|
||||
import {
|
||||
getCloudflareConnectionListItem,
|
||||
|
||||
@@ -86,6 +86,6 @@ export const APP_CONNECTION_PLAN_MAP: Record<AppConnection, AppConnectionPlanTyp
|
||||
[AppConnection.Netlify]: AppConnectionPlanType.Regular,
|
||||
[AppConnection.Okta]: AppConnectionPlanType.Regular,
|
||||
[AppConnection.Redis]: AppConnectionPlanType.Regular,
|
||||
[AppConnection.Chef]: AppConnectionPlanType.Regular,
|
||||
[AppConnection.Chef]: AppConnectionPlanType.Enterprise,
|
||||
[AppConnection.Northflank]: AppConnectionPlanType.Regular
|
||||
};
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { ForbiddenError, subject } from "@casl/ability";
|
||||
|
||||
import { ActionProjectType, OrganizationActionScope, TAppConnections } from "@app/db/schemas";
|
||||
import { ValidateChefConnectionCredentialsSchema } from "@app/ee/services/app-connections/chef";
|
||||
import { chefConnectionService } from "@app/ee/services/app-connections/chef/chef-connection-service";
|
||||
import { ValidateOCIConnectionCredentialsSchema } from "@app/ee/services/app-connections/oci";
|
||||
import { ociConnectionService } from "@app/ee/services/app-connections/oci/oci-connection-service";
|
||||
import { ValidateOracleDBConnectionCredentialsSchema } from "@app/ee/services/app-connections/oracledb";
|
||||
@@ -67,8 +69,6 @@ import { ValidateCamundaConnectionCredentialsSchema } from "./camunda";
|
||||
import { camundaConnectionService } from "./camunda/camunda-connection-service";
|
||||
import { ValidateChecklyConnectionCredentialsSchema } from "./checkly";
|
||||
import { checklyConnectionService } from "./checkly/checkly-connection-service";
|
||||
import { ValidateChefConnectionCredentialsSchema } from "./chef";
|
||||
import { chefConnectionService } from "./chef/chef-connection-service";
|
||||
import { ValidateCloudflareConnectionCredentialsSchema } from "./cloudflare/cloudflare-connection-schema";
|
||||
import { cloudflareConnectionService } from "./cloudflare/cloudflare-connection-service";
|
||||
import { ValidateDatabricksConnectionCredentialsSchema } from "./databricks";
|
||||
@@ -885,6 +885,6 @@ export const appConnectionServiceFactory = ({
|
||||
northflank: northflankConnectionService(connectAppConnectionById),
|
||||
okta: oktaConnectionService(connectAppConnectionById),
|
||||
laravelForge: laravelForgeConnectionService(connectAppConnectionById),
|
||||
chef: chefConnectionService(connectAppConnectionById)
|
||||
chef: chefConnectionService(connectAppConnectionById, licenseService)
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
import {
|
||||
TChefConnection,
|
||||
TChefConnectionConfig,
|
||||
TChefConnectionInput,
|
||||
TValidateChefConnectionCredentialsSchema
|
||||
} from "@app/ee/services/app-connections/chef";
|
||||
import {
|
||||
TOCIConnection,
|
||||
TOCIConnectionConfig,
|
||||
@@ -82,12 +88,6 @@ import {
|
||||
TChecklyConnectionInput,
|
||||
TValidateChecklyConnectionCredentialsSchema
|
||||
} from "./checkly";
|
||||
import {
|
||||
TChefConnection,
|
||||
TChefConnectionConfig,
|
||||
TChefConnectionInput,
|
||||
TValidateChefConnectionCredentialsSchema
|
||||
} from "./chef";
|
||||
import {
|
||||
TCloudflareConnection,
|
||||
TCloudflareConnectionConfig,
|
||||
|
||||
@@ -4,6 +4,7 @@ import handlebars from "handlebars";
|
||||
import { TGatewayServiceFactory } from "@app/ee/services/gateway/gateway-service";
|
||||
import { TGatewayV2ServiceFactory } from "@app/ee/services/gateway-v2/gateway-v2-service";
|
||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||
import { CHEF_SYNC_LIST_OPTION, ChefSyncFns } from "@app/ee/services/secret-sync/chef";
|
||||
import { OCI_VAULT_SYNC_LIST_OPTION, OCIVaultSyncFns } from "@app/ee/services/secret-sync/oci-vault";
|
||||
import { BadRequestError } from "@app/lib/errors";
|
||||
import {
|
||||
@@ -34,7 +35,6 @@ import { BITBUCKET_SYNC_LIST_OPTION, BitbucketSyncFns } from "./bitbucket";
|
||||
import { CAMUNDA_SYNC_LIST_OPTION, camundaSyncFactory } from "./camunda";
|
||||
import { CHECKLY_SYNC_LIST_OPTION } from "./checkly/checkly-sync-constants";
|
||||
import { ChecklySyncFns } from "./checkly/checkly-sync-fns";
|
||||
import { CHEF_SYNC_LIST_OPTION, ChefSyncFns } from "./chef";
|
||||
import { CLOUDFLARE_PAGES_SYNC_LIST_OPTION } from "./cloudflare-pages/cloudflare-pages-constants";
|
||||
import { CloudflarePagesSyncFns } from "./cloudflare-pages/cloudflare-pages-fns";
|
||||
import { CLOUDFLARE_WORKERS_SYNC_LIST_OPTION, CloudflareWorkersSyncFns } from "./cloudflare-workers";
|
||||
|
||||
@@ -107,7 +107,7 @@ export const SECRET_SYNC_PLAN_MAP: Record<SecretSync, SecretSyncPlanType> = {
|
||||
[SecretSync.Northflank]: SecretSyncPlanType.Regular,
|
||||
[SecretSync.Bitbucket]: SecretSyncPlanType.Regular,
|
||||
[SecretSync.LaravelForge]: SecretSyncPlanType.Regular,
|
||||
[SecretSync.Chef]: SecretSyncPlanType.Regular
|
||||
[SecretSync.Chef]: SecretSyncPlanType.Enterprise
|
||||
};
|
||||
|
||||
export const SECRET_SYNC_SKIP_FIELDS_MAP: Record<SecretSync, string[]> = {
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
import { Job } from "bullmq";
|
||||
|
||||
import { AuditLogInfo } from "@app/ee/services/audit-log/audit-log-types";
|
||||
import {
|
||||
TChefSync,
|
||||
TChefSyncInput,
|
||||
TChefSyncListItem,
|
||||
TChefSyncWithCredentials
|
||||
} from "@app/ee/services/secret-sync/chef";
|
||||
import {
|
||||
TOCIVaultSync,
|
||||
TOCIVaultSyncInput,
|
||||
@@ -21,7 +27,6 @@ import {
|
||||
TCamundaSyncListItem,
|
||||
TCamundaSyncWithCredentials
|
||||
} from "@app/services/secret-sync/camunda";
|
||||
import { TChefSync, TChefSyncInput, TChefSyncListItem, TChefSyncWithCredentials } from "@app/services/secret-sync/chef";
|
||||
import {
|
||||
TDatabricksSync,
|
||||
TDatabricksSyncInput,
|
||||
|
||||
@@ -784,7 +784,10 @@
|
||||
"groups": [
|
||||
{
|
||||
"group": "Infisical PAM",
|
||||
"pages": ["documentation/platform/pam/overview"]
|
||||
"pages": [
|
||||
"documentation/platform/pam/overview",
|
||||
"documentation/platform/pam/session-recording"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
60
docs/documentation/platform/pam/session-recording.mdx
Normal file
60
docs/documentation/platform/pam/session-recording.mdx
Normal file
@@ -0,0 +1,60 @@
|
||||
---
|
||||
title: "Session Recording"
|
||||
sidebarTitle: "Session Recording"
|
||||
description: "Learn how Infisical records and stores session activity for auditing and monitoring."
|
||||
---
|
||||
|
||||
Infisical's Privileged Access Management (PAM) provides robust session recording capabilities to help you audit and monitor user activity across your infrastructure.
|
||||
|
||||
## How It Works
|
||||
|
||||
When a user initiates a session through the Infisical Gateway, a recording of the session begins. The gateway securely caches all recording data in temporary encrypted files on its local system.
|
||||
|
||||
Once the session concludes, the gateway transmits the complete recording to the Infisical platform for long-term, centralized storage. This asynchronous process ensures that sessions remain operational even if the connection to the Infisical platform is temporarily lost. After the upload is complete, administrators can search and review the session logs in the Infisical UI.
|
||||
|
||||
## What's Captured
|
||||
|
||||
The content captured during a session depends on the type of resource being accessed.
|
||||
|
||||
### Database Sessions
|
||||
|
||||
For database connections, Infisical captures all queries executed and their corresponding responses.
|
||||
|
||||
<Note>
|
||||
Support for additional resource types like SSH and RDP is coming soon.
|
||||
</Note>
|
||||
|
||||
## Viewing Recordings
|
||||
|
||||
To review session recordings:
|
||||
|
||||
1. Navigate to the **PAM Sessions** page in your project.
|
||||
2. Click on a session from the list to view its details.
|
||||
|
||||

|
||||
|
||||
The session details page provides key information, including the complete session logs, connection status, the user who initiated it, and more.
|
||||
|
||||

|
||||
|
||||
### Searching Logs
|
||||
|
||||
You can use the search bar to quickly find relevant information:
|
||||
|
||||
- **On the main Sessions page:** Search across all session logs to locate specific queries or outputs.
|
||||
- **On an individual session page:** Search within that specific session's logs to pinpoint activity.
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
## FAQ
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Are session recordings encrypted?">
|
||||
Yes. All session recordings are encrypted at rest by default, ensuring your audit data is always secure.
|
||||
</Accordion>
|
||||
<Accordion title="Why aren't recordings streamed in real-time?">
|
||||
Currently, Infisical uses an asynchronous approach where the gateway records the entire session locally before uploading it. This design makes your PAM sessions more resilient, as they don't depend on a constant, active connection to the Infisical platform. We may introduce live streaming capabilities in a future release.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 415 KiB |
BIN
docs/images/pam/session-recording/individual-session-page.png
Normal file
BIN
docs/images/pam/session-recording/individual-session-page.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 462 KiB |
BIN
docs/images/pam/session-recording/sessions-page-search.png
Normal file
BIN
docs/images/pam/session-recording/sessions-page-search.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 500 KiB |
BIN
docs/images/pam/session-recording/sessions-page.png
Normal file
BIN
docs/images/pam/session-recording/sessions-page.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 570 KiB |
@@ -3,6 +3,14 @@ title: "Chef Connection"
|
||||
description: "Learn how to configure a Chef Connection for Infisical."
|
||||
---
|
||||
|
||||
<Info>
|
||||
Chef App Connection is a paid feature.
|
||||
|
||||
If you're using Infisical Cloud, then it is available under the **Enterprise Tier**. If you're self-hosting Infisical,
|
||||
then you should contact team@infisical.com to purchase an enterprise license to use it.
|
||||
|
||||
</Info>
|
||||
|
||||
Infisical supports the use of User Private Key to connect with Chef Server.
|
||||
|
||||
Please access your **starter kit** to get all the required information to create a Chef Connection.
|
||||
|
||||
@@ -3,6 +3,14 @@ title: "Chef Sync"
|
||||
description: "Learn how to configure a Chef Sync for Infisical."
|
||||
---
|
||||
|
||||
<Info>
|
||||
Chef Sync is a paid feature.
|
||||
|
||||
If you're using Infisical Cloud, then it is available under the **Enterprise Tier**. If you're self-hosting Infisical,
|
||||
then you should contact team@infisical.com to purchase an enterprise license to use it.
|
||||
|
||||
</Info>
|
||||
|
||||
**Prerequisites:**
|
||||
|
||||
- Create a [Chef Connection](/integrations/app-connections/chef)
|
||||
|
||||
@@ -27,7 +27,9 @@ Both approaches provide the same metrics data in OTEL format, so you can choose
|
||||
- Access to deploy monitoring services (Prometheus, Grafana, etc.)
|
||||
- Basic understanding of Prometheus and Grafana
|
||||
|
||||
## Environment Variables
|
||||
## Setup
|
||||
|
||||
### Environment Variables
|
||||
|
||||
Configure the following environment variables in your Infisical backend:
|
||||
|
||||
@@ -37,287 +39,304 @@ OTEL_TELEMETRY_COLLECTION_ENABLED=true
|
||||
|
||||
# Choose export type: "prometheus" or "otlp"
|
||||
OTEL_EXPORT_TYPE=prometheus
|
||||
|
||||
# For OTLP push mode, also configure:
|
||||
# OTEL_EXPORT_OTLP_ENDPOINT=http://otel-collector:4318/v1/metrics
|
||||
# OTEL_COLLECTOR_BASIC_AUTH_USERNAME=your_collector_username
|
||||
# OTEL_COLLECTOR_BASIC_AUTH_PASSWORD=your_collector_password
|
||||
# OTEL_OTLP_PUSH_INTERVAL=30000
|
||||
```
|
||||
|
||||
**Note**: The `OTEL_COLLECTOR_BASIC_AUTH_USERNAME` and `OTEL_COLLECTOR_BASIC_AUTH_PASSWORD` values must match the credentials configured in your OpenTelemetry Collector's `basicauth/server` extension. These are not hardcoded values - you configure them in your collector configuration file.
|
||||
<Tabs>
|
||||
<Tab title="Pull-based Monitoring (Prometheus)">
|
||||
This approach exposes metrics on port 9464 at the `/metrics` endpoint, allowing Prometheus to scrape the data. The metrics are exposed in Prometheus format but originate from OpenTelemetry instrumentation.
|
||||
|
||||
## Option 1: Pull-based Monitoring (Prometheus)
|
||||
### Configuration
|
||||
|
||||
This approach exposes metrics on port 9464 at the `/metrics` endpoint, allowing Prometheus to scrape the data. The metrics are exposed in Prometheus format but originate from OpenTelemetry instrumentation.
|
||||
<Steps>
|
||||
<Step title="Enable Prometheus export in Infisical">
|
||||
```bash
|
||||
OTEL_TELEMETRY_COLLECTION_ENABLED=true
|
||||
OTEL_EXPORT_TYPE=prometheus
|
||||
```
|
||||
</Step>
|
||||
|
||||
### Configuration
|
||||
<Step title="Expose the metrics port">
|
||||
Expose the metrics port in your Infisical backend:
|
||||
|
||||
1. **Enable Prometheus export in Infisical**:
|
||||
- **Docker**: Expose port 9464
|
||||
- **Kubernetes**: Create a service exposing port 9464
|
||||
- **Other**: Ensure port 9464 is accessible to your monitoring stack
|
||||
</Step>
|
||||
|
||||
```bash
|
||||
OTEL_TELEMETRY_COLLECTION_ENABLED=true
|
||||
OTEL_EXPORT_TYPE=prometheus
|
||||
```
|
||||
|
||||
2. **Expose the metrics port** in your Infisical backend:
|
||||
|
||||
- **Docker**: Expose port 9464
|
||||
- **Kubernetes**: Create a service exposing port 9464
|
||||
- **Other**: Ensure port 9464 is accessible to your monitoring stack
|
||||
|
||||
3. **Create Prometheus configuration** (`prometheus.yml`):
|
||||
|
||||
```yaml
|
||||
global:
|
||||
scrape_interval: 30s
|
||||
evaluation_interval: 30s
|
||||
|
||||
scrape_configs:
|
||||
- job_name: "infisical"
|
||||
scrape_interval: 30s
|
||||
static_configs:
|
||||
- targets: ["infisical-backend:9464"] # Adjust hostname/port based on your deployment
|
||||
metrics_path: "/metrics"
|
||||
```
|
||||
|
||||
**Note**: Replace `infisical-backend:9464` with the actual hostname and port where your Infisical backend is running. This could be:
|
||||
|
||||
- **Docker Compose**: `infisical-backend:9464` (service name)
|
||||
- **Kubernetes**: `infisical-backend.default.svc.cluster.local:9464` (service name)
|
||||
- **Bare Metal**: `192.168.1.100:9464` (actual IP address)
|
||||
- **Cloud**: `your-infisical.example.com:9464` (domain name)
|
||||
|
||||
### Deployment Options
|
||||
|
||||
#### Docker Compose
|
||||
<Step title="Create Prometheus configuration">
|
||||
Create `prometheus.yml`:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
global:
|
||||
scrape_interval: 30s
|
||||
evaluation_interval: 30s
|
||||
|
||||
scrape_configs:
|
||||
- job_name: "infisical"
|
||||
scrape_interval: 30s
|
||||
static_configs:
|
||||
- targets: ["infisical-backend:9464"] # Adjust hostname/port based on your deployment
|
||||
metrics_path: "/metrics"
|
||||
```
|
||||
|
||||
<Note>
|
||||
Replace `infisical-backend:9464` with the actual hostname and port where your Infisical backend is running. This could be:
|
||||
|
||||
- **Docker Compose**: `infisical-backend:9464` (service name)
|
||||
- **Kubernetes**: `infisical-backend.default.svc.cluster.local:9464` (service name)
|
||||
- **Bare Metal**: `192.168.1.100:9464` (actual IP address)
|
||||
- **Cloud**: `your-infisical.example.com:9464` (domain name)
|
||||
</Note>
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
### Deployment Options
|
||||
|
||||
Once you've configured Infisical to expose metrics, you'll need to deploy Prometheus to scrape and store them. Below are examples for different deployment environments. Choose the option that matches your infrastructure.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Docker Compose">
|
||||
```yaml
|
||||
services:
|
||||
prometheus:
|
||||
image: prom/prometheus:latest
|
||||
ports:
|
||||
- "9090:9090"
|
||||
volumes:
|
||||
- ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
|
||||
command:
|
||||
- "--config.file=/etc/prometheus/prometheus.yml"
|
||||
|
||||
grafana:
|
||||
image: grafana/grafana:latest
|
||||
ports:
|
||||
- "3000:3000"
|
||||
environment:
|
||||
- GF_SECURITY_ADMIN_USER=admin
|
||||
- GF_SECURITY_ADMIN_PASSWORD=admin
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="Kubernetes">
|
||||
```yaml
|
||||
# prometheus-deployment.yaml
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: prometheus
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: prometheus
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: prometheus
|
||||
spec:
|
||||
containers:
|
||||
- name: prometheus
|
||||
image: prom/prometheus:latest
|
||||
ports:
|
||||
- containerPort: 9090
|
||||
volumeMounts:
|
||||
- name: config
|
||||
mountPath: /etc/prometheus
|
||||
volumes:
|
||||
- name: config
|
||||
configMap:
|
||||
name: prometheus-config
|
||||
|
||||
---
|
||||
# prometheus-service.yaml
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: prometheus
|
||||
spec:
|
||||
selector:
|
||||
app: prometheus
|
||||
ports:
|
||||
- port: 9090
|
||||
targetPort: 9090
|
||||
type: ClusterIP
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="Helm">
|
||||
```bash
|
||||
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
|
||||
helm install prometheus prometheus-community/prometheus \
|
||||
--set server.config.global.scrape_interval=30s \
|
||||
--set server.config.scrape_configs[0].job_name=infisical \
|
||||
--set server.config.scrape_configs[0].static_configs[0].targets[0]=infisical-backend:9464
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
</Tab>
|
||||
<Tab title="Push-based Monitoring (OTLP)">
|
||||
This approach sends metrics directly to an OpenTelemetry Collector via the OTLP protocol. This gives you the most flexibility as you can configure the collector to export to multiple backends simultaneously.
|
||||
|
||||
### Configuration
|
||||
|
||||
<Steps>
|
||||
<Step title="Enable OTLP export in Infisical">
|
||||
```bash
|
||||
OTEL_TELEMETRY_COLLECTION_ENABLED=true
|
||||
OTEL_EXPORT_TYPE=otlp
|
||||
OTEL_EXPORT_OTLP_ENDPOINT=http://otel-collector:4318/v1/metrics
|
||||
OTEL_COLLECTOR_BASIC_AUTH_USERNAME=infisical
|
||||
OTEL_COLLECTOR_BASIC_AUTH_PASSWORD=infisical
|
||||
OTEL_OTLP_PUSH_INTERVAL=30000
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Create OpenTelemetry Collector configuration">
|
||||
Create `otel-collector-config.yaml`:
|
||||
|
||||
```yaml
|
||||
extensions:
|
||||
health_check:
|
||||
pprof:
|
||||
zpages:
|
||||
basicauth/server:
|
||||
htpasswd:
|
||||
inline: |
|
||||
your_username:your_password
|
||||
|
||||
receivers:
|
||||
otlp:
|
||||
protocols:
|
||||
http:
|
||||
endpoint: 0.0.0.0:4318
|
||||
auth:
|
||||
authenticator: basicauth/server
|
||||
|
||||
prometheus:
|
||||
image: prom/prometheus:latest
|
||||
ports:
|
||||
- "9090:9090"
|
||||
volumes:
|
||||
- ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
|
||||
command:
|
||||
- "--config.file=/etc/prometheus/prometheus.yml"
|
||||
config:
|
||||
scrape_configs:
|
||||
- job_name: otel-collector
|
||||
scrape_interval: 30s
|
||||
static_configs:
|
||||
- targets: [infisical-backend:9464]
|
||||
metric_relabel_configs:
|
||||
- action: labeldrop
|
||||
regex: "service_instance_id|service_name"
|
||||
|
||||
grafana:
|
||||
image: grafana/grafana:latest
|
||||
ports:
|
||||
- "3000:3000"
|
||||
environment:
|
||||
- GF_SECURITY_ADMIN_USER=admin
|
||||
- GF_SECURITY_ADMIN_PASSWORD=admin
|
||||
processors:
|
||||
batch:
|
||||
|
||||
exporters:
|
||||
prometheus:
|
||||
endpoint: "0.0.0.0:8889"
|
||||
auth:
|
||||
authenticator: basicauth/server
|
||||
resource_to_telemetry_conversion:
|
||||
enabled: true
|
||||
|
||||
service:
|
||||
extensions: [basicauth/server, health_check, pprof, zpages]
|
||||
pipelines:
|
||||
metrics:
|
||||
receivers: [otlp]
|
||||
processors: [batch]
|
||||
exporters: [prometheus]
|
||||
```
|
||||
|
||||
#### Kubernetes
|
||||
<Warning>
|
||||
Replace `your_username:your_password` with your chosen credentials. These must match the values you set in Infisical's `OTEL_COLLECTOR_BASIC_AUTH_USERNAME` and `OTEL_COLLECTOR_BASIC_AUTH_PASSWORD` environment variables.
|
||||
</Warning>
|
||||
</Step>
|
||||
|
||||
<Step title="Create Prometheus configuration">
|
||||
Create Prometheus configuration for the collector:
|
||||
|
||||
```yaml
|
||||
# prometheus-deployment.yaml
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: prometheus
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: prometheus
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: prometheus
|
||||
spec:
|
||||
containers:
|
||||
- name: prometheus
|
||||
image: prom/prometheus:latest
|
||||
ports:
|
||||
- containerPort: 9090
|
||||
volumeMounts:
|
||||
- name: config
|
||||
mountPath: /etc/prometheus
|
||||
volumes:
|
||||
- name: config
|
||||
configMap:
|
||||
name: prometheus-config
|
||||
global:
|
||||
scrape_interval: 30s
|
||||
evaluation_interval: 30s
|
||||
|
||||
---
|
||||
# prometheus-service.yaml
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: prometheus
|
||||
spec:
|
||||
selector:
|
||||
app: prometheus
|
||||
ports:
|
||||
- port: 9090
|
||||
targetPort: 9090
|
||||
type: ClusterIP
|
||||
scrape_configs:
|
||||
- job_name: "otel-collector"
|
||||
scrape_interval: 30s
|
||||
static_configs:
|
||||
- targets: ["otel-collector:8889"] # Adjust hostname/port based on your deployment
|
||||
metrics_path: "/metrics"
|
||||
```
|
||||
|
||||
#### Helm
|
||||
<Note>
|
||||
Replace `otel-collector:8889` with the actual hostname and port where your OpenTelemetry Collector is running. This could be:
|
||||
|
||||
```bash
|
||||
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
|
||||
helm install prometheus prometheus-community/prometheus \
|
||||
--set server.config.global.scrape_interval=30s \
|
||||
--set server.config.scrape_configs[0].job_name=infisical \
|
||||
--set server.config.scrape_configs[0].static_configs[0].targets[0]=infisical-backend:9464
|
||||
```
|
||||
- **Docker Compose**: `otel-collector:8889` (service name)
|
||||
- **Kubernetes**: `otel-collector.default.svc.cluster.local:8889` (service name)
|
||||
- **Bare Metal**: `192.168.1.100:8889` (actual IP address)
|
||||
- **Cloud**: `your-collector.example.com:8889` (domain name)
|
||||
</Note>
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Option 2: Push-based Monitoring (OTLP)
|
||||
### Deployment Options
|
||||
|
||||
This approach sends metrics directly to an OpenTelemetry Collector via the OTLP protocol. This gives you the most flexibility as you can configure the collector to export to multiple backends simultaneously.
|
||||
After configuring Infisical and the OpenTelemetry Collector, you'll need to deploy the collector to receive metrics from Infisical. Below are examples for different deployment environments. Choose the option that matches your infrastructure.
|
||||
|
||||
### Configuration
|
||||
<Tabs>
|
||||
<Tab title="Docker Compose">
|
||||
```yaml
|
||||
services:
|
||||
otel-collector:
|
||||
image: otel/opentelemetry-collector-contrib:latest
|
||||
ports:
|
||||
- 4318:4318 # OTLP http receiver
|
||||
- 8889:8889 # Prometheus exporter metrics
|
||||
volumes:
|
||||
- ./otel-collector-config.yaml:/etc/otelcol-contrib/config.yaml:ro
|
||||
command:
|
||||
- "--config=/etc/otelcol-contrib/config.yaml"
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="Kubernetes">
|
||||
```yaml
|
||||
# otel-collector-deployment.yaml
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: otel-collector
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: otel-collector
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: otel-collector
|
||||
spec:
|
||||
containers:
|
||||
- name: otel-collector
|
||||
image: otel/opentelemetry-collector-contrib:latest
|
||||
ports:
|
||||
- containerPort: 4318
|
||||
- containerPort: 8889
|
||||
volumeMounts:
|
||||
- name: config
|
||||
mountPath: /etc/otelcol-contrib
|
||||
volumes:
|
||||
- name: config
|
||||
configMap:
|
||||
name: otel-collector-config
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="Helm">
|
||||
```bash
|
||||
helm repo add open-telemetry https://open-telemetry.github.io/opentelemetry-helm-charts
|
||||
helm install otel-collector open-telemetry/opentelemetry-collector \
|
||||
--set config.receivers.otlp.protocols.http.endpoint=0.0.0.0:4318 \
|
||||
--set config.exporters.prometheus.endpoint=0.0.0.0:8889
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
1. **Enable OTLP export in Infisical**:
|
||||
|
||||
```bash
|
||||
OTEL_TELEMETRY_COLLECTION_ENABLED=true
|
||||
OTEL_EXPORT_TYPE=otlp
|
||||
OTEL_EXPORT_OTLP_ENDPOINT=http://otel-collector:4318/v1/metrics
|
||||
OTEL_COLLECTOR_BASIC_AUTH_USERNAME=infisical
|
||||
OTEL_COLLECTOR_BASIC_AUTH_PASSWORD=infisical
|
||||
OTEL_OTLP_PUSH_INTERVAL=30000
|
||||
```
|
||||
|
||||
2. **Create OpenTelemetry Collector configuration** (`otel-collector-config.yaml`):
|
||||
|
||||
```yaml
|
||||
extensions:
|
||||
health_check:
|
||||
pprof:
|
||||
zpages:
|
||||
basicauth/server:
|
||||
htpasswd:
|
||||
inline: |
|
||||
your_username:your_password
|
||||
|
||||
receivers:
|
||||
otlp:
|
||||
protocols:
|
||||
http:
|
||||
endpoint: 0.0.0.0:4318
|
||||
auth:
|
||||
authenticator: basicauth/server
|
||||
|
||||
prometheus:
|
||||
config:
|
||||
scrape_configs:
|
||||
- job_name: otel-collector
|
||||
scrape_interval: 30s
|
||||
static_configs:
|
||||
- targets: [infisical-backend:9464]
|
||||
metric_relabel_configs:
|
||||
- action: labeldrop
|
||||
regex: "service_instance_id|service_name"
|
||||
|
||||
processors:
|
||||
batch:
|
||||
|
||||
exporters:
|
||||
prometheus:
|
||||
endpoint: "0.0.0.0:8889"
|
||||
auth:
|
||||
authenticator: basicauth/server
|
||||
resource_to_telemetry_conversion:
|
||||
enabled: true
|
||||
|
||||
service:
|
||||
extensions: [basicauth/server, health_check, pprof, zpages]
|
||||
pipelines:
|
||||
metrics:
|
||||
receivers: [otlp]
|
||||
processors: [batch]
|
||||
exporters: [prometheus]
|
||||
```
|
||||
|
||||
**Important**: Replace `your_username:your_password` with your chosen credentials. These must match the values you set in Infisical's `OTEL_COLLECTOR_BASIC_AUTH_USERNAME` and `OTEL_COLLECTOR_BASIC_AUTH_PASSWORD` environment variables.
|
||||
|
||||
3. **Create Prometheus configuration** for the collector:
|
||||
|
||||
```yaml
|
||||
global:
|
||||
scrape_interval: 30s
|
||||
evaluation_interval: 30s
|
||||
|
||||
scrape_configs:
|
||||
- job_name: "otel-collector"
|
||||
scrape_interval: 30s
|
||||
static_configs:
|
||||
- targets: ["otel-collector:8889"] # Adjust hostname/port based on your deployment
|
||||
metrics_path: "/metrics"
|
||||
```
|
||||
|
||||
**Note**: Replace `otel-collector:8889` with the actual hostname and port where your OpenTelemetry Collector is running. This could be:
|
||||
|
||||
- **Docker Compose**: `otel-collector:8889` (service name)
|
||||
- **Kubernetes**: `otel-collector.default.svc.cluster.local:8889` (service name)
|
||||
- **Bare Metal**: `192.168.1.100:8889` (actual IP address)
|
||||
- **Cloud**: `your-collector.example.com:8889` (domain name)
|
||||
|
||||
### Deployment Options
|
||||
|
||||
#### Docker Compose
|
||||
|
||||
```yaml
|
||||
services:
|
||||
otel-collector:
|
||||
image: otel/opentelemetry-collector-contrib:latest
|
||||
ports:
|
||||
- 4318:4318 # OTLP http receiver
|
||||
- 8889:8889 # Prometheus exporter metrics
|
||||
volumes:
|
||||
- ./otel-collector-config.yaml:/etc/otelcol-contrib/config.yaml:ro
|
||||
command:
|
||||
- "--config=/etc/otelcol-contrib/config.yaml"
|
||||
```
|
||||
|
||||
#### Kubernetes
|
||||
|
||||
```yaml
|
||||
# otel-collector-deployment.yaml
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: otel-collector
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: otel-collector
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: otel-collector
|
||||
spec:
|
||||
containers:
|
||||
- name: otel-collector
|
||||
image: otel/opentelemetry-collector-contrib:latest
|
||||
ports:
|
||||
- containerPort: 4318
|
||||
- containerPort: 8889
|
||||
volumeMounts:
|
||||
- name: config
|
||||
mountPath: /etc/otelcol-contrib
|
||||
volumes:
|
||||
- name: config
|
||||
configMap:
|
||||
name: otel-collector-config
|
||||
```
|
||||
|
||||
#### Helm
|
||||
|
||||
```bash
|
||||
helm repo add open-telemetry https://open-telemetry.github.io/opentelemetry-helm-charts
|
||||
helm install otel-collector open-telemetry/opentelemetry-collector \
|
||||
--set config.receivers.otlp.protocols.http.endpoint=0.0.0.0:4318 \
|
||||
--set config.exporters.prometheus.endpoint=0.0.0.0:8889
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Available Metrics
|
||||
|
||||
@@ -327,166 +346,211 @@ Infisical exposes the following key metrics in OpenTelemetry format:
|
||||
|
||||
These metrics track all HTTP API requests to Infisical, including request counts, latency, and errors. Use these to monitor overall API health, identify performance bottlenecks, and track usage patterns across users and machine identities.
|
||||
|
||||
#### Total API Requests
|
||||
<AccordionGroup>
|
||||
<Accordion title="Total API Requests">
|
||||
**Metric Name**: `infisical.http.server.request.count`
|
||||
|
||||
- **Metric Name**: `infisical.http.server.request.count`
|
||||
- **Type**: Counter
|
||||
- **Unit**: `{request}`
|
||||
- **Description**: Total number of API requests to Infisical (covers both human users and machine identities)
|
||||
- **Attributes**:
|
||||
- `infisical.organization.id` (string): Organization ID
|
||||
- `infisical.organization.name` (string): Organization name (e.g., "Platform Engineering Team")
|
||||
- `infisical.user.id` (string, optional): User ID if human user
|
||||
- `infisical.user.email` (string, optional): User email (e.g., "jane.doe@cisco.com")
|
||||
- `infisical.identity.id` (string, optional): Machine identity ID
|
||||
- `infisical.identity.name` (string, optional): Machine identity name (e.g., "prod-k8s-operator")
|
||||
- `infisical.auth.method` (string, optional): Auth method used
|
||||
- `http.request.method` (string): HTTP method (GET, POST, PUT, DELETE)
|
||||
- `http.route` (string): API endpoint route pattern
|
||||
- `http.response.status_code` (int): HTTP status code
|
||||
- `infisical.project.id` (string, optional): Project ID
|
||||
- `infisical.project.name` (string, optional): Project name
|
||||
- `user_agent.original` (string, optional): User agent string
|
||||
- `client.address` (string, optional): IP address
|
||||
**Type**: Counter
|
||||
|
||||
#### Request Duration
|
||||
**Unit**: `{request}`
|
||||
|
||||
- **Metric Name**: `infisical.http.server.request.duration`
|
||||
- **Type**: Histogram
|
||||
- **Unit**: `s` (seconds)
|
||||
- **Description**: API request latency
|
||||
- **Buckets**: [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10]
|
||||
- **Attributes**:
|
||||
- `infisical.organization.id` (string): Organization ID
|
||||
- `infisical.organization.name` (string): Organization name
|
||||
- `infisical.user.id` (string, optional): User ID if human user
|
||||
- `infisical.user.email` (string, optional): User email
|
||||
- `infisical.identity.id` (string, optional): Machine identity ID
|
||||
- `infisical.identity.name` (string, optional): Machine identity name
|
||||
- `http.request.method` (string): HTTP method
|
||||
- `http.route` (string): API endpoint route pattern
|
||||
- `http.response.status_code` (int): HTTP status code
|
||||
- `infisical.project.id` (string, optional): Project ID
|
||||
- `infisical.project.name` (string, optional): Project name
|
||||
**Description**: Total number of API requests to Infisical (covers both human users and machine identities)
|
||||
|
||||
#### API Errors by Actor
|
||||
**Attributes**:
|
||||
- `infisical.organization.id` (string): Organization ID
|
||||
- `infisical.organization.name` (string): Organization name (e.g., "Platform Engineering Team")
|
||||
- `infisical.user.id` (string, optional): User ID if human user
|
||||
- `infisical.user.email` (string, optional): User email (e.g., "jane.doe@cisco.com")
|
||||
- `infisical.identity.id` (string, optional): Machine identity ID
|
||||
- `infisical.identity.name` (string, optional): Machine identity name (e.g., "prod-k8s-operator")
|
||||
- `infisical.auth.method` (string, optional): Auth method used
|
||||
- `http.request.method` (string): HTTP method (GET, POST, PUT, DELETE)
|
||||
- `http.route` (string): API endpoint route pattern
|
||||
- `http.response.status_code` (int): HTTP status code
|
||||
- `infisical.project.id` (string, optional): Project ID
|
||||
- `infisical.project.name` (string, optional): Project name
|
||||
- `user_agent.original` (string, optional): User agent string
|
||||
- `client.address` (string, optional): IP address
|
||||
</Accordion>
|
||||
|
||||
- **Metric Name**: `infisical.http.server.error.count`
|
||||
- **Type**: Counter
|
||||
- **Unit**: `{error}`
|
||||
- **Description**: API errors grouped by actor (for identifying misconfigured services)
|
||||
- **Attributes**:
|
||||
- `infisical.organization.id` (string): Organization ID
|
||||
- `infisical.organization.name` (string): Organization name
|
||||
- `infisical.user.id` (string, optional): User ID if human
|
||||
- `infisical.user.email` (string, optional): User email
|
||||
- `infisical.identity.id` (string, optional): Identity ID if machine
|
||||
- `infisical.identity.name` (string, optional): Identity name
|
||||
- `http.route` (string): API endpoint where error occurred
|
||||
- `http.request.method` (string): HTTP method
|
||||
- `error.type` (string): Error category/type (client_error, server_error, auth_error, rate_limit_error, etc.)
|
||||
- `infisical.project.id` (string, optional): Project ID
|
||||
- `infisical.project.name` (string, optional): Project name
|
||||
- `client.address` (string, optional): IP address
|
||||
- `user_agent.original` (string, optional): User agent information
|
||||
<Accordion title="Request Duration">
|
||||
**Metric Name**: `infisical.http.server.request.duration`
|
||||
|
||||
**Type**: Histogram
|
||||
|
||||
**Unit**: `s` (seconds)
|
||||
|
||||
**Description**: API request latency
|
||||
|
||||
**Buckets**: [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10]
|
||||
|
||||
**Attributes**:
|
||||
- `infisical.organization.id` (string): Organization ID
|
||||
- `infisical.organization.name` (string): Organization name
|
||||
- `infisical.user.id` (string, optional): User ID if human user
|
||||
- `infisical.user.email` (string, optional): User email
|
||||
- `infisical.identity.id` (string, optional): Machine identity ID
|
||||
- `infisical.identity.name` (string, optional): Machine identity name
|
||||
- `http.request.method` (string): HTTP method
|
||||
- `http.route` (string): API endpoint route pattern
|
||||
- `http.response.status_code` (int): HTTP status code
|
||||
- `infisical.project.id` (string, optional): Project ID
|
||||
- `infisical.project.name` (string, optional): Project name
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="API Errors by Actor">
|
||||
**Metric Name**: `infisical.http.server.error.count`
|
||||
|
||||
**Type**: Counter
|
||||
|
||||
**Unit**: `{error}`
|
||||
|
||||
**Description**: API errors grouped by actor (for identifying misconfigured services)
|
||||
|
||||
**Attributes**:
|
||||
- `infisical.organization.id` (string): Organization ID
|
||||
- `infisical.organization.name` (string): Organization name
|
||||
- `infisical.user.id` (string, optional): User ID if human
|
||||
- `infisical.user.email` (string, optional): User email
|
||||
- `infisical.identity.id` (string, optional): Identity ID if machine
|
||||
- `infisical.identity.name` (string, optional): Identity name
|
||||
- `http.route` (string): API endpoint where error occurred
|
||||
- `http.request.method` (string): HTTP method
|
||||
- `error.type` (string): Error category/type (client_error, server_error, auth_error, rate_limit_error, etc.)
|
||||
- `infisical.project.id` (string, optional): Project ID
|
||||
- `infisical.project.name` (string, optional): Project name
|
||||
- `client.address` (string, optional): IP address
|
||||
- `user_agent.original` (string, optional): User agent information
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
### Secret Operations Metrics
|
||||
|
||||
These metrics provide visibility into secret access patterns, helping you understand which secrets are being accessed, by whom, and from where. Essential for security auditing and access pattern analysis.
|
||||
|
||||
#### Secret Read Operations
|
||||
|
||||
- **Metric Name**: `infisical.secret.read.count`
|
||||
- **Type**: Counter
|
||||
- **Unit**: `{operation}`
|
||||
- **Description**: Number of secret read operations
|
||||
- **Attributes**:
|
||||
- `infisical.organization.id` (string): Organization ID
|
||||
- `infisical.organization.name` (string): Organization name
|
||||
- `infisical.project.id` (string): Project ID
|
||||
- `infisical.project.name` (string): Project name (e.g., "payment-service-secrets")
|
||||
- `infisical.environment` (string): Environment (dev, staging, prod)
|
||||
- `infisical.secret.path` (string): Path to secrets (e.g., "/microservice-a/database")
|
||||
- `infisical.secret.name` (string, optional): Name of secret
|
||||
- `infisical.user.id` (string, optional): User ID if human
|
||||
- `infisical.user.email` (string, optional): User email
|
||||
- `infisical.identity.id` (string, optional): Machine identity ID
|
||||
- `infisical.identity.name` (string, optional): Machine identity name
|
||||
- `user_agent.original` (string, optional): User agent/SDK information
|
||||
- `client.address` (string, optional): IP address
|
||||
<AccordionGroup>
|
||||
<Accordion title="Secret Read Operations">
|
||||
**Metric Name**: `infisical.secret.read.count`
|
||||
|
||||
**Type**: Counter
|
||||
|
||||
**Unit**: `{operation}`
|
||||
|
||||
**Description**: Number of secret read operations
|
||||
|
||||
**Attributes**:
|
||||
- `infisical.organization.id` (string): Organization ID
|
||||
- `infisical.organization.name` (string): Organization name
|
||||
- `infisical.project.id` (string): Project ID
|
||||
- `infisical.project.name` (string): Project name (e.g., "payment-service-secrets")
|
||||
- `infisical.environment` (string): Environment (dev, staging, prod)
|
||||
- `infisical.secret.path` (string): Path to secrets (e.g., "/microservice-a/database")
|
||||
- `infisical.secret.name` (string, optional): Name of secret
|
||||
- `infisical.user.id` (string, optional): User ID if human
|
||||
- `infisical.user.email` (string, optional): User email
|
||||
- `infisical.identity.id` (string, optional): Machine identity ID
|
||||
- `infisical.identity.name` (string, optional): Machine identity name
|
||||
- `user_agent.original` (string, optional): User agent/SDK information
|
||||
- `client.address` (string, optional): IP address
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
### Authentication Metrics
|
||||
|
||||
These metrics track authentication attempts and outcomes, enabling you to monitor login success rates, detect potential security threats, and identify authentication issues.
|
||||
|
||||
#### Login Attempts
|
||||
|
||||
- **Metric Name**: `infisical.auth.attempt.count`
|
||||
- **Type**: Counter
|
||||
- **Unit**: `{attempt}`
|
||||
- **Description**: Authentication attempts (both successful and failed)
|
||||
- **Attributes**:
|
||||
- `infisical.organization.id` (string): Organization ID
|
||||
- `infisical.organization.name` (string): Organization name
|
||||
- `infisical.user.id` (string, optional): User ID if human (if identifiable)
|
||||
- `infisical.user.email` (string, optional): User email (if identifiable)
|
||||
- `infisical.identity.id` (string, optional): Identity ID if machine (if identifiable)
|
||||
- `infisical.identity.name` (string, optional): Identity name (if identifiable)
|
||||
- `infisical.auth.method` (string): Authentication method attempted
|
||||
- `infisical.auth.result` (string): success or failure
|
||||
- `error.type` (string, optional): Reason for failure if failed (invalid_credentials, expired_token, invalid_token, etc.)
|
||||
- `client.address` (string): IP address
|
||||
- `user_agent.original` (string, optional): User agent/client information
|
||||
- `infisical.auth.attempt.username` (string, optional): Attempted username/email (if available)
|
||||
|
||||
### Legacy Metrics
|
||||
|
||||
These metrics are from the previous instrumentation and may be deprecated in future versions. Consider migrating to the new Core API Metrics for more comprehensive observability.
|
||||
|
||||
- `API_latency` - API request latency histogram in milliseconds (Labels: `route`, `method`, `statusCode`)
|
||||
- `API_errors` - API error count histogram (Labels: `route`, `method`, `type`, `name`)
|
||||
<AccordionGroup>
|
||||
<Accordion title="Login Attempts">
|
||||
**Metric Name**: `infisical.auth.attempt.count`
|
||||
|
||||
**Type**: Counter
|
||||
|
||||
**Unit**: `{attempt}`
|
||||
|
||||
**Description**: Authentication attempts (both successful and failed)
|
||||
|
||||
**Attributes**:
|
||||
- `infisical.organization.id` (string): Organization ID
|
||||
- `infisical.organization.name` (string): Organization name
|
||||
- `infisical.user.id` (string, optional): User ID if human (if identifiable)
|
||||
- `infisical.user.email` (string, optional): User email (if identifiable)
|
||||
- `infisical.identity.id` (string, optional): Identity ID if machine (if identifiable)
|
||||
- `infisical.identity.name` (string, optional): Identity name (if identifiable)
|
||||
- `infisical.auth.method` (string): Authentication method attempted
|
||||
- `infisical.auth.result` (string): success or failure
|
||||
- `error.type` (string, optional): Reason for failure if failed (invalid_credentials, expired_token, invalid_token, etc.)
|
||||
- `client.address` (string): IP address
|
||||
- `user_agent.original` (string, optional): User agent/client information
|
||||
- `infisical.auth.attempt.username` (string, optional): Attempted username/email (if available)
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
### Integration & Secret Sync Metrics
|
||||
|
||||
These metrics monitor secret synchronization operations between Infisical and external systems, helping you track sync health, identify integration failures, and troubleshoot connectivity issues.
|
||||
|
||||
- `integration_secret_sync_errors` - Integration secret sync error count
|
||||
<AccordionGroup>
|
||||
<Accordion title="integration_secret_sync_errors">
|
||||
Integration secret sync error count
|
||||
|
||||
- **Labels**: `version`, `integration`, `integrationId`, `type`, `status`, `name`, `projectId`
|
||||
- **Example**: Monitor integration sync failures across different services
|
||||
- **Labels**: `version`, `integration`, `integrationId`, `type`, `status`, `name`, `projectId`
|
||||
- **Example**: Monitor integration sync failures across different services
|
||||
</Accordion>
|
||||
|
||||
- `secret_sync_sync_secrets_errors` - Secret sync operation error count
|
||||
<Accordion title="secret_sync_sync_secrets_errors">
|
||||
Secret sync operation error count
|
||||
|
||||
- **Labels**: `version`, `destination`, `syncId`, `projectId`, `type`, `status`, `name`
|
||||
- **Example**: Track secret sync failures to external systems
|
||||
- **Labels**: `version`, `destination`, `syncId`, `projectId`, `type`, `status`, `name`
|
||||
- **Example**: Track secret sync failures to external systems
|
||||
</Accordion>
|
||||
|
||||
- `secret_sync_import_secrets_errors` - Secret import operation error count
|
||||
<Accordion title="secret_sync_import_secrets_errors">
|
||||
Secret import operation error count
|
||||
|
||||
- **Labels**: `version`, `destination`, `syncId`, `projectId`, `type`, `status`, `name`
|
||||
- **Example**: Monitor secret import failures
|
||||
- **Labels**: `version`, `destination`, `syncId`, `projectId`, `type`, `status`, `name`
|
||||
- **Example**: Monitor secret import failures
|
||||
</Accordion>
|
||||
|
||||
- `secret_sync_remove_secrets_errors` - Secret removal operation error count
|
||||
- **Labels**: `version`, `destination`, `syncId`, `projectId`, `type`, `status`, `name`
|
||||
- **Example**: Track secret removal operation failures
|
||||
<Accordion title="secret_sync_remove_secrets_errors">
|
||||
Secret removal operation error count
|
||||
|
||||
- **Labels**: `version`, `destination`, `syncId`, `projectId`, `type`, `status`, `name`
|
||||
- **Example**: Track secret removal operation failures
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
### System Metrics
|
||||
|
||||
These low-level HTTP metrics are automatically collected by OpenTelemetry's instrumentation layer, providing baseline performance data for all HTTP traffic.
|
||||
|
||||
- `http_server_duration` - HTTP server request duration metrics (histogram buckets, count, sum)
|
||||
- `http_client_duration` - HTTP client request duration metrics (histogram buckets, count, sum)
|
||||
<AccordionGroup>
|
||||
<Accordion title="http_server_duration">
|
||||
HTTP server request duration metrics (histogram buckets, count, sum)
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="http_client_duration">
|
||||
HTTP client request duration metrics (histogram buckets, count, sum)
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
<Accordion title="Metrics not appearing">
|
||||
If your metrics are not showing up in Prometheus or your monitoring system, check the following:
|
||||
|
||||
1. **Metrics not appearing**:
|
||||
- Verify `OTEL_TELEMETRY_COLLECTION_ENABLED=true` is set in your Infisical environment variables
|
||||
- Ensure the correct `OTEL_EXPORT_TYPE` is set (`prometheus` or `otlp`)
|
||||
- Check network connectivity between Infisical and your monitoring services (Prometheus or OTLP collector)
|
||||
- For pull-based monitoring: Verify port 9464 is exposed and accessible
|
||||
- For push-based monitoring: Verify the OTLP endpoint URL is correct and reachable
|
||||
- Check Infisical backend logs for any errors related to metrics export
|
||||
</Accordion>
|
||||
|
||||
- Check if `OTEL_TELEMETRY_COLLECTION_ENABLED=true`
|
||||
- Verify the correct `OTEL_EXPORT_TYPE` is set
|
||||
- Check network connectivity between services
|
||||
<Accordion title="Authentication errors">
|
||||
If you're experiencing authentication errors with the OpenTelemetry Collector:
|
||||
|
||||
2. **Authentication errors**:
|
||||
|
||||
- Verify basic auth credentials in OTLP configuration
|
||||
- Check if credentials match between Infisical and collector
|
||||
- Verify basic auth credentials in your OTLP configuration match between Infisical and the collector
|
||||
- Check that `OTEL_COLLECTOR_BASIC_AUTH_USERNAME` and `OTEL_COLLECTOR_BASIC_AUTH_PASSWORD` match the credentials in your `otel-collector-config.yaml`
|
||||
- Ensure the htpasswd format in the collector configuration is correct
|
||||
- Test the collector endpoint manually using curl with the same credentials to verify they work
|
||||
</Accordion>
|
||||
|
||||
@@ -35,23 +35,16 @@ export const WishForm = () => {
|
||||
const [isOpen, setIsOpen] = useToggle(false);
|
||||
|
||||
const createWish = async (data: TFormData) => {
|
||||
try {
|
||||
await mutateAsync({
|
||||
text: data.text
|
||||
});
|
||||
await mutateAsync({
|
||||
text: data.text
|
||||
});
|
||||
|
||||
createNotification({
|
||||
text: "Your wish has been sent to the Infisical team!",
|
||||
type: "success"
|
||||
});
|
||||
createNotification({
|
||||
text: "Your wish has been sent to the Infisical team!",
|
||||
type: "success"
|
||||
});
|
||||
|
||||
setIsOpen.off();
|
||||
} catch {
|
||||
createNotification({
|
||||
text: "An error occured while sending your wish to the Infisical team.",
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
setIsOpen.off();
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -25,27 +25,20 @@ const TotpRegistration = ({ onComplete, shouldCenterQr }: Props) => {
|
||||
|
||||
const handleTotpVerify = async (event: React.FormEvent<HTMLFormElement>) => {
|
||||
event.preventDefault();
|
||||
try {
|
||||
const result = await verifyUserTotp({
|
||||
totp
|
||||
});
|
||||
const result = await verifyUserTotp({
|
||||
totp
|
||||
});
|
||||
|
||||
createNotification({
|
||||
text: "Successfully configured mobile authenticator",
|
||||
type: "success"
|
||||
});
|
||||
createNotification({
|
||||
text: "Successfully configured mobile authenticator",
|
||||
type: "success"
|
||||
});
|
||||
|
||||
if (result.recoveryCodes && result.recoveryCodes.length > 0) {
|
||||
setRecoveryCodes(result.recoveryCodes);
|
||||
setShowRecoveryModal(true);
|
||||
} else if (onComplete) {
|
||||
onComplete();
|
||||
}
|
||||
} catch {
|
||||
createNotification({
|
||||
text: "Failed to verify TOTP code",
|
||||
type: "error"
|
||||
});
|
||||
if (result.recoveryCodes && result.recoveryCodes.length > 0) {
|
||||
setRecoveryCodes(result.recoveryCodes);
|
||||
setShowRecoveryModal(true);
|
||||
} else if (onComplete) {
|
||||
onComplete();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -43,35 +43,27 @@ export const CreateOrgModal: FC<CreateOrgModalProps> = ({ isOpen, onClose }) =>
|
||||
const { mutateAsync: selectOrg } = useSelectOrganization();
|
||||
|
||||
const onFormSubmit = async ({ name }: FormData) => {
|
||||
try {
|
||||
const organization = await createOrg({
|
||||
name
|
||||
});
|
||||
const organization = await createOrg({
|
||||
name
|
||||
});
|
||||
|
||||
await selectOrg({
|
||||
organizationId: organization.id
|
||||
});
|
||||
await selectOrg({
|
||||
organizationId: organization.id
|
||||
});
|
||||
|
||||
createNotification({
|
||||
text: "Successfully created organization",
|
||||
type: "success"
|
||||
});
|
||||
createNotification({
|
||||
text: "Successfully created organization",
|
||||
type: "success"
|
||||
});
|
||||
|
||||
navigate({
|
||||
to: "/organization/projects"
|
||||
});
|
||||
navigate({
|
||||
to: "/organization/projects"
|
||||
});
|
||||
|
||||
localStorage.setItem("orgData.id", organization.id);
|
||||
localStorage.setItem("orgData.id", organization.id);
|
||||
|
||||
reset();
|
||||
onClose();
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
createNotification({
|
||||
text: "Failed to created organization",
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
reset();
|
||||
onClose();
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -20,28 +20,19 @@ export const DeletePkiSyncModal = ({ isOpen, onOpenChange, pkiSync, onComplete }
|
||||
const handleDeletePkiSync = async () => {
|
||||
const destinationName = PKI_SYNC_MAP[destination].name;
|
||||
|
||||
try {
|
||||
await deleteSync.mutateAsync({
|
||||
syncId,
|
||||
projectId,
|
||||
destination
|
||||
});
|
||||
await deleteSync.mutateAsync({
|
||||
syncId,
|
||||
projectId,
|
||||
destination
|
||||
});
|
||||
|
||||
createNotification({
|
||||
text: `Successfully deleted ${destinationName} PKI Sync`,
|
||||
type: "success"
|
||||
});
|
||||
createNotification({
|
||||
text: `Successfully deleted ${destinationName} PKI Sync`,
|
||||
type: "success"
|
||||
});
|
||||
|
||||
if (onComplete) onComplete();
|
||||
onOpenChange(false);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
|
||||
createNotification({
|
||||
text: `Failed to delete ${destinationName} PKI Sync`,
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
if (onComplete) onComplete();
|
||||
onOpenChange(false);
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -21,27 +21,18 @@ const Content = ({ pkiSync, onComplete }: ContentProps) => {
|
||||
const triggerImportCertificates = useTriggerPkiSyncImportCertificates();
|
||||
|
||||
const handleTriggerImportCertificates = async () => {
|
||||
try {
|
||||
await triggerImportCertificates.mutateAsync({
|
||||
syncId,
|
||||
destination,
|
||||
projectId
|
||||
});
|
||||
await triggerImportCertificates.mutateAsync({
|
||||
syncId,
|
||||
destination,
|
||||
projectId
|
||||
});
|
||||
|
||||
createNotification({
|
||||
text: `Successfully triggered certificate import for ${destinationName} Sync`,
|
||||
type: "success"
|
||||
});
|
||||
createNotification({
|
||||
text: `Successfully triggered certificate import for ${destinationName} Sync`,
|
||||
type: "success"
|
||||
});
|
||||
|
||||
onComplete();
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
|
||||
createNotification({
|
||||
text: `Failed to trigger certificate import for ${destinationName} Sync`,
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
onComplete();
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -21,27 +21,18 @@ const Content = ({ pkiSync, onComplete }: ContentProps) => {
|
||||
const triggerRemoveCertificates = useTriggerPkiSyncRemoveCertificates();
|
||||
|
||||
const handleTriggerRemoveCertificates = async () => {
|
||||
try {
|
||||
await triggerRemoveCertificates.mutateAsync({
|
||||
syncId,
|
||||
destination,
|
||||
projectId
|
||||
});
|
||||
await triggerRemoveCertificates.mutateAsync({
|
||||
syncId,
|
||||
destination,
|
||||
projectId
|
||||
});
|
||||
|
||||
createNotification({
|
||||
text: `Successfully triggered certificate removal for ${destinationName} Sync`,
|
||||
type: "success"
|
||||
});
|
||||
createNotification({
|
||||
text: `Successfully triggered certificate removal for ${destinationName} Sync`,
|
||||
type: "success"
|
||||
});
|
||||
|
||||
onComplete();
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
|
||||
createNotification({
|
||||
text: `Failed to trigger certificate removal for ${destinationName} Sync`,
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
onComplete();
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -86,14 +86,8 @@ export const CreatePkiSyncForm = ({ destination, onComplete, onCancel, initialDa
|
||||
type: "success"
|
||||
});
|
||||
onComplete(pkiSync);
|
||||
} catch (err: Error | unknown) {
|
||||
console.error("PKI sync creation failed:", err);
|
||||
} catch {
|
||||
setShowConfirmation(false);
|
||||
createNotification({
|
||||
title: `Failed to create ${destinationName} Certificate Sync`,
|
||||
text: err instanceof Error ? err.message : "An unknown error occurred",
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -42,28 +42,19 @@ export const EditPkiSyncForm = ({ pkiSync, fields, onComplete }: Props) => {
|
||||
});
|
||||
|
||||
const onSubmit = async ({ connection, ...formData }: TUpdatePkiSyncForm) => {
|
||||
try {
|
||||
const updatedPkiSync = await updatePkiSync.mutateAsync({
|
||||
syncId: pkiSync.id,
|
||||
...formData,
|
||||
connectionId: connection.id,
|
||||
projectId: pkiSync.projectId,
|
||||
destination: pkiSync.destination
|
||||
});
|
||||
const updatedPkiSync = await updatePkiSync.mutateAsync({
|
||||
syncId: pkiSync.id,
|
||||
...formData,
|
||||
connectionId: connection.id,
|
||||
projectId: pkiSync.projectId,
|
||||
destination: pkiSync.destination
|
||||
});
|
||||
|
||||
createNotification({
|
||||
text: `Successfully updated ${destinationName} PKI Sync`,
|
||||
type: "success"
|
||||
});
|
||||
onComplete(updatedPkiSync);
|
||||
} catch (err: any) {
|
||||
console.error(err);
|
||||
createNotification({
|
||||
title: `Failed to update ${destinationName} PKI Sync`,
|
||||
text: err.message,
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
createNotification({
|
||||
text: `Successfully updated ${destinationName} PKI Sync`,
|
||||
type: "success"
|
||||
});
|
||||
onComplete(updatedPkiSync);
|
||||
};
|
||||
|
||||
let Component: ReactNode;
|
||||
|
||||
@@ -71,7 +71,7 @@ export const PkiSyncOptionsFields = ({ destination }: Props) => {
|
||||
isChecked={value}
|
||||
>
|
||||
<p>
|
||||
Enable Removal of Active/Revoked Certificates{" "}
|
||||
Enable Removal of Expired/Revoked Certificates{" "}
|
||||
<Tooltip
|
||||
className="max-w-md"
|
||||
content={
|
||||
@@ -152,7 +152,7 @@ export const PkiSyncOptionsFields = ({ destination }: Props) => {
|
||||
isChecked={value}
|
||||
>
|
||||
<p>
|
||||
Preserve Version on Renewal{" "}
|
||||
Enable Versioning on Renewal{" "}
|
||||
<Tooltip
|
||||
className="max-w-md"
|
||||
content={
|
||||
|
||||
@@ -56,30 +56,22 @@ export const ProjectOverviewChangeSection = ({ showSlugField = false }: Props) =
|
||||
}, [currentProject, showSlugField]);
|
||||
|
||||
const onFormSubmit = async (data: BaseFormData | FormDataWithSlug) => {
|
||||
try {
|
||||
if (!currentProject?.id) return;
|
||||
if (!currentProject?.id) return;
|
||||
|
||||
await mutateAsync({
|
||||
projectId: currentProject.id,
|
||||
newProjectName: data.name,
|
||||
newProjectDescription: data.description,
|
||||
...(showSlugField &&
|
||||
"slug" in data && {
|
||||
newSlug: data.slug !== currentProject.slug ? data.slug : undefined
|
||||
})
|
||||
});
|
||||
await mutateAsync({
|
||||
projectId: currentProject.id,
|
||||
newProjectName: data.name,
|
||||
newProjectDescription: data.description,
|
||||
...(showSlugField &&
|
||||
"slug" in data && {
|
||||
newSlug: data.slug !== currentProject.slug ? data.slug : undefined
|
||||
})
|
||||
});
|
||||
|
||||
createNotification({
|
||||
text: "Successfully updated project overview",
|
||||
type: "success"
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
createNotification({
|
||||
text: "Failed to update project overview",
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
createNotification({
|
||||
text: "Successfully updated project overview",
|
||||
type: "success"
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -141,29 +141,24 @@ const NewProjectForm = ({ onOpenChange }: NewProjectFormProps) => {
|
||||
// type check
|
||||
if (!currentOrg) return;
|
||||
if (!user) return;
|
||||
try {
|
||||
const {
|
||||
data: { project }
|
||||
} = await createWs.mutateAsync({
|
||||
projectName: name,
|
||||
projectDescription: description,
|
||||
kmsKeyId: kmsKeyId !== INTERNAL_KMS_KEY_ID ? kmsKeyId : undefined,
|
||||
template,
|
||||
type
|
||||
});
|
||||
await refetchWorkspaces();
|
||||
const {
|
||||
data: { project }
|
||||
} = await createWs.mutateAsync({
|
||||
projectName: name,
|
||||
projectDescription: description,
|
||||
kmsKeyId: kmsKeyId !== INTERNAL_KMS_KEY_ID ? kmsKeyId : undefined,
|
||||
template,
|
||||
type
|
||||
});
|
||||
await refetchWorkspaces();
|
||||
|
||||
createNotification({ text: "Project created", type: "success" });
|
||||
reset();
|
||||
onOpenChange(false);
|
||||
navigate({
|
||||
to: getProjectHomePage(project.type, project.environments),
|
||||
params: { projectId: project.id }
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
createNotification({ text: "Failed to create project", type: "error" });
|
||||
}
|
||||
createNotification({ text: "Project created", type: "success" });
|
||||
reset();
|
||||
onOpenChange(false);
|
||||
navigate({
|
||||
to: getProjectHomePage(project.type, project.environments),
|
||||
params: { projectId: project.id }
|
||||
});
|
||||
};
|
||||
const onSubmit = handleSubmit((data) => {
|
||||
return onCreateProject(data);
|
||||
|
||||
@@ -37,29 +37,22 @@ export const DeleteSecretRotationV2Modal = ({
|
||||
const handleDeleteSecretRotation = async () => {
|
||||
const rotationType = SECRET_ROTATION_MAP[type].name;
|
||||
|
||||
try {
|
||||
await deleteSecretRotation.mutateAsync({
|
||||
rotationId,
|
||||
type,
|
||||
revokeGeneratedCredentials,
|
||||
deleteSecrets,
|
||||
projectId,
|
||||
secretPath: folder.path
|
||||
});
|
||||
await deleteSecretRotation.mutateAsync({
|
||||
rotationId,
|
||||
type,
|
||||
revokeGeneratedCredentials,
|
||||
deleteSecrets,
|
||||
projectId,
|
||||
secretPath: folder.path
|
||||
});
|
||||
|
||||
createNotification({
|
||||
text: `Successfully deleted ${rotationType} Rotation`,
|
||||
type: "success"
|
||||
});
|
||||
createNotification({
|
||||
text: `Successfully deleted ${rotationType} Rotation`,
|
||||
type: "success"
|
||||
});
|
||||
|
||||
if (onComplete) onComplete();
|
||||
onOpenChange(false);
|
||||
} catch {
|
||||
createNotification({
|
||||
text: `Failed to delete ${rotationType} Rotation`,
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
if (onComplete) onComplete();
|
||||
onOpenChange(false);
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -22,28 +22,19 @@ const Content = ({ secretRotation, onComplete }: ContentProps) => {
|
||||
const rotationType = SECRET_ROTATION_MAP[type].name;
|
||||
|
||||
const handleRotateSecrets = async () => {
|
||||
try {
|
||||
await rotateSecrets.mutateAsync({
|
||||
rotationId,
|
||||
type,
|
||||
projectId,
|
||||
secretPath: folder.path
|
||||
});
|
||||
await rotateSecrets.mutateAsync({
|
||||
rotationId,
|
||||
type,
|
||||
projectId,
|
||||
secretPath: folder.path
|
||||
});
|
||||
|
||||
createNotification({
|
||||
text: `Successfully rotated ${rotationType} secrets`,
|
||||
type: "success"
|
||||
});
|
||||
createNotification({
|
||||
text: `Successfully rotated ${rotationType} secrets`,
|
||||
type: "success"
|
||||
});
|
||||
|
||||
onComplete();
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
|
||||
createNotification({
|
||||
text: `Failed to rotate ${rotationType} secrets`,
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
onComplete();
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -120,21 +120,13 @@ export const SecretRotationV2Form = ({
|
||||
environment: environment.slug,
|
||||
projectId: currentProject.id
|
||||
});
|
||||
try {
|
||||
const rotation = await mutation;
|
||||
const rotation = await mutation;
|
||||
|
||||
createNotification({
|
||||
text: `Successfully ${secretRotation ? "updated" : "created"} ${rotationType} Rotation`,
|
||||
type: "success"
|
||||
});
|
||||
onComplete(rotation);
|
||||
} catch (err: any) {
|
||||
createNotification({
|
||||
title: `Failed to ${secretRotation ? "update" : "create"} ${rotationType} Rotation`,
|
||||
text: err.message,
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
createNotification({
|
||||
text: `Successfully ${secretRotation ? "updated" : "created"} ${rotationType} Rotation`,
|
||||
type: "success"
|
||||
});
|
||||
onComplete(rotation);
|
||||
};
|
||||
|
||||
const handlePrev = () => {
|
||||
|
||||
@@ -28,26 +28,19 @@ export const DeleteSecretScanningDataSourceModal = ({
|
||||
const handleDeleteDataSource = async () => {
|
||||
const dataSourceType = SECRET_SCANNING_DATA_SOURCE_MAP[type].name;
|
||||
|
||||
try {
|
||||
await deleteDataSource.mutateAsync({
|
||||
dataSourceId,
|
||||
type,
|
||||
projectId
|
||||
});
|
||||
await deleteDataSource.mutateAsync({
|
||||
dataSourceId,
|
||||
type,
|
||||
projectId
|
||||
});
|
||||
|
||||
createNotification({
|
||||
text: `Successfully deleted ${dataSourceType} Data Source`,
|
||||
type: "success"
|
||||
});
|
||||
createNotification({
|
||||
text: `Successfully deleted ${dataSourceType} Data Source`,
|
||||
type: "success"
|
||||
});
|
||||
|
||||
if (onComplete) onComplete();
|
||||
onOpenChange(false);
|
||||
} catch {
|
||||
createNotification({
|
||||
text: `Failed to delete ${dataSourceType} Data Source`,
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
if (onComplete) onComplete();
|
||||
onOpenChange(false);
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -73,21 +73,13 @@ export const SecretScanningDataSourceForm = ({
|
||||
connectionId: connection?.id,
|
||||
projectId: currentProject.id
|
||||
});
|
||||
try {
|
||||
const source = await mutation;
|
||||
const source = await mutation;
|
||||
|
||||
createNotification({
|
||||
text: `Successfully ${source ? "updated" : "created"} ${sourceType} Data Source`,
|
||||
type: "success"
|
||||
});
|
||||
onComplete(source);
|
||||
} catch (err: any) {
|
||||
createNotification({
|
||||
title: `Failed to ${dataSource ? "update" : "create"} ${sourceType} Data Source`,
|
||||
text: err.message,
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
createNotification({
|
||||
text: `Successfully ${source ? "updated" : "created"} ${sourceType} Data Source`,
|
||||
type: "success"
|
||||
});
|
||||
onComplete(source);
|
||||
};
|
||||
|
||||
const handlePrev = () => {
|
||||
|
||||
@@ -23,29 +23,20 @@ export const DeleteSecretSyncModal = ({ isOpen, onOpenChange, secretSync, onComp
|
||||
const handleDeleteSecretSync = async () => {
|
||||
const destinationName = SECRET_SYNC_MAP[destination].name;
|
||||
|
||||
try {
|
||||
await deleteSync.mutateAsync({
|
||||
syncId,
|
||||
destination,
|
||||
removeSecrets,
|
||||
projectId
|
||||
});
|
||||
await deleteSync.mutateAsync({
|
||||
syncId,
|
||||
destination,
|
||||
removeSecrets,
|
||||
projectId
|
||||
});
|
||||
|
||||
createNotification({
|
||||
text: `Successfully removed ${destinationName} Sync`,
|
||||
type: "success"
|
||||
});
|
||||
createNotification({
|
||||
text: `Successfully removed ${destinationName} Sync`,
|
||||
type: "success"
|
||||
});
|
||||
|
||||
if (onComplete) onComplete();
|
||||
onOpenChange(false);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
|
||||
createNotification({
|
||||
text: `Failed to remove ${destinationName} Sync`,
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
if (onComplete) onComplete();
|
||||
onOpenChange(false);
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -51,28 +51,19 @@ const Content = ({ secretSync, onComplete }: ContentProps) => {
|
||||
const triggerImportSecrets = useTriggerSecretSyncImportSecrets();
|
||||
|
||||
const handleTriggerImportSecrets = async ({ importBehavior }: TFormData) => {
|
||||
try {
|
||||
await triggerImportSecrets.mutateAsync({
|
||||
syncId,
|
||||
destination,
|
||||
importBehavior,
|
||||
projectId
|
||||
});
|
||||
await triggerImportSecrets.mutateAsync({
|
||||
syncId,
|
||||
destination,
|
||||
importBehavior,
|
||||
projectId
|
||||
});
|
||||
|
||||
createNotification({
|
||||
text: `Successfully triggered secret import for ${destinationName} Sync`,
|
||||
type: "success"
|
||||
});
|
||||
createNotification({
|
||||
text: `Successfully triggered secret import for ${destinationName} Sync`,
|
||||
type: "success"
|
||||
});
|
||||
|
||||
onComplete();
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
|
||||
createNotification({
|
||||
text: `Failed to trigger secret import for ${destinationName} Sync`,
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
onComplete();
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -21,27 +21,18 @@ const Content = ({ secretSync, onComplete }: ContentProps) => {
|
||||
const triggerSyncImport = useTriggerSecretSyncRemoveSecrets();
|
||||
|
||||
const handleTriggerRemoveSecrets = async () => {
|
||||
try {
|
||||
await triggerSyncImport.mutateAsync({
|
||||
syncId,
|
||||
destination,
|
||||
projectId
|
||||
});
|
||||
await triggerSyncImport.mutateAsync({
|
||||
syncId,
|
||||
destination,
|
||||
projectId
|
||||
});
|
||||
|
||||
createNotification({
|
||||
text: `Successfully triggered secret removal for ${destinationName} Sync`,
|
||||
type: "success"
|
||||
});
|
||||
createNotification({
|
||||
text: `Successfully triggered secret removal for ${destinationName} Sync`,
|
||||
type: "success"
|
||||
});
|
||||
|
||||
onComplete();
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
|
||||
createNotification({
|
||||
text: `Failed to trigger secret removal for ${destinationName} Sync`,
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
onComplete();
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -88,14 +88,8 @@ export const CreateSecretSyncForm = ({
|
||||
type: "success"
|
||||
});
|
||||
onComplete(secretSync);
|
||||
} catch (err: any) {
|
||||
console.error(err);
|
||||
} catch {
|
||||
setShowConfirmation(false);
|
||||
createNotification({
|
||||
title: `Failed to add ${destinationName} Sync`,
|
||||
text: err.message,
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -58,29 +58,20 @@ export const EditSecretSyncForm = ({ secretSync, fields, onComplete }: Props) =>
|
||||
|
||||
const performUpdate = useCallback(
|
||||
async (formData: TSecretSyncForm) => {
|
||||
try {
|
||||
const { environment, connection, ...updateData } = formData;
|
||||
const updatedSecretSync = await updateSecretSync.mutateAsync({
|
||||
syncId: secretSync.id,
|
||||
...updateData,
|
||||
environment: environment?.slug,
|
||||
connectionId: connection.id,
|
||||
projectId: secretSync.projectId
|
||||
});
|
||||
const { environment, connection, ...updateData } = formData;
|
||||
const updatedSecretSync = await updateSecretSync.mutateAsync({
|
||||
syncId: secretSync.id,
|
||||
...updateData,
|
||||
environment: environment?.slug,
|
||||
connectionId: connection.id,
|
||||
projectId: secretSync.projectId
|
||||
});
|
||||
|
||||
createNotification({
|
||||
text: `Successfully updated ${destinationName} Sync`,
|
||||
type: "success"
|
||||
});
|
||||
onComplete(updatedSecretSync);
|
||||
} catch (err: any) {
|
||||
console.error(err);
|
||||
createNotification({
|
||||
title: `Failed to update ${destinationName} Sync`,
|
||||
text: err.message,
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
createNotification({
|
||||
text: `Successfully updated ${destinationName} Sync`,
|
||||
type: "success"
|
||||
});
|
||||
onComplete(updatedSecretSync);
|
||||
},
|
||||
[updateSecretSync, secretSync.id, secretSync.projectId, destinationName, onComplete]
|
||||
);
|
||||
|
||||
@@ -130,26 +130,18 @@ export const CreateTagModal = ({ isOpen, onToggle, append, currentSecret }: Prop
|
||||
}, [isOpen]);
|
||||
|
||||
const onFormSubmit = async ({ slug, color }: FormData) => {
|
||||
try {
|
||||
const data = await createWsTag({
|
||||
projectId,
|
||||
tagColor: color,
|
||||
tagSlug: slug
|
||||
});
|
||||
append(data);
|
||||
onToggle(false);
|
||||
reset();
|
||||
createNotification({
|
||||
text: "Successfully created a tag",
|
||||
type: "success"
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
createNotification({
|
||||
text: "Failed to create a tag",
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
const data = await createWsTag({
|
||||
projectId,
|
||||
tagColor: color,
|
||||
tagSlug: slug
|
||||
});
|
||||
append(data);
|
||||
onToggle(false);
|
||||
reset();
|
||||
createNotification({
|
||||
text: "Successfully created a tag",
|
||||
type: "success"
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -9,22 +9,10 @@ export const HighlightText = ({
|
||||
}) => {
|
||||
if (!text) return null;
|
||||
|
||||
const renderTextWithNewlines = (input: string, baseKeyPrefix: string = ""): React.ReactNode[] => {
|
||||
if (!input) return [];
|
||||
const lines = input.split("\n");
|
||||
return lines.flatMap((line, index) => {
|
||||
const nodes: React.ReactNode[] = [line];
|
||||
if (index < lines.length - 1) {
|
||||
nodes.push(<br key={`${baseKeyPrefix}-br-${line}`} />);
|
||||
}
|
||||
return nodes;
|
||||
});
|
||||
};
|
||||
|
||||
const searchTerm = highlight.toLowerCase().trim();
|
||||
|
||||
if (!searchTerm) {
|
||||
return <span>{renderTextWithNewlines(text, "full-text")}</span>;
|
||||
return <span>{text}</span>;
|
||||
}
|
||||
|
||||
const parts: React.ReactNode[] = [];
|
||||
@@ -36,16 +24,12 @@ export const HighlightText = ({
|
||||
text.replace(regex, (match: string, offset: number) => {
|
||||
if (offset > lastIndex) {
|
||||
const preMatchText = text.substring(lastIndex, offset);
|
||||
parts.push(
|
||||
<span key={`pre-${lastIndex}`}>
|
||||
{renderTextWithNewlines(preMatchText, `pre-${lastIndex}`)}
|
||||
</span>
|
||||
);
|
||||
parts.push(<span key={`pre-${lastIndex}`}>{preMatchText}</span>);
|
||||
}
|
||||
|
||||
parts.push(
|
||||
<span key={`match-${offset}`} className={highlightClassName || "bg-yellow/30"}>
|
||||
{renderTextWithNewlines(match, `match-${offset}`)}
|
||||
{match}
|
||||
</span>
|
||||
);
|
||||
|
||||
@@ -56,11 +40,7 @@ export const HighlightText = ({
|
||||
|
||||
if (lastIndex < text.length) {
|
||||
const postMatchText = text.substring(lastIndex);
|
||||
parts.push(
|
||||
<span key={`post-${lastIndex}`}>
|
||||
{renderTextWithNewlines(postMatchText, `post-${lastIndex}`)}
|
||||
</span>
|
||||
);
|
||||
parts.push(<span key={`post-${lastIndex}`}>{postMatchText}</span>);
|
||||
}
|
||||
|
||||
return parts;
|
||||
|
||||
@@ -131,7 +131,7 @@ export const APP_CONNECTION_MAP: Record<
|
||||
image: "Laravel Forge.png",
|
||||
size: 65
|
||||
},
|
||||
[AppConnection.Chef]: { name: "Chef", image: "Chef.png" }
|
||||
[AppConnection.Chef]: { name: "Chef", image: "Chef.png", enterprise: true }
|
||||
};
|
||||
|
||||
export const getAppConnectionMethodDetails = (method: TAppConnection["method"]) => {
|
||||
|
||||
@@ -36,28 +36,21 @@ export const NewSubOrganizationForm = ({ onClose }: ContentProps) => {
|
||||
const router = useRouter();
|
||||
|
||||
const onSubmit = async ({ name }: FormData) => {
|
||||
try {
|
||||
const { organization } = await createSubOrg.mutateAsync({
|
||||
name
|
||||
});
|
||||
const { organization } = await createSubOrg.mutateAsync({
|
||||
name
|
||||
});
|
||||
|
||||
createNotification({
|
||||
type: "success",
|
||||
text: "Successfully created sub organization"
|
||||
});
|
||||
onClose();
|
||||
createNotification({
|
||||
type: "success",
|
||||
text: "Successfully created sub organization"
|
||||
});
|
||||
onClose();
|
||||
|
||||
navigate({
|
||||
to: "/organization/projects",
|
||||
search: (prev) => ({ ...prev, subOrganization: organization.name })
|
||||
});
|
||||
await router.invalidate({ sync: true }).catch(() => null);
|
||||
} catch {
|
||||
createNotification({
|
||||
text: "Failed to create sub organization",
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
navigate({
|
||||
to: "/organization/projects",
|
||||
search: (prev) => ({ ...prev, subOrganization: organization.name })
|
||||
});
|
||||
await router.invalidate({ sync: true }).catch(() => null);
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -11,7 +11,6 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { Link, linkOptions } from "@tanstack/react-router";
|
||||
|
||||
import { UpgradePlanModal } from "@app/components/license/UpgradePlanModal";
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
import { OrgPermissionCan } from "@app/components/permissions";
|
||||
import { NewProjectModal } from "@app/components/projects";
|
||||
import {
|
||||
@@ -59,31 +58,17 @@ export const ProjectSelect = () => {
|
||||
const { mutateAsync: updateUserProjectFavorites } = useUpdateUserProjectFavorites();
|
||||
|
||||
const addProjectToFavorites = async (projectId: string) => {
|
||||
try {
|
||||
await updateUserProjectFavorites({
|
||||
orgId: currentOrg!.id,
|
||||
projectFavorites: [...(projectFavorites || []), projectId]
|
||||
});
|
||||
} catch {
|
||||
createNotification({
|
||||
text: "Failed to add project to favorites.",
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
await updateUserProjectFavorites({
|
||||
orgId: currentOrg!.id,
|
||||
projectFavorites: [...(projectFavorites || []), projectId]
|
||||
});
|
||||
};
|
||||
|
||||
const removeProjectFromFavorites = async (projectId: string) => {
|
||||
try {
|
||||
await updateUserProjectFavorites({
|
||||
orgId: currentOrg!.id,
|
||||
projectFavorites: [...(projectFavorites || []).filter((entry) => entry !== projectId)]
|
||||
});
|
||||
} catch {
|
||||
createNotification({
|
||||
text: "Failed to remove project from favorites.",
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
await updateUserProjectFavorites({
|
||||
orgId: currentOrg!.id,
|
||||
projectFavorites: [...(projectFavorites || []).filter((entry) => entry !== projectId)]
|
||||
});
|
||||
};
|
||||
|
||||
const isAddingProjectsAllowed = subscription?.workspaceLimit
|
||||
|
||||
@@ -65,20 +65,13 @@ const Content = ({ onClose }: ContentProps) => {
|
||||
const users = usersData.filter((user) => !user.superAdmin);
|
||||
|
||||
const onSubmit = async ({ user }: FormData) => {
|
||||
try {
|
||||
await grantAdmin.mutateAsync(user.id);
|
||||
await grantAdmin.mutateAsync(user.id);
|
||||
|
||||
createNotification({
|
||||
type: "success",
|
||||
text: "Successfully granted server admin status"
|
||||
});
|
||||
onClose();
|
||||
} catch {
|
||||
createNotification({
|
||||
text: "Failed to grant server admin status",
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
createNotification({
|
||||
type: "success",
|
||||
text: "Successfully granted server admin status"
|
||||
});
|
||||
onClose();
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -303,18 +303,11 @@ export const ServerAdminsTable = () => {
|
||||
const handleRemoveUser = async () => {
|
||||
const { id } = popUp?.removeUser?.data as { id: string; username: string };
|
||||
|
||||
try {
|
||||
await deleteUser(id);
|
||||
createNotification({
|
||||
type: "success",
|
||||
text: "Successfully deleted user"
|
||||
});
|
||||
} catch {
|
||||
createNotification({
|
||||
type: "error",
|
||||
text: "Error deleting user"
|
||||
});
|
||||
}
|
||||
await deleteUser(id);
|
||||
createNotification({
|
||||
type: "success",
|
||||
text: "Successfully deleted user"
|
||||
});
|
||||
|
||||
handlePopUpClose("removeUser");
|
||||
};
|
||||
@@ -322,39 +315,25 @@ export const ServerAdminsTable = () => {
|
||||
const handleRemoveServerAdminAccess = async () => {
|
||||
const { id } = popUp?.removeServerAdmin?.data as { id: string; username: string };
|
||||
|
||||
try {
|
||||
await removeAdminAccess(id);
|
||||
createNotification({
|
||||
type: "success",
|
||||
text: "Successfully removed server admin access from user"
|
||||
});
|
||||
} catch {
|
||||
createNotification({
|
||||
type: "error",
|
||||
text: "Error removing server admin access from user"
|
||||
});
|
||||
}
|
||||
await removeAdminAccess(id);
|
||||
createNotification({
|
||||
type: "success",
|
||||
text: "Successfully removed server admin access from user"
|
||||
});
|
||||
|
||||
handlePopUpClose("removeServerAdmin");
|
||||
};
|
||||
|
||||
const handleRemoveUsers = async () => {
|
||||
try {
|
||||
await deleteUsers(selectedUsers.map((user) => user.id));
|
||||
await deleteUsers(selectedUsers.map((user) => user.id));
|
||||
|
||||
createNotification({
|
||||
text: "Successfully removed users",
|
||||
type: "success"
|
||||
});
|
||||
createNotification({
|
||||
text: "Successfully removed users",
|
||||
type: "success"
|
||||
});
|
||||
|
||||
setSelectedUsers([]);
|
||||
handlePopUpClose("removeUsers");
|
||||
} catch {
|
||||
createNotification({
|
||||
text: "Failed to remove users",
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
setSelectedUsers([]);
|
||||
handlePopUpClose("removeUsers");
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -54,59 +54,51 @@ export const AuthenticationPageForm = () => {
|
||||
});
|
||||
|
||||
const onAuthFormSubmit = async (formData: TAuthForm) => {
|
||||
try {
|
||||
const enabledMethods: LoginMethod[] = [];
|
||||
if (formData.isEmailEnabled) {
|
||||
enabledMethods.push(LoginMethod.EMAIL);
|
||||
}
|
||||
const enabledMethods: LoginMethod[] = [];
|
||||
if (formData.isEmailEnabled) {
|
||||
enabledMethods.push(LoginMethod.EMAIL);
|
||||
}
|
||||
|
||||
if (formData.isGoogleEnabled) {
|
||||
enabledMethods.push(LoginMethod.GOOGLE);
|
||||
}
|
||||
if (formData.isGoogleEnabled) {
|
||||
enabledMethods.push(LoginMethod.GOOGLE);
|
||||
}
|
||||
|
||||
if (formData.isGithubEnabled) {
|
||||
enabledMethods.push(LoginMethod.GITHUB);
|
||||
}
|
||||
if (formData.isGithubEnabled) {
|
||||
enabledMethods.push(LoginMethod.GITHUB);
|
||||
}
|
||||
|
||||
if (formData.isGitlabEnabled) {
|
||||
enabledMethods.push(LoginMethod.GITLAB);
|
||||
}
|
||||
if (formData.isGitlabEnabled) {
|
||||
enabledMethods.push(LoginMethod.GITLAB);
|
||||
}
|
||||
|
||||
if (formData.isSamlEnabled) {
|
||||
enabledMethods.push(LoginMethod.SAML);
|
||||
}
|
||||
if (formData.isSamlEnabled) {
|
||||
enabledMethods.push(LoginMethod.SAML);
|
||||
}
|
||||
|
||||
if (formData.isLdapEnabled) {
|
||||
enabledMethods.push(LoginMethod.LDAP);
|
||||
}
|
||||
if (formData.isLdapEnabled) {
|
||||
enabledMethods.push(LoginMethod.LDAP);
|
||||
}
|
||||
|
||||
if (formData.isOidcEnabled) {
|
||||
enabledMethods.push(LoginMethod.OIDC);
|
||||
}
|
||||
if (formData.isOidcEnabled) {
|
||||
enabledMethods.push(LoginMethod.OIDC);
|
||||
}
|
||||
|
||||
if (!enabledMethods.length) {
|
||||
createNotification({
|
||||
type: "error",
|
||||
text: "At least one login method should be enabled."
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
await updateServerConfig({
|
||||
enabledLoginMethods: enabledMethods
|
||||
});
|
||||
|
||||
createNotification({
|
||||
text: "Login methods have been successfully updated.",
|
||||
type: "success"
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
if (!enabledMethods.length) {
|
||||
createNotification({
|
||||
type: "error",
|
||||
text: "Failed to update login methods."
|
||||
text: "At least one login method should be enabled."
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
await updateServerConfig({
|
||||
enabledLoginMethods: enabledMethods
|
||||
});
|
||||
|
||||
createNotification({
|
||||
text: "Login methods have been successfully updated.",
|
||||
type: "success"
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -31,15 +31,10 @@ export const CachingPageForm = () => {
|
||||
const handleInvalidateCacheSubmit = async () => {
|
||||
if (!type || isInvalidating) return;
|
||||
|
||||
try {
|
||||
await invalidateCache({ type });
|
||||
createNotification({ text: `Began invalidating ${type} cache`, type: "success" });
|
||||
setShouldPoll(true);
|
||||
handlePopUpClose("invalidateCache");
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
createNotification({ text: `Failed to invalidate ${type} cache`, type: "error" });
|
||||
}
|
||||
await invalidateCache({ type });
|
||||
createNotification({ text: `Began invalidating ${type} cache`, type: "success" });
|
||||
setShouldPoll(true);
|
||||
handlePopUpClose("invalidateCache");
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -60,19 +60,12 @@ export const EncryptionPageForm = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await updateEncryptionStrategy(formData.encryptionStrategy);
|
||||
await updateEncryptionStrategy(formData.encryptionStrategy);
|
||||
|
||||
createNotification({
|
||||
type: "success",
|
||||
text: "Encryption strategy updated successfully"
|
||||
});
|
||||
} catch {
|
||||
createNotification({
|
||||
type: "error",
|
||||
text: "Failed to update encryption strategy"
|
||||
});
|
||||
}
|
||||
createNotification({
|
||||
type: "success",
|
||||
text: "Encryption strategy updated successfully"
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
|
||||
@@ -176,31 +176,19 @@ export const EnvironmentPageForm = () => {
|
||||
|
||||
const onSubmit = useCallback(
|
||||
async (formData: TForm) => {
|
||||
try {
|
||||
const filteredFormData = Object.fromEntries(
|
||||
Object.entries(formData).filter(([, value]) => value !== "")
|
||||
);
|
||||
await updateServerConfig({
|
||||
envOverrides: filteredFormData
|
||||
});
|
||||
const filteredFormData = Object.fromEntries(
|
||||
Object.entries(formData).filter(([, value]) => value !== "")
|
||||
);
|
||||
await updateServerConfig({
|
||||
envOverrides: filteredFormData
|
||||
});
|
||||
|
||||
createNotification({
|
||||
type: "success",
|
||||
text: "Environment overrides updated successfully. It can take up to 5 minutes to take effect."
|
||||
});
|
||||
createNotification({
|
||||
type: "success",
|
||||
text: "Environment overrides updated successfully. It can take up to 5 minutes to take effect."
|
||||
});
|
||||
|
||||
reset(formData);
|
||||
} catch (error) {
|
||||
const errorMessage =
|
||||
(error as any)?.response?.data?.message ||
|
||||
(error as any)?.message ||
|
||||
"An unknown error occurred";
|
||||
createNotification({
|
||||
type: "error",
|
||||
title: "Failed to update environment overrides",
|
||||
text: errorMessage
|
||||
});
|
||||
}
|
||||
reset(formData);
|
||||
},
|
||||
[reset, updateServerConfig]
|
||||
);
|
||||
|
||||
@@ -68,37 +68,29 @@ export const GeneralPageForm = () => {
|
||||
const organizations = useGetOrganizations();
|
||||
|
||||
const onFormSubmit = async (formData: TDashboardForm) => {
|
||||
try {
|
||||
const {
|
||||
allowedSignUpDomain,
|
||||
trustSamlEmails,
|
||||
trustLdapEmails,
|
||||
trustOidcEmails,
|
||||
authConsentContent,
|
||||
pageFrameContent
|
||||
} = formData;
|
||||
const {
|
||||
allowedSignUpDomain,
|
||||
trustSamlEmails,
|
||||
trustLdapEmails,
|
||||
trustOidcEmails,
|
||||
authConsentContent,
|
||||
pageFrameContent
|
||||
} = formData;
|
||||
|
||||
await updateServerConfig({
|
||||
defaultAuthOrgId: defaultAuthOrgId || null,
|
||||
allowSignUp: signUpMode !== SignUpModes.Disabled,
|
||||
allowedSignUpDomain: signUpMode === SignUpModes.Anyone ? allowedSignUpDomain : null,
|
||||
trustSamlEmails,
|
||||
trustLdapEmails,
|
||||
trustOidcEmails,
|
||||
authConsentContent,
|
||||
pageFrameContent
|
||||
});
|
||||
createNotification({
|
||||
text: "Successfully changed sign up setting.",
|
||||
type: "success"
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
createNotification({
|
||||
type: "error",
|
||||
text: "Failed to update sign up setting."
|
||||
});
|
||||
}
|
||||
await updateServerConfig({
|
||||
defaultAuthOrgId: defaultAuthOrgId || null,
|
||||
allowSignUp: signUpMode !== SignUpModes.Disabled,
|
||||
allowedSignUpDomain: signUpMode === SignUpModes.Anyone ? allowedSignUpDomain : null,
|
||||
trustSamlEmails,
|
||||
trustLdapEmails,
|
||||
trustOidcEmails,
|
||||
authConsentContent,
|
||||
pageFrameContent
|
||||
});
|
||||
createNotification({
|
||||
text: "Successfully changed sign up setting.",
|
||||
type: "success"
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -10,23 +10,15 @@ export const UsageReportSection = () => {
|
||||
const generateUsageReport = useGenerateUsageReport();
|
||||
|
||||
const handleGenerateReport = async () => {
|
||||
try {
|
||||
const response = await generateUsageReport.mutateAsync();
|
||||
const { csvContent, filename } = response;
|
||||
const response = await generateUsageReport.mutateAsync();
|
||||
const { csvContent, filename } = response;
|
||||
|
||||
downloadFile(csvContent, filename, "text/csv");
|
||||
downloadFile(csvContent, filename, "text/csv");
|
||||
|
||||
createNotification({
|
||||
text: `Usage report downloaded: "${filename}"`,
|
||||
type: "success"
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Failed to generate usage report:", error);
|
||||
createNotification({
|
||||
text: "Failed to generate usage report. Please try again.",
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
createNotification({
|
||||
text: `Usage report downloaded: "${filename}"`,
|
||||
type: "success"
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -81,25 +81,18 @@ const Content = ({ onClose }: ContentProps) => {
|
||||
const { users = [] } = data ?? {};
|
||||
|
||||
const onSubmit = async ({ name, invitees }: FormData) => {
|
||||
try {
|
||||
await createOrg.mutateAsync({
|
||||
name,
|
||||
inviteAdminEmails: invitees
|
||||
.filter((user) => Boolean(user.email))
|
||||
.map((user) => user.email) as string[]
|
||||
});
|
||||
await createOrg.mutateAsync({
|
||||
name,
|
||||
inviteAdminEmails: invitees
|
||||
.filter((user) => Boolean(user.email))
|
||||
.map((user) => user.email) as string[]
|
||||
});
|
||||
|
||||
createNotification({
|
||||
type: "success",
|
||||
text: "Successfully created organization"
|
||||
});
|
||||
onClose();
|
||||
} catch {
|
||||
createNotification({
|
||||
text: "Failed to create organization",
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
createNotification({
|
||||
type: "success",
|
||||
text: "Successfully created organization"
|
||||
});
|
||||
onClose();
|
||||
};
|
||||
|
||||
const { append } = useFieldArray<FormData>({ control, name: "invitees" });
|
||||
|
||||
@@ -185,18 +185,11 @@ export const MachineIdentitiesTable = () => {
|
||||
const handleRemoveServerAdmin = async () => {
|
||||
const { id } = popUp?.removeServerAdmin?.data as { id: string; name: string };
|
||||
|
||||
try {
|
||||
await deleteIdentitySuperAdminAccess(id);
|
||||
createNotification({
|
||||
type: "success",
|
||||
text: "Successfully removed server admin permissions"
|
||||
});
|
||||
} catch {
|
||||
createNotification({
|
||||
type: "error",
|
||||
text: "Error removing server admin permissions"
|
||||
});
|
||||
}
|
||||
await deleteIdentitySuperAdminAccess(id);
|
||||
createNotification({
|
||||
type: "success",
|
||||
text: "Successfully removed server admin permissions"
|
||||
});
|
||||
|
||||
handlePopUpClose("removeServerAdmin");
|
||||
};
|
||||
|
||||
@@ -179,12 +179,6 @@ const ViewMembersModalContent = ({
|
||||
text: "Successfully resent org invitation",
|
||||
type: "success"
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
createNotification({
|
||||
text: "Failed to resend org invitation",
|
||||
type: "error"
|
||||
});
|
||||
} finally {
|
||||
setResendInviteId(null);
|
||||
}
|
||||
@@ -479,26 +473,19 @@ const OrganizationsPanelTable = ({
|
||||
const { mutateAsync: accessOrganization } = useServerAdminAccessOrg();
|
||||
|
||||
const handleAccessOrg = async (orgId: string) => {
|
||||
try {
|
||||
await accessOrganization(orgId);
|
||||
await accessOrganization(orgId);
|
||||
|
||||
navigate({
|
||||
to: "/login/select-organization",
|
||||
search: {
|
||||
org_id: orgId
|
||||
}
|
||||
});
|
||||
navigate({
|
||||
to: "/login/select-organization",
|
||||
search: {
|
||||
org_id: orgId
|
||||
}
|
||||
});
|
||||
|
||||
createNotification({
|
||||
text: "Successfully joined organization",
|
||||
type: "success"
|
||||
});
|
||||
} catch {
|
||||
createNotification({
|
||||
text: "Failed to join organization",
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
createNotification({
|
||||
text: "Successfully joined organization",
|
||||
type: "success"
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -364,18 +364,11 @@ export const UserIdentitiesTable = () => {
|
||||
const handleRemoveUser = async () => {
|
||||
const { id } = popUp?.removeUser?.data as { id: string; username: string };
|
||||
|
||||
try {
|
||||
await deleteUser(id);
|
||||
createNotification({
|
||||
type: "success",
|
||||
text: "Successfully deleted user"
|
||||
});
|
||||
} catch {
|
||||
createNotification({
|
||||
type: "error",
|
||||
text: "Error deleting user"
|
||||
});
|
||||
}
|
||||
await deleteUser(id);
|
||||
createNotification({
|
||||
type: "success",
|
||||
text: "Successfully deleted user"
|
||||
});
|
||||
|
||||
handlePopUpClose("removeUser");
|
||||
};
|
||||
@@ -383,18 +376,11 @@ export const UserIdentitiesTable = () => {
|
||||
const handleGrantServerAdminAccess = async () => {
|
||||
const { id } = popUp?.upgradeToServerAdmin?.data as { id: string; username: string };
|
||||
|
||||
try {
|
||||
await grantAdminAccess(id);
|
||||
createNotification({
|
||||
type: "success",
|
||||
text: "Successfully granted server admin access to user"
|
||||
});
|
||||
} catch {
|
||||
createNotification({
|
||||
type: "error",
|
||||
text: "Error granting server admin access to user"
|
||||
});
|
||||
}
|
||||
await grantAdminAccess(id);
|
||||
createNotification({
|
||||
type: "success",
|
||||
text: "Successfully granted server admin access to user"
|
||||
});
|
||||
|
||||
handlePopUpClose("upgradeToServerAdmin");
|
||||
};
|
||||
@@ -402,39 +388,25 @@ export const UserIdentitiesTable = () => {
|
||||
const handleRemoveServerAdminAccess = async () => {
|
||||
const { id } = popUp?.removeServerAdmin?.data as { id: string; username: string };
|
||||
|
||||
try {
|
||||
await removeAdminAccess(id);
|
||||
createNotification({
|
||||
type: "success",
|
||||
text: "Successfully removed server admin access from user"
|
||||
});
|
||||
} catch {
|
||||
createNotification({
|
||||
type: "error",
|
||||
text: "Error removing server admin access from user"
|
||||
});
|
||||
}
|
||||
await removeAdminAccess(id);
|
||||
createNotification({
|
||||
type: "success",
|
||||
text: "Successfully removed server admin access from user"
|
||||
});
|
||||
|
||||
handlePopUpClose("removeServerAdmin");
|
||||
};
|
||||
|
||||
const handleRemoveUsers = async () => {
|
||||
try {
|
||||
await deleteUsers(selectedUsers.map((user) => user.id));
|
||||
await deleteUsers(selectedUsers.map((user) => user.id));
|
||||
|
||||
createNotification({
|
||||
text: "Successfully removed users",
|
||||
type: "success"
|
||||
});
|
||||
createNotification({
|
||||
text: "Successfully removed users",
|
||||
type: "success"
|
||||
});
|
||||
|
||||
setSelectedUsers([]);
|
||||
handlePopUpClose("removeUsers");
|
||||
} catch {
|
||||
createNotification({
|
||||
text: "Failed to remove users",
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
setSelectedUsers([]);
|
||||
handlePopUpClose("removeUsers");
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -6,7 +6,6 @@ import { useNavigate } from "@tanstack/react-router";
|
||||
import { AnimatePresence, motion } from "framer-motion";
|
||||
import { z } from "zod";
|
||||
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
// TODO(akhilmhdh): rewrite this into module functions in lib
|
||||
import SecurityClient from "@app/components/utilities/SecurityClient";
|
||||
import { Button, ContentLoader, FormControl, Input } from "@app/components/v2";
|
||||
@@ -46,29 +45,21 @@ export const SignUpPage = () => {
|
||||
const handleFormSubmit = async ({ email, password, firstName, lastName }: TFormSchema) => {
|
||||
// avoid multi submission
|
||||
if (isSubmitting) return;
|
||||
try {
|
||||
const res = await createAdminUser({
|
||||
email,
|
||||
password,
|
||||
firstName,
|
||||
lastName
|
||||
});
|
||||
const res = await createAdminUser({
|
||||
email,
|
||||
password,
|
||||
firstName,
|
||||
lastName
|
||||
});
|
||||
|
||||
SecurityClient.setToken(res.token);
|
||||
await selectOrganization({ organizationId: res.organization.id });
|
||||
SecurityClient.setToken(res.token);
|
||||
await selectOrganization({ organizationId: res.organization.id });
|
||||
|
||||
// TODO(akhilmhdh): This is such a confusing pattern and too unreliable
|
||||
// Will be refactored in next iteration to make it url based rather than local storage ones
|
||||
// Part of migration to nextjs 14
|
||||
localStorage.setItem("orgData.id", res.organization.id);
|
||||
navigate({ to: "/admin" });
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
createNotification({
|
||||
type: "error",
|
||||
text: "Failed to create admin"
|
||||
});
|
||||
}
|
||||
// TODO(akhilmhdh): This is such a confusing pattern and too unreliable
|
||||
// Will be refactored in next iteration to make it url based rather than local storage ones
|
||||
// Part of migration to nextjs 14
|
||||
localStorage.setItem("orgData.id", res.organization.id);
|
||||
navigate({ to: "/admin" });
|
||||
};
|
||||
|
||||
if (config?.initialized) return <ContentLoader text="Redirecting to admin page..." />;
|
||||
|
||||
@@ -75,11 +75,7 @@ export const PasswordSetupPage = () => {
|
||||
setTimeout(() => {
|
||||
window.location.href = "/login";
|
||||
}, 3000);
|
||||
} catch (error) {
|
||||
createNotification({
|
||||
type: "error",
|
||||
text: (error as Error).message ?? "Error setting password"
|
||||
});
|
||||
} catch {
|
||||
navigate({ to: "/personal-settings" });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,67 +73,53 @@ export const EmailConfirmationStep = ({
|
||||
const { mutateAsync: verifyEmailVerificationCode } = useVerifyEmailVerificationCode();
|
||||
|
||||
const checkCode = async () => {
|
||||
try {
|
||||
await verifyEmailVerificationCode({ username, code });
|
||||
setCodeError(false);
|
||||
await verifyEmailVerificationCode({ username, code });
|
||||
setCodeError(false);
|
||||
|
||||
createNotification({
|
||||
text: "Successfully verified code",
|
||||
type: "success"
|
||||
});
|
||||
createNotification({
|
||||
text: "Successfully verified code",
|
||||
type: "success"
|
||||
});
|
||||
|
||||
switch (authType) {
|
||||
case UserAliasType.SAML: {
|
||||
window.open(`/api/v1/sso/redirect/saml2/organizations/${organizationSlug}`);
|
||||
window.close();
|
||||
break;
|
||||
}
|
||||
case UserAliasType.LDAP: {
|
||||
navigate({ to: "/login/ldap", search: { organizationSlug } });
|
||||
break;
|
||||
}
|
||||
case UserAliasType.OIDC: {
|
||||
window.open(`/api/v1/sso/oidc/login?orgSlug=${organizationSlug}`);
|
||||
window.close();
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
setStep(1);
|
||||
break;
|
||||
}
|
||||
switch (authType) {
|
||||
case UserAliasType.SAML: {
|
||||
window.open(`/api/v1/sso/redirect/saml2/organizations/${organizationSlug}`);
|
||||
window.close();
|
||||
break;
|
||||
}
|
||||
case UserAliasType.LDAP: {
|
||||
navigate({ to: "/login/ldap", search: { organizationSlug } });
|
||||
break;
|
||||
}
|
||||
case UserAliasType.OIDC: {
|
||||
window.open(`/api/v1/sso/oidc/login?orgSlug=${organizationSlug}`);
|
||||
window.close();
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
setStep(1);
|
||||
break;
|
||||
}
|
||||
} catch {
|
||||
createNotification({
|
||||
text: "Failed to verify code",
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
|
||||
setCode("");
|
||||
};
|
||||
|
||||
const resendCode = async () => {
|
||||
try {
|
||||
const queryParams = new URLSearchParams(window.location.search);
|
||||
const token = queryParams.get("token");
|
||||
if (!token) {
|
||||
createNotification({
|
||||
text: "Failed to resend code, no token found",
|
||||
type: "error"
|
||||
});
|
||||
return;
|
||||
}
|
||||
await sendEmailVerificationCode(token);
|
||||
const queryParams = new URLSearchParams(window.location.search);
|
||||
const token = queryParams.get("token");
|
||||
if (!token) {
|
||||
createNotification({
|
||||
text: "Successfully resent code",
|
||||
type: "success"
|
||||
});
|
||||
} catch {
|
||||
createNotification({
|
||||
text: "Failed to resend code",
|
||||
text: "Failed to resend code, no token found",
|
||||
type: "error"
|
||||
});
|
||||
return;
|
||||
}
|
||||
await sendEmailVerificationCode(token);
|
||||
createNotification({
|
||||
text: "Successfully resent code",
|
||||
type: "success"
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -113,52 +113,44 @@ export const PkiAlertModal = ({ popUp, handlePopUpToggle }: Props) => {
|
||||
alertUnit,
|
||||
emails
|
||||
}: FormData) => {
|
||||
try {
|
||||
if (!projectId) return;
|
||||
if (!projectId) return;
|
||||
|
||||
const emailArray = emails
|
||||
.split(",")
|
||||
.map((email) => email.trim())
|
||||
.filter((email) => email.length > 0);
|
||||
const emailArray = emails
|
||||
.split(",")
|
||||
.map((email) => email.trim())
|
||||
.filter((email) => email.length > 0);
|
||||
|
||||
const alertBeforeDays = convertToDays(alertUnit, Number(alertBefore));
|
||||
const alertBeforeDays = convertToDays(alertUnit, Number(alertBefore));
|
||||
|
||||
if (alert) {
|
||||
// update
|
||||
await updatePkiAlert({
|
||||
alertId: alert.id,
|
||||
pkiCollectionId,
|
||||
name,
|
||||
projectId,
|
||||
alertBeforeDays,
|
||||
emails: emailArray
|
||||
});
|
||||
} else {
|
||||
// create
|
||||
await createPkiAlert({
|
||||
name,
|
||||
projectId,
|
||||
pkiCollectionId,
|
||||
alertBeforeDays,
|
||||
emails: emailArray
|
||||
});
|
||||
}
|
||||
|
||||
handlePopUpToggle("pkiAlert", false);
|
||||
|
||||
reset();
|
||||
|
||||
createNotification({
|
||||
text: `Successfully ${alert ? "updated" : "created"} alert`,
|
||||
type: "success"
|
||||
if (alert) {
|
||||
// update
|
||||
await updatePkiAlert({
|
||||
alertId: alert.id,
|
||||
pkiCollectionId,
|
||||
name,
|
||||
projectId,
|
||||
alertBeforeDays,
|
||||
emails: emailArray
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
createNotification({
|
||||
text: `Failed to ${alert ? "updated" : "created"} alert`,
|
||||
type: "error"
|
||||
} else {
|
||||
// create
|
||||
await createPkiAlert({
|
||||
name,
|
||||
projectId,
|
||||
pkiCollectionId,
|
||||
alertBeforeDays,
|
||||
emails: emailArray
|
||||
});
|
||||
}
|
||||
|
||||
handlePopUpToggle("pkiAlert", false);
|
||||
|
||||
reset();
|
||||
|
||||
createNotification({
|
||||
text: `Successfully ${alert ? "updated" : "created"} alert`,
|
||||
type: "success"
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -22,27 +22,19 @@ export const PkiAlertsSection = () => {
|
||||
] as const);
|
||||
|
||||
const onRemoveAlertSubmit = async (alertId: string) => {
|
||||
try {
|
||||
if (!projectId) return;
|
||||
if (!projectId) return;
|
||||
|
||||
await deletePkiAlert({
|
||||
alertId,
|
||||
projectId
|
||||
});
|
||||
await deletePkiAlert({
|
||||
alertId,
|
||||
projectId
|
||||
});
|
||||
|
||||
createNotification({
|
||||
text: "Successfully deleted alert",
|
||||
type: "success"
|
||||
});
|
||||
createNotification({
|
||||
text: "Successfully deleted alert",
|
||||
type: "success"
|
||||
});
|
||||
|
||||
handlePopUpClose("deletePkiAlert");
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
createNotification({
|
||||
text: "Failed to delete alert",
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
handlePopUpClose("deletePkiAlert");
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -62,49 +62,41 @@ export const PkiCollectionModal = ({ popUp, handlePopUpToggle }: Props) => {
|
||||
}, [pkiCollection]);
|
||||
|
||||
const onFormSubmit = async ({ name, description }: FormData) => {
|
||||
try {
|
||||
if (!projectId) return;
|
||||
if (!projectId) return;
|
||||
|
||||
if (pkiCollection) {
|
||||
// update
|
||||
await updatePkiCollection({
|
||||
collectionId: pkiCollection.id,
|
||||
name,
|
||||
description,
|
||||
projectId
|
||||
});
|
||||
} else {
|
||||
// create
|
||||
const { id: collectionId } = await createPkiCollection({
|
||||
name,
|
||||
description,
|
||||
projectId
|
||||
});
|
||||
|
||||
navigate({
|
||||
to: "/projects/cert-management/$projectId/pki-collections/$collectionId",
|
||||
params: {
|
||||
projectId,
|
||||
collectionId
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
handlePopUpToggle("pkiCollection", false);
|
||||
|
||||
reset();
|
||||
|
||||
createNotification({
|
||||
text: `Successfully ${pkiCollection ? "updated" : "created"} PKI collection`,
|
||||
type: "success"
|
||||
if (pkiCollection) {
|
||||
// update
|
||||
await updatePkiCollection({
|
||||
collectionId: pkiCollection.id,
|
||||
name,
|
||||
description,
|
||||
projectId
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
createNotification({
|
||||
text: `Failed to ${pkiCollection ? "updated" : "created"} PKI collection`,
|
||||
type: "error"
|
||||
} else {
|
||||
// create
|
||||
const { id: collectionId } = await createPkiCollection({
|
||||
name,
|
||||
description,
|
||||
projectId
|
||||
});
|
||||
|
||||
navigate({
|
||||
to: "/projects/cert-management/$projectId/pki-collections/$collectionId",
|
||||
params: {
|
||||
projectId,
|
||||
collectionId
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
handlePopUpToggle("pkiCollection", false);
|
||||
|
||||
reset();
|
||||
|
||||
createNotification({
|
||||
text: `Successfully ${pkiCollection ? "updated" : "created"} PKI collection`,
|
||||
type: "success"
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -22,27 +22,19 @@ export const PkiCollectionSection = () => {
|
||||
] as const);
|
||||
|
||||
const onRemovePkiCollectionSubmit = async (collectionId: string) => {
|
||||
try {
|
||||
if (!projectId) return;
|
||||
if (!projectId) return;
|
||||
|
||||
await deletePkiCollection({
|
||||
collectionId,
|
||||
projectId
|
||||
});
|
||||
await deletePkiCollection({
|
||||
collectionId,
|
||||
projectId
|
||||
});
|
||||
|
||||
createNotification({
|
||||
text: "Successfully deleted PKI collection",
|
||||
type: "success"
|
||||
});
|
||||
createNotification({
|
||||
text: "Successfully deleted PKI collection",
|
||||
type: "success"
|
||||
});
|
||||
|
||||
handlePopUpClose("deletePkiCollection");
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
createNotification({
|
||||
text: "Failed to delete PKI collection",
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
handlePopUpClose("deletePkiCollection");
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -57,33 +57,26 @@ const Page = () => {
|
||||
] as const);
|
||||
|
||||
const onRemoveCaSubmit = async () => {
|
||||
try {
|
||||
if (!currentProject?.slug) return;
|
||||
if (!currentProject?.slug) return;
|
||||
|
||||
await deleteCa({
|
||||
caName,
|
||||
projectId: currentProject.id,
|
||||
type: CaType.INTERNAL
|
||||
});
|
||||
await deleteCa({
|
||||
caName,
|
||||
projectId: currentProject.id,
|
||||
type: CaType.INTERNAL
|
||||
});
|
||||
|
||||
createNotification({
|
||||
text: "Successfully deleted CA",
|
||||
type: "success"
|
||||
});
|
||||
createNotification({
|
||||
text: "Successfully deleted CA",
|
||||
type: "success"
|
||||
});
|
||||
|
||||
handlePopUpClose("deleteCa");
|
||||
navigate({
|
||||
to: "/projects/cert-management/$projectId/certificate-authorities",
|
||||
params: {
|
||||
projectId
|
||||
}
|
||||
});
|
||||
} catch {
|
||||
createNotification({
|
||||
text: "Failed to delete CA",
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
handlePopUpClose("deleteCa");
|
||||
navigate({
|
||||
to: "/projects/cert-management/$projectId/certificate-authorities",
|
||||
params: {
|
||||
projectId
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -84,27 +84,23 @@ export const CaRenewalModal = ({ popUp, handlePopUpToggle }: Props) => {
|
||||
// }, [ca, parentCa]);
|
||||
|
||||
const onFormSubmit = async ({ type, notAfter }: FormData) => {
|
||||
try {
|
||||
if (!projectSlug || !popUpData.caId) return;
|
||||
if (!projectSlug || !popUpData.caId) return;
|
||||
|
||||
await renewCa({
|
||||
projectSlug,
|
||||
caId: popUpData.caId,
|
||||
notAfter,
|
||||
type
|
||||
});
|
||||
await renewCa({
|
||||
projectSlug,
|
||||
caId: popUpData.caId,
|
||||
notAfter,
|
||||
type
|
||||
});
|
||||
|
||||
handlePopUpToggle("renewCa", false);
|
||||
handlePopUpToggle("renewCa", false);
|
||||
|
||||
createNotification({
|
||||
text: "Successfully renewed CA",
|
||||
type: "success"
|
||||
});
|
||||
createNotification({
|
||||
text: "Successfully renewed CA",
|
||||
type: "success"
|
||||
});
|
||||
|
||||
reset();
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
reset();
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -48,29 +48,22 @@ export const ExternalCaInstallForm = ({ caId, handlePopUpToggle }: Props) => {
|
||||
}, []);
|
||||
|
||||
const onFormSubmit = async ({ certificate, certificateChain }: FormData) => {
|
||||
try {
|
||||
if (!csr || !caId || !currentProject?.slug) return;
|
||||
if (!csr || !caId || !currentProject?.slug) return;
|
||||
|
||||
await importCaCertificate({
|
||||
caId,
|
||||
projectSlug: currentProject?.slug,
|
||||
certificate,
|
||||
certificateChain
|
||||
});
|
||||
await importCaCertificate({
|
||||
caId,
|
||||
projectSlug: currentProject?.slug,
|
||||
certificate,
|
||||
certificateChain
|
||||
});
|
||||
|
||||
reset();
|
||||
reset();
|
||||
|
||||
createNotification({
|
||||
text: "Successfully installed certificate for CA",
|
||||
type: "success"
|
||||
});
|
||||
handlePopUpToggle("installCaCert", false);
|
||||
} catch {
|
||||
createNotification({
|
||||
text: "Failed to install certificate for CA",
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
createNotification({
|
||||
text: "Successfully installed certificate for CA",
|
||||
type: "success"
|
||||
});
|
||||
handlePopUpToggle("installCaCert", false);
|
||||
};
|
||||
|
||||
const downloadTxtFile = (filename: string, content: string) => {
|
||||
|
||||
@@ -101,37 +101,30 @@ export const InternalCaInstallForm = ({ caId, handlePopUpToggle }: Props) => {
|
||||
}, [parentCa]);
|
||||
|
||||
const onFormSubmit = async ({ notAfter, maxPathLength }: FormData) => {
|
||||
try {
|
||||
if (!csr || !caId || !currentProject?.slug) return;
|
||||
if (!csr || !caId || !currentProject?.slug) return;
|
||||
|
||||
const { certificate, certificateChain } = await signIntermediate({
|
||||
caId: parentCaId,
|
||||
csr,
|
||||
maxPathLength: Number(maxPathLength),
|
||||
notAfter,
|
||||
notBefore: new Date().toISOString()
|
||||
});
|
||||
const { certificate, certificateChain } = await signIntermediate({
|
||||
caId: parentCaId,
|
||||
csr,
|
||||
maxPathLength: Number(maxPathLength),
|
||||
notAfter,
|
||||
notBefore: new Date().toISOString()
|
||||
});
|
||||
|
||||
await importCaCertificate({
|
||||
caId,
|
||||
projectSlug: currentProject?.slug,
|
||||
certificate,
|
||||
certificateChain
|
||||
});
|
||||
await importCaCertificate({
|
||||
caId,
|
||||
projectSlug: currentProject?.slug,
|
||||
certificate,
|
||||
certificateChain
|
||||
});
|
||||
|
||||
reset();
|
||||
reset();
|
||||
|
||||
createNotification({
|
||||
text: "Successfully installed certificate for CA",
|
||||
type: "success"
|
||||
});
|
||||
handlePopUpToggle("installCaCert", false);
|
||||
} catch {
|
||||
createNotification({
|
||||
text: "Failed to install certificate for CA",
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
createNotification({
|
||||
text: "Successfully installed certificate for CA",
|
||||
type: "success"
|
||||
});
|
||||
handlePopUpToggle("installCaCert", false);
|
||||
};
|
||||
|
||||
function generatePathLengthOpts(parentCaMaxPathLength: number): number[] {
|
||||
|
||||
@@ -175,48 +175,40 @@ export const CaModal = ({ popUp, handlePopUpToggle }: Props) => {
|
||||
status,
|
||||
configuration
|
||||
}: FormData) => {
|
||||
try {
|
||||
if (!currentProject?.slug) return;
|
||||
if (!currentProject?.slug) return;
|
||||
|
||||
if (ca) {
|
||||
// update
|
||||
await updateMutateAsync({
|
||||
caName: ca.name,
|
||||
projectId: currentProject.id,
|
||||
name,
|
||||
type: CaType.INTERNAL,
|
||||
status,
|
||||
enableDirectIssuance
|
||||
});
|
||||
} else {
|
||||
// create
|
||||
await createMutateAsync({
|
||||
projectId: currentProject.id,
|
||||
name,
|
||||
type,
|
||||
status,
|
||||
enableDirectIssuance,
|
||||
configuration: {
|
||||
...configuration,
|
||||
maxPathLength: Number(configuration.maxPathLength)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
reset();
|
||||
handlePopUpToggle("ca", false);
|
||||
|
||||
createNotification({
|
||||
text: `Successfully ${ca ? "updated" : "created"} CA`,
|
||||
type: "success"
|
||||
if (ca) {
|
||||
// update
|
||||
await updateMutateAsync({
|
||||
caName: ca.name,
|
||||
projectId: currentProject.id,
|
||||
name,
|
||||
type: CaType.INTERNAL,
|
||||
status,
|
||||
enableDirectIssuance
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
createNotification({
|
||||
text: "Failed to create CA",
|
||||
type: "error"
|
||||
} else {
|
||||
// create
|
||||
await createMutateAsync({
|
||||
projectId: currentProject.id,
|
||||
name,
|
||||
type,
|
||||
status,
|
||||
enableDirectIssuance,
|
||||
configuration: {
|
||||
...configuration,
|
||||
maxPathLength: Number(configuration.maxPathLength)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
reset();
|
||||
handlePopUpToggle("ca", false);
|
||||
|
||||
createNotification({
|
||||
text: `Successfully ${ca ? "updated" : "created"} CA`,
|
||||
type: "success"
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -27,44 +27,29 @@ export const CaSection = () => {
|
||||
] as const);
|
||||
|
||||
const onRemoveCaSubmit = async (caName: string) => {
|
||||
try {
|
||||
if (!currentProject?.slug) return;
|
||||
if (!currentProject?.slug) return;
|
||||
|
||||
await deleteCa({ caName, projectId: currentProject.id, type: CaType.INTERNAL });
|
||||
await deleteCa({ caName, projectId: currentProject.id, type: CaType.INTERNAL });
|
||||
|
||||
createNotification({
|
||||
text: "Successfully deleted CA",
|
||||
type: "success"
|
||||
});
|
||||
createNotification({
|
||||
text: "Successfully deleted CA",
|
||||
type: "success"
|
||||
});
|
||||
|
||||
handlePopUpClose("deleteCa");
|
||||
} catch {
|
||||
createNotification({
|
||||
text: "Failed to delete CA",
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
handlePopUpClose("deleteCa");
|
||||
};
|
||||
|
||||
const onUpdateCaStatus = async ({ caName, status }: { caName: string; status: CaStatus }) => {
|
||||
try {
|
||||
if (!currentProject?.slug) return;
|
||||
if (!currentProject?.slug) return;
|
||||
|
||||
await updateCa({ caName, projectId: currentProject.id, type: CaType.INTERNAL, status });
|
||||
await updateCa({ caName, projectId: currentProject.id, type: CaType.INTERNAL, status });
|
||||
|
||||
createNotification({
|
||||
text: `Successfully ${status === CaStatus.ACTIVE ? "enabled" : "disabled"} CA`,
|
||||
type: "success"
|
||||
});
|
||||
createNotification({
|
||||
text: `Successfully ${status === CaStatus.ACTIVE ? "enabled" : "disabled"} CA`,
|
||||
type: "success"
|
||||
});
|
||||
|
||||
handlePopUpClose("caStatus");
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
createNotification({
|
||||
text: `Failed to ${status === CaStatus.ACTIVE ? "enabled" : "disabled"} CA`,
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
handlePopUpClose("caStatus");
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -297,63 +297,55 @@ export const ExternalCaModal = ({ popUp, handlePopUpToggle }: Props) => {
|
||||
status,
|
||||
configuration: formConfiguration
|
||||
}: FormData) => {
|
||||
try {
|
||||
if (!currentProject?.slug) return;
|
||||
if (!currentProject?.slug) return;
|
||||
|
||||
let configPayload: any;
|
||||
let configPayload: any;
|
||||
|
||||
if (type === CaType.ACME && "dnsAppConnection" in formConfiguration) {
|
||||
configPayload = {
|
||||
dnsProviderConfig: formConfiguration.dnsProviderConfig,
|
||||
directoryUrl: formConfiguration.directoryUrl,
|
||||
accountEmail: formConfiguration.accountEmail,
|
||||
dnsAppConnectionId: formConfiguration.dnsAppConnection.id,
|
||||
eabKid: formConfiguration.eabKid,
|
||||
eabHmacKey: formConfiguration.eabHmacKey
|
||||
};
|
||||
} else if (type === CaType.AZURE_AD_CS && "azureAdcsConnection" in formConfiguration) {
|
||||
configPayload = {
|
||||
azureAdcsConnectionId: formConfiguration.azureAdcsConnection.id
|
||||
};
|
||||
} else {
|
||||
throw new Error("Invalid certificate authority configuration");
|
||||
}
|
||||
if (type === CaType.ACME && "dnsAppConnection" in formConfiguration) {
|
||||
configPayload = {
|
||||
dnsProviderConfig: formConfiguration.dnsProviderConfig,
|
||||
directoryUrl: formConfiguration.directoryUrl,
|
||||
accountEmail: formConfiguration.accountEmail,
|
||||
dnsAppConnectionId: formConfiguration.dnsAppConnection.id,
|
||||
eabKid: formConfiguration.eabKid,
|
||||
eabHmacKey: formConfiguration.eabHmacKey
|
||||
};
|
||||
} else if (type === CaType.AZURE_AD_CS && "azureAdcsConnection" in formConfiguration) {
|
||||
configPayload = {
|
||||
azureAdcsConnectionId: formConfiguration.azureAdcsConnection.id
|
||||
};
|
||||
} else {
|
||||
throw new Error("Invalid certificate authority configuration");
|
||||
}
|
||||
|
||||
if (ca) {
|
||||
await updateMutateAsync({
|
||||
caName: ca.name,
|
||||
projectId: currentProject.id,
|
||||
name,
|
||||
type,
|
||||
status,
|
||||
enableDirectIssuance: type === CaType.AZURE_AD_CS ? false : enableDirectIssuance,
|
||||
configuration: configPayload
|
||||
});
|
||||
} else {
|
||||
await createMutateAsync({
|
||||
projectId: currentProject.id,
|
||||
name,
|
||||
type,
|
||||
status,
|
||||
enableDirectIssuance: type === CaType.AZURE_AD_CS ? false : enableDirectIssuance,
|
||||
configuration: configPayload
|
||||
});
|
||||
}
|
||||
|
||||
reset();
|
||||
handlePopUpToggle("ca", false);
|
||||
|
||||
createNotification({
|
||||
text: `Successfully ${ca ? "updated" : "created"} CA`,
|
||||
type: "success"
|
||||
if (ca) {
|
||||
await updateMutateAsync({
|
||||
caName: ca.name,
|
||||
projectId: currentProject.id,
|
||||
name,
|
||||
type,
|
||||
status,
|
||||
enableDirectIssuance: type === CaType.AZURE_AD_CS ? false : enableDirectIssuance,
|
||||
configuration: configPayload
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
createNotification({
|
||||
text: "Failed to create CA",
|
||||
type: "error"
|
||||
} else {
|
||||
await createMutateAsync({
|
||||
projectId: currentProject.id,
|
||||
name,
|
||||
type,
|
||||
status,
|
||||
enableDirectIssuance: type === CaType.AZURE_AD_CS ? false : enableDirectIssuance,
|
||||
configuration: configPayload
|
||||
});
|
||||
}
|
||||
|
||||
reset();
|
||||
handlePopUpToggle("ca", false);
|
||||
|
||||
createNotification({
|
||||
text: `Successfully ${ca ? "updated" : "created"} CA`,
|
||||
type: "success"
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -23,23 +23,16 @@ export const ExternalCaSection = () => {
|
||||
] as const);
|
||||
|
||||
const onRemoveCaSubmit = async (caName: string, type: CaType) => {
|
||||
try {
|
||||
if (!currentProject?.id) return;
|
||||
if (!currentProject?.id) return;
|
||||
|
||||
await deleteCa({ caName, type, projectId: currentProject.id });
|
||||
await deleteCa({ caName, type, projectId: currentProject.id });
|
||||
|
||||
createNotification({
|
||||
text: "Successfully deleted CA",
|
||||
type: "success"
|
||||
});
|
||||
createNotification({
|
||||
text: "Successfully deleted CA",
|
||||
type: "success"
|
||||
});
|
||||
|
||||
handlePopUpClose("deleteCa");
|
||||
} catch {
|
||||
createNotification({
|
||||
text: "Failed to delete CA",
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
handlePopUpClose("deleteCa");
|
||||
};
|
||||
|
||||
const onUpdateCaStatus = async ({
|
||||
@@ -51,24 +44,16 @@ export const ExternalCaSection = () => {
|
||||
type: CaType;
|
||||
status: CaStatus;
|
||||
}) => {
|
||||
try {
|
||||
if (!currentProject?.slug) return;
|
||||
if (!currentProject?.slug) return;
|
||||
|
||||
await updateCa({ caName: name, type, status, projectId: currentProject.id });
|
||||
await updateCa({ caName: name, type, status, projectId: currentProject.id });
|
||||
|
||||
createNotification({
|
||||
text: `Successfully ${status === CaStatus.ACTIVE ? "enabled" : "disabled"} CA`,
|
||||
type: "success"
|
||||
});
|
||||
createNotification({
|
||||
text: `Successfully ${status === CaStatus.ACTIVE ? "enabled" : "disabled"} CA`,
|
||||
type: "success"
|
||||
});
|
||||
|
||||
handlePopUpClose("caStatus");
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
createNotification({
|
||||
text: `Failed to ${status === CaStatus.ACTIVE ? "enable" : "disable"} CA`,
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
handlePopUpClose("caStatus");
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -71,38 +71,30 @@ export const CertificateImportModal = ({ popUp, handlePopUpToggle }: Props) => {
|
||||
chainPem,
|
||||
collectionId
|
||||
}: FormData) => {
|
||||
try {
|
||||
if (!currentProject?.slug) return;
|
||||
if (!currentProject?.slug) return;
|
||||
|
||||
const { serialNumber, certificate, certificateChain, privateKey } = await importCertificate({
|
||||
projectSlug: currentProject.slug,
|
||||
const { serialNumber, certificate, certificateChain, privateKey } = await importCertificate({
|
||||
projectSlug: currentProject.slug,
|
||||
|
||||
certificatePem,
|
||||
privateKeyPem,
|
||||
chainPem,
|
||||
pkiCollectionId: collectionId
|
||||
});
|
||||
certificatePem,
|
||||
privateKeyPem,
|
||||
chainPem,
|
||||
pkiCollectionId: collectionId
|
||||
});
|
||||
|
||||
reset();
|
||||
reset();
|
||||
|
||||
setCertificateDetails({
|
||||
serialNumber,
|
||||
certificate,
|
||||
certificateChain,
|
||||
privateKey
|
||||
});
|
||||
setCertificateDetails({
|
||||
serialNumber,
|
||||
certificate,
|
||||
certificateChain,
|
||||
privateKey
|
||||
});
|
||||
|
||||
createNotification({
|
||||
text: "Successfully imported certificate",
|
||||
type: "success"
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
createNotification({
|
||||
text: "Failed to import certificate",
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
createNotification({
|
||||
text: "Successfully imported certificate",
|
||||
type: "success"
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -243,84 +243,72 @@ export const CertificateIssuanceModal = ({ popUp, handlePopUpToggle, profileId }
|
||||
keyUsages,
|
||||
extendedKeyUsages
|
||||
}: FormData) => {
|
||||
try {
|
||||
if (!currentProject?.slug) {
|
||||
createNotification({
|
||||
text: "Project not found. Please refresh and try again.",
|
||||
type: "error"
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!formProfileId) {
|
||||
createNotification({
|
||||
text: "Please select a certificate profile.",
|
||||
type: "error"
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
let commonName = "";
|
||||
if (
|
||||
constraints.shouldShowSubjectSection &&
|
||||
subjectAttributes &&
|
||||
subjectAttributes.length > 0
|
||||
) {
|
||||
commonName = getAttributeValue(subjectAttributes, "common_name");
|
||||
if (!commonName.trim()) {
|
||||
createNotification({
|
||||
text: "Common name is required.",
|
||||
type: "error"
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const certificateRequest: any = {
|
||||
profileId: formProfileId,
|
||||
projectSlug: currentProject.slug,
|
||||
ttl,
|
||||
signatureAlgorithm,
|
||||
keyAlgorithm,
|
||||
keyUsages: filterUsages(keyUsages) as CertKeyUsage[],
|
||||
extendedKeyUsages: filterUsages(extendedKeyUsages) as CertExtendedKeyUsage[]
|
||||
};
|
||||
|
||||
if (constraints.shouldShowSubjectSection && commonName) {
|
||||
certificateRequest.commonName = commonName;
|
||||
}
|
||||
if (constraints.shouldShowSanSection && subjectAltNames && subjectAltNames.length > 0) {
|
||||
const formattedSans = formatSubjectAltNames(subjectAltNames);
|
||||
if (formattedSans && formattedSans.length > 0) {
|
||||
certificateRequest.altNames = formattedSans;
|
||||
}
|
||||
}
|
||||
|
||||
const { serialNumber, certificate, certificateChain, privateKey } =
|
||||
await createCertificate(certificateRequest);
|
||||
|
||||
setCertificateDetails({
|
||||
serialNumber,
|
||||
certificate,
|
||||
certificateChain,
|
||||
privateKey
|
||||
});
|
||||
|
||||
if (!currentProject?.slug) {
|
||||
createNotification({
|
||||
text: "Successfully created certificate",
|
||||
type: "success"
|
||||
});
|
||||
} catch (err) {
|
||||
console.error("Certificate creation failed:", err);
|
||||
const errorMessage =
|
||||
err instanceof Error
|
||||
? err.message
|
||||
: "An unexpected error occurred while creating the certificate";
|
||||
createNotification({
|
||||
text: `Failed to create certificate: ${errorMessage}`,
|
||||
text: "Project not found. Please refresh and try again.",
|
||||
type: "error"
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!formProfileId) {
|
||||
createNotification({
|
||||
text: "Please select a certificate profile.",
|
||||
type: "error"
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
let commonName = "";
|
||||
if (
|
||||
constraints.shouldShowSubjectSection &&
|
||||
subjectAttributes &&
|
||||
subjectAttributes.length > 0
|
||||
) {
|
||||
commonName = getAttributeValue(subjectAttributes, "common_name");
|
||||
if (!commonName.trim()) {
|
||||
createNotification({
|
||||
text: "Common name is required.",
|
||||
type: "error"
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const certificateRequest: any = {
|
||||
profileId: formProfileId,
|
||||
projectSlug: currentProject.slug,
|
||||
ttl,
|
||||
signatureAlgorithm,
|
||||
keyAlgorithm,
|
||||
keyUsages: filterUsages(keyUsages) as CertKeyUsage[],
|
||||
extendedKeyUsages: filterUsages(extendedKeyUsages) as CertExtendedKeyUsage[]
|
||||
};
|
||||
|
||||
if (constraints.shouldShowSubjectSection && commonName) {
|
||||
certificateRequest.commonName = commonName;
|
||||
}
|
||||
if (constraints.shouldShowSanSection && subjectAltNames && subjectAltNames.length > 0) {
|
||||
const formattedSans = formatSubjectAltNames(subjectAltNames);
|
||||
if (formattedSans && formattedSans.length > 0) {
|
||||
certificateRequest.altNames = formattedSans;
|
||||
}
|
||||
}
|
||||
|
||||
const { serialNumber, certificate, certificateChain, privateKey } =
|
||||
await createCertificate(certificateRequest);
|
||||
|
||||
setCertificateDetails({
|
||||
serialNumber,
|
||||
certificate,
|
||||
certificateChain,
|
||||
privateKey
|
||||
});
|
||||
|
||||
createNotification({
|
||||
text: "Successfully created certificate",
|
||||
type: "success"
|
||||
});
|
||||
},
|
||||
[
|
||||
currentProject?.slug,
|
||||
|
||||
@@ -163,38 +163,28 @@ export const CertificateManageRenewalModal = ({ popUp, handlePopUpToggle }: Prop
|
||||
}, [popUp.manageRenewal.isOpen, defaultRenewalDays, reset]);
|
||||
|
||||
const onUpdateRenewal = async (data: FormData) => {
|
||||
try {
|
||||
if (!currentProject?.slug) {
|
||||
createNotification({
|
||||
text: "Unable to update auto-renewal: Project not found. Please refresh the page and try again.",
|
||||
type: "error"
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
await updateRenewalConfig({
|
||||
certificateId: certificateData.certificateId,
|
||||
renewBeforeDays: data.renewBeforeDays,
|
||||
projectSlug: currentProject.slug
|
||||
});
|
||||
|
||||
if (!currentProject?.slug) {
|
||||
createNotification({
|
||||
text: isAutoRenewalEnabled
|
||||
? "Auto-renewal configuration updated successfully"
|
||||
: "Auto-renewal enabled successfully",
|
||||
type: "success"
|
||||
});
|
||||
|
||||
handlePopUpToggle("manageRenewal", false);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
createNotification({
|
||||
text: isAutoRenewalEnabled
|
||||
? "Failed to update auto-renewal configuration. Please check your inputs and try again."
|
||||
: "Failed to enable auto-renewal. Please check your inputs and try again.",
|
||||
text: "Unable to update auto-renewal: Project not found. Please refresh the page and try again.",
|
||||
type: "error"
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
await updateRenewalConfig({
|
||||
certificateId: certificateData.certificateId,
|
||||
renewBeforeDays: data.renewBeforeDays,
|
||||
projectSlug: currentProject.slug
|
||||
});
|
||||
|
||||
createNotification({
|
||||
text: isAutoRenewalEnabled
|
||||
? "Auto-renewal configuration updated successfully"
|
||||
: "Auto-renewal enabled successfully",
|
||||
type: "success"
|
||||
});
|
||||
|
||||
handlePopUpToggle("manageRenewal", false);
|
||||
};
|
||||
|
||||
const getModalTitle = () => {
|
||||
|
||||
@@ -186,45 +186,37 @@ export const CertificateModal = ({ popUp, handlePopUpToggle }: Props) => {
|
||||
keyUsages,
|
||||
extendedKeyUsages
|
||||
}: FormData) => {
|
||||
try {
|
||||
if (!currentProject?.slug) return;
|
||||
if (!currentProject?.slug) return;
|
||||
|
||||
const { serialNumber, certificate, certificateChain, privateKey } = await createCertificate({
|
||||
caId: !selectedCertTemplate ? caId : undefined,
|
||||
certificateTemplateId: selectedCertTemplate ? selectedCertTemplateId : undefined,
|
||||
projectSlug: currentProject.slug,
|
||||
pkiCollectionId: collectionId,
|
||||
commonName,
|
||||
subjectAltNames,
|
||||
ttl,
|
||||
keyUsages: Object.entries(keyUsages)
|
||||
.filter(([, value]) => value)
|
||||
.map(([key]) => key as CertKeyUsage),
|
||||
extendedKeyUsages: Object.entries(extendedKeyUsages)
|
||||
.filter(([, value]) => value)
|
||||
.map(([key]) => key as CertExtendedKeyUsage)
|
||||
});
|
||||
const { serialNumber, certificate, certificateChain, privateKey } = await createCertificate({
|
||||
caId: !selectedCertTemplate ? caId : undefined,
|
||||
certificateTemplateId: selectedCertTemplate ? selectedCertTemplateId : undefined,
|
||||
projectSlug: currentProject.slug,
|
||||
pkiCollectionId: collectionId,
|
||||
commonName,
|
||||
subjectAltNames,
|
||||
ttl,
|
||||
keyUsages: Object.entries(keyUsages)
|
||||
.filter(([, value]) => value)
|
||||
.map(([key]) => key as CertKeyUsage),
|
||||
extendedKeyUsages: Object.entries(extendedKeyUsages)
|
||||
.filter(([, value]) => value)
|
||||
.map(([key]) => key as CertExtendedKeyUsage)
|
||||
});
|
||||
|
||||
reset();
|
||||
reset();
|
||||
|
||||
setCertificateDetails({
|
||||
serialNumber,
|
||||
certificate,
|
||||
certificateChain,
|
||||
privateKey
|
||||
});
|
||||
setCertificateDetails({
|
||||
serialNumber,
|
||||
certificate,
|
||||
certificateChain,
|
||||
privateKey
|
||||
});
|
||||
|
||||
createNotification({
|
||||
text: "Successfully created certificate",
|
||||
type: "success"
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
createNotification({
|
||||
text: "Failed to create certificate",
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
createNotification({
|
||||
text: "Successfully created certificate",
|
||||
type: "success"
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -61,34 +61,26 @@ export const CertificateRenewalConfigModal = ({ popUp, handlePopUpToggle }: Prop
|
||||
const renewBeforeDays = watch("renewBeforeDays");
|
||||
|
||||
const onSubmit = async (data: FormData) => {
|
||||
try {
|
||||
if (!currentProject?.slug) {
|
||||
createNotification({
|
||||
text: "Project not found",
|
||||
type: "error"
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
await updateRenewalConfig({
|
||||
certificateId: certificateData.certificateId,
|
||||
renewBeforeDays: data.renewBeforeDays,
|
||||
projectSlug: currentProject.slug
|
||||
});
|
||||
|
||||
if (!currentProject?.slug) {
|
||||
createNotification({
|
||||
text: "Successfully updated auto-renewal configuration",
|
||||
type: "success"
|
||||
});
|
||||
|
||||
handlePopUpToggle("configureRenewal", false);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
createNotification({
|
||||
text: "Failed to update auto-renewal configuration",
|
||||
text: "Project not found",
|
||||
type: "error"
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
await updateRenewalConfig({
|
||||
certificateId: certificateData.certificateId,
|
||||
renewBeforeDays: data.renewBeforeDays,
|
||||
projectSlug: currentProject.slug
|
||||
});
|
||||
|
||||
createNotification({
|
||||
text: "Successfully updated auto-renewal configuration",
|
||||
type: "success"
|
||||
});
|
||||
|
||||
handlePopUpToggle("configureRenewal", false);
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -19,34 +19,26 @@ export const CertificateRenewalDisableModal = ({ popUp, handlePopUpToggle }: Pro
|
||||
};
|
||||
|
||||
const onDisableConfirm = async () => {
|
||||
try {
|
||||
if (!currentProject?.slug) {
|
||||
createNotification({
|
||||
text: "Project not found",
|
||||
type: "error"
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
await updateRenewalConfig({
|
||||
certificateId: certificateData.certificateId,
|
||||
projectSlug: currentProject.slug,
|
||||
enableAutoRenewal: false
|
||||
});
|
||||
|
||||
if (!currentProject?.slug) {
|
||||
createNotification({
|
||||
text: "Successfully disabled auto-renewal",
|
||||
type: "success"
|
||||
});
|
||||
|
||||
handlePopUpToggle("disableRenewal", false);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
createNotification({
|
||||
text: "Failed to disable auto-renewal",
|
||||
text: "Project not found",
|
||||
type: "error"
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
await updateRenewalConfig({
|
||||
certificateId: certificateData.certificateId,
|
||||
projectSlug: currentProject.slug,
|
||||
enableAutoRenewal: false
|
||||
});
|
||||
|
||||
createNotification({
|
||||
text: "Successfully disabled auto-renewal",
|
||||
type: "success"
|
||||
});
|
||||
|
||||
handlePopUpToggle("disableRenewal", false);
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -18,22 +18,18 @@ export const CertificateRenewalModal = ({ popUp, handlePopUpToggle }: Props) =>
|
||||
const { mutateAsync: renewCertificate, isPending: isRenewing } = useRenewCertificate();
|
||||
|
||||
const onRenewConfirm = async () => {
|
||||
try {
|
||||
const { certificateId } = popUp.renewCertificate.data as { certificateId: string };
|
||||
const { certificateId } = popUp.renewCertificate.data as { certificateId: string };
|
||||
|
||||
await renewCertificate({
|
||||
certificateId
|
||||
});
|
||||
await renewCertificate({
|
||||
certificateId
|
||||
});
|
||||
|
||||
createNotification({
|
||||
text: "Certificate renewed successfully",
|
||||
type: "success"
|
||||
});
|
||||
createNotification({
|
||||
text: "Certificate renewed successfully",
|
||||
type: "success"
|
||||
});
|
||||
|
||||
handlePopUpToggle("renewCertificate", false);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
handlePopUpToggle("renewCertificate", false);
|
||||
};
|
||||
|
||||
const certificateData = popUp.renewCertificate.data as {
|
||||
|
||||
@@ -48,31 +48,23 @@ export const CertificateRevocationModal = ({ popUp, handlePopUpToggle }: Props)
|
||||
});
|
||||
|
||||
const onFormSubmit = async ({ revocationReason }: FormData) => {
|
||||
try {
|
||||
if (!currentProject?.slug) return;
|
||||
if (!currentProject?.slug) return;
|
||||
|
||||
const { serialNumber } = popUp.revokeCertificate.data as { serialNumber: string };
|
||||
const { serialNumber } = popUp.revokeCertificate.data as { serialNumber: string };
|
||||
|
||||
await revokeCertificate({
|
||||
projectSlug: currentProject.slug,
|
||||
serialNumber,
|
||||
revocationReason
|
||||
});
|
||||
await revokeCertificate({
|
||||
projectSlug: currentProject.slug,
|
||||
serialNumber,
|
||||
revocationReason
|
||||
});
|
||||
|
||||
reset();
|
||||
handlePopUpToggle("revokeCertificate", false);
|
||||
reset();
|
||||
handlePopUpToggle("revokeCertificate", false);
|
||||
|
||||
createNotification({
|
||||
text: "Successfully revoked certificate",
|
||||
type: "success"
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
createNotification({
|
||||
text: "Failed to revoke certificate",
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
createNotification({
|
||||
text: "Successfully revoked certificate",
|
||||
type: "success"
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -159,69 +159,61 @@ export const CertificateTemplateModal = ({ popUp, handlePopUpToggle, caId }: Pro
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (certTemplate) {
|
||||
await updateCertTemplate({
|
||||
id: certTemplate.id,
|
||||
projectId: currentProject.id,
|
||||
pkiCollectionId: collectionId,
|
||||
caId,
|
||||
name,
|
||||
commonName,
|
||||
subjectAlternativeName,
|
||||
ttl,
|
||||
keyUsages: Object.entries(keyUsages)
|
||||
.filter(([, value]) => value)
|
||||
.map(([key]) =>
|
||||
key === CertKeyUsage.CRL_SIGN
|
||||
? "cRLSign"
|
||||
: key.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase())
|
||||
),
|
||||
extendedKeyUsages: Object.entries(extendedKeyUsages)
|
||||
.filter(([, value]) => value)
|
||||
.map(([key]) => key.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase()))
|
||||
});
|
||||
if (certTemplate) {
|
||||
await updateCertTemplate({
|
||||
id: certTemplate.id,
|
||||
projectId: currentProject.id,
|
||||
pkiCollectionId: collectionId,
|
||||
caId,
|
||||
name,
|
||||
commonName,
|
||||
subjectAlternativeName,
|
||||
ttl,
|
||||
keyUsages: Object.entries(keyUsages)
|
||||
.filter(([, value]) => value)
|
||||
.map(([key]) =>
|
||||
key === CertKeyUsage.CRL_SIGN
|
||||
? "cRLSign"
|
||||
: key.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase())
|
||||
),
|
||||
extendedKeyUsages: Object.entries(extendedKeyUsages)
|
||||
.filter(([, value]) => value)
|
||||
.map(([key]) => key.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase()))
|
||||
});
|
||||
|
||||
createNotification({
|
||||
text: "Successfully updated certificate template",
|
||||
type: "success"
|
||||
});
|
||||
} else {
|
||||
await createCertTemplate({
|
||||
projectId: currentProject.id,
|
||||
pkiCollectionId: collectionId,
|
||||
caId,
|
||||
name,
|
||||
commonName,
|
||||
subjectAlternativeName,
|
||||
ttl,
|
||||
keyUsages: Object.entries(keyUsages)
|
||||
.filter(([, value]) => value)
|
||||
.map(([key]) =>
|
||||
key === CertKeyUsage.CRL_SIGN
|
||||
? "cRLSign"
|
||||
: key.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase())
|
||||
),
|
||||
extendedKeyUsages: Object.entries(extendedKeyUsages)
|
||||
.filter(([, value]) => value)
|
||||
.map(([key]) => key.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase()))
|
||||
});
|
||||
|
||||
createNotification({
|
||||
text: "Successfully created certificate template",
|
||||
type: "success"
|
||||
});
|
||||
}
|
||||
|
||||
reset();
|
||||
handlePopUpToggle("certificateTemplate", false);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
createNotification({
|
||||
text: "Failed to save changes",
|
||||
type: "error"
|
||||
text: "Successfully updated certificate template",
|
||||
type: "success"
|
||||
});
|
||||
} else {
|
||||
await createCertTemplate({
|
||||
projectId: currentProject.id,
|
||||
pkiCollectionId: collectionId,
|
||||
caId,
|
||||
name,
|
||||
commonName,
|
||||
subjectAlternativeName,
|
||||
ttl,
|
||||
keyUsages: Object.entries(keyUsages)
|
||||
.filter(([, value]) => value)
|
||||
.map(([key]) =>
|
||||
key === CertKeyUsage.CRL_SIGN
|
||||
? "cRLSign"
|
||||
: key.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase())
|
||||
),
|
||||
extendedKeyUsages: Object.entries(extendedKeyUsages)
|
||||
.filter(([, value]) => value)
|
||||
.map(([key]) => key.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase()))
|
||||
});
|
||||
|
||||
createNotification({
|
||||
text: "Successfully created certificate template",
|
||||
type: "success"
|
||||
});
|
||||
}
|
||||
|
||||
reset();
|
||||
handlePopUpToggle("certificateTemplate", false);
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -41,25 +41,17 @@ export const CertificateTemplatesSection = ({ caId }: Props) => {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await deleteCertTemplate({
|
||||
id,
|
||||
projectId: currentProject.id
|
||||
});
|
||||
await deleteCertTemplate({
|
||||
id,
|
||||
projectId: currentProject.id
|
||||
});
|
||||
|
||||
createNotification({
|
||||
text: "Successfully deleted certificate template",
|
||||
type: "success"
|
||||
});
|
||||
createNotification({
|
||||
text: "Successfully deleted certificate template",
|
||||
type: "success"
|
||||
});
|
||||
|
||||
handlePopUpClose("deleteCertificateTemplate");
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
createNotification({
|
||||
text: "Failed to delete certificate template",
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
handlePopUpClose("deleteCertificateTemplate");
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -42,24 +42,16 @@ export const CertificatesSection = () => {
|
||||
] as const);
|
||||
|
||||
const onRemoveCertificateSubmit = async (serialNumber: string) => {
|
||||
try {
|
||||
if (!currentProject?.slug) return;
|
||||
if (!currentProject?.slug) return;
|
||||
|
||||
await deleteCert({ serialNumber, projectSlug: currentProject.slug });
|
||||
await deleteCert({ serialNumber, projectSlug: currentProject.slug });
|
||||
|
||||
createNotification({
|
||||
text: "Successfully deleted certificate",
|
||||
type: "success"
|
||||
});
|
||||
createNotification({
|
||||
text: "Successfully deleted certificate",
|
||||
type: "success"
|
||||
});
|
||||
|
||||
handlePopUpClose("deleteCertificate");
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
createNotification({
|
||||
text: "Failed to delete certificate",
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
handlePopUpClose("deleteCertificate");
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user