Merge branch 'main' of https://github.com/Infisical/infisical into feat/adds-GETtokenAuthTokenById-api-endpoint

This commit is contained in:
Piyush Gupta
2025-11-18 11:29:33 +05:30
43 changed files with 420 additions and 380 deletions

View File

@@ -354,16 +354,21 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
(tx || db.replicaNode())(TableName.SecretApprovalRequest)
.join(TableName.SecretFolder, `${TableName.SecretApprovalRequest}.folderId`, `${TableName.SecretFolder}.id`)
.join(TableName.Environment, `${TableName.SecretFolder}.envId`, `${TableName.Environment}.id`)
.join(
TableName.SecretApprovalPolicyApprover,
`${TableName.SecretApprovalRequest}.policyId`,
`${TableName.SecretApprovalPolicyApprover}.policyId`
)
.join(
TableName.SecretApprovalPolicy,
`${TableName.SecretApprovalRequest}.policyId`,
`${TableName.SecretApprovalPolicy}.id`
)
.leftJoin(
TableName.SecretApprovalPolicyApprover,
`${TableName.SecretApprovalPolicy}.id`,
`${TableName.SecretApprovalPolicyApprover}.policyId`
)
.leftJoin(
TableName.UserGroupMembership,
`${TableName.SecretApprovalPolicyApprover}.approverGroupId`,
`${TableName.UserGroupMembership}.groupId`
)
.where({ projectId })
.where((qb) => {
if (policyId) void qb.where(`${TableName.SecretApprovalPolicy}.id`, policyId);
@@ -373,10 +378,10 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
void bd
.where(`${TableName.SecretApprovalPolicyApprover}.approverUserId`, userId)
.orWhere(`${TableName.SecretApprovalRequest}.committerUserId`, userId)
.orWhere(`${TableName.UserGroupMembership}.userId`, userId)
)
.select("status", `${TableName.SecretApprovalRequest}.id`)
.groupBy(`${TableName.SecretApprovalRequest}.id`, "status")
.count("status")
)
.select("status")
.from("temp")
@@ -499,7 +504,6 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
const query = (tx || db.replicaNode())
.select("*")
.select(db.raw("count(*) OVER() as total_count"))
.from(innerQuery)
.orderBy("createdAt", "desc") as typeof innerQuery;
@@ -519,6 +523,14 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
});
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const countResult = await (tx || db.replicaNode())
.count({ count: "*" })
.from(query.clone().as("count_query"))
.first();
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
const totalCount = Number(countResult?.count || 0);
const docs = await (tx || db)
.with("w", query)
.select("*")
@@ -526,9 +538,6 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
.where("w.rank", ">=", offset)
.andWhere("w.rank", "<", offset + limit);
// @ts-expect-error knex does not infer
const totalCount = Number(docs[0]?.total_count || 0);
const formattedDoc = sqlNestRelationships({
data: docs,
key: "id",

View File

@@ -742,6 +742,7 @@
"pages": [
"documentation/platform/pki/enrollment-methods/overview",
"documentation/platform/pki/enrollment-methods/api",
"documentation/platform/pki/enrollment-methods/acme",
"documentation/platform/pki/enrollment-methods/est"
]
},

View File

@@ -35,7 +35,7 @@ Infisical consists of several tightly integrated products, each designed to solv
- [Secrets Management](/documentation/platform/secrets-mgmt/overview): Securely store, access, and distribute secrets across environments with fine-grained controls, automatic rotation, and audit logging.
- [Secrets Scanning](/documentation/platform/secret-scanning/overview): Detect hardcoded secrets in code, CI pipelines, and infrastructure—integrated with GitHub, GitLab, Bitbucket, and more.
- [Infisical PKI](/documentation/platform/pki/overview): Issue and manage X.509 certificates using protocols like EST, with support for internal and external CAs.
- [Certificate Management](/documentation/platform/pki/overview): Issue and manage X.509 certificates using protocols like EST, with support for internal and external CAs.
- [Infisical SSH](/documentation/platform/ssh/overview): Provide short-lived SSH access to servers using certificate-based authentication, replacing static keys with policy-driven, time-bound control.
- [Infisical KMS](/documentation/platform/kms/overview): Encrypt and decrypt data using centrally managed keys with enforced access policies and full audit visibility.
- [Infisical PAM](/documentation/platform/pam/overview): Manage access to resources like databases, servers, and accounts with policy-based controls and approvals.

View File

@@ -16,15 +16,37 @@ Key Features:
- Role Assignment: Identities must be assigned [roles](/documentation/platform/access-controls/role-based-access-controls). These roles determine the scope of access to resources, either at the organization level or project level.
- Auth/Token Configuration: Identities must be configured with corresponding authentication methods and access token properties to securely interact with the Infisical API.
## Scopes
Identities can be created either at the organization-level or the project-level. Outside of identity management and scope of operation, organization and project identities are functionally identical.
- Project identities are managed at the project-level and can only operate within their respective project.
Project-level identities are useful for organizations that delegate responsibility to autonomous teams via projects.
- Organization identities are managed at the organization-level and can be assigned to one or more projects, as well as
perform organization-level operations. Organization-level identities are useful for organizations that have cross-project operations.
## Workflow
A typical workflow for using identities consists of four steps:
<Tabs>
<Tab title="Project Identities">
A typical workflow for using project identities consists of three steps:
1. Creating the identity with a name and [role](/documentation/platform/access-controls/role-based-access-controls) in Organization Access Control > Machine Identities.
This step also involves configuring an authentication method for it.
2. Adding the identity to the project(s) you want it to have access to.
3. Authenticating the identity with the Infisical API based on the configured authentication method on it and receiving a short-lived access token back.
4. Authenticating subsequent requests with the Infisical API using the short-lived access token.
1. Creating the identity with a name and [role](/documentation/platform/access-controls/role-based-access-controls) in Project > Access Control > Machine Identities.
This step also involves configuring an authentication method for it.
2. Authenticating the identity with the Infisical API based on the configured authentication method on it and receiving a short-lived access token back.
3. Authenticating subsequent requests with the Infisical API using the short-lived access token.
</Tab>
<Tab title="Organization Identities">
A typical workflow for using organization identities consists of four steps:
1. Creating the identity with a name and [role](/documentation/platform/access-controls/role-based-access-controls) in Organization > Access Control > Machine Identities.
This step also involves configuring an authentication method for it.
2. Adding the identity to the project(s) you want it to have access to.
3. Authenticating the identity with the Infisical API based on the configured authentication method on it and receiving a short-lived access token back.
4. Authenticating subsequent requests with the Infisical API using the short-lived access token.
</Tab>
</Tabs>
## Authentication Methods

View File

@@ -22,10 +22,7 @@ where you can manage various aspects of its lifecycle including deployment to cl
To issue a certificate, you must first create a [certificate profile](/documentation/platform/pki/certificates/profiles) and a [certificate template](/documentation/platform/pki/certificates/templates) to go along with it.
The [enrollment method](/documentation/platform/pki/enrollment-methods/overview) configured on the certificate profile determines how a certificate is issued for it.
Refer to the documentation for each enrollment method below to learn more about how to issue certificates using it.
- [API](/documentation/platform/pki/enrollment-methods/api): Issue a certificate over UI or by making an API request to Infisical.
- [EST](/documentation/platform/pki/enrollment-methods/est): Issue a certificate over the EST protocol.
Refer to the documentation for each enrollment method to learn more about how to issue certificates using it.
## Guide to Renewing Certificates
@@ -61,9 +58,9 @@ A certificate can be considered for auto-renewal at time of issuance if the **En
The following examples demonstrate different approaches to certificate renewal:
- Using the ACME enrollment method, you may connect an ACME client like [certbot](https://certbot.eff.org/) to fetch back and renew certificates for Apache, Nginx, or other server. The ACME client will pursue a client-driven approach and submit certificate requests upon certificate expiration for you, saving renewed certificates back to the server's configuration.
- Using the ACME enrollment method, you may use [cert-manager](https://cert-manager.io/) with Infisical to issue and renew certificates for Kubernetes workloads; cert-manager will pursue a client-driven approach and submit certificate requests upon certificate expiration for you, saving renewed certificates back to Kubernetes secrets.
- Using the API enrollment method, you may push and auto-renew certificates to AWS and Azure using [certificate syncs](/documentation/platform/pki/certificate-syncs/overview). Certificates issued over the API enrollment method, where key pairs are generated server-side, are also eligible for server-side auto-renewal; once renewed, certificates are automatically pushed back to their sync destination.
- Using the [ACME enrollment method](/documentation/platform/pki/enrollment-methods/acme), you may connect an ACME client like [certbot](https://certbot.eff.org/) to fetch back and renew certificates for [Apache](/documentation/platform/pki/integration-guides/apache-certbot), [Nginx](/documentation/platform/pki/integration-guides/nginx-certbot), or other server. The ACME client will pursue a client-driven approach and submit certificate requests upon certificate expiration for you, saving renewed certificates back to the server's configuration.
- Using the [ACME enrollment method](/documentation/platform/pki/enrollment-methods/acme), you may use [cert-manager](https://cert-manager.io/) with Infisical to issue and renew certificates for Kubernetes workloads; cert-manager will pursue a client-driven approach and submit certificate requests upon certificate expiration for you, saving renewed certificates back to Kubernetes secrets.
- Using the [API enrollment method](/documentation/platform/pki/enrollment-methods/api), you may push and auto-renew certificates to AWS and Azure using [certificate syncs](/documentation/platform/pki/certificate-syncs/overview). Certificates issued over the API enrollment method, where key pairs are generated server-side, are also eligible for server-side auto-renewal; once renewed, certificates are automatically pushed back to their sync destination.
## Guide to Downloading Certificates

View File

@@ -7,7 +7,7 @@ sidebarTitle: "Templates"
A certificate template is a policy structure specifying permitted attributes for requested certificates. This includes constraints around subject naming conventions, SAN fields, key usages, and extended key usages.
Each certificate requested against a profile is validated against the template bound to that profile. If the request fails any criteria included in the template, the certificate is not issued. This helps administrators enforce uniformity and security standards across all issued certificates.
Each certificate requested against a [certificate profile](/documentation/platform/pki/certificates/profiles) is validated against the template bound to that profile. If the request fails any criteria included in the template, the certificate is not issued. This helps administrators enforce uniformity and security standards across all issued certificates.
## Guide to Creating a Certificate Template

View File

@@ -3,6 +3,62 @@ title: "Certificate Enrollment via ACME"
sidebarTitle: "ACME"
---
<Info>
ACME-based certificate enrollment is currently under development and will be included in a future release.
</Info>
## Concept
The ACME enrollment method allows you to issue and manage certificates against a specific [certificate profile](/documentation/platform/pki/certificates/profiles) using the [ACME protocol](https://en.wikipedia.org/wiki/Automatic_Certificate_Management_Environment).
This method is suitable for web servers, load balancers, and other general-purpose servers that can run an [ACME client](https://letsencrypt.org/docs/client-options/) for automated certificate management.
Infisical's ACME enrollment method is based on [RFC 8555](https://datatracker.ietf.org/doc/html/rfc8555/).
## Prerequisites
Install an [ACME client](https://letsencrypt.org/docs/client-options/) onto your server. This client will handle [ACME challenges](https://letsencrypt.org/docs/challenge-types/) and request/renew certificates from Infisical.
## Guide to Certificate Enrollment via ACME
In the following steps, we explore how to issue a X.509 certificate using the ACME enrollment method.
<Steps>
<Step title="Create a certificate profile in Infisical">
Create a [certificate
profile](/documentation/platform/pki/certificates/profiles) with **ACME**
selected as the enrollment method.
![pki acme config](/images/platform/pki/enrollment-methods/acme/acme-config.png)
</Step>
<Step title="Obtain the ACME configuration">
Once you've created the certificate profile, you can obtain its ACME configuration details by clicking the **Reveal ACME EAB** option on the profile.
![pki acme eab config](/images/platform/pki/enrollment-methods/acme/acme-eab.png)
From the ACME configuration, gather the following values:
- ACME Directory URL: The URL that the ACME client will use to communicate with Infisical's ACME server.
- EAB Key Identifier (KID): A unique identifier that tells Infisical which ACME account is making the request.
- EAB Secret: A secret key that authenticates your ACME client with Infisical.
</Step>
<Step title="Configure your ACME client">
Provide the **ACME Directory URL**, **EAB KID**, and **EAB Secret** from Step 2 to your ACME client to authenticate with Infisical and request a certificate.
For example, if using [Certbot](https://certbot.eff.org/) as an ACME client, you can configure and start requesting certificates with the following command:
```bash
sudo certbot certonly \
--standalone \
--server "https://your-infisical-instance.com/api/v1/pki/certificate-profiles/{profile-id}/acme/directory" \
--eab-kid "your-eab-kid" \
--eab-hmac-key "your-eab-secret" \
-d example.infisical.com \
--email admin@example.com \
--agree-tos \
--non-interactive
```
Certbot stores the private key and resulting leaf certificate and full certificate chain in `/etc/letsencrypt/live/{domain-name}/`.
For client-specific setup and usage instructions, refer to the documentation for your ACME client.
</Step>
</Steps>

View File

@@ -5,7 +5,7 @@ sidebarTitle: "API"
## Concept
The API enrollment method allows you to issue certificates against a specific certificate profile over Web UI or by making an API request to Infisical.
The API enrollment method allows you to issue certificates against a specific [certificate profile](/documentation/platform/pki/certificates/profiles) over Web UI or by making an API request to Infisical.
## Guide to Certificate Enrollment via API
@@ -15,7 +15,7 @@ In the following steps, we explore how to issue a X.509 certificate using the AP
<Tab title="Infisical UI">
<Steps>
<Step title="Create a certificate profile">
<Step title="Create a certificate profile in Infisical">
Create a [certificate
profile](/documentation/platform/pki/certificates/profiles) with **API**
selected as the enrollment method.
@@ -54,7 +54,7 @@ Here, select the certificate profile from step 1 that will be used to issue the
<Tab title="API">
<Steps>
<Step title="Create a certificate profile">
<Step title="Create a certificate profile in Infisical">
To create a certificate [profile](/documentation/platform/pki/certificates/profiles), make an API request to the [Create Certificate Profile](/api-reference/endpoints/certificate-profiles/create) API endpoint.

View File

@@ -5,7 +5,7 @@ sidebarTitle: "EST"
## Concept
The API enrollment method allows you to issue and manage certificates against a specific certificate profile using the [EST protocol](https://en.wikipedia.org/wiki/Enrollment_over_Secure_Transport).
The EST enrollment method allows you to issue and manage certificates against a specific [certificate profile](/documentation/platform/pki/certificates/profiles) using the [EST protocol](https://en.wikipedia.org/wiki/Enrollment_over_Secure_Transport).
This method is suitable for environments requiring strong authentication and encrypted communication, such as in IoT, enterprise networks, and secure web services.
Infisical's EST service is based on [RFC 7030](https://datatracker.ietf.org/doc/html/rfc7030) and implements the following endpoints:
@@ -32,7 +32,7 @@ and structured under `https://app.infisical.com:8443/.well-known/est/{profile_id
In the following steps, we explore how to issue a X.509 certificate using the EST enrollment method.
<Steps>
<Step title="Set up up a certificate profile">
<Step title="Create a certificate profile in Infisical">
Create a [certificate
profile](/documentation/platform/pki/certificates/profiles) with **EST**
selected as the enrollment method and fill in EST-specific configuration.

View File

@@ -5,7 +5,10 @@ sidebarTitle: "Overview"
Enrollment methods determine how certificates are issued and managed for a [certificate profile](/documentation/platform/pki/certificates/profiles).
Refer to the documentation for each enrollment method to learn more about how to enroll certificates using it.
Refer to the documentation for each enrollment method below to learn more about how to enroll certificates using it.
- [API](/documentation/platform/pki/enrollment-methods/api): Enroll certificates via API.
- [EST](/documentation/platform/pki/enrollment-methods/est): Enroll certificates via EST protocol.
- [ACME](/documentation/platform/pki/enrollment-methods/acme): Enroll certificates using the ACME protocol.
- [EST](/documentation/platform/pki/enrollment-methods/est): Enroll certificates using the EST protocol.
Note that beyond using an enrollment method, you can also deliver a certificate to a target destination using supported [certificate syncs](https://infisical.com/docs/documentation/platform/pki/certificate-syncs/overview).

View File

@@ -12,7 +12,7 @@ Core capabilities include:
- [Private CA](/documentation/platform/pki/ca/private-ca): Create and manage your own private CA hierarchy including root and intermediate CAs.
- [External CA integration](/documentation/platform/pki/ca/external-ca): Integrate with external public and private CAs including [Azure ADCS](/documentation/platform/pki/ca/azure-adcs) and [ACME-compatible CAs](/documentation/platform/pki/ca/acme-ca) like Let's Encrypt and DigiCert.
- [Certificate Enrollment](/documentation/platform/pki/enrollment-methods/overview): Support enrollment methods including [API](/documentation/platform/pki/enrollment-methods/api), ACME, [EST](/documentation/platform/pki/enrollment-methods/est), and more to automate certificate issuance for services, devices, and workloads.
- [Certificate Enrollment](/documentation/platform/pki/enrollment-methods/overview): Support enrollment methods including [API](/documentation/platform/pki/enrollment-methods/api), [ACME](/documentation/platform/pki/enrollment-methods/acme), [EST](/documentation/platform/pki/enrollment-methods/est), and more to automate certificate issuance for services, devices, and workloads.
- Certificate Inventory: Track and monitor issued X.509 certificates, maintaining a comprehensive inventory of all active and expired certificates.
- Certificate Lifecycle Automation: Automate issuance, [renewal](/documentation/platform/pki/certificates/certificates#guide-to-renewing-certificates), and [revocation](/documentation/platform/pki/certificates/certificates#guide-to-revoking-certificates) with policy-based workflows, ensuring certificates remain valid, compliant, and up to date across your infrastructure.
- [Certificate Syncs](/documentation/platform/pki/certificate-syncs/overview): Push certificates to cloud certificate managers like [AWS Certificate Manager](/documentation/platform/pki/certificate-syncs/aws-certificate-manager) and [Azure Key Vault](/documentation/platform/pki/certificate-syncs/azure-key-vault).

Binary file not shown.

After

Width:  |  Height:  |  Size: 313 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 298 KiB

View File

@@ -694,4 +694,20 @@ For enterprise deployments requiring compliance certifications:
### Standards Compliance
**FIPS 140-3 Compliance**. Infisical is actively working on FIPS 140-3 compliance to meet U.S. and Canadian government cryptographic standards. This will provide validated cryptographic modules for organizations requiring certified encryption implementations.
#### FIPS 140-3 Compliance
Infisical is compliant with FIPS 140-3, meeting U.S. and Canadian government cryptographic standards through validated cryptographic modules.
This certification is designed for organizations that require government-approved encryption implementations.
To deploy a FIPS-compliant instance, use the [infisical/infisical-fips](https://hub.docker.com/r/infisical/infisical-fips) Docker image, available to Enterprise customers.
Our FIPS 140-3 attestation letter is available in the [Infisical Trust Center](https://trust.infisical.com/).
#### SOC 2 Compliance
Infisical is SOC 2 compliant, demonstrating adherence to rigorous security, availability, and confidentiality standards established by the American Institute of CPAs (AICPA).
This certification validates our security controls and operational practices for organizations requiring third-party audited security assurance. Our SOC 2 report is available in the [Infisical Trust Center](https://trust.infisical.com/).
#### HIPAA Compliance
Infisical is HIPAA compliant, meeting the security and privacy requirements of the Health Insurance Portability and Accountability Act.
This compliance framework ensures appropriate safeguards for protected health information (PHI) for healthcare organizations and their business associates.
Our HIPAA certification is available in the [Infisical Trust Center](https://trust.infisical.com/).

View File

@@ -41,7 +41,6 @@
"common": {
"head-title": "{{title}} | Infisical",
"error_project-already-exists": "A project with this name already exists.",
"no-mobile": " To use Infisical, please log in through a device with larger dimensions. ",
"email": "Email",
"password": "Password",
"first-name": "First Name",

View File

@@ -41,7 +41,6 @@
"common": {
"head-title": "{{title}} | Infisical",
"error_project-already-exists": "Ya existe un proyecto con este nombre.",
"no-mobile": "Para usar Infisical, inicia sesión con un dispositivo de mayores dimesiones.",
"email": "Correo electrónico",
"password": "Contraseña",
"first-name": "Nombre",

View File

@@ -41,7 +41,6 @@
"common": {
"head-title": "{{title}} | Infisical",
"error_project-already-exists": "Un projet avec ce nom existe déjà.",
"no-mobile": " Pour utiliser Infisical, veuillez vous connecter avec un appareil avec des dimensions plus grandes. ",
"email": "Email",
"password": "Mot de passe",
"first-name": "Prénom",

View File

@@ -30,7 +30,6 @@
"common": {
"head-title": "{{title}} | Infisical",
"error_project-already-exists": "동일한 이름을 가진 프로젝트가 이미 존재해요.",
"no-mobile": " Infisical을 사용하려면, 큰 화면을 가진 디바이스로 로그인하여 주세요.",
"email": "메일",
"password": "비밀번호",
"first-name": "이름",

View File

@@ -41,7 +41,6 @@
"common": {
"head-title": "{{title}} | Infisical",
"error_project-already-exists": "Já exite um projeto com este nome.",
"no-mobile": "Para usar o Infisical, faça o login através de um dispositivo com dimensões maiores.",
"email": "Email",
"password": "Senha",
"first-name": "Primeiro Nome",

View File

@@ -41,7 +41,6 @@
"common": {
"head-title": "{{title}} | Infisical",
"error_project-already-exists": "Bu isimle bir proje zaten mevcut.",
"no-mobile": " Infisical'ı kullanmak için, lütfen daha büyük boyutlara sahip bir cihaz üzerinden giriş yapın. ",
"email": "Email",
"password": "Şifre",
"first-name": "Adınız",

View File

@@ -76,8 +76,8 @@ export const ProjectOverviewChangeSection = ({ showSlugField = false }: Props) =
return (
<div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
<div className="justify-betweens flex">
<h2 className="mb-8 flex-1 text-xl font-medium text-mineshaft-100">Project Overview</h2>
<div className="justify-betweens mb-8 flex flex-wrap gap-2">
<h2 className="flex-1 text-xl font-medium text-mineshaft-100">Project Overview</h2>
<div className="space-x-2">
<Button
variant="outline_bg"

View File

@@ -1,6 +1,3 @@
import { useTranslation } from "react-i18next";
import { faMobile } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Outlet } from "@tanstack/react-router";
import { Banner } from "@app/components/page-frames/Banner";
@@ -15,7 +12,6 @@ import { InsecureConnectionBanner } from "../OrganizationLayout/components/Insec
import { AdminNavBar } from "./AdminNavBar";
export const AdminLayout = () => {
const { t } = useTranslation();
const { config } = useServerConfig();
const { data: serverDetails, isLoading } = useFetchServerStatus();
const { subscription } = useSubscription();
@@ -26,7 +22,7 @@ export const AdminLayout = () => {
<>
<Banner />
<div
className={`dark hidden ${containerHeight} w-full flex-col overflow-x-hidden bg-bunker-800 transition-all md:flex`}
className={`dark ${containerHeight} flex w-full flex-col overflow-x-hidden bg-bunker-800 transition-all`}
>
<Navbar />
{!isLoading && !serverDetails?.redisConfigured && <RedisBanner />}
@@ -40,12 +36,6 @@ export const AdminLayout = () => {
</div>
</div>
</div>
<div className="z-200 flex h-screen w-screen flex-col items-center justify-center bg-bunker-800 md:hidden">
<FontAwesomeIcon icon={faMobile} className="mb-8 text-7xl text-gray-300" />
<p className="max-w-sm px-6 text-center text-lg text-gray-200">
{` ${t("common.no-mobile")} `}
</p>
</div>
<Banner />
</>
);

View File

@@ -13,7 +13,7 @@ export const KmsLayout = () => {
const location = useLocation();
return (
<div className="dark hidden h-full w-full flex-col overflow-x-hidden md:flex">
<div className="dark flex h-full w-full flex-col overflow-x-hidden">
<div className="border-b border-mineshaft-600 bg-mineshaft-900">
<motion.div
key="menu-project-items"

View File

@@ -1,6 +1,3 @@
import { useTranslation } from "react-i18next";
import { faMobile } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Outlet, useParams } from "@tanstack/react-router";
import { twMerge } from "tailwind-merge";
@@ -27,8 +24,6 @@ export const OrganizationLayout = () => {
const { popUp, handlePopUpToggle } = usePopUp(["createOrg"] as const);
const { t } = useTranslation();
const containerHeight = config.pageFrameContent ? "h-[94vh]" : "h-screen";
const { data: serverDetails, isLoading } = useFetchServerStatus();
@@ -38,7 +33,7 @@ export const OrganizationLayout = () => {
<>
<Banner />
<div
className={`dark hidden ${containerHeight} w-full flex-col overflow-x-hidden bg-bunker-800 transition-all md:flex`}
className={`dark ${containerHeight} flex w-full flex-col overflow-x-hidden bg-bunker-800 transition-all`}
>
<Navbar />
<div className="flex grow flex-col overflow-y-hidden">
@@ -61,12 +56,6 @@ export const OrganizationLayout = () => {
isOpen={popUp?.createOrg?.isOpen}
onClose={() => handlePopUpToggle("createOrg", false)}
/>
<div className="z-200 flex h-screen w-screen flex-col items-center justify-center bg-bunker-800 md:hidden">
<FontAwesomeIcon icon={faMobile} className="mb-8 text-7xl text-gray-300" />
<p className="max-w-sm px-6 text-center text-lg text-gray-200">
{` ${t("common.no-mobile")} `}
</p>
</div>
<Banner />
</>
);

View File

@@ -294,7 +294,7 @@ export const Navbar = () => {
</>
) : (
<>
<div className="flex items-center overflow-hidden">
<div className="flex min-w-12 items-center overflow-hidden">
<DropdownMenu modal={false} open={isOrgSelectOpen} onOpenChange={setIsOrgSelectOpen}>
<div className="group flex cursor-pointer items-center gap-2 overflow-hidden text-sm text-white transition-all duration-100 hover:text-primary">
<Badge
@@ -324,7 +324,7 @@ export const Navbar = () => {
<span>{currentOrg?.name}</span>
</button>
</Badge>
<div className="mr-1 rounded-sm border border-mineshaft-500 px-1 text-xs text-bunker-300 no-underline!">
<div className="mr-1 hidden rounded-sm border border-mineshaft-500 px-1 text-xs text-bunker-300 no-underline! md:inline-block">
{getPlan(subscription)}
</div>
{subscription.cardDeclined && (
@@ -468,7 +468,7 @@ export const Navbar = () => {
className={twMerge(
"gap-x-1.5 text-sm",
!isOrgScope &&
"bg-transparent text-mineshaft-200 hover:!bg-transparent hover:underline [&>svg]:!text-sub-org"
"min-w-6 bg-transparent text-mineshaft-200 hover:!bg-transparent hover:underline [&>svg]:!text-sub-org"
)}
>
<Link to="/organization/projects">
@@ -569,11 +569,11 @@ export const Navbar = () => {
)}
{user.superAdmin && !location.pathname.startsWith("/admin") && (
<Link
className="mr-2 flex items-center rounded-md border border-mineshaft-500 px-2.5 py-1.5 text-sm whitespace-nowrap text-mineshaft-200 hover:bg-mineshaft-600"
className="mr-2 flex h-[34px] items-center rounded-md border border-mineshaft-500 px-2.5 py-1.5 text-sm whitespace-nowrap text-mineshaft-200 hover:bg-mineshaft-600"
to="/admin"
>
<InstanceIcon className="mr-2 inline-block size-3.5" />
Server Console
<InstanceIcon className="inline-block size-3.5" />
<span className="ml-2 hidden md:inline-block">Server Console</span>
</Link>
)}
<DropdownMenu modal={false}>

View File

@@ -21,7 +21,7 @@ export const OrgNavBar = ({ isHidden }: Props) => {
return (
<>
{!isHidden && (
<div className="dark hidden w-full flex-col overflow-x-hidden border-b border-mineshaft-600 bg-mineshaft-900 px-4 md:flex">
<div className="dark flex w-full flex-col overflow-x-hidden border-b border-mineshaft-600 bg-mineshaft-900 px-4">
<motion.div
key="menu-org-items"
initial={{ x: -150 }}

View File

@@ -28,7 +28,7 @@ export const PamLayout = () => {
return (
<>
<div className="dark hidden h-full w-full flex-col overflow-x-hidden md:flex">
<div className="dark flex h-full w-full flex-col overflow-x-hidden">
<div className="border-b border-mineshaft-600 bg-mineshaft-900">
<motion.div
key="menu-project-items"

View File

@@ -1,29 +1,16 @@
import { useTranslation } from "react-i18next";
import { faMobile } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Outlet } from "@tanstack/react-router";
import { InsecureConnectionBanner } from "../OrganizationLayout/components/InsecureConnectionBanner";
export const PersonalSettingsLayout = () => {
const { t } = useTranslation();
return (
<>
<div className="dark hidden h-screen w-full flex-col overflow-x-hidden bg-bunker-800 md:flex">
{!window.isSecureContext && <InsecureConnectionBanner />}
<div className="flex grow flex-col overflow-y-hidden md:flex-row">
<main className="flex-1 overflow-x-hidden overflow-y-auto bg-bunker-800 px-12 pt-10 pb-4 dark:scheme-dark">
<Outlet />
</main>
</div>
<div className="dark flex h-screen w-full flex-col overflow-x-hidden bg-bunker-800">
{!window.isSecureContext && <InsecureConnectionBanner />}
<div className="flex grow flex-col overflow-y-hidden md:flex-row">
<main className="flex-1 overflow-x-hidden overflow-y-auto bg-bunker-800 px-12 pt-10 pb-4 dark:scheme-dark">
<Outlet />
</main>
</div>
<div className="z-200 flex h-screen w-screen flex-col items-center justify-center bg-bunker-800 md:hidden">
<FontAwesomeIcon icon={faMobile} className="mb-8 text-7xl text-gray-300" />
<p className="max-w-sm px-6 text-center text-lg text-gray-200">
{` ${t("common.no-mobile")} `}
</p>
</div>
</>
</div>
);
};

View File

@@ -1,6 +1,3 @@
import { useTranslation } from "react-i18next";
import { faMobile } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Link, Outlet, useLocation } from "@tanstack/react-router";
import { motion } from "framer-motion";
@@ -17,7 +14,6 @@ export const PkiManagerLayout = () => {
const { currentProject } = useProject();
const { assumedPrivilegeDetails } = useProjectPermission();
const { subscription } = useSubscription();
const { t } = useTranslation();
const { data: subscribers = [] } = useListWorkspacePkiSubscribers(currentProject?.id || "");
const { data: templatesData } = useListWorkspaceCertificateTemplates({
@@ -32,147 +28,137 @@ export const PkiManagerLayout = () => {
const location = useLocation();
return (
<>
<div className="dark hidden h-full w-full flex-col overflow-x-hidden md:flex">
<div className="border-b border-mineshaft-600 bg-mineshaft-900">
<motion.div
key="menu-project-items"
initial={{ x: -150 }}
animate={{ x: 0 }}
exit={{ x: -150 }}
transition={{ duration: 0.2 }}
className="px-4"
>
<nav className="w-full">
<Tabs value="selected">
<TabList className="border-b-0">
<Link
to="/projects/cert-management/$projectId/policies"
params={{
projectId: currentProject.id
}}
>
{({ isActive }) => <Tab value={isActive ? "selected" : ""}>Certificates</Tab>}
</Link>
<Link
to="/projects/cert-management/$projectId/certificate-authorities"
params={{
projectId: currentProject.id
}}
>
{({ isActive }) => (
<Tab value={isActive || location.pathname.match(/\/ca\//) ? "selected" : ""}>
Certificate Authorities
</Tab>
)}
</Link>
<Link
to="/projects/cert-management/$projectId/alerting"
params={{
projectId: currentProject.id
}}
>
{({ isActive }) => <Tab value={isActive ? "selected" : ""}>Alerting</Tab>}
</Link>
<Link
to="/projects/cert-management/$projectId/integrations"
params={{
projectId: currentProject.id
}}
>
{({ isActive }) => <Tab value={isActive ? "selected" : ""}>Integrations</Tab>}
</Link>
<Link
to="/projects/cert-management/$projectId/app-connections"
params={{
projectId: currentProject.id
}}
>
{({ isActive }) => (
<Tab value={isActive ? "selected" : ""}>App Connections</Tab>
)}
</Link>
{showLegacySection && (
<>
{(subscription.pkiLegacyTemplates || hasExistingSubscribers) && (
<Link
to="/projects/cert-management/$projectId/subscribers"
params={{
projectId: currentProject.id
}}
>
{({ isActive }) => (
<Tab value={isActive ? "selected" : ""}>Subscribers (Legacy)</Tab>
)}
</Link>
)}
{(subscription.pkiLegacyTemplates || hasExistingTemplates) && (
<Link
to="/projects/cert-management/$projectId/certificate-templates"
params={{
projectId: currentProject.id
}}
>
{({ isActive }) => (
<Tab value={isActive ? "selected" : ""}>
Certificate Templates (Legacy)
</Tab>
)}
</Link>
)}
</>
<div className="dark flex h-full w-full flex-col overflow-x-hidden">
<div className="border-b border-mineshaft-600 bg-mineshaft-900">
<motion.div
key="menu-project-items"
initial={{ x: -150 }}
animate={{ x: 0 }}
exit={{ x: -150 }}
transition={{ duration: 0.2 }}
className="px-4"
>
<nav className="w-full">
<Tabs value="selected">
<TabList className="border-b-0">
<Link
to="/projects/cert-management/$projectId/policies"
params={{
projectId: currentProject.id
}}
>
{({ isActive }) => <Tab value={isActive ? "selected" : ""}>Certificates</Tab>}
</Link>
<Link
to="/projects/cert-management/$projectId/certificate-authorities"
params={{
projectId: currentProject.id
}}
>
{({ isActive }) => (
<Tab value={isActive || location.pathname.match(/\/ca\//) ? "selected" : ""}>
Certificate Authorities
</Tab>
)}
<Link
to="/projects/cert-management/$projectId/access-management"
params={{
projectId: currentProject.id
}}
>
{({ isActive }) => (
<Tab
value={
isActive ||
location.pathname.match(/\/groups\/|\/identities\/|\/members\/|\/roles\//)
? "selected"
: ""
}
</Link>
<Link
to="/projects/cert-management/$projectId/alerting"
params={{
projectId: currentProject.id
}}
>
{({ isActive }) => <Tab value={isActive ? "selected" : ""}>Alerting</Tab>}
</Link>
<Link
to="/projects/cert-management/$projectId/integrations"
params={{
projectId: currentProject.id
}}
>
{({ isActive }) => <Tab value={isActive ? "selected" : ""}>Integrations</Tab>}
</Link>
<Link
to="/projects/cert-management/$projectId/app-connections"
params={{
projectId: currentProject.id
}}
>
{({ isActive }) => <Tab value={isActive ? "selected" : ""}>App Connections</Tab>}
</Link>
{showLegacySection && (
<>
{(subscription.pkiLegacyTemplates || hasExistingSubscribers) && (
<Link
to="/projects/cert-management/$projectId/subscribers"
params={{
projectId: currentProject.id
}}
>
Access Control
</Tab>
{({ isActive }) => (
<Tab value={isActive ? "selected" : ""}>Subscribers (Legacy)</Tab>
)}
</Link>
)}
</Link>
<Link
to="/projects/cert-management/$projectId/audit-logs"
params={{
projectId: currentProject.id
}}
>
{({ isActive }) => <Tab value={isActive ? "selected" : ""}>Audit Logs</Tab>}
</Link>
<Link
to="/projects/cert-management/$projectId/settings"
params={{
projectId: currentProject.id
}}
>
{({ isActive }) => <Tab value={isActive ? "selected" : ""}>Settings</Tab>}
</Link>
</TabList>
</Tabs>
</nav>
</motion.div>
</div>
{assumedPrivilegeDetails && <AssumePrivilegeModeBanner />}
<div className="flex-1 overflow-x-hidden overflow-y-auto bg-bunker-800 px-12 pt-10 pb-4">
<Outlet />
</div>
{(subscription.pkiLegacyTemplates || hasExistingTemplates) && (
<Link
to="/projects/cert-management/$projectId/certificate-templates"
params={{
projectId: currentProject.id
}}
>
{({ isActive }) => (
<Tab value={isActive ? "selected" : ""}>
Certificate Templates (Legacy)
</Tab>
)}
</Link>
)}
</>
)}
<Link
to="/projects/cert-management/$projectId/access-management"
params={{
projectId: currentProject.id
}}
>
{({ isActive }) => (
<Tab
value={
isActive ||
location.pathname.match(/\/groups\/|\/identities\/|\/members\/|\/roles\//)
? "selected"
: ""
}
>
Access Control
</Tab>
)}
</Link>
<Link
to="/projects/cert-management/$projectId/audit-logs"
params={{
projectId: currentProject.id
}}
>
{({ isActive }) => <Tab value={isActive ? "selected" : ""}>Audit Logs</Tab>}
</Link>
<Link
to="/projects/cert-management/$projectId/settings"
params={{
projectId: currentProject.id
}}
>
{({ isActive }) => <Tab value={isActive ? "selected" : ""}>Settings</Tab>}
</Link>
</TabList>
</Tabs>
</nav>
</motion.div>
</div>
<div className="z-200 flex h-screen w-screen flex-col items-center justify-center bg-bunker-800 md:hidden">
<FontAwesomeIcon icon={faMobile} className="mb-8 text-7xl text-gray-300" />
<p className="max-w-sm px-6 text-center text-lg text-gray-200">
{` ${t("common.no-mobile")} `}
</p>
{assumedPrivilegeDetails && <AssumePrivilegeModeBanner />}
<div className="flex-1 overflow-x-hidden overflow-y-auto bg-bunker-800 px-12 pt-10 pb-4">
<Outlet />
</div>
</>
</div>
);
};

View File

@@ -106,7 +106,9 @@ export const ProjectSelect = () => {
</p>
<Badge variant="project">
<ProjectIcon />
{currentWorkspace.type ? PROJECT_TYPE_NAME[currentWorkspace.type] : "Project"}
<span className="hidden sm:inline-block">
{currentWorkspace.type ? PROJECT_TYPE_NAME[currentWorkspace.type] : "Project"}
</span>
</Badge>
</Link>
<DropdownMenuTrigger asChild>

View File

@@ -1,6 +1,3 @@
import { useTranslation } from "react-i18next";
import { faMobile } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Link, Outlet, useLocation } from "@tanstack/react-router";
import { motion } from "framer-motion";
@@ -20,7 +17,6 @@ export const SecretManagerLayout = () => {
const { assumedPrivilegeDetails } = useProjectPermission();
const location = useLocation();
const { t } = useTranslation();
const projectSlug = currentProject?.slug || "";
const { data: secretApprovalReqCount } = useGetSecretApprovalRequestCount({
@@ -42,141 +38,131 @@ export const SecretManagerLayout = () => {
(secretApprovalReqCount?.open || 0) + (accessApprovalRequestCount?.pendingCount || 0);
return (
<>
<div className="dark hidden h-full w-full flex-col overflow-x-hidden md:flex">
<div className="border-b border-mineshaft-600 bg-mineshaft-900">
<motion.div
key="menu-project-items"
initial={{ x: -150 }}
animate={{ x: 0 }}
exit={{ x: -150 }}
transition={{ duration: 0.2 }}
className="px-4"
>
<nav className="w-full">
<Tabs value="selected">
<TabList className="border-b-0">
<Link
to="/projects/secret-management/$projectId/overview"
params={{
projectId: currentProject.id,
...(currentProject.environments.length
? { envSlug: currentProject.environments[0]?.slug }
: {})
}}
>
{({ isActive }) => (
<Tab
value={
isActive || location.pathname.match(/\/secrets\/|\/commits\//)
? "selected"
: ""
}
>
Overview
</Tab>
)}
</Link>
<Link
to="/projects/secret-management/$projectId/approval"
params={{
projectId: currentProject.id
}}
>
{({ isActive }) => (
<Tab value={isActive ? "selected" : ""}>
Approvals
{Boolean(
secretApprovalReqCount?.open || accessApprovalRequestCount?.pendingCount
) && (
<Badge variant="warning" isSquare className="ml-1.5">
{pendingRequestsCount}
</Badge>
)}
</Tab>
)}
</Link>
<Link
to="/projects/secret-management/$projectId/integrations"
params={{
projectId: currentProject.id
}}
>
{({ isActive }) => <Tab value={isActive ? "selected" : ""}>Integrations</Tab>}
</Link>
{Boolean(secretRotations?.length) && (
<Link
to="/projects/secret-management/$projectId/secret-rotation"
params={{
projectId: currentProject.id
}}
<div className="dark flex h-full w-full flex-col overflow-x-hidden">
<div className="border-b border-mineshaft-600 bg-mineshaft-900">
<motion.div
key="menu-project-items"
initial={{ x: -150 }}
animate={{ x: 0 }}
exit={{ x: -150 }}
transition={{ duration: 0.2 }}
className="px-4"
>
<nav className="w-full">
<Tabs value="selected">
<TabList className="border-b-0">
<Link
to="/projects/secret-management/$projectId/overview"
params={{
projectId: currentProject.id,
...(currentProject.environments.length
? { envSlug: currentProject.environments[0]?.slug }
: {})
}}
>
{({ isActive }) => (
<Tab
value={
isActive || location.pathname.match(/\/secrets\/|\/commits\//)
? "selected"
: ""
}
>
{({ isActive }) => (
<Tab value={isActive ? "selected" : ""}>Secret Rotations</Tab>
)}
</Link>
Overview
</Tab>
)}
</Link>
<Link
to="/projects/secret-management/$projectId/approval"
params={{
projectId: currentProject.id
}}
>
{({ isActive }) => (
<Tab value={isActive ? "selected" : ""}>
Approvals
{Boolean(
secretApprovalReqCount?.open || accessApprovalRequestCount?.pendingCount
) && (
<Badge variant="warning" isSquare className="ml-1.5">
{pendingRequestsCount}
</Badge>
)}
</Tab>
)}
</Link>
<Link
to="/projects/secret-management/$projectId/integrations"
params={{
projectId: currentProject.id
}}
>
{({ isActive }) => <Tab value={isActive ? "selected" : ""}>Integrations</Tab>}
</Link>
{Boolean(secretRotations?.length) && (
<Link
to="/projects/secret-management/$projectId/app-connections"
to="/projects/secret-management/$projectId/secret-rotation"
params={{
projectId: currentProject.id
}}
>
{({ isActive }) => (
<Tab value={isActive ? "selected" : ""}>App Connections</Tab>
<Tab value={isActive ? "selected" : ""}>Secret Rotations</Tab>
)}
</Link>
<Link
to="/projects/secret-management/$projectId/access-management"
params={{
projectId: currentProject.id
}}
>
{({ isActive }) => (
<Tab
value={
isActive ||
location.pathname.match(/\/groups\/|\/identities\/|\/members\/|\/roles\//)
? "selected"
: ""
}
>
Access Control
</Tab>
)}
</Link>
<Link
to="/projects/secret-management/$projectId/audit-logs"
params={{
projectId: currentProject.id
}}
>
{({ isActive }) => <Tab value={isActive ? "selected" : ""}>Audit Logs</Tab>}
</Link>
<Link
to="/projects/secret-management/$projectId/settings"
params={{
projectId: currentProject.id
}}
>
{({ isActive }) => <Tab value={isActive ? "selected" : ""}>Settings</Tab>}
</Link>
</TabList>
</Tabs>
</nav>
</motion.div>
</div>
{assumedPrivilegeDetails && <AssumePrivilegeModeBanner />}
<div className="flex-1 overflow-x-hidden overflow-y-auto bg-bunker-800 px-12 pt-10 pb-4">
<Outlet />
</div>
)}
<Link
to="/projects/secret-management/$projectId/app-connections"
params={{
projectId: currentProject.id
}}
>
{({ isActive }) => <Tab value={isActive ? "selected" : ""}>App Connections</Tab>}
</Link>
<Link
to="/projects/secret-management/$projectId/access-management"
params={{
projectId: currentProject.id
}}
>
{({ isActive }) => (
<Tab
value={
isActive ||
location.pathname.match(/\/groups\/|\/identities\/|\/members\/|\/roles\//)
? "selected"
: ""
}
>
Access Control
</Tab>
)}
</Link>
<Link
to="/projects/secret-management/$projectId/audit-logs"
params={{
projectId: currentProject.id
}}
>
{({ isActive }) => <Tab value={isActive ? "selected" : ""}>Audit Logs</Tab>}
</Link>
<Link
to="/projects/secret-management/$projectId/settings"
params={{
projectId: currentProject.id
}}
>
{({ isActive }) => <Tab value={isActive ? "selected" : ""}>Settings</Tab>}
</Link>
</TabList>
</Tabs>
</nav>
</motion.div>
</div>
<div className="z-200 flex h-screen w-screen flex-col items-center justify-center bg-bunker-800 md:hidden">
<FontAwesomeIcon icon={faMobile} className="mb-8 text-7xl text-gray-300" />
<p className="max-w-sm px-6 text-center text-lg text-gray-200">
{` ${t("common.no-mobile")} `}
</p>
{assumedPrivilegeDetails && <AssumePrivilegeModeBanner />}
<div className="flex-1 overflow-x-hidden overflow-y-auto bg-bunker-800 px-12 pt-10 pb-4">
<Outlet />
</div>
</>
</div>
);
};

View File

@@ -36,7 +36,7 @@ export const SecretScanningLayout = () => {
);
return (
<div className="dark hidden h-full w-full flex-col overflow-x-hidden md:flex">
<div className="dark flex h-full w-full flex-col overflow-x-hidden">
<div className="border-b border-mineshaft-600 bg-mineshaft-900">
<motion.div
key="menu-project-items"

View File

@@ -18,7 +18,7 @@ export const SshLayout = () => {
const location = useLocation();
return (
<div className="dark hidden h-full w-full flex-col overflow-x-hidden md:flex">
<div className="dark flex h-full w-full flex-col overflow-x-hidden">
<div className="border-b border-mineshaft-600 bg-mineshaft-900">
<motion.div
key="menu-project-items"

View File

@@ -209,7 +209,7 @@ export const AppConnectionsTable = ({ projectId, projectType }: Props) => {
return (
<div className="rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
<div className="mb-4 flex items-center justify-between">
<div className="mb-4 flex flex-wrap items-center justify-between gap-2">
<div>
<div className="flex items-center gap-x-2">
<p className="text-xl font-medium text-mineshaft-100">App Connections</p>

View File

@@ -125,7 +125,7 @@ export const AllProjectView = ({
return (
<div>
<div className="flex w-full flex-row">
<div className="flex w-full flex-row flex-wrap gap-2 md:flex-nowrap md:gap-0">
<ProjectListToggle value={projectListView} onChange={onProjectListViewChange} />
<Input
className="h-[2.3rem] bg-mineshaft-800 text-sm placeholder-mineshaft-50 duration-200 focus:bg-mineshaft-700/80"

View File

@@ -360,7 +360,7 @@ export const MyProjectView = ({
return (
<div>
<div className="flex w-full flex-row">
<div className="flex w-full flex-row flex-wrap gap-2 md:flex-nowrap md:gap-0">
<ProjectListToggle value={projectListView} onChange={onProjectListViewChange} />
<Input
className="h-[2.3rem] bg-mineshaft-800 text-sm placeholder-mineshaft-50/60 duration-200 focus:bg-mineshaft-700/80"

View File

@@ -90,7 +90,7 @@ export const SecretSyncsTab = () => {
return (
<>
<div className="w-full rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
<div className="mb-4 flex items-center justify-between">
<div className="mb-4 flex flex-wrap items-center justify-between gap-2">
<div>
<div className="flex items-center gap-1.5">
<p className="text-lg font-medium text-mineshaft-100">Secret Syncs</p>

View File

@@ -959,9 +959,9 @@ export const OverviewPage = () => {
}
/>
</div>
<div className="flex items-center justify-between">
<div className="flex flex-wrap items-center justify-between gap-2">
<FolderBreadCrumbs secretPath={secretPath} onResetSearch={handleResetSearch} />
<div className="flex flex-row items-center justify-center space-x-2">
<div className="flex flex-row flex-wrap items-center gap-2">
{isTableFiltered && (
<Button
variant="plain"

View File

@@ -294,7 +294,7 @@ export const AccessApprovalRequest = ({
return (
<>
<div className="w-full rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
<div className="mb-4 flex items-center justify-between">
<div className="mb-4 flex flex-wrap items-center justify-between gap-2">
<div>
<div className="flex items-center gap-x-2">
<p className="text-xl font-medium text-mineshaft-100">Access Requests</p>

View File

@@ -240,7 +240,7 @@ export const ApprovalPolicyList = ({ projectId }: IProps) => {
return (
<>
<div className="w-full rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
<div className="mb-4 flex items-center justify-between">
<div className="mb-4 flex flex-wrap items-center justify-between gap-2">
<div>
<div className="flex items-center gap-x-2">
<p className="text-xl font-medium text-mineshaft-100">Policies</p>

View File

@@ -690,7 +690,7 @@ export const ActionBar = ({
return (
<>
<div className="mt-4 flex items-center space-x-2">
<div className="mt-4 flex flex-wrap items-center gap-2">
<SecretSearchInput
isSingleEnv
className="w-2/5"

View File

@@ -40,12 +40,13 @@ export const SecretSharingSection = () => {
<p className="mb-3 text-xl font-medium">Allow Secret Sharing</p>
<ProjectPermissionCan I={ProjectPermissionActions.Edit} a={ProjectPermissionSub.Settings}>
{(isAllowed) => (
<div className="w-max">
<div>
<Checkbox
id="secretSharing"
isDisabled={!isAllowed || isLoading}
isChecked={currentProject?.secretSharing ?? true}
onCheckedChange={(state) => handleToggle(state as boolean)}
allowMultilineLabel
>
This feature enables your project members to securely share secrets.
</Checkbox>

View File

@@ -40,12 +40,13 @@ export const SecretSnapshotsLegacySection = () => {
<p className="mb-3 text-xl font-medium">Show Secret Snapshots ( legacy )</p>
<ProjectPermissionCan I={ProjectPermissionActions.Edit} a={ProjectPermissionSub.Settings}>
{(isAllowed) => (
<div className="w-max">
<div>
<Checkbox
id="showSnapshotsLegacy"
isDisabled={!isAllowed || isLoading}
isChecked={currentProject?.showSnapshotsLegacy ?? false}
onCheckedChange={(state) => handleToggle(state as boolean)}
allowMultilineLabel
>
This feature enables your project members to view secret snapshots in the legacy
format.