mirror of
https://github.com/Infisical/infisical.git
synced 2025-03-22 20:01:38 +00:00
Compare commits
23 Commits
fix/db-hos
...
fix-secret
Author | SHA1 | Date | |
---|---|---|---|
d702a61586 | |||
1c16f406a7 | |||
90f739caa6 | |||
ede8b6f286 | |||
232c547d75 | |||
fe08bbb691 | |||
2bd06ecde4 | |||
ffc7249c7c | |||
90bcf23097 | |||
5fa4d9029d | |||
7160cf58ee | |||
6b2d757e39 | |||
c075fcceca | |||
e25f5dd65f | |||
3eef023c30 | |||
e63deb0860 | |||
02b2851990 | |||
fa1b28b33f | |||
415cf31b2d | |||
9002e6cb33 | |||
1ede551c3e | |||
b7b43858f6 | |||
ee215bccfa |
28
.github/workflows/build-staging-and-deploy.yml
vendored
28
.github/workflows/build-staging-and-deploy.yml
vendored
@ -8,6 +8,15 @@ jobs:
|
||||
steps:
|
||||
- name: ☁️ Checkout source
|
||||
uses: actions/checkout@v3
|
||||
- name: Configure AWS credentials
|
||||
uses: aws-actions/configure-aws-credentials@v1
|
||||
with:
|
||||
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID_FOR_ECR }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY_FOR_ECR }}
|
||||
aws-region: us-east-1
|
||||
- name: Login to Amazon ECR
|
||||
id: login-ecr
|
||||
uses: aws-actions/amazon-ecr-login@v1
|
||||
- name: 📦 Install dependencies to test all dependencies
|
||||
run: npm ci --only-production
|
||||
working-directory: backend
|
||||
@ -35,17 +44,11 @@ jobs:
|
||||
context: .
|
||||
file: Dockerfile.standalone-infisical
|
||||
tags: infisical/infisical:test
|
||||
# - name: ⏻ Spawn backend container and dependencies
|
||||
# run: |
|
||||
# docker compose -f .github/resources/docker-compose.be-test.yml up --wait --quiet-pull
|
||||
# - name: 🧪 Test backend image
|
||||
# run: |
|
||||
# ./.github/resources/healthcheck.sh infisical-backend-test
|
||||
# - name: ⏻ Shut down backend container and dependencies
|
||||
# run: |
|
||||
# docker compose -f .github/resources/docker-compose.be-test.yml down
|
||||
- name: 🏗️ Build backend and push
|
||||
- name: 🏗️ Build backend and push to docker hub
|
||||
uses: depot/build-push-action@v1
|
||||
env:
|
||||
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
|
||||
ECR_REPOSITORY: ${{ secrets.AWS_ECR_REPO_NAME }}
|
||||
with:
|
||||
project: 64mmf0n610
|
||||
token: ${{ secrets.DEPOT_PROJECT_TOKEN }}
|
||||
@ -55,10 +58,15 @@ jobs:
|
||||
tags: |
|
||||
infisical/staging_infisical:${{ steps.commit.outputs.short }}
|
||||
infisical/staging_infisical:latest
|
||||
${{ env.ECR_REGISTRY }}/${{ env.ECR_REPOSITORY }}:latest
|
||||
${{ env.ECR_REGISTRY }}/${{ env.ECR_REPOSITORY }}:${{ steps.commit.outputs.short }}
|
||||
platforms: linux/amd64,linux/arm64
|
||||
build-args: |
|
||||
POSTHOG_API_KEY=${{ secrets.PUBLIC_POSTHOG_API_KEY }}
|
||||
INFISICAL_PLATFORM_VERSION=${{ steps.extract_version.outputs.version }}
|
||||
|
||||
|
||||
|
||||
postgres-migration:
|
||||
name: Run latest migration files
|
||||
runs-on: ubuntu-latest
|
||||
|
@ -118,9 +118,6 @@ WORKDIR /backend
|
||||
|
||||
ENV TELEMETRY_ENABLED true
|
||||
|
||||
HEALTHCHECK --interval=10s --timeout=3s --start-period=10s \
|
||||
CMD node healthcheck.js
|
||||
|
||||
EXPOSE 8080
|
||||
EXPOSE 443
|
||||
|
||||
|
@ -10,7 +10,8 @@
|
||||
<a href="https://infisical.com/">Infisical Cloud</a> |
|
||||
<a href="https://infisical.com/docs/self-hosting/overview">Self-Hosting</a> |
|
||||
<a href="https://infisical.com/docs/documentation/getting-started/introduction">Docs</a> |
|
||||
<a href="https://www.infisical.com">Website</a>
|
||||
<a href="https://www.infisical.com">Website</a> |
|
||||
<a href="https://infisical.com/careers">Hiring (Remote/SF)</a>
|
||||
</h4>
|
||||
|
||||
<p align="center">
|
||||
|
@ -19,7 +19,7 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
|
||||
.min(1)
|
||||
.trim()
|
||||
.refine(
|
||||
(val) => Object.keys(OrgMembershipRole).includes(val),
|
||||
(val) => !Object.keys(OrgMembershipRole).includes(val),
|
||||
"Please choose a different slug, the slug you have entered is reserved"
|
||||
)
|
||||
.refine((v) => slugify(v) === v, {
|
||||
|
@ -419,7 +419,7 @@ func getSecretsByNames(cmd *cobra.Command, args []string) {
|
||||
util.HandleError(err, "Unable to parse path flag")
|
||||
}
|
||||
|
||||
secrets, err := util.GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: environmentName, InfisicalToken: infisicalToken, TagSlugs: tagSlugs, SecretsPath: secretsPath}, "")
|
||||
secrets, err := util.GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: environmentName, InfisicalToken: infisicalToken, TagSlugs: tagSlugs, SecretsPath: secretsPath, IncludeImport: true}, "")
|
||||
if err != nil {
|
||||
util.HandleError(err, "To fetch all secrets")
|
||||
}
|
||||
@ -477,7 +477,7 @@ func generateExampleEnv(cmd *cobra.Command, args []string) {
|
||||
util.HandleError(err, "Unable to parse flag")
|
||||
}
|
||||
|
||||
secrets, err := util.GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: environmentName, InfisicalToken: infisicalToken, TagSlugs: tagSlugs, SecretsPath: secretsPath}, "")
|
||||
secrets, err := util.GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: environmentName, InfisicalToken: infisicalToken, TagSlugs: tagSlugs, SecretsPath: secretsPath, IncludeImport: true}, "")
|
||||
if err != nil {
|
||||
util.HandleError(err, "To fetch all secrets")
|
||||
}
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 733 KiB After Width: | Height: | Size: 739 KiB |
@ -29,6 +29,7 @@ module.exports = {
|
||||
},
|
||||
plugins: ["react", "prettier", "simple-import-sort", "import"],
|
||||
rules: {
|
||||
"@typescript-eslint/no-empty-function": "off",
|
||||
quotes: ["error", "double", { avoidEscape: true }],
|
||||
"comma-dangle": ["error", "only-multiline"],
|
||||
"react/react-in-jsx-scope": "off",
|
||||
@ -72,7 +73,6 @@ module.exports = {
|
||||
],
|
||||
"@typescript-eslint/no-non-null-assertion": "off",
|
||||
"simple-import-sort/exports": "warn",
|
||||
"@typescript-eslint/no-empty-function": "off",
|
||||
"simple-import-sort/imports": [
|
||||
"warn",
|
||||
{
|
||||
|
@ -1,28 +1,28 @@
|
||||
const path = require('path');
|
||||
const path = require("path");
|
||||
module.exports = {
|
||||
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
|
||||
stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|ts|tsx)"],
|
||||
addons: [
|
||||
'@storybook/addon-links',
|
||||
'@storybook/addon-essentials',
|
||||
'@storybook/addon-interactions',
|
||||
'storybook-dark-mode',
|
||||
"@storybook/addon-links",
|
||||
"@storybook/addon-essentials",
|
||||
"@storybook/addon-interactions",
|
||||
"storybook-dark-mode",
|
||||
{
|
||||
name: '@storybook/addon-styling',
|
||||
name: "@storybook/addon-styling",
|
||||
options: {
|
||||
postCss: {
|
||||
implementation: require('postcss')
|
||||
implementation: require("postcss")
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
framework: {
|
||||
name: '@storybook/nextjs',
|
||||
name: "@storybook/nextjs",
|
||||
options: {}
|
||||
},
|
||||
core: {
|
||||
disableTelemetry: true
|
||||
},
|
||||
docs: {
|
||||
autodocs: 'tag'
|
||||
autodocs: "tag"
|
||||
}
|
||||
};
|
||||
|
@ -6,7 +6,7 @@ import { ENV, POSTHOG_API_KEY, POSTHOG_HOST } from "../utilities/config";
|
||||
|
||||
export const initPostHog = () => {
|
||||
// @ts-ignore
|
||||
console.log("Hi there 👋")
|
||||
console.log("Hi there 👋");
|
||||
try {
|
||||
if (typeof window !== "undefined") {
|
||||
// @ts-ignore
|
||||
@ -19,7 +19,7 @@ export const initPostHog = () => {
|
||||
|
||||
return posthog;
|
||||
} catch (e) {
|
||||
console.log("posthog err", e)
|
||||
console.log("posthog err", e);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
|
@ -3,9 +3,9 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
const Error = ({ text }: { text: string }): JSX.Element => {
|
||||
return (
|
||||
<div className="relative flex flex-row justify-center m-auto items-center w-fit rounded-full">
|
||||
<FontAwesomeIcon icon={faExclamationTriangle} className="text-red mt-1.5 mb-2 mx-2" />
|
||||
{text && <p className="relative top-0 text-red mr-2 text-sm py-1">{text}</p>}
|
||||
<div className="relative m-auto flex w-fit flex-row items-center justify-center rounded-full">
|
||||
<FontAwesomeIcon icon={faExclamationTriangle} className="mx-2 mt-1.5 mb-2 text-red" />
|
||||
{text && <p className="relative top-0 mr-2 py-1 text-sm text-red">{text}</p>}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -39,16 +39,16 @@ const InputField = ({
|
||||
|
||||
if (isStatic === true) {
|
||||
return (
|
||||
<div className="flex flex-col my-2 md:my-4 justify-center w-full max-w-md">
|
||||
<p className="text-sm font-semibold text-gray-400 mb-0.5">{label}</p>
|
||||
{text && <p className="text-xs text-gray-400 mb-2">{text}</p>}
|
||||
<div className="my-2 flex w-full max-w-md flex-col justify-center md:my-4">
|
||||
<p className="mb-0.5 text-sm font-semibold text-gray-400">{label}</p>
|
||||
{text && <p className="mb-2 text-xs text-gray-400">{text}</p>}
|
||||
<input
|
||||
onChange={(e) => onChangeHandler(e.target.value)}
|
||||
type={type}
|
||||
placeholder={placeholder}
|
||||
value={value}
|
||||
required={isRequired}
|
||||
className="bg-bunker-800 text-gray-400 border border-gray-600 rounded-md text-md p-2 w-full min-w-16 outline-none"
|
||||
className="text-md min-w-16 w-full rounded-md border border-gray-600 bg-bunker-800 p-2 text-gray-400 outline-none"
|
||||
name={name}
|
||||
readOnly
|
||||
autoComplete={autoComplete}
|
||||
@ -58,12 +58,12 @@ const InputField = ({
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div className="flex-col w-full">
|
||||
<div className="flex flex-row text-mineshaft-300 items-center mb-0.5">
|
||||
<p className="text-sm font-semibold mr-1">{label}</p>
|
||||
<div className="w-full flex-col">
|
||||
<div className="mb-0.5 flex flex-row items-center text-mineshaft-300">
|
||||
<p className="mr-1 text-sm font-semibold">{label}</p>
|
||||
</div>
|
||||
<div
|
||||
className={`group relative flex flex-col justify-center w-full max-w-2xl border ${
|
||||
className={`group relative flex w-full max-w-2xl flex-col justify-center border ${
|
||||
error ? "border-red" : "border-mineshaft-500"
|
||||
} rounded-md`}
|
||||
>
|
||||
@ -75,11 +75,11 @@ const InputField = ({
|
||||
required={isRequired}
|
||||
className={`${
|
||||
blurred
|
||||
? "text-bunker-800 group-hover:text-gray-400 focus:text-gray-400 active:text-gray-400"
|
||||
? "text-bunker-800 focus:text-gray-400 active:text-gray-400 group-hover:text-gray-400"
|
||||
: ""
|
||||
} ${
|
||||
error ? "focus:ring-red/50" : "focus:ring-primary/50"
|
||||
} relative peer bg-mineshaft-900 rounded-md text-gray-400 text-md p-2 w-full min-w-16 outline-none focus:ring-4 duration-200`}
|
||||
} text-md min-w-16 peer relative w-full rounded-md bg-mineshaft-900 p-2 text-gray-400 outline-none duration-200 focus:ring-4`}
|
||||
name={name}
|
||||
spellCheck="false"
|
||||
autoComplete={autoComplete}
|
||||
@ -91,7 +91,7 @@ const InputField = ({
|
||||
onClick={() => {
|
||||
setPasswordVisible(!passwordVisible);
|
||||
}}
|
||||
className="absolute self-end mr-3 text-gray-400 cursor-pointer"
|
||||
className="absolute mr-3 cursor-pointer self-end text-gray-400"
|
||||
>
|
||||
{passwordVisible ? (
|
||||
<FontAwesomeIcon icon={faEyeSlash} />
|
||||
@ -101,7 +101,7 @@ const InputField = ({
|
||||
</button>
|
||||
)}
|
||||
{blurred && (
|
||||
<div className="peer group-hover:hidden peer-hover:hidden peer-focus:hidden peer-active:invisible absolute h-10 w-fit max-w-xl rounded-md flex items-center text-gray-400/50 text-clip overflow-hidden">
|
||||
<div className="peer absolute flex h-10 w-fit max-w-xl items-center overflow-hidden text-clip rounded-md text-gray-400/50 group-hover:hidden peer-hover:hidden peer-focus:hidden peer-active:invisible">
|
||||
<p className="ml-2" />
|
||||
{value
|
||||
.split("")
|
||||
@ -109,7 +109,7 @@ const InputField = ({
|
||||
.map(() => (
|
||||
<FontAwesomeIcon
|
||||
key={guidGenerator()}
|
||||
className="text-xxs mx-0.5"
|
||||
className="mx-0.5 text-xxs"
|
||||
icon={faCircle}
|
||||
/>
|
||||
))}
|
||||
@ -121,7 +121,7 @@ const InputField = ({
|
||||
</div>
|
||||
)} */}
|
||||
</div>
|
||||
{error && <p className="text-red text-xs mt-0.5 mx-0 mb-2 max-w-xs">{errorText}</p>}
|
||||
{error && <p className="mx-0 mt-0.5 mb-2 max-w-xs text-xs text-red">{errorText}</p>}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -34,19 +34,19 @@ const ListBox = ({
|
||||
<Listbox value={isSelected} onChange={onChange}>
|
||||
<div className="relative">
|
||||
<Listbox.Button
|
||||
className={`text-gray-400 relative ${
|
||||
className={`relative text-gray-400 ${
|
||||
isFull ? "w-full" : "w-52"
|
||||
} cursor-default rounded-md bg-white/[0.07] hover:bg-white/[0.11] duration-200 py-2.5 pl-3 pr-10 text-left shadow-md focus:outline-none focus-visible:border-indigo-500 focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75 focus-visible:ring-offset-2 focus-visible:ring-offset-orange-300 sm:text-sm`}
|
||||
} focus-visible:ring-offset-orange-300 cursor-default rounded-md bg-white/[0.07] py-2.5 pl-3 pr-10 text-left shadow-md duration-200 hover:bg-white/[0.11] focus:outline-none focus-visible:border-indigo-500 focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75 focus-visible:ring-offset-2 sm:text-sm`}
|
||||
>
|
||||
<div className="flex flex-row">
|
||||
{text}
|
||||
<span className="ml-1 cursor-pointer block truncate font-semibold text-gray-300">
|
||||
<span className="ml-1 block cursor-pointer truncate font-semibold text-gray-300">
|
||||
{" "}
|
||||
{isSelected}
|
||||
</span>
|
||||
</div>
|
||||
{data && (
|
||||
<div className="cursor-pointer pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2">
|
||||
<div className="pointer-events-none absolute inset-y-0 right-0 flex cursor-pointer items-center pr-2">
|
||||
<FontAwesomeIcon icon={faAngleDown} className="text-md mr-1.5" />
|
||||
</div>
|
||||
)}
|
||||
@ -58,16 +58,16 @@ const ListBox = ({
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<Listbox.Options className="border border-mineshaft-700 z-[70] p-2 absolute mt-1 max-h-60 w-full overflow-auto rounded-md bg-bunker text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm no-scrollbar no-scrollbar::-webkit-scrollbar">
|
||||
<Listbox.Options className="no-scrollbar::-webkit-scrollbar absolute z-[70] mt-1 max-h-60 w-full overflow-auto rounded-md border border-mineshaft-700 bg-bunker p-2 text-base shadow-lg ring-1 ring-black ring-opacity-5 no-scrollbar focus:outline-none sm:text-sm">
|
||||
{data.map((person, personIdx) => (
|
||||
<Listbox.Option
|
||||
key={`${person}.${personIdx + 1}`}
|
||||
className={({ active, selected }) =>
|
||||
`my-0.5 relative cursor-default select-none py-2 pl-10 pr-4 rounded-md ${
|
||||
selected ? "bg-white/10 text-gray-400 font-bold" : ""
|
||||
`relative my-0.5 cursor-default select-none rounded-md py-2 pl-10 pr-4 ${
|
||||
selected ? "bg-white/10 font-bold text-gray-400" : ""
|
||||
} ${
|
||||
active && !selected
|
||||
? "bg-white/5 text-mineshaft-200 cursor-pointer"
|
||||
? "cursor-pointer bg-white/5 text-mineshaft-200"
|
||||
: "text-gray-400"
|
||||
} `
|
||||
}
|
||||
@ -83,7 +83,7 @@ const ListBox = ({
|
||||
{person}
|
||||
</span>
|
||||
{selected ? (
|
||||
<span className="text-primary rounded-lg absolute inset-y-0 left-0 flex items-center pl-3">
|
||||
<span className="absolute inset-y-0 left-0 flex items-center rounded-lg pl-3 text-primary">
|
||||
<FontAwesomeIcon icon={faCheck} className="text-md ml-1" />
|
||||
</span>
|
||||
) : null}
|
||||
@ -92,9 +92,9 @@ const ListBox = ({
|
||||
</Listbox.Option>
|
||||
))}
|
||||
{buttonAction && (
|
||||
<button type="button" onClick={buttonAction} className="cursor-pointer w-full">
|
||||
<div className="my-0.5 relative flex justify-start cursor-pointer select-none py-2 pl-10 pr-4 rounded-md text-gray-400 hover:bg-lime-300 duration-200 hover:text-black hover:font-semibold mt-2">
|
||||
<span className="rounded-lg absolute inset-y-0 left-0 flex items-center pl-3 pr-4">
|
||||
<button type="button" onClick={buttonAction} className="w-full cursor-pointer">
|
||||
<div className="relative my-0.5 mt-2 flex cursor-pointer select-none justify-start rounded-md py-2 pl-10 pr-4 text-gray-400 duration-200 hover:bg-lime-300 hover:font-semibold hover:text-black">
|
||||
<span className="absolute inset-y-0 left-0 flex items-center rounded-lg pl-3 pr-4">
|
||||
<FontAwesomeIcon icon={faPlus} className="text-lg" />
|
||||
</span>
|
||||
Add Project
|
||||
|
@ -43,7 +43,7 @@ const Button = ({
|
||||
loading,
|
||||
icon,
|
||||
iconDisabled,
|
||||
type = "button",
|
||||
type = "button"
|
||||
}: ButtonProps): JSX.Element => {
|
||||
// Check if the button show always be 'active' - then true;
|
||||
// or if it should switch between 'active' and 'disabled' - then give the status
|
||||
@ -53,9 +53,13 @@ const Button = ({
|
||||
"group m-auto md:m-0 inline-block rounded-md duration-200",
|
||||
|
||||
// Setting background colors and hover modes
|
||||
color === "mineshaft" && activityStatus && "bg-mineshaft-800 border border-mineshaft-600 hover:bg-primary/[0.15] hover:border-primary/60",
|
||||
color === "mineshaft" &&
|
||||
activityStatus &&
|
||||
"bg-mineshaft-800 border border-mineshaft-600 hover:bg-primary/[0.15] hover:border-primary/60",
|
||||
color === "mineshaft" && !activityStatus && "bg-mineshaft",
|
||||
(color === "primary" || !color) && activityStatus && "bg-primary border border-primary-400 opacity-80 hover:opacity-100",
|
||||
(color === "primary" || !color) &&
|
||||
activityStatus &&
|
||||
"bg-primary border border-primary-400 opacity-80 hover:opacity-100",
|
||||
(color === "primary" || !color) && !activityStatus && "bg-primary",
|
||||
color === "red" && "bg-red-800 border border-red",
|
||||
|
||||
@ -78,7 +82,9 @@ const Button = ({
|
||||
color !== "mineshaft" && color !== "red" && color !== "none" && "text-black",
|
||||
color === "red" && "text-gray-200",
|
||||
color === "none" && "text-gray-200 text-xl",
|
||||
activityStatus && color !== "red" && color !== "mineshaft" && color !== "none" ? "group-hover:text-black" : "",
|
||||
activityStatus && color !== "red" && color !== "mineshaft" && color !== "none"
|
||||
? "group-hover:text-black"
|
||||
: "",
|
||||
|
||||
size === "icon" && "flex items-center justify-center"
|
||||
);
|
||||
@ -103,7 +109,7 @@ const Button = ({
|
||||
<div
|
||||
className={`${
|
||||
loading === true ? "opacity-100" : "opacity-0"
|
||||
} absolute flex items-center px-3 bg-primary duration-200 w-full`}
|
||||
} absolute flex w-full items-center bg-primary px-3 duration-200`}
|
||||
>
|
||||
<Image
|
||||
src="/images/loading/loadingblack.gif"
|
||||
@ -116,7 +122,7 @@ const Button = ({
|
||||
{icon && (
|
||||
<FontAwesomeIcon
|
||||
icon={icon}
|
||||
className={`flex my-auto font-extrabold ${size === "icon-sm" ? "text-sm" : "text-sm"} ${
|
||||
className={`my-auto flex font-extrabold ${size === "icon-sm" ? "text-sm" : "text-sm"} ${
|
||||
(text || textDisabled) && "mr-2"
|
||||
}`}
|
||||
/>
|
||||
@ -124,7 +130,7 @@ const Button = ({
|
||||
{iconDisabled && (
|
||||
<FontAwesomeIcon
|
||||
icon={iconDisabled as IconProp}
|
||||
className={`flex my-auto font-extrabold ${size === "icon-sm" ? "text-sm" : "text-md"} ${
|
||||
className={`my-auto flex font-extrabold ${size === "icon-sm" ? "text-sm" : "text-md"} ${
|
||||
(text || textDisabled) && "mr-2"
|
||||
}`}
|
||||
/>
|
||||
|
@ -64,7 +64,7 @@ const AddProjectMemberDialog = ({
|
||||
) : (
|
||||
<Dialog.Title
|
||||
as="h3"
|
||||
className="z-50 text-lg font-medium text-mineshaft-300 mb-4"
|
||||
className="z-50 mb-4 text-lg font-medium text-mineshaft-300"
|
||||
>
|
||||
{t("section.members.add-dialog.already-all-invited")}
|
||||
</Dialog.Title>
|
||||
@ -127,7 +127,9 @@ const AddProjectMemberDialog = ({
|
||||
</div>
|
||||
) : (
|
||||
<Button
|
||||
onButtonPressed={() => router.push(`/org/${localStorage.getItem("orgData.id")}/members`)}
|
||||
onButtonPressed={() =>
|
||||
router.push(`/org/${localStorage.getItem("orgData.id")}/members`)
|
||||
}
|
||||
color="mineshaft"
|
||||
text={t("section.members.add-dialog.add-user-to-org") as string}
|
||||
size="md"
|
||||
|
@ -28,11 +28,11 @@ export const AddUpdateEnvironmentDialog = ({
|
||||
onCreateSubmit,
|
||||
onEditSubmit,
|
||||
initialValues,
|
||||
isEditMode,
|
||||
isEditMode
|
||||
}: Props) => {
|
||||
const [formInput, setFormInput] = useState<FormFields>({
|
||||
name: "",
|
||||
slug: "",
|
||||
slug: ""
|
||||
});
|
||||
|
||||
// This use effect can be removed when the unmount is happening from outside the component
|
||||
@ -50,7 +50,7 @@ export const AddUpdateEnvironmentDialog = ({
|
||||
e.preventDefault();
|
||||
const data = {
|
||||
name: formInput.name,
|
||||
slug: formInput.slug.toLowerCase(),
|
||||
slug: formInput.slug.toLowerCase()
|
||||
};
|
||||
if (isEditMode) {
|
||||
onEditSubmit(data);
|
||||
@ -62,75 +62,70 @@ export const AddUpdateEnvironmentDialog = ({
|
||||
return (
|
||||
<div>
|
||||
<Transition appear show={isOpen} as={Fragment}>
|
||||
<Dialog as='div' className='relative z-20' onClose={onClose}>
|
||||
<Dialog as="div" className="relative z-20" onClose={onClose}>
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter='ease-out duration-300'
|
||||
enterFrom='opacity-0'
|
||||
enterTo='opacity-100'
|
||||
leave='ease-out duration-150'
|
||||
leaveFrom='opacity-100'
|
||||
leaveTo='opacity-0'
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="ease-out duration-150"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<div className='fixed inset-0 bg-black bg-opacity-70' />
|
||||
<div className="fixed inset-0 bg-black bg-opacity-70" />
|
||||
</Transition.Child>
|
||||
|
||||
<div className='fixed inset-0 overflow-y-auto z-50'>
|
||||
<div className='flex min-h-full items-center justify-center p-4 text-center'>
|
||||
<div className="fixed inset-0 z-50 overflow-y-auto">
|
||||
<div className="flex min-h-full items-center justify-center p-4 text-center">
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter='ease-out duration-300'
|
||||
enterFrom='opacity-0 scale-95'
|
||||
enterTo='opacity-100 scale-100'
|
||||
leave='ease-in duration-200'
|
||||
leaveFrom='opacity-100 scale-100'
|
||||
leaveTo='opacity-0 scale-95'
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0 scale-95"
|
||||
enterTo="opacity-100 scale-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
leaveTo="opacity-0 scale-95"
|
||||
>
|
||||
<Dialog.Panel className='w-full max-w-md transform overflow-hidden rounded-md bg-bunker-800 border border-gray-700 p-6 text-left align-middle shadow-xl transition-all'>
|
||||
<Dialog.Title
|
||||
as='h3'
|
||||
className='text-lg font-medium leading-6 text-gray-400'
|
||||
>
|
||||
{isEditMode
|
||||
? "Update environment"
|
||||
: "Create a new environment"}
|
||||
<Dialog.Panel className="w-full max-w-md transform overflow-hidden rounded-md border border-gray-700 bg-bunker-800 p-6 text-left align-middle shadow-xl transition-all">
|
||||
<Dialog.Title as="h3" className="text-lg font-medium leading-6 text-gray-400">
|
||||
{isEditMode ? "Update environment" : "Create a new environment"}
|
||||
</Dialog.Title>
|
||||
<form onSubmit={onFormSubmit}>
|
||||
<div className='max-h-28 mt-4'>
|
||||
<div className="mt-4 max-h-28">
|
||||
<InputField
|
||||
label='Environment Name'
|
||||
label="Environment Name"
|
||||
onChangeHandler={(val) => onInputChange("name", val)}
|
||||
type='varName'
|
||||
type="varName"
|
||||
value={formInput.name}
|
||||
placeholder=''
|
||||
placeholder=""
|
||||
isRequired
|
||||
// error={error.length > 0}
|
||||
// errorText={error}
|
||||
/>
|
||||
</div>
|
||||
<div className='max-h-28 mt-4'>
|
||||
<div className="mt-4 max-h-28">
|
||||
<InputField
|
||||
label='Environment Slug'
|
||||
label="Environment Slug"
|
||||
onChangeHandler={(val) => onInputChange("slug", val)}
|
||||
type='varName'
|
||||
type="varName"
|
||||
value={formInput.slug}
|
||||
placeholder=''
|
||||
placeholder=""
|
||||
isRequired
|
||||
// error={error.length > 0}
|
||||
// errorText={error}
|
||||
/>
|
||||
</div>
|
||||
<p className='text-xs text-gray-500 mt-2'>
|
||||
<p className="mt-2 text-xs text-gray-500">
|
||||
Slugs are shorthands used in cli to access environment
|
||||
</p>
|
||||
<div className='mt-4 max-w-min'>
|
||||
<div className="mt-4 max-w-min">
|
||||
<Button
|
||||
onButtonPressed={() => null}
|
||||
type='submit'
|
||||
color='mineshaft'
|
||||
type="submit"
|
||||
color="mineshaft"
|
||||
text={isEditMode ? "Update" : "Create"}
|
||||
active={formInput.name !== "" && formInput.slug !== ""}
|
||||
size='md'
|
||||
size="md"
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
|
@ -13,76 +13,63 @@ type Props = {
|
||||
orgName: string;
|
||||
};
|
||||
|
||||
const AddUserDialog = ({
|
||||
isOpen,
|
||||
closeModal,
|
||||
submitModal,
|
||||
email,
|
||||
setEmail,
|
||||
orgName,
|
||||
}: Props) => {
|
||||
const AddUserDialog = ({ isOpen, closeModal, submitModal, email, setEmail, orgName }: Props) => {
|
||||
const submit = () => {
|
||||
submitModal(email);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className='z-50'>
|
||||
<div className="z-50">
|
||||
<Transition appear show={isOpen} as={Fragment}>
|
||||
<Dialog as='div' className='relative' onClose={closeModal}>
|
||||
<Dialog as="div" className="relative" onClose={closeModal}>
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter='ease-out duration-300'
|
||||
enterFrom='opacity-0'
|
||||
enterTo='opacity-100'
|
||||
leave='ease-in duration-200'
|
||||
leaveFrom='opacity-100'
|
||||
leaveTo='opacity-0'
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<div className='fixed inset-0 bg-black bg-opacity-70' />
|
||||
<div className="fixed inset-0 bg-black bg-opacity-70" />
|
||||
</Transition.Child>
|
||||
|
||||
<div className='fixed inset-0 overflow-y-auto'>
|
||||
<div className='flex min-h-full items-center justify-center p-4 text-center'>
|
||||
<div className="fixed inset-0 overflow-y-auto">
|
||||
<div className="flex min-h-full items-center justify-center p-4 text-center">
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter='ease-out duration-300'
|
||||
enterFrom='opacity-0 scale-95'
|
||||
enterTo='opacity-100 scale-100'
|
||||
leave='ease-in duration-200'
|
||||
leaveFrom='opacity-100 scale-100'
|
||||
leaveTo='opacity-0 scale-95'
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0 scale-95"
|
||||
enterTo="opacity-100 scale-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
leaveTo="opacity-0 scale-95"
|
||||
>
|
||||
<Dialog.Panel className='w-full max-w-lg transform overflow-hidden rounded-md bg-bunker-800 border border-gray-700 p-6 text-left align-middle shadow-xl transition-all'>
|
||||
<Dialog.Panel className="w-full max-w-lg transform overflow-hidden rounded-md border border-gray-700 bg-bunker-800 p-6 text-left align-middle shadow-xl transition-all">
|
||||
<Dialog.Title
|
||||
as='h3'
|
||||
className='text-lg font-medium leading-6 text-gray-400 z-50'
|
||||
as="h3"
|
||||
className="z-50 text-lg font-medium leading-6 text-gray-400"
|
||||
>
|
||||
Invite others to {orgName}
|
||||
</Dialog.Title>
|
||||
<div className='mt-2 mb-4'>
|
||||
<p className='text-sm text-gray-500'>
|
||||
An invite is specific to an email address and expires
|
||||
after 1 day. For security reasons, you will need to
|
||||
separately add members to projects.
|
||||
<div className="mt-2 mb-4">
|
||||
<p className="text-sm text-gray-500">
|
||||
An invite is specific to an email address and expires after 1 day. For
|
||||
security reasons, you will need to separately add members to projects.
|
||||
</p>
|
||||
</div>
|
||||
<div className='max-h-28'>
|
||||
<div className="max-h-28">
|
||||
<InputField
|
||||
label='Email'
|
||||
label="Email"
|
||||
onChangeHandler={setEmail}
|
||||
type='varName'
|
||||
type="varName"
|
||||
value={email}
|
||||
placeholder=''
|
||||
placeholder=""
|
||||
isRequired
|
||||
/>
|
||||
</div>
|
||||
<div className='mt-4 max-w-max'>
|
||||
<Button
|
||||
onButtonPressed={submit}
|
||||
color='mineshaft'
|
||||
text='Invite'
|
||||
size='md'
|
||||
/>
|
||||
<div className="mt-4 max-w-max">
|
||||
<Button onButtonPressed={submit} color="mineshaft" text="Invite" size="md" />
|
||||
</div>
|
||||
</Dialog.Panel>
|
||||
{/* <Dialog.Panel className="w-full max-w-md transform overflow-hidden rounded-md bg-bunker-800 border border-gray-700 p-6 text-left align-middle shadow-xl transition-all">
|
||||
|
@ -5,7 +5,6 @@ import Button from "../buttons/Button";
|
||||
import InputField from "../InputField";
|
||||
import { Checkbox } from "../table/Checkbox";
|
||||
|
||||
|
||||
type Props = {
|
||||
isOpen: boolean;
|
||||
closeModal: () => void;
|
||||
@ -26,8 +25,8 @@ const AddWorkspaceDialog = ({
|
||||
workspaceName,
|
||||
setWorkspaceName,
|
||||
error,
|
||||
loading,
|
||||
}:Props) => {
|
||||
loading
|
||||
}: Props) => {
|
||||
const [addAllUsers, setAddAllUsers] = useState(true);
|
||||
const submit = () => {
|
||||
submitModal(workspaceName, addAllUsers);
|
||||
@ -60,11 +59,8 @@ const AddWorkspaceDialog = ({
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
leaveTo="opacity-0 scale-95"
|
||||
>
|
||||
<Dialog.Panel className="w-full max-w-md transform overflow-hidden rounded-md bg-bunker-800 border border-gray-700 p-6 text-left align-middle shadow-xl transition-all">
|
||||
<Dialog.Title
|
||||
as="h3"
|
||||
className="text-lg font-medium leading-6 text-gray-400"
|
||||
>
|
||||
<Dialog.Panel className="w-full max-w-md transform overflow-hidden rounded-md border border-gray-700 bg-bunker-800 p-6 text-left align-middle shadow-xl transition-all">
|
||||
<Dialog.Title as="h3" className="text-lg font-medium leading-6 text-gray-400">
|
||||
Create a new project
|
||||
</Dialog.Title>
|
||||
<div className="mt-2">
|
||||
@ -72,7 +68,7 @@ const AddWorkspaceDialog = ({
|
||||
This project will contain your secrets and configs.
|
||||
</p>
|
||||
</div>
|
||||
<div className="max-h-28 mt-4">
|
||||
<div className="mt-4 max-h-28">
|
||||
<InputField
|
||||
label="Project Name"
|
||||
onChangeHandler={setWorkspaceName}
|
||||
@ -84,10 +80,7 @@ const AddWorkspaceDialog = ({
|
||||
/>
|
||||
</div>
|
||||
<div className="mt-4 ml-1">
|
||||
<Checkbox
|
||||
addAllUsers={addAllUsers}
|
||||
setAddAllUsers={setAddAllUsers}
|
||||
/>
|
||||
<Checkbox addAllUsers={addAllUsers} setAddAllUsers={setAddAllUsers} />
|
||||
</div>
|
||||
<div className="mt-4 max-w-min">
|
||||
<Button
|
||||
|
@ -3,89 +3,76 @@ import { Dialog, Transition } from "@headlessui/react";
|
||||
|
||||
import InputField from "../InputField";
|
||||
|
||||
// REFACTOR: Move all these modals into one reusable one
|
||||
// REFACTOR: Move all these modals into one reusable one
|
||||
type Props = {
|
||||
isOpen?: boolean;
|
||||
onClose: ()=>void;
|
||||
onClose: () => void;
|
||||
title: string;
|
||||
onSubmit:()=>void;
|
||||
deleteKey?:string;
|
||||
}
|
||||
onSubmit: () => void;
|
||||
deleteKey?: string;
|
||||
};
|
||||
|
||||
const DeleteActionModal = ({
|
||||
isOpen,
|
||||
onClose,
|
||||
title,
|
||||
onSubmit,
|
||||
deleteKey
|
||||
}:Props) => {
|
||||
const [deleteInputField, setDeleteInputField] = useState("")
|
||||
const DeleteActionModal = ({ isOpen, onClose, title, onSubmit, deleteKey }: Props) => {
|
||||
const [deleteInputField, setDeleteInputField] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
setDeleteInputField("");
|
||||
}, [isOpen]);
|
||||
useEffect(() => {
|
||||
setDeleteInputField("");
|
||||
}, [isOpen]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Transition appear show={isOpen} as={Fragment}>
|
||||
<Dialog as='div' className='relative z-10' onClose={onClose}>
|
||||
<Dialog as="div" className="relative z-10" onClose={onClose}>
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter='ease-out duration-300'
|
||||
enterFrom='opacity-0'
|
||||
enterTo='opacity-100'
|
||||
leave='ease-in duration-150'
|
||||
leaveFrom='opacity-100'
|
||||
leaveTo='opacity-0'
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="ease-in duration-150"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<div className='fixed inset-0 bg-black bg-opacity-70' />
|
||||
<div className="fixed inset-0 bg-black bg-opacity-70" />
|
||||
</Transition.Child>
|
||||
<div className='fixed inset-0 overflow-y-auto'>
|
||||
<div className='flex min-h-full items-center justify-center p-4 text-center'>
|
||||
<div className="fixed inset-0 overflow-y-auto">
|
||||
<div className="flex min-h-full items-center justify-center p-4 text-center">
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter='ease-out duration-300'
|
||||
enterFrom='opacity-0 scale-95'
|
||||
enterTo='opacity-100 scale-100'
|
||||
leave='ease-in duration-200'
|
||||
leaveFrom='opacity-100 scale-100'
|
||||
leaveTo='opacity-0 scale-95'
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0 scale-95"
|
||||
enterTo="opacity-100 scale-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
leaveTo="opacity-0 scale-95"
|
||||
>
|
||||
<Dialog.Panel className='w-full max-w-md transform overflow-hidden rounded-md bg-grey border border-gray-700 p-6 text-left align-middle shadow-xl transition-all'>
|
||||
<Dialog.Title
|
||||
as='h3'
|
||||
className='text-lg font-medium leading-6 text-gray-400'
|
||||
>
|
||||
<Dialog.Panel className="w-full max-w-md transform overflow-hidden rounded-md border border-gray-700 bg-grey p-6 text-left align-middle shadow-xl transition-all">
|
||||
<Dialog.Title as="h3" className="text-lg font-medium leading-6 text-gray-400">
|
||||
{title}
|
||||
</Dialog.Title>
|
||||
<div className='mt-2'>
|
||||
<p className='text-sm text-gray-500'>
|
||||
This action is irrevertible.
|
||||
</p>
|
||||
<div className="mt-2">
|
||||
<p className="text-sm text-gray-500">This action is irrevertible.</p>
|
||||
</div>
|
||||
<div className='mt-2'>
|
||||
<div className="mt-2">
|
||||
<InputField
|
||||
isRequired
|
||||
label={`Type ${deleteKey} to delete the resource`}
|
||||
onChangeHandler={(val) => setDeleteInputField(val)}
|
||||
value={deleteInputField}
|
||||
type='text'
|
||||
type="text"
|
||||
/>
|
||||
</div>
|
||||
<div className='mt-6'>
|
||||
<div className="mt-6">
|
||||
<button
|
||||
type='button'
|
||||
className='inline-flex justify-center rounded-md border border-transparent bg-gray-800 px-4 py-2 text-sm font-medium text-gray-400 hover:bg-alizarin hover:text-white hover:text-semibold duration-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2'
|
||||
type="button"
|
||||
className="hover:bg-alizarin hover:text-semibold inline-flex justify-center rounded-md border border-transparent bg-gray-800 px-4 py-2 text-sm font-medium text-gray-400 duration-200 hover:text-white focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2"
|
||||
onClick={onSubmit}
|
||||
disabled={
|
||||
Boolean(deleteKey) && deleteInputField !== deleteKey
|
||||
}
|
||||
disabled={Boolean(deleteKey) && deleteInputField !== deleteKey}
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
<button
|
||||
type='button'
|
||||
className='ml-2 inline-flex justify-center rounded-md border border-transparent bg-gray-800 px-4 py-2 text-sm font-medium text-gray-400 hover:border-white hover:text-white hover:text-semibold duration-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2'
|
||||
type="button"
|
||||
className="hover:text-semibold ml-2 inline-flex justify-center rounded-md border border-transparent bg-gray-800 px-4 py-2 text-sm font-medium text-gray-400 duration-200 hover:border-white hover:text-white focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2"
|
||||
onClick={onClose}
|
||||
>
|
||||
Cancel
|
||||
|
@ -5,13 +5,13 @@ import { Dialog, Transition } from "@headlessui/react";
|
||||
// #TODO: USE THIS. Currently it's not. Kinda complicated to set up because of state.
|
||||
|
||||
type Props = {
|
||||
isOpen: boolean
|
||||
onClose: () => void
|
||||
onSubmit: () => void
|
||||
}
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
onSubmit: () => void;
|
||||
};
|
||||
|
||||
export const DeleteEnvVar = ({ isOpen, onClose, onSubmit }: Props) => {
|
||||
const { t } = useTranslation()
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div>
|
||||
<Transition appear show={isOpen} as={Fragment}>
|
||||
@ -45,7 +45,7 @@ export const DeleteEnvVar = ({ isOpen, onClose, onSubmit }: Props) => {
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
leaveTo="opacity-0 scale-95"
|
||||
>
|
||||
<Dialog.Panel className="w-full max-w-md transform overflow-hidden rounded-md bg-bunker border border-mineshaft-600 p-6 text-left align-middle shadow-xl transition-all">
|
||||
<Dialog.Panel className="w-full max-w-md transform overflow-hidden rounded-md border border-mineshaft-600 bg-bunker p-6 text-left align-middle shadow-xl transition-all">
|
||||
<Dialog.Title as="h3" className="text-lg font-medium leading-6 text-bunker-200">
|
||||
{t("dashboard:sidebar.delete-key-dialog.title")}
|
||||
</Dialog.Title>
|
||||
@ -57,14 +57,14 @@ export const DeleteEnvVar = ({ isOpen, onClose, onSubmit }: Props) => {
|
||||
<div className="mt-6 flex justify-start">
|
||||
<button
|
||||
type="button"
|
||||
className="inline-flex justify-center rounded-md border border-transparent bg-red-500 opacity-80 hover:opacity-100 px-4 py-2 text-sm font-medium text-bunker-100 text-semibold duration-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2"
|
||||
className="text-semibold inline-flex justify-center rounded-md border border-transparent bg-red-500 px-4 py-2 text-sm font-medium text-bunker-100 opacity-80 duration-200 hover:opacity-100 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2"
|
||||
onClick={onSubmit}
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="ml-2 inline-flex justify-center rounded-md border border-transparent bg-bunker-500 px-4 py-2 text-sm font-medium text-gray-400 hover:bg-mineshaft-500 hover:text-white hover:text-semibold duration-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2"
|
||||
className="hover:text-semibold ml-2 inline-flex justify-center rounded-md border border-transparent bg-bunker-500 px-4 py-2 text-sm font-medium text-gray-400 duration-200 hover:bg-mineshaft-500 hover:text-white focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2"
|
||||
onClick={onClose}
|
||||
>
|
||||
Cancel
|
||||
|
@ -10,66 +10,55 @@ type Props = {
|
||||
userIdToBeDeleted: string;
|
||||
};
|
||||
|
||||
const DeleteUserDialog = ({
|
||||
isOpen,
|
||||
closeModal,
|
||||
submitModal,
|
||||
userIdToBeDeleted,
|
||||
}: Props) => {
|
||||
const DeleteUserDialog = ({ isOpen, closeModal, submitModal, userIdToBeDeleted }: Props) => {
|
||||
const submit = () => {
|
||||
submitModal(userIdToBeDeleted);
|
||||
};
|
||||
return (
|
||||
<div>
|
||||
<Transition appear show={isOpen} as={Fragment}>
|
||||
<Dialog as='div' className='relative z-10' onClose={closeModal}>
|
||||
<Dialog as="div" className="relative z-10" onClose={closeModal}>
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter='ease-out duration-300'
|
||||
enterFrom='opacity-0'
|
||||
enterTo='opacity-100'
|
||||
leave='ease-in duration-200'
|
||||
leaveFrom='opacity-100'
|
||||
leaveTo='opacity-0'
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<div className='fixed inset-0 bg-black bg-opacity-25' />
|
||||
<div className="fixed inset-0 bg-black bg-opacity-25" />
|
||||
</Transition.Child>
|
||||
|
||||
<div className='fixed inset-0 overflow-y-auto'>
|
||||
<div className='flex min-h-full items-center justify-center p-4 text-center'>
|
||||
<div className="fixed inset-0 overflow-y-auto">
|
||||
<div className="flex min-h-full items-center justify-center p-4 text-center">
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter='ease-out duration-300'
|
||||
enterFrom='opacity-0 scale-95'
|
||||
enterTo='opacity-100 scale-100'
|
||||
leave='ease-in duration-200'
|
||||
leaveFrom='opacity-100 scale-100'
|
||||
leaveTo='opacity-0 scale-95'
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0 scale-95"
|
||||
enterTo="opacity-100 scale-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
leaveTo="opacity-0 scale-95"
|
||||
>
|
||||
<Dialog.Panel className='w-full max-w-md transform overflow-hidden rounded-2xl bg-grey border border-gray-700 p-6 text-left align-middle shadow-xl transition-all'>
|
||||
<Dialog.Title
|
||||
as='h3'
|
||||
className='text-lg font-medium leading-6 text-gray-400'
|
||||
>
|
||||
Are you sure you want to remove this user from the
|
||||
workspace?
|
||||
<Dialog.Panel className="w-full max-w-md transform overflow-hidden rounded-2xl border border-gray-700 bg-grey p-6 text-left align-middle shadow-xl transition-all">
|
||||
<Dialog.Title as="h3" className="text-lg font-medium leading-6 text-gray-400">
|
||||
Are you sure you want to remove this user from the workspace?
|
||||
</Dialog.Title>
|
||||
<div className='mt-2'>
|
||||
<p className='text-sm text-gray-500'>
|
||||
This action is irrevertible.
|
||||
</p>
|
||||
<div className="mt-2">
|
||||
<p className="text-sm text-gray-500">This action is irrevertible.</p>
|
||||
</div>
|
||||
<div className='mt-6'>
|
||||
<div className="mt-6">
|
||||
<button
|
||||
type='button'
|
||||
className='inline-flex justify-center rounded-md border border-transparent bg-gray-800 px-4 py-2 text-sm font-medium text-gray-400 hover:bg-alizarin hover:text-white hover:text-semibold duration-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2'
|
||||
type="button"
|
||||
className="hover:bg-alizarin hover:text-semibold inline-flex justify-center rounded-md border border-transparent bg-gray-800 px-4 py-2 text-sm font-medium text-gray-400 duration-200 hover:text-white focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2"
|
||||
onClick={submit}
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
<button
|
||||
type='button'
|
||||
className='ml-2 inline-flex justify-center rounded-md border border-transparent bg-gray-800 px-4 py-2 text-sm font-medium text-gray-400 hover:border-white hover:text-white hover:text-semibold duration-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2'
|
||||
type="button"
|
||||
className="hover:text-semibold ml-2 inline-flex justify-center rounded-md border border-transparent bg-gray-800 px-4 py-2 text-sm font-medium text-gray-400 duration-200 hover:border-white hover:text-white focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2"
|
||||
onClick={submit}
|
||||
>
|
||||
Cancel
|
||||
|
@ -34,27 +34,27 @@ const BottonRightPopup = ({
|
||||
}: PopupProps): JSX.Element => {
|
||||
return (
|
||||
<div
|
||||
className="z-[100] drop-shadow-xl border-gray-600/50 border flex flex-col items-start bg-bunker max-w-xl text-gray-200 pt-3 pb-4 rounded-md absolute bottom-0 right-0 mr-6 mb-6"
|
||||
className="absolute bottom-0 right-0 z-[100] mr-6 mb-6 flex max-w-xl flex-col items-start rounded-md border border-gray-600/50 bg-bunker pt-3 pb-4 text-gray-200 drop-shadow-xl"
|
||||
role="alert"
|
||||
>
|
||||
<div className="flex flex-row items-center justify-between w-full border-b border-gray-600/70 pb-3 px-6">
|
||||
<div className="font-bold text-xl mr-2 mt-0.5 flex flex-row">
|
||||
<div className="flex w-full flex-row items-center justify-between border-b border-gray-600/70 px-6 pb-3">
|
||||
<div className="mr-2 mt-0.5 flex flex-row text-xl font-bold">
|
||||
<div>{titleText}</div>
|
||||
<div className="ml-2.5">{emoji}</div>
|
||||
</div>
|
||||
<button className="mt-1" onClick={() => setCheckDocsPopUpVisible(false)} type="button">
|
||||
<FontAwesomeIcon
|
||||
icon={faXmark}
|
||||
className="text-gray-400 text-2xl hover:text-red duration-200 cursor-pointer"
|
||||
className="cursor-pointer text-2xl text-gray-400 duration-200 hover:text-red"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
<div className="block sm:inline px-6 mt-4 mb-0.5 text-gray-300">{textLine1}</div>
|
||||
<div className="block sm:inline mb-4 px-6">{textLine2}</div>
|
||||
<div className="flex flex-row px-6 w-full">
|
||||
<div className="mt-4 mb-0.5 block px-6 text-gray-300 sm:inline">{textLine1}</div>
|
||||
<div className="mb-4 block px-6 sm:inline">{textLine2}</div>
|
||||
<div className="flex w-full flex-row px-6">
|
||||
{/* eslint-disable-next-line react/jsx-no-target-blank */}
|
||||
<a
|
||||
className="font-bold p-2 bg-white/10 rounded-md w-full hover:bg-primary duration-200 hover:text-black flex justify-center"
|
||||
className="flex w-full justify-center rounded-md bg-white/10 p-2 font-bold duration-200 hover:bg-primary hover:text-black"
|
||||
href={buttonLink}
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
|
@ -9,7 +9,7 @@ export const Checkbox = ({ addAllUsers, setAddAllUsers }: Props) => (
|
||||
{addAllUsers === true ? (
|
||||
<input
|
||||
type="checkbox"
|
||||
className="accent-primary h-4 w-4"
|
||||
className="h-4 w-4 accent-primary"
|
||||
checked
|
||||
readOnly
|
||||
onClick={() => setAddAllUsers(!addAllUsers)}
|
||||
@ -20,12 +20,12 @@ export const Checkbox = ({ addAllUsers, setAddAllUsers }: Props) => (
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
aria-label="add all users"
|
||||
className="h-4 w-4 bg-bunker border border-gray-600 rounded-sm"
|
||||
className="h-4 w-4 rounded-sm border border-gray-600 bg-bunker"
|
||||
onClick={() => setAddAllUsers(!addAllUsers)}
|
||||
/>
|
||||
)}
|
||||
|
||||
<label className="ml-2 text-gray-500 text-sm">
|
||||
<label className="ml-2 text-sm text-gray-500">
|
||||
Add all members of my organization to this project.
|
||||
</label>
|
||||
</div>
|
||||
|
@ -36,25 +36,28 @@ const Notification = ({ notification, clearNotification }: NotificationProps) =>
|
||||
|
||||
return (
|
||||
<div
|
||||
className="relative w-full flex items-center justify-between px-6 py-4 rounded-md border border-bunker-500 pointer-events-auto bg-mineshaft-700 mb-3 right-3"
|
||||
className="pointer-events-auto relative right-3 mb-3 flex w-full items-center justify-between rounded-md border border-bunker-500 bg-mineshaft-700 px-6 py-4"
|
||||
role="alert"
|
||||
>
|
||||
{notification.type === "error" && (
|
||||
<div className="absolute w-full h-1 bg-red top-0 left-0 rounded-t-md" />
|
||||
<div className="absolute top-0 left-0 h-1 w-full rounded-t-md bg-red" />
|
||||
)}
|
||||
{notification.type === "success" && (
|
||||
<div className="absolute w-full h-1 bg-green top-0 left-0 rounded-t-md" />
|
||||
<div className="absolute top-0 left-0 h-1 w-full rounded-t-md bg-green" />
|
||||
)}
|
||||
{notification.type === "info" && (
|
||||
<div className="absolute w-full h-1 bg-yellow top-0 left-0 rounded-t-md" />
|
||||
<div className="absolute top-0 left-0 h-1 w-full rounded-t-md bg-yellow" />
|
||||
)}
|
||||
<p className="text-bunker-200 text-md font-base mt-0.5">{notification.text}</p>
|
||||
<p className="text-md font-base mt-0.5 text-bunker-200">{notification.text}</p>
|
||||
<button
|
||||
type="button"
|
||||
className="rounded-lg"
|
||||
onClick={() => clearNotification(notification.text)}
|
||||
>
|
||||
<FontAwesomeIcon className="absolute right-2 top-3 text-bunker-300 pl-2 w-4 h-4 hover:text-white" icon={faXmark} />
|
||||
<FontAwesomeIcon
|
||||
className="absolute right-2 top-3 h-4 w-4 pl-2 text-bunker-300 hover:text-white"
|
||||
icon={faXmark}
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
|
@ -11,7 +11,7 @@ const Notifications = ({ notifications, clearNotification }: NoticationsProps) =
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="hidden fixed z-50 md:flex md:flex-col-reverse gap-y-2 w-96 h-full right-2 bottom-2 pointer-events-none">
|
||||
<div className="pointer-events-none fixed right-2 bottom-2 z-[100] hidden h-full w-96 gap-y-2 md:flex md:flex-col-reverse">
|
||||
{notifications.map((notif) => (
|
||||
<Notification key={notif.text} notification={notif} clearNotification={clearNotification} />
|
||||
))}
|
||||
|
@ -30,9 +30,9 @@ const ConfirmEnvOverwriteModal = ({
|
||||
onClose={onClose}
|
||||
>
|
||||
<div className="flex flex-col gap-2">
|
||||
<p className='text-gray-400'>Your file contains the following duplicate secrets:</p>
|
||||
<p className="text-gray-400">Your file contains the following duplicate secrets:</p>
|
||||
<p className="text-sm text-gray-500">{duplicateKeys.join(", ")}</p>
|
||||
<p className='text-md text-gray-400'>Are you sure you want to overwrite these secrets?</p>
|
||||
<p className="text-md text-gray-400">Are you sure you want to overwrite these secrets?</p>
|
||||
</div>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
|
@ -1,5 +1,10 @@
|
||||
import { memo, SyntheticEvent, useRef } from "react";
|
||||
import { faCircle, faCodeBranch, faExclamationCircle, faEye } from "@fortawesome/free-solid-svg-icons";
|
||||
import {
|
||||
faCircle,
|
||||
faCodeBranch,
|
||||
faExclamationCircle,
|
||||
faEye
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import guidGenerator from "../utilities/randomId";
|
||||
@ -31,8 +36,8 @@ interface DashboardInputFieldProps {
|
||||
* @param {boolean} obj.blurred - whether the input field should be blurred (behind the gray dots) or not; this can be turned on/off in the dashboard
|
||||
* @param {boolean} obj.isDuplicate - if the key name is duplicated
|
||||
* @param {boolean} obj.override - whether a secret/row should be displalyed as overriden
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
* @returns
|
||||
*/
|
||||
|
||||
@ -61,29 +66,31 @@ const DashboardInputField = ({
|
||||
const error = startsWithNumber || isDuplicate;
|
||||
|
||||
return (
|
||||
<div className={`relative flex-col w-full h-10 ${
|
||||
error && value !== "" ? "bg-red/[0.15]" : ""
|
||||
} ${
|
||||
isSideBarOpen && "bg-mineshaft-700 duration-200"
|
||||
}`}>
|
||||
<div
|
||||
className={`relative h-10 w-full flex-col ${error && value !== "" ? "bg-red/[0.15]" : ""} ${
|
||||
isSideBarOpen && "bg-mineshaft-700 duration-200"
|
||||
}`}
|
||||
>
|
||||
<div
|
||||
className={`group relative flex flex-col justify-center items-center h-full ${
|
||||
className={`group relative flex h-full flex-col items-center justify-center ${
|
||||
error ? "w-max" : "w-full"
|
||||
}`}
|
||||
>
|
||||
<input
|
||||
onChange={(e) => onChangeHandler(isCapitalized ? e.target.value.toUpperCase() : e.target.value, id)}
|
||||
onChange={(e) =>
|
||||
onChangeHandler(isCapitalized ? e.target.value.toUpperCase() : e.target.value, id)
|
||||
}
|
||||
type={type}
|
||||
value={value}
|
||||
className={`z-10 peer font-mono ph-no-capture bg-transparent h-full caret-bunker-200 text-sm px-2 w-full min-w-16 outline-none ${
|
||||
className={`ph-no-capture min-w-16 peer z-10 h-full w-full bg-transparent px-2 font-mono text-sm caret-bunker-200 outline-none ${
|
||||
error ? "text-red-600 focus:text-red-500" : "text-bunker-300 focus:text-bunker-100"
|
||||
} duration-200`}
|
||||
spellCheck="false"
|
||||
/>
|
||||
</div>
|
||||
{startsWithNumber && (
|
||||
<div className='absolute right-2 top-2 text-red z-50'>
|
||||
<HoverObject
|
||||
<div className="absolute right-2 top-2 z-50 text-red">
|
||||
<HoverObject
|
||||
text="Secret names should not start with a number"
|
||||
icon={faExclamationCircle}
|
||||
color="red"
|
||||
@ -91,33 +98,44 @@ const DashboardInputField = ({
|
||||
</div>
|
||||
)}
|
||||
{isDuplicate && value !== "" && !startsWithNumber && (
|
||||
<div className='absolute right-2 top-2 text-red z-50'>
|
||||
<HoverObject
|
||||
<div className="absolute right-2 top-2 z-50 text-red">
|
||||
<HoverObject
|
||||
text="Secret names should be unique"
|
||||
icon={faExclamationCircle}
|
||||
color="red"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{!error && <div className={`absolute right-0 top-0 text-red z-50 bg-mineshaft-800 group-hover:bg-mineshaft-700 ${
|
||||
overrideEnabled ? "visible" : "invisible group-hover:visible"
|
||||
} cursor-pointer duration-0 h-10 flex items-center px-2`}>
|
||||
<button type="button" onClick={() => {
|
||||
if (modifyValueOverride) {
|
||||
if (overrideEnabled === false) {
|
||||
modifyValueOverride("", id);
|
||||
} else {
|
||||
modifyValueOverride(undefined, id);
|
||||
}
|
||||
}
|
||||
}}>
|
||||
<HoverObject
|
||||
text={overrideEnabled ? "This secret is overriden with your personal value" : "You can override this secret with a personal value"}
|
||||
icon={faCodeBranch}
|
||||
color={overrideEnabled ? "primary" : "bunker-400"}
|
||||
/>
|
||||
</button>
|
||||
</div>}
|
||||
{!error && (
|
||||
<div
|
||||
className={`absolute right-0 top-0 z-50 bg-mineshaft-800 text-red group-hover:bg-mineshaft-700 ${
|
||||
overrideEnabled ? "visible" : "invisible group-hover:visible"
|
||||
} duration-0 flex h-10 cursor-pointer items-center px-2`}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
if (modifyValueOverride) {
|
||||
if (overrideEnabled === false) {
|
||||
modifyValueOverride("", id);
|
||||
} else {
|
||||
modifyValueOverride(undefined, id);
|
||||
}
|
||||
}
|
||||
}}
|
||||
>
|
||||
<HoverObject
|
||||
text={
|
||||
overrideEnabled
|
||||
? "This secret is overriden with your personal value"
|
||||
: "You can override this secret with a personal value"
|
||||
}
|
||||
icon={faCodeBranch}
|
||||
color={overrideEnabled ? "primary" : "bunker-400"}
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -127,20 +145,29 @@ const DashboardInputField = ({
|
||||
|
||||
return (
|
||||
<PopoverObject text={value || ""} onChangeHandler={onChangeHandler} id={id}>
|
||||
<div title={value} className={`relative flex-col w-full h-10 overflow-hidden ${
|
||||
isSideBarOpen && "bg-mineshaft-700 duration-200"
|
||||
}`}>
|
||||
<div
|
||||
title={value}
|
||||
className={`relative h-10 w-full flex-col overflow-hidden ${
|
||||
isSideBarOpen && "bg-mineshaft-700 duration-200"
|
||||
}`}
|
||||
>
|
||||
<div
|
||||
className={`group relative flex flex-col justify-center items-center h-full ${
|
||||
className={`group relative flex h-full flex-col items-center justify-center ${
|
||||
error ? "w-max" : "w-full"
|
||||
}`}
|
||||
>
|
||||
{value?.split("\n")[0] ? <span className='ph-no-capture truncate break-all bg-transparent leading-tight text-xs px-2 w-full min-w-16 outline-none text-bunker-300 focus:text-bunker-100 placeholder:text-bunker-400 placeholder:focus:text-transparent placeholder duration-200'>
|
||||
{value?.split("\n")[0]}
|
||||
</span> : <span className='text-bunker-400'>-</span> }
|
||||
{value?.split("\n")[1] && <span className='ph-no-capture truncate break-all bg-transparent leading-tight text-xs px-2 w-full min-w-16 outline-none text-bunker-300 focus:text-bunker-100 placeholder:text-bunker-400 placeholder:focus:text-transparent placeholder duration-200'>
|
||||
{value?.split("\n")[1]}
|
||||
</span>}
|
||||
{value?.split("\n")[0] ? (
|
||||
<span className="ph-no-capture min-w-16 placeholder w-full truncate break-all bg-transparent px-2 text-xs leading-tight text-bunker-300 outline-none duration-200 placeholder:text-bunker-400 focus:text-bunker-100 placeholder:focus:text-transparent">
|
||||
{value?.split("\n")[0]}
|
||||
</span>
|
||||
) : (
|
||||
<span className="text-bunker-400">-</span>
|
||||
)}
|
||||
{value?.split("\n")[1] && (
|
||||
<span className="ph-no-capture min-w-16 placeholder w-full truncate break-all bg-transparent px-2 text-xs leading-tight text-bunker-300 outline-none duration-200 placeholder:text-bunker-400 focus:text-bunker-100 placeholder:focus:text-transparent">
|
||||
{value?.split("\n")[1]}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</PopoverObject>
|
||||
@ -148,10 +175,10 @@ const DashboardInputField = ({
|
||||
}
|
||||
if (type === "value") {
|
||||
return (
|
||||
<div className="flex-col w-full">
|
||||
<div className="group relative whitespace-pre flex flex-col justify-center w-full">
|
||||
<div className="w-full flex-col">
|
||||
<div className="group relative flex w-full flex-col justify-center whitespace-pre">
|
||||
{overrideEnabled === true && (
|
||||
<div className="bg-primary-500 rounded-sm absolute top-[0.1rem] right-[0.1rem] z-0 w-min text-xxs px-1 text-black opacity-80">
|
||||
<div className="absolute top-[0.1rem] right-[0.1rem] z-0 w-min rounded-sm bg-primary-500 px-1 text-xxs text-black opacity-80">
|
||||
Override enabled
|
||||
</div>
|
||||
)}
|
||||
@ -160,20 +187,20 @@ const DashboardInputField = ({
|
||||
onChange={(e) => onChangeHandler(e.target.value, id)}
|
||||
onScroll={syncScroll}
|
||||
className={`${
|
||||
blurred
|
||||
? "text-transparent focus:text-transparent active:text-transparent"
|
||||
: ""
|
||||
} z-10 peer font-mono ph-no-capture bg-transparent caret-white text-transparent text-sm px-2 py-2 w-full min-w-16 outline-none duration-200 no-scrollbar no-scrollbar::-webkit-scrollbar`}
|
||||
blurred ? "text-transparent focus:text-transparent active:text-transparent" : ""
|
||||
} ph-no-capture min-w-16 no-scrollbar::-webkit-scrollbar peer z-10 w-full bg-transparent px-2 py-2 font-mono text-sm text-transparent caret-white outline-none duration-200 no-scrollbar`}
|
||||
spellCheck="false"
|
||||
/>
|
||||
<div
|
||||
ref={ref}
|
||||
className={`${
|
||||
blurred && !overrideEnabled
|
||||
? "text-bunker-800 group-hover:text-gray-400 peer-focus:text-gray-100 peer-active:text-gray-400 duration-200"
|
||||
? "text-bunker-800 duration-200 group-hover:text-gray-400 peer-focus:text-gray-100 peer-active:text-gray-400"
|
||||
: ""
|
||||
} ${overrideEnabled ? "text-primary-300" : "text-gray-400"}
|
||||
absolute flex flex-row whitespace-pre font-mono z-0 ${blurred ? "invisible" : "visible"} peer-focus:visible mt-0.5 ph-no-capture overflow-x-scroll bg-transparent h-10 text-sm px-2 py-2 w-full min-w-16 outline-none duration-100 no-scrollbar no-scrollbar::-webkit-scrollbar`}
|
||||
absolute z-0 flex flex-row whitespace-pre font-mono ${
|
||||
blurred ? "invisible" : "visible"
|
||||
} ph-no-capture min-w-16 no-scrollbar::-webkit-scrollbar mt-0.5 h-10 w-full overflow-x-scroll bg-transparent px-2 py-2 text-sm outline-none duration-100 no-scrollbar peer-focus:visible`}
|
||||
>
|
||||
{value?.split(REGEX).map((word) => {
|
||||
if (word.match(REGEX) !== null) {
|
||||
@ -203,20 +230,24 @@ const DashboardInputField = ({
|
||||
})}
|
||||
</div>
|
||||
{blurred && (
|
||||
<div className={`absolute flex flex-row justify-between items-center z-0 peer pr-2 ${
|
||||
isSideBarOpen ? "bg-mineshaft-700 duration-200" : "bg-mineshaft-800"
|
||||
} peer-active:hidden peer-focus:hidden group-hover:bg-white/[0.00] duration-100 h-10 w-full text-bunker-400 text-clip`}>
|
||||
<div className="px-2 flex flex-row items-center overflow-x-scroll no-scrollbar no-scrollbar::-webkit-scrollbar">
|
||||
<div
|
||||
className={`peer absolute z-0 flex flex-row items-center justify-between pr-2 ${
|
||||
isSideBarOpen ? "bg-mineshaft-700 duration-200" : "bg-mineshaft-800"
|
||||
} h-10 w-full text-clip text-bunker-400 duration-100 group-hover:bg-white/[0.00] peer-focus:hidden peer-active:hidden`}
|
||||
>
|
||||
<div className="no-scrollbar::-webkit-scrollbar flex flex-row items-center overflow-x-scroll px-2 no-scrollbar">
|
||||
{value?.split("").map(() => (
|
||||
<FontAwesomeIcon
|
||||
key={guidGenerator()}
|
||||
className="text-xxs mr-0.5"
|
||||
className="mr-0.5 text-xxs"
|
||||
icon={faCircle}
|
||||
/>
|
||||
))}
|
||||
{value?.split("").length === 0 && <span className='text-bunker-400/80'>EMPTY</span>}
|
||||
{value?.split("").length === 0 && <span className="text-bunker-400/80">EMPTY</span>}
|
||||
</div>
|
||||
<div className="invisible z-[100] cursor-default group-hover:visible">
|
||||
<FontAwesomeIcon icon={faEye} />
|
||||
</div>
|
||||
<div className='invisible group-hover:visible cursor-default z-[100]'><FontAwesomeIcon icon={faEye} /></div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React from "react"
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { faXmark } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
@ -8,32 +8,35 @@ import Button from "../basic/buttons/Button";
|
||||
type Props = {
|
||||
onSubmit: () => void;
|
||||
isPlain?: boolean;
|
||||
}
|
||||
};
|
||||
|
||||
export const DeleteActionButton = ({ onSubmit, isPlain }: Props) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div className={`${
|
||||
!isPlain
|
||||
? "bg-[#9B3535] opacity-70 hover:opacity-100 w-[4.5rem] h-[2.5rem] rounded-md duration-200 ml-2"
|
||||
: "cursor-pointer w-[1.5rem] h-[2.35rem] mr-2 flex items-center justfy-center"}`}>
|
||||
{isPlain
|
||||
? <div
|
||||
onKeyDown={() => null}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onClick={onSubmit}
|
||||
className="invisible group-hover:visible"
|
||||
>
|
||||
<FontAwesomeIcon className="text-bunker-300 hover:text-red pl-2 pr-6 text-lg mt-0.5" icon={faXmark} />
|
||||
</div>
|
||||
: <Button
|
||||
text={String(t("Delete"))}
|
||||
color="red"
|
||||
size="md"
|
||||
onButtonPressed={onSubmit}
|
||||
/>}
|
||||
<div
|
||||
className={`${
|
||||
!isPlain
|
||||
? "ml-2 h-[2.5rem] w-[4.5rem] rounded-md bg-[#9B3535] opacity-70 duration-200 hover:opacity-100"
|
||||
: "justfy-center mr-2 flex h-[2.35rem] w-[1.5rem] cursor-pointer items-center"
|
||||
}`}
|
||||
>
|
||||
{isPlain ? (
|
||||
<div
|
||||
onKeyDown={() => null}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onClick={onSubmit}
|
||||
className="invisible group-hover:visible"
|
||||
>
|
||||
<FontAwesomeIcon
|
||||
className="mt-0.5 pl-2 pr-6 text-lg text-bunker-300 hover:text-red"
|
||||
icon={faXmark}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<Button text={String(t("Delete"))} color="red" size="md" onButtonPressed={onSubmit} />
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
@ -18,7 +18,7 @@ const DownloadSecretMenu = ({ data, env }: { data: SecretDataProps[]; env: strin
|
||||
<Menu as="div" className="relative inline-block text-left">
|
||||
<Menu.Button
|
||||
as="div"
|
||||
className="inline-flex w-full justify-center text-sm font-medium text-gray-200 rounded-md hover:bg-white/10 duration-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75"
|
||||
className="inline-flex w-full justify-center rounded-md text-sm font-medium text-gray-200 duration-200 hover:bg-white/10 focus:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75"
|
||||
>
|
||||
<Button color="mineshaft" size="icon-md" icon={faDownload} onButtonPressed={() => {}} />
|
||||
</Menu.Button>
|
||||
@ -31,7 +31,7 @@ const DownloadSecretMenu = ({ data, env }: { data: SecretDataProps[]; env: strin
|
||||
leaveFrom="transform opacity-100 scale-100"
|
||||
leaveTo="transform opacity-0 scale-95"
|
||||
>
|
||||
<Menu.Items className="absolute z-[90] drop-shadow-xl right-0 mt-0.5 w-[12rem] origin-top-right rounded-md bg-bunker border border-mineshaft-500 shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none p-2 space-y-2">
|
||||
<Menu.Items className="absolute right-0 z-[90] mt-0.5 w-[12rem] origin-top-right space-y-2 rounded-md border border-mineshaft-500 bg-bunker p-2 shadow-lg ring-1 ring-black ring-opacity-5 drop-shadow-xl focus:outline-none">
|
||||
<Menu.Item>
|
||||
<Button
|
||||
color="mineshaft"
|
||||
|
@ -56,7 +56,7 @@ export default function NavHeader({
|
||||
|
||||
return (
|
||||
<div className="flex flex-row items-center pt-6">
|
||||
<div className="mr-2 flex h-5 w-5 items-center justify-center rounded-md bg-primary text-sm text-black min-w-[1.25rem]">
|
||||
<div className="mr-2 flex h-5 w-5 min-w-[1.25rem] items-center justify-center rounded-md bg-primary text-sm text-black">
|
||||
{currentOrg?.name?.charAt(0)}
|
||||
</div>
|
||||
<Link passHref legacyBehavior href={`/org/${currentOrg?.id}/overview`}>
|
||||
|
@ -6,7 +6,6 @@ import { useOrganization, useWorkspace } from "@app/context";
|
||||
|
||||
import { Select, SelectItem, Tooltip } from "../v2";
|
||||
|
||||
|
||||
/**
|
||||
* This is the component at the top of almost every page.
|
||||
* It shows how to navigate to a certain page.
|
||||
@ -39,10 +38,12 @@ export default function NavHeaderSecrets({
|
||||
}): JSX.Element {
|
||||
const { currentWorkspace } = useWorkspace();
|
||||
const { currentOrg } = useOrganization();
|
||||
const router = useRouter()
|
||||
const router = useRouter();
|
||||
|
||||
return (
|
||||
<div className={`${!isSnapshot && "absolute"} ml-6 flex flex-row items-center pt-6 cursor-default`}>
|
||||
<div
|
||||
className={`${!isSnapshot && "absolute"} ml-6 flex cursor-default flex-row items-center pt-6`}
|
||||
>
|
||||
<div className="mr-3 flex h-6 w-6 items-center justify-center rounded-md bg-primary-900 text-mineshaft-100">
|
||||
{currentOrg?.name?.charAt(0)}
|
||||
</div>
|
||||
@ -60,31 +61,39 @@ export default function NavHeaderSecrets({
|
||||
</>
|
||||
)}
|
||||
<FontAwesomeIcon icon={faAngleRight} className="ml-3 mr-3 text-sm text-gray-400" />
|
||||
{pageName === "Secrets"
|
||||
? <a className="text-md font-medium text-primary/80 hover:text-primary" href={`${router.asPath.split("?")[0]}`}>{pageName}</a>
|
||||
: <div className="text-md text-gray-400">{pageName}</div>}
|
||||
{currentEnv &&
|
||||
<>
|
||||
<FontAwesomeIcon icon={faAngleRight} className="ml-3 mr-1.5 text-sm text-gray-400" />
|
||||
<div className='pl-3 rounded-md hover:bg-bunker-100/10'>
|
||||
<Tooltip content="Select environment">
|
||||
<Select
|
||||
value={userAvailableEnvs?.filter(uae => uae.name === currentEnv)[0]?.slug}
|
||||
onValueChange={(value) => {
|
||||
if (value && onEnvChange) onEnvChange(value);
|
||||
}}
|
||||
className="text-md pl-0 font-medium text-primary/80 hover:text-primary bg-transparent"
|
||||
dropdownContainerClassName="text-bunker-200 bg-mineshaft-800 border border-mineshaft-600 drop-shadow-2xl"
|
||||
>
|
||||
{userAvailableEnvs?.map(({ name, slug }) => (
|
||||
<SelectItem value={slug} key={slug}>
|
||||
{name}
|
||||
</SelectItem>
|
||||
))}
|
||||
</Select>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</>}
|
||||
{pageName === "Secrets" ? (
|
||||
<a
|
||||
className="text-md font-medium text-primary/80 hover:text-primary"
|
||||
href={`${router.asPath.split("?")[0]}`}
|
||||
>
|
||||
{pageName}
|
||||
</a>
|
||||
) : (
|
||||
<div className="text-md text-gray-400">{pageName}</div>
|
||||
)}
|
||||
{currentEnv && (
|
||||
<>
|
||||
<FontAwesomeIcon icon={faAngleRight} className="ml-3 mr-1.5 text-sm text-gray-400" />
|
||||
<div className="rounded-md pl-3 hover:bg-bunker-100/10">
|
||||
<Tooltip content="Select environment">
|
||||
<Select
|
||||
value={userAvailableEnvs?.filter((uae) => uae.name === currentEnv)[0]?.slug}
|
||||
onValueChange={(value) => {
|
||||
if (value && onEnvChange) onEnvChange(value);
|
||||
}}
|
||||
className="text-md bg-transparent pl-0 font-medium text-primary/80 hover:text-primary"
|
||||
dropdownContainerClassName="text-bunker-200 bg-mineshaft-800 border border-mineshaft-600 drop-shadow-2xl"
|
||||
>
|
||||
{userAvailableEnvs?.map(({ name, slug }) => (
|
||||
<SelectItem value={slug} key={slug}>
|
||||
{name}
|
||||
</SelectItem>
|
||||
))}
|
||||
</Select>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -3,9 +3,7 @@ import React, { useState } from "react";
|
||||
import ReactCodeInput from "react-code-input";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import {
|
||||
useSendVerificationEmail
|
||||
} from "@app/hooks/api";
|
||||
import { useSendVerificationEmail } from "@app/hooks/api";
|
||||
|
||||
import Error from "../basic/Error";
|
||||
import { Button } from "../v2";
|
||||
@ -90,8 +88,8 @@ export default function CodeInputStep({
|
||||
return (
|
||||
<div className="mx-auto h-full w-full pb-4 md:px-8">
|
||||
<p className="text-md flex justify-center text-bunker-200">{t("signup.step2-message")}</p>
|
||||
<p className="text-md flex justify-center font-semibold my-1 text-bunker-200">{email} </p>
|
||||
<div className="hidden md:block w-max min-w-[20rem] mx-auto">
|
||||
<p className="text-md my-1 flex justify-center font-semibold text-bunker-200">{email} </p>
|
||||
<div className="mx-auto hidden w-max min-w-[20rem] md:block">
|
||||
<ReactCodeInput
|
||||
name=""
|
||||
inputMode="tel"
|
||||
@ -102,7 +100,7 @@ export default function CodeInputStep({
|
||||
className="mt-6 mb-2"
|
||||
/>
|
||||
</div>
|
||||
<div className="block md:hidden w-max mt-4 mx-auto">
|
||||
<div className="mx-auto mt-4 block w-max md:hidden">
|
||||
<ReactCodeInput
|
||||
name=""
|
||||
inputMode="tel"
|
||||
@ -114,26 +112,29 @@ export default function CodeInputStep({
|
||||
/>
|
||||
</div>
|
||||
{codeError && <Error text={t("signup.step2-code-error")} />}
|
||||
<div className="flex flex-col items-center justify-center lg:w-[19%] w-1/4 min-w-[20rem] mt-2 max-w-xs md:max-w-md mx-auto text-sm text-center md:text-left">
|
||||
<div className="text-l py-1 text-lg w-full">
|
||||
<div className="mx-auto mt-2 flex w-1/4 min-w-[20rem] max-w-xs flex-col items-center justify-center text-center text-sm md:max-w-md md:text-left lg:w-[19%]">
|
||||
<div className="text-l w-full py-1 text-lg">
|
||||
<Button
|
||||
type="submit"
|
||||
onClick={incrementStep}
|
||||
size="sm"
|
||||
isFullWidth
|
||||
className='h-14'
|
||||
className="h-14"
|
||||
colorSchema="primary"
|
||||
variant="outline_bg"
|
||||
isLoading={isCodeInputCheckLoading}
|
||||
> {String(t("signup.verify"))} </Button>
|
||||
>
|
||||
{" "}
|
||||
{String(t("signup.verify"))}{" "}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col items-center justify-center w-full max-h-24 max-w-md mx-auto pt-2">
|
||||
<div className="mx-auto flex max-h-24 w-full max-w-md flex-col items-center justify-center pt-2">
|
||||
<div className="flex flex-row items-baseline gap-1 text-sm">
|
||||
<span className="text-bunker-400">{t("signup.step2-resend-alert")}</span>
|
||||
<div className="mt-2 text-bunker-400 text-md flex flex-row">
|
||||
<div className="text-md mt-2 flex flex-row text-bunker-400">
|
||||
<button disabled={isLoading} onClick={resendVerificationEmail} type="button">
|
||||
<span className='hover:underline hover:underline-offset-4 hover:decoration-primary-700 hover:text-bunker-200 duration-200 cursor-pointer'>
|
||||
<span className="cursor-pointer duration-200 hover:text-bunker-200 hover:underline hover:decoration-primary-700 hover:underline-offset-4">
|
||||
{isResendingVerificationEmail
|
||||
? t("signup.step2-resend-progress")
|
||||
: t("signup.step2-resend-submit")}
|
||||
@ -141,7 +142,7 @@ export default function CodeInputStep({
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-sm text-bunker-400 pb-2">{t("signup.step2-spam-alert")}</p>
|
||||
<p className="pb-2 text-sm text-bunker-400">{t("signup.step2-spam-alert")}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -57,19 +57,22 @@ export default function DonwloadBackupPDFStep({
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-col items-center w-full h-full md:px-6 mx-auto mb-36 md:mb-16">
|
||||
<p className="text-xl text-center font-medium flex flex-col justify-center items-center text-transparent bg-clip-text bg-gradient-to-b from-white to-bunker-200">
|
||||
<FontAwesomeIcon icon={faWarning} className="ml-2 mr-3 pt-1 mb-6 text-6xl text-bunker-200" />
|
||||
<div className="mx-auto mb-36 flex h-full w-full flex-col items-center md:mb-16 md:px-6">
|
||||
<p className="flex flex-col items-center justify-center bg-gradient-to-b from-white to-bunker-200 bg-clip-text text-center text-xl font-medium text-transparent">
|
||||
<FontAwesomeIcon
|
||||
icon={faWarning}
|
||||
className="ml-2 mr-3 mb-6 pt-1 text-6xl text-bunker-200"
|
||||
/>
|
||||
{t("signup.step4-message")}
|
||||
</p>
|
||||
<div className="flex flex-col pb-2 bg-mineshaft-800 border border-mineshaft-600 items-center justify-center text-center lg:w-1/6 w-full md:min-w-[24rem] mt-8 max-w-md text-bunker-300 text-md rounded-md">
|
||||
<div className="w-full mt-4 md:mt-8 flex flex-row text-center items-center m-2 text-bunker-300 rounded-md lg:w-1/6 lg:w-1/6 w-full md:min-w-[23rem] px-3 mx-auto">
|
||||
<div className="text-md mt-8 flex w-full max-w-md flex-col items-center justify-center rounded-md border border-mineshaft-600 bg-mineshaft-800 pb-2 text-center text-bunker-300 md:min-w-[24rem] lg:w-1/6">
|
||||
<div className="m-2 mx-auto mt-4 flex w-full w-full flex-row items-center rounded-md px-3 text-center text-bunker-300 md:mt-8 md:min-w-[23rem] lg:w-1/6 lg:w-1/6">
|
||||
<span className="mb-2">
|
||||
{t("signup.step4-description1")} {t("signup.step4-description3")}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex flex-col items-center px-3 justify-center mt-0 md:mt-4 mb-2 md:mb-4 lg:w-1/6 w-full md:min-w-[20rem] mt-2 md:max-w-md mx-auto text-sm text-center md:text-left">
|
||||
<div className="text-l py-1 text-lg w-full">
|
||||
<div className="mx-auto mt-0 mb-2 mt-2 flex w-full flex-col items-center justify-center px-3 text-center text-sm md:mt-4 md:mb-4 md:min-w-[20rem] md:max-w-md md:text-left lg:w-1/6">
|
||||
<div className="text-l w-full py-1 text-lg">
|
||||
<Button
|
||||
onClick={handleBackupKeyGenerate}
|
||||
size="sm"
|
||||
|
@ -52,13 +52,13 @@ export default function EnterEmailStep({
|
||||
try {
|
||||
await mutateAsync({ email });
|
||||
incrementStep();
|
||||
} catch(e) {
|
||||
} catch (e) {
|
||||
if (axios.isAxiosError(e)) {
|
||||
const { message = "Something went wrong" } = e.response?.data as { message: string};
|
||||
const { message = "Something went wrong" } = e.response?.data as { message: string };
|
||||
createNotification({
|
||||
type: "error",
|
||||
text: message
|
||||
})
|
||||
text: message
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -66,11 +66,11 @@ export default function EnterEmailStep({
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="w-full md:px-6 mx-auto">
|
||||
<p className="text-xl font-medium flex justify-center text-transparent bg-clip-text bg-gradient-to-b from-white to-bunker-200">
|
||||
<div className="mx-auto w-full md:px-6">
|
||||
<p className="flex justify-center bg-gradient-to-b from-white to-bunker-200 bg-clip-text text-xl font-medium text-transparent">
|
||||
{t("signup.step1-start")}
|
||||
</p>
|
||||
<div className="flex flex-col items-center justify-center lg:w-1/6 w-1/4 min-w-[20rem] m-auto rounded-lg mt-8">
|
||||
<div className="m-auto mt-8 flex w-1/4 min-w-[20rem] flex-col items-center justify-center rounded-lg lg:w-1/6">
|
||||
<Input
|
||||
placeholder="Enter your email address..."
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
@ -79,28 +79,35 @@ export default function EnterEmailStep({
|
||||
autoComplete="username"
|
||||
className="h-12"
|
||||
/>
|
||||
{emailError && <p className="text-red-600 text-xs text-left w-full ml-1.5 mt-1.5">Please enter a valid email.</p>}
|
||||
{emailError && (
|
||||
<p className="ml-1.5 mt-1.5 w-full text-left text-xs text-red-600">
|
||||
Please enter a valid email.
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex flex-col items-center justify-center lg:w-1/6 w-1/4 min-w-[20rem] mt-2 max-w-xs md:max-w-md mx-auto text-sm text-center md:text-left">
|
||||
<div className="text-l py-1 text-lg w-full">
|
||||
<div className="mx-auto mt-2 flex w-1/4 min-w-[20rem] max-w-xs flex-col items-center justify-center text-center text-sm md:max-w-md md:text-left lg:w-1/6">
|
||||
<div className="text-l w-full py-1 text-lg">
|
||||
<Button
|
||||
type="submit"
|
||||
onClick={emailCheck}
|
||||
size="sm"
|
||||
isFullWidth
|
||||
className='h-14'
|
||||
className="h-14"
|
||||
colorSchema="primary"
|
||||
variant="outline_bg"
|
||||
isLoading={isLoading}
|
||||
isDisabled={isLoading}
|
||||
> {String(t("signup.step1-submit"))} </Button>
|
||||
>
|
||||
{" "}
|
||||
{String(t("signup.step1-submit"))}{" "}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mx-auto mb-48 mt-2 flex w-full max-w-md flex-col items-center justify-center pt-2 md:mb-16 md:pb-2">
|
||||
<Link href="/login">
|
||||
<button type="button" className="w-max pb-3 duration-200 hover:opacity-90">
|
||||
<span className="text-sm text-mineshaft-400 hover:underline hover:underline-offset-4 hover:decoration-primary-700 hover:text-bunker-200 duration-200 cursor-pointer">
|
||||
<span className="cursor-pointer text-sm text-mineshaft-400 duration-200 hover:text-bunker-200 hover:underline hover:decoration-primary-700 hover:underline-offset-4">
|
||||
{t("signup.already-have-account")}
|
||||
</span>
|
||||
</button>
|
||||
|
@ -16,7 +16,7 @@ export default function TeamInviteStep(): JSX.Element {
|
||||
const router = useRouter();
|
||||
const [emails, setEmails] = useState("");
|
||||
const { data: serverDetails } = useFetchServerStatus();
|
||||
|
||||
|
||||
const { mutateAsync } = useAddUserToOrg();
|
||||
const { handlePopUpToggle, popUp, handlePopUpOpen } = usePopUp(["setUpEmail"] as const);
|
||||
|
||||
@ -40,55 +40,61 @@ export default function TeamInviteStep(): JSX.Element {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="w-max mx-auto min-w-lg h-full pb-4 px-8 mb-64 md:mb-32">
|
||||
<p className="text-2xl font-semibold flex justify-center text-transparent bg-clip-text bg-gradient-to-b from-white to-bunker-200">
|
||||
<div className="min-w-lg mx-auto mb-64 h-full w-max px-8 pb-4 md:mb-32">
|
||||
<p className="flex justify-center bg-gradient-to-b from-white to-bunker-200 bg-clip-text text-2xl font-semibold text-transparent">
|
||||
{t("signup.step5-invite-team")}
|
||||
</p>
|
||||
<p className="text-center flex justify-center text-bunker-400 md:mx-8 mb-6 mt-4">
|
||||
<p className="mb-6 mt-4 flex justify-center text-center text-bunker-400 md:mx-8">
|
||||
{t("signup.step5-subtitle")}
|
||||
</p>
|
||||
<div className="bg-mineshaft-800 border border-mineshaft-500 w-max mx-auto pt-6 pb-4 px-8 rounded-xl drop-shadow-xl mb-6">
|
||||
<div className="mx-auto mb-6 w-max rounded-xl border border-mineshaft-500 bg-mineshaft-800 px-8 pt-6 pb-4 drop-shadow-xl">
|
||||
<div>
|
||||
<div className="text-bunker-300 font-medium pl-1 pb-1 text-sm">
|
||||
<div className="pl-1 pb-1 text-sm font-medium text-bunker-300">
|
||||
<span>Emails</span>
|
||||
</div>
|
||||
<textarea
|
||||
className="bg-mineshaft-900/70 min-w-[30rem] h-20 w-full placeholder:text-bunker-400 py-1 px-2 rounded-md border border-mineshaft-500 text-sm text-bunker-300 outline-none focus:ring-2 ring-primary-800 ring-opacity-70"
|
||||
className="h-20 w-full min-w-[30rem] rounded-md border border-mineshaft-500 bg-mineshaft-900/70 py-1 px-2 text-sm text-bunker-300 outline-none ring-primary-800 ring-opacity-70 placeholder:text-bunker-400 focus:ring-2"
|
||||
value={emails}
|
||||
onChange={(e) => setEmails(e.target.value)}
|
||||
placeholder="email@example.com, email2@example.com..."
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-row items-end justify-end mt-0 md:mt-4 md:mb-2 w-full md:min-w-[30rem] mt-2 md:max-w-md mx-auto text-sm">
|
||||
<div className="mx-auto mt-0 mt-2 flex w-full flex-row items-end justify-end text-sm md:mt-4 md:mb-2 md:min-w-[30rem] md:max-w-md">
|
||||
<Button
|
||||
onClick={() => {
|
||||
if (serverDetails?.emailConfigured) {
|
||||
inviteUsers({ emails })
|
||||
inviteUsers({ emails });
|
||||
} else {
|
||||
handlePopUpOpen("setUpEmail");
|
||||
}
|
||||
}}
|
||||
size="sm"
|
||||
// isFullWidth
|
||||
className='h-10'
|
||||
className="h-10"
|
||||
colorSchema="primary"
|
||||
variant="solid"
|
||||
> {t("signup.step5-send-invites") ?? ""} </Button>
|
||||
>
|
||||
{" "}
|
||||
{t("signup.step5-send-invites") ?? ""}{" "}
|
||||
</Button>
|
||||
</div>
|
||||
<EmailServiceSetupModal
|
||||
isOpen={popUp.setUpEmail?.isOpen}
|
||||
onOpenChange={(isOpen) => handlePopUpToggle("setUpEmail", isOpen)}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-row max-w-max min-w-28 items-center justify-center md:p-2 min-w-[20rem] max-h-24 mx-auto text-lg px-4 mt-4 mb-2">
|
||||
<div className="min-w-28 mx-auto mt-4 mb-2 flex max-h-24 min-w-[20rem] max-w-max flex-row items-center justify-center px-4 text-lg md:p-2">
|
||||
<Button
|
||||
onClick={redirectToHome}
|
||||
size="sm"
|
||||
isFullWidth
|
||||
className='h-12'
|
||||
className="h-12"
|
||||
colorSchema="secondary"
|
||||
variant="outline"
|
||||
> {t("signup.step5-skip") ?? "Skip"} </Button>
|
||||
>
|
||||
{" "}
|
||||
{t("signup.step5-skip") ?? "Skip"}{" "}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -1,17 +1,11 @@
|
||||
import {
|
||||
getAuthToken,
|
||||
setAuthToken,
|
||||
setMfaTempToken,
|
||||
setSignupTempToken} from "@app/reactQuery";
|
||||
|
||||
import { getAuthToken, setAuthToken, setMfaTempToken, setSignupTempToken } from "@app/reactQuery";
|
||||
|
||||
export const PROVIDER_AUTH_TOKEN_KEY = "infisical__provider-auth-token";
|
||||
|
||||
// depreciated: go for apiRequest module in config/api
|
||||
export default class SecurityClient {
|
||||
|
||||
static setProviderAuthToken(tokenStr: string) {
|
||||
localStorage.setItem(PROVIDER_AUTH_TOKEN_KEY, tokenStr || "")
|
||||
localStorage.setItem(PROVIDER_AUTH_TOKEN_KEY, tokenStr || "");
|
||||
}
|
||||
|
||||
static getProviderAuthToken() {
|
||||
|
@ -3,9 +3,7 @@ import crypto from "crypto";
|
||||
|
||||
import jsrp from "jsrp";
|
||||
|
||||
import {
|
||||
changePassword,
|
||||
srp1} from "@app/hooks/api/auth/queries";
|
||||
import { changePassword, srp1 } from "@app/hooks/api/auth/queries";
|
||||
|
||||
import Aes256Gcm from "./cryptography/aes-256-gcm";
|
||||
import { deriveArgonKey } from "./cryptography/crypto";
|
||||
@ -15,95 +13,95 @@ const clientOldPassword = new jsrp.client();
|
||||
const clientNewPassword = new jsrp.client();
|
||||
|
||||
type Params = {
|
||||
email: string;
|
||||
currentPassword: string;
|
||||
newPassword: string;
|
||||
}
|
||||
email: string;
|
||||
currentPassword: string;
|
||||
newPassword: string;
|
||||
};
|
||||
|
||||
const attemptChangePassword = ({ email, currentPassword, newPassword }: Params): Promise<void> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
clientOldPassword.init({ username: email, password: currentPassword }, async () => {
|
||||
let serverPublicKey; let salt;
|
||||
return new Promise((resolve, reject) => {
|
||||
clientOldPassword.init({ username: email, password: currentPassword }, async () => {
|
||||
let serverPublicKey;
|
||||
let salt;
|
||||
|
||||
try {
|
||||
const clientPublicKey = clientOldPassword.getPublicKey();
|
||||
|
||||
const res = await srp1({ clientPublicKey });
|
||||
|
||||
serverPublicKey = res.serverPublicKey;
|
||||
salt = res.salt;
|
||||
|
||||
clientOldPassword.setSalt(salt);
|
||||
clientOldPassword.setServerPublicKey(serverPublicKey);
|
||||
|
||||
const clientProof = clientOldPassword.getProof();
|
||||
|
||||
clientNewPassword.init({ username: email, password: newPassword }, async () => {
|
||||
clientNewPassword.createVerifier(async (err, result) => {
|
||||
try {
|
||||
const clientPublicKey = clientOldPassword.getPublicKey();
|
||||
const derivedKey = await deriveArgonKey({
|
||||
password: newPassword,
|
||||
salt: result.salt,
|
||||
mem: 65536,
|
||||
time: 3,
|
||||
parallelism: 1,
|
||||
hashLen: 32
|
||||
});
|
||||
|
||||
const res = await srp1({ clientPublicKey });
|
||||
if (!derivedKey) throw new Error("Failed to derive key from password");
|
||||
|
||||
serverPublicKey = res.serverPublicKey;
|
||||
salt = res.salt;
|
||||
const key = crypto.randomBytes(32);
|
||||
|
||||
clientOldPassword.setSalt(salt);
|
||||
clientOldPassword.setServerPublicKey(serverPublicKey);
|
||||
const {
|
||||
ciphertext: encryptedPrivateKey,
|
||||
iv: encryptedPrivateKeyIV,
|
||||
tag: encryptedPrivateKeyTag
|
||||
} = Aes256Gcm.encrypt({
|
||||
text: localStorage.getItem("PRIVATE_KEY") as string,
|
||||
secret: key
|
||||
});
|
||||
|
||||
const clientProof = clientOldPassword.getProof();
|
||||
const {
|
||||
ciphertext: protectedKey,
|
||||
iv: protectedKeyIV,
|
||||
tag: protectedKeyTag
|
||||
} = Aes256Gcm.encrypt({
|
||||
text: key.toString("hex"),
|
||||
secret: Buffer.from(derivedKey.hash)
|
||||
});
|
||||
|
||||
clientNewPassword.init({ username: email, password: newPassword }, async () => {
|
||||
clientNewPassword.createVerifier(async (err, result) => {
|
||||
try {
|
||||
const derivedKey = await deriveArgonKey({
|
||||
password: newPassword,
|
||||
salt: result.salt,
|
||||
mem: 65536,
|
||||
time: 3,
|
||||
parallelism: 1,
|
||||
hashLen: 32
|
||||
});
|
||||
await changePassword({
|
||||
clientProof,
|
||||
protectedKey,
|
||||
protectedKeyIV,
|
||||
protectedKeyTag,
|
||||
encryptedPrivateKey,
|
||||
encryptedPrivateKeyIV,
|
||||
encryptedPrivateKeyTag,
|
||||
salt: result.salt,
|
||||
verifier: result.verifier
|
||||
});
|
||||
|
||||
if (!derivedKey) throw new Error("Failed to derive key from password");
|
||||
saveTokenToLocalStorage({
|
||||
encryptedPrivateKey,
|
||||
iv: encryptedPrivateKeyIV,
|
||||
tag: encryptedPrivateKeyTag
|
||||
});
|
||||
|
||||
const key = crypto.randomBytes(32);
|
||||
|
||||
const {
|
||||
ciphertext: encryptedPrivateKey,
|
||||
iv: encryptedPrivateKeyIV,
|
||||
tag: encryptedPrivateKeyTag
|
||||
} = Aes256Gcm.encrypt({
|
||||
text: localStorage.getItem("PRIVATE_KEY") as string,
|
||||
secret: key
|
||||
});
|
||||
|
||||
const {
|
||||
ciphertext: protectedKey,
|
||||
iv: protectedKeyIV,
|
||||
tag: protectedKeyTag
|
||||
} = Aes256Gcm.encrypt({
|
||||
text: key.toString("hex"),
|
||||
secret: Buffer.from(derivedKey.hash)
|
||||
});
|
||||
|
||||
await changePassword({
|
||||
clientProof,
|
||||
protectedKey,
|
||||
protectedKeyIV,
|
||||
protectedKeyTag,
|
||||
encryptedPrivateKey,
|
||||
encryptedPrivateKeyIV,
|
||||
encryptedPrivateKeyTag,
|
||||
salt: result.salt,
|
||||
verifier: result.verifier
|
||||
});
|
||||
|
||||
saveTokenToLocalStorage({
|
||||
encryptedPrivateKey,
|
||||
iv: encryptedPrivateKeyIV,
|
||||
tag: encryptedPrivateKeyTag
|
||||
});
|
||||
|
||||
resolve();
|
||||
} catch (err2) {
|
||||
console.error(err2);
|
||||
reject(err2);
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
reject(err);
|
||||
resolve();
|
||||
} catch (err2) {
|
||||
console.error(err2);
|
||||
reject(err2);
|
||||
}
|
||||
});
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export default attemptChangePassword;
|
||||
export default attemptChangePassword;
|
||||
|
@ -1,7 +1,7 @@
|
||||
/* eslint-disable prefer-destructuring */
|
||||
import jsrp from "jsrp";
|
||||
|
||||
import { login1 , verifyMfaToken } from "@app/hooks/api/auth/queries";
|
||||
import { login1, verifyMfaToken } from "@app/hooks/api/auth/queries";
|
||||
import KeyService from "@app/services/KeyService";
|
||||
|
||||
import { saveTokenToLocalStorage } from "./saveTokenToLocalStorage";
|
||||
@ -11,11 +11,11 @@ import SecurityClient from "./SecurityClient";
|
||||
const client = new jsrp.client();
|
||||
|
||||
interface IsMfaLoginSuccessful {
|
||||
success: boolean;
|
||||
loginResponse:{
|
||||
privateKey: string;
|
||||
JTWToken: string;
|
||||
}
|
||||
success: boolean;
|
||||
loginResponse: {
|
||||
privateKey: string;
|
||||
JTWToken: string;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@ -26,81 +26,84 @@ interface IsMfaLoginSuccessful {
|
||||
* @param {String} obj.mfaToken - MFA code/token
|
||||
*/
|
||||
const attemptLoginMfa = async ({
|
||||
email,
|
||||
password,
|
||||
providerAuthToken,
|
||||
mfaToken
|
||||
email,
|
||||
password,
|
||||
providerAuthToken,
|
||||
mfaToken
|
||||
}: {
|
||||
email: string;
|
||||
password: string;
|
||||
providerAuthToken?: string,
|
||||
mfaToken: string;
|
||||
email: string;
|
||||
password: string;
|
||||
providerAuthToken?: string;
|
||||
mfaToken: string;
|
||||
}): Promise<IsMfaLoginSuccessful> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
client.init({
|
||||
username: email,
|
||||
password
|
||||
}, async () => {
|
||||
try {
|
||||
const clientPublicKey = client.getPublicKey();
|
||||
const { salt } = await login1({
|
||||
email,
|
||||
clientPublicKey,
|
||||
providerAuthToken,
|
||||
});
|
||||
return new Promise((resolve, reject) => {
|
||||
client.init(
|
||||
{
|
||||
username: email,
|
||||
password
|
||||
},
|
||||
async () => {
|
||||
try {
|
||||
const clientPublicKey = client.getPublicKey();
|
||||
const { salt } = await login1({
|
||||
email,
|
||||
clientPublicKey,
|
||||
providerAuthToken
|
||||
});
|
||||
|
||||
const {
|
||||
encryptionVersion,
|
||||
protectedKey,
|
||||
protectedKeyIV,
|
||||
protectedKeyTag,
|
||||
token,
|
||||
publicKey,
|
||||
encryptedPrivateKey,
|
||||
iv,
|
||||
tag
|
||||
} = await verifyMfaToken({
|
||||
email,
|
||||
mfaCode: mfaToken
|
||||
});
|
||||
const {
|
||||
encryptionVersion,
|
||||
protectedKey,
|
||||
protectedKeyIV,
|
||||
protectedKeyTag,
|
||||
token,
|
||||
publicKey,
|
||||
encryptedPrivateKey,
|
||||
iv,
|
||||
tag
|
||||
} = await verifyMfaToken({
|
||||
email,
|
||||
mfaCode: mfaToken
|
||||
});
|
||||
|
||||
// unset temporary (MFA) JWT token and set JWT token
|
||||
SecurityClient.setMfaToken("");
|
||||
SecurityClient.setToken(token);
|
||||
SecurityClient.setProviderAuthToken("");
|
||||
// unset temporary (MFA) JWT token and set JWT token
|
||||
SecurityClient.setMfaToken("");
|
||||
SecurityClient.setToken(token);
|
||||
SecurityClient.setProviderAuthToken("");
|
||||
|
||||
const privateKey = await KeyService.decryptPrivateKey({
|
||||
encryptionVersion,
|
||||
encryptedPrivateKey,
|
||||
iv,
|
||||
tag,
|
||||
password,
|
||||
salt,
|
||||
protectedKey,
|
||||
protectedKeyIV,
|
||||
protectedKeyTag
|
||||
});
|
||||
const privateKey = await KeyService.decryptPrivateKey({
|
||||
encryptionVersion,
|
||||
encryptedPrivateKey,
|
||||
iv,
|
||||
tag,
|
||||
password,
|
||||
salt,
|
||||
protectedKey,
|
||||
protectedKeyIV,
|
||||
protectedKeyTag
|
||||
});
|
||||
|
||||
saveTokenToLocalStorage({
|
||||
publicKey,
|
||||
encryptedPrivateKey,
|
||||
iv,
|
||||
tag,
|
||||
privateKey
|
||||
});
|
||||
saveTokenToLocalStorage({
|
||||
publicKey,
|
||||
encryptedPrivateKey,
|
||||
iv,
|
||||
tag,
|
||||
privateKey
|
||||
});
|
||||
|
||||
resolve({
|
||||
success: true,
|
||||
loginResponse:{
|
||||
privateKey,
|
||||
JTWToken: token
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
resolve({
|
||||
success: true,
|
||||
loginResponse: {
|
||||
privateKey,
|
||||
JTWToken: token
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
export default attemptLoginMfa;
|
||||
export default attemptLoginMfa;
|
||||
|
@ -1,7 +1,7 @@
|
||||
/* eslint-disable prefer-destructuring */
|
||||
import jsrp from "jsrp";
|
||||
|
||||
import { login1 , verifyMfaToken } from "@app/hooks/api/auth/queries";
|
||||
import { login1, verifyMfaToken } from "@app/hooks/api/auth/queries";
|
||||
import KeyService from "@app/services/KeyService";
|
||||
|
||||
import { saveTokenToLocalStorage } from "./saveTokenToLocalStorage";
|
||||
@ -18,75 +18,78 @@ const client = new jsrp.client();
|
||||
* @param {String} obj.mfaToken - MFA code/token
|
||||
*/
|
||||
const attemptLoginMfa = async ({
|
||||
email,
|
||||
password,
|
||||
providerAuthToken,
|
||||
mfaToken
|
||||
email,
|
||||
password,
|
||||
providerAuthToken,
|
||||
mfaToken
|
||||
}: {
|
||||
email: string;
|
||||
password: string;
|
||||
providerAuthToken?: string,
|
||||
mfaToken: string;
|
||||
email: string;
|
||||
password: string;
|
||||
providerAuthToken?: string;
|
||||
mfaToken: string;
|
||||
}): Promise<Boolean> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
client.init({
|
||||
username: email,
|
||||
password
|
||||
}, async () => {
|
||||
try {
|
||||
const clientPublicKey = client.getPublicKey();
|
||||
const { salt } = await login1({
|
||||
email,
|
||||
clientPublicKey,
|
||||
providerAuthToken,
|
||||
});
|
||||
|
||||
const {
|
||||
encryptionVersion,
|
||||
protectedKey,
|
||||
protectedKeyIV,
|
||||
protectedKeyTag,
|
||||
token,
|
||||
publicKey,
|
||||
encryptedPrivateKey,
|
||||
iv,
|
||||
tag
|
||||
} = await verifyMfaToken({
|
||||
email,
|
||||
mfaCode: mfaToken
|
||||
});
|
||||
return new Promise((resolve, reject) => {
|
||||
client.init(
|
||||
{
|
||||
username: email,
|
||||
password
|
||||
},
|
||||
async () => {
|
||||
try {
|
||||
const clientPublicKey = client.getPublicKey();
|
||||
const { salt } = await login1({
|
||||
email,
|
||||
clientPublicKey,
|
||||
providerAuthToken
|
||||
});
|
||||
|
||||
// unset temporary (MFA) JWT token and set JWT token
|
||||
SecurityClient.setMfaToken("");
|
||||
SecurityClient.setToken(token);
|
||||
SecurityClient.setProviderAuthToken("");
|
||||
const {
|
||||
encryptionVersion,
|
||||
protectedKey,
|
||||
protectedKeyIV,
|
||||
protectedKeyTag,
|
||||
token,
|
||||
publicKey,
|
||||
encryptedPrivateKey,
|
||||
iv,
|
||||
tag
|
||||
} = await verifyMfaToken({
|
||||
email,
|
||||
mfaCode: mfaToken
|
||||
});
|
||||
|
||||
const privateKey = await KeyService.decryptPrivateKey({
|
||||
encryptionVersion,
|
||||
encryptedPrivateKey,
|
||||
iv,
|
||||
tag,
|
||||
password,
|
||||
salt,
|
||||
protectedKey,
|
||||
protectedKeyIV,
|
||||
protectedKeyTag
|
||||
});
|
||||
|
||||
saveTokenToLocalStorage({
|
||||
publicKey,
|
||||
encryptedPrivateKey,
|
||||
iv,
|
||||
tag,
|
||||
privateKey
|
||||
});
|
||||
// unset temporary (MFA) JWT token and set JWT token
|
||||
SecurityClient.setMfaToken("");
|
||||
SecurityClient.setToken(token);
|
||||
SecurityClient.setProviderAuthToken("");
|
||||
|
||||
resolve(true);
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
const privateKey = await KeyService.decryptPrivateKey({
|
||||
encryptionVersion,
|
||||
encryptedPrivateKey,
|
||||
iv,
|
||||
tag,
|
||||
password,
|
||||
salt,
|
||||
protectedKey,
|
||||
protectedKeyIV,
|
||||
protectedKeyTag
|
||||
});
|
||||
|
||||
export default attemptLoginMfa;
|
||||
saveTokenToLocalStorage({
|
||||
publicKey,
|
||||
encryptedPrivateKey,
|
||||
iv,
|
||||
tag,
|
||||
privateKey
|
||||
});
|
||||
|
||||
resolve(true);
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
export default attemptLoginMfa;
|
||||
|
@ -1,5 +1,11 @@
|
||||
import { checkIsPasswordBreached } from "./checkIsPasswordBreached";
|
||||
import { escapeCharRegex, letterCharRegex, lowEntropyRegexes,numAndSpecialCharRegex, repeatedCharRegex } from "./passwordRegexes";
|
||||
import {
|
||||
escapeCharRegex,
|
||||
letterCharRegex,
|
||||
lowEntropyRegexes,
|
||||
numAndSpecialCharRegex,
|
||||
repeatedCharRegex
|
||||
} from "./passwordRegexes";
|
||||
|
||||
interface PasswordCheckProps {
|
||||
password: string;
|
||||
@ -29,40 +35,38 @@ const passwordCheck = async ({
|
||||
{
|
||||
name: "tooShort",
|
||||
validator: (pwd: string) => pwd.length >= 14,
|
||||
setError: setPasswordErrorTooShort,
|
||||
setError: setPasswordErrorTooShort
|
||||
},
|
||||
{
|
||||
name: "tooLong",
|
||||
validator: (pwd: string) => pwd.length < 101,
|
||||
setError: setPasswordErrorTooLong,
|
||||
setError: setPasswordErrorTooLong
|
||||
},
|
||||
{
|
||||
name: "noLetterChar",
|
||||
validator: (pwd: string) => letterCharRegex.test(pwd),
|
||||
setError: setPasswordErrorNoLetterChar,
|
||||
setError: setPasswordErrorNoLetterChar
|
||||
},
|
||||
{
|
||||
name: "noNumOrSpecialChar",
|
||||
validator: (pwd: string) => numAndSpecialCharRegex.test(pwd),
|
||||
setError: setPasswordErrorNoNumOrSpecialChar,
|
||||
setError: setPasswordErrorNoNumOrSpecialChar
|
||||
},
|
||||
{
|
||||
name: "repeatedChar",
|
||||
validator: (pwd: string) => !repeatedCharRegex.test(pwd),
|
||||
setError: setPasswordErrorRepeatedChar,
|
||||
setError: setPasswordErrorRepeatedChar
|
||||
},
|
||||
{
|
||||
name: "escapeChar",
|
||||
validator: (pwd: string) => !escapeCharRegex.test(pwd),
|
||||
setError: setPasswordErrorEscapeChar,
|
||||
setError: setPasswordErrorEscapeChar
|
||||
},
|
||||
{
|
||||
name: "lowEntropy",
|
||||
validator: (pwd: string) => (
|
||||
!lowEntropyRegexes.some(regex => regex.test(pwd))
|
||||
),
|
||||
setError: setPasswordErrorLowEntropy,
|
||||
},
|
||||
validator: (pwd: string) => !lowEntropyRegexes.some((regex) => regex.test(pwd)),
|
||||
setError: setPasswordErrorLowEntropy
|
||||
}
|
||||
];
|
||||
|
||||
const isBreached = await checkIsPasswordBreached(password);
|
||||
@ -73,7 +77,7 @@ const passwordCheck = async ({
|
||||
} else {
|
||||
setPasswordErrorBreached(false);
|
||||
}
|
||||
|
||||
|
||||
tests.forEach((test) => {
|
||||
if (!test.validator(password)) {
|
||||
errorCheck = true;
|
||||
@ -81,7 +85,7 @@ const passwordCheck = async ({
|
||||
} else {
|
||||
test.setError(false);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
return errorCheck;
|
||||
};
|
||||
|
@ -17,25 +17,25 @@ function bufferToHex(buffer: ArrayBuffer): string {
|
||||
return hexParts.join("");
|
||||
}
|
||||
|
||||
// see API details here: https://haveibeenpwned.com/API/v3#SearchingPwnedPasswordsByRange
|
||||
// in short, the pending password is hashed (SHA-1), the first 5 chars are sliced and compared against a ranged hash table
|
||||
// this hash table is formed from the 5 char hash prefix (ie. 00000-FFFFF) so 16^5 results
|
||||
// returns a hash table of 800-1000 results
|
||||
// padding has been added to prevent MitM attacker determining which hash table was called by the response size
|
||||
// the last 35 chars of the password hash are compared client-side against the table
|
||||
// if there is a match, that password has been involved in a password breach (ie. pwnd) and should NOT be accepted
|
||||
// the database consists of ~700 mln breached passwords and is continuously updated, including with law enforcement ingestion
|
||||
// https://www.troyhunt.com/open-source-pwned-passwords-with-fbi-feed-and-225m-new-nca-passwords-is-now-live/
|
||||
// see API details here: https://haveibeenpwned.com/API/v3#SearchingPwnedPasswordsByRange
|
||||
// in short, the pending password is hashed (SHA-1), the first 5 chars are sliced and compared against a ranged hash table
|
||||
// this hash table is formed from the 5 char hash prefix (ie. 00000-FFFFF) so 16^5 results
|
||||
// returns a hash table of 800-1000 results
|
||||
// padding has been added to prevent MitM attacker determining which hash table was called by the response size
|
||||
// the last 35 chars of the password hash are compared client-side against the table
|
||||
// if there is a match, that password has been involved in a password breach (ie. pwnd) and should NOT be accepted
|
||||
// the database consists of ~700 mln breached passwords and is continuously updated, including with law enforcement ingestion
|
||||
// https://www.troyhunt.com/open-source-pwned-passwords-with-fbi-feed-and-225m-new-nca-passwords-is-now-live/
|
||||
|
||||
// The HIBP API follows NIST guidance (pg.14) https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-63b.pdf
|
||||
// "When processing requests to establish and change memorized secrets, verifiers SHALL compare
|
||||
// the prospective secrets against a list that contains values known to be commonly-used, expected,
|
||||
// or compromised. For example, the list MAY include, but is not limited to:
|
||||
// • Passwords obtained from previous breach corpuses.
|
||||
// • Dictionary words.
|
||||
// • Repetitive or sequential characters (e.g. ‘aaaaaa’, ‘1234abcd’).
|
||||
// • Context-specific words, such as the name of the service, the username, and derivatives
|
||||
// thereof."
|
||||
// The HIBP API follows NIST guidance (pg.14) https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-63b.pdf
|
||||
// "When processing requests to establish and change memorized secrets, verifiers SHALL compare
|
||||
// the prospective secrets against a list that contains values known to be commonly-used, expected,
|
||||
// or compromised. For example, the list MAY include, but is not limited to:
|
||||
// • Passwords obtained from previous breach corpuses.
|
||||
// • Dictionary words.
|
||||
// • Repetitive or sequential characters (e.g. ‘aaaaaa’, ‘1234abcd’).
|
||||
// • Context-specific words, such as the name of the service, the username, and derivatives
|
||||
// thereof."
|
||||
|
||||
export const checkIsPasswordBreached = async (password: string): Promise<boolean> => {
|
||||
const HAVE_I_BEEN_PWNED_API_URL = "https://api.pwnedpasswords.com";
|
||||
@ -66,8 +66,8 @@ export const checkIsPasswordBreached = async (password: string): Promise<boolean
|
||||
response = await axios.get(rangedHashTableUri, {
|
||||
headers: {
|
||||
"Add-Padding": "true", // see https://www.troyhunt.com/enhancing-pwned-passwords-privacy-with-padding/
|
||||
"Content-Type": "text/plain",
|
||||
},
|
||||
"Content-Type": "text/plain"
|
||||
}
|
||||
});
|
||||
|
||||
if (response.status === 200) {
|
||||
@ -76,9 +76,8 @@ export const checkIsPasswordBreached = async (password: string): Promise<boolean
|
||||
// check the last 35 hash chars to see if there's a match
|
||||
const isBreachedPassword: boolean = responseData.includes(hashedPwd.slice(5, 40));
|
||||
return isBreachedPassword;
|
||||
}
|
||||
retryAttempt += 1;
|
||||
|
||||
}
|
||||
retryAttempt += 1;
|
||||
} catch (err) {
|
||||
if (!axios.isAxiosError(err)) {
|
||||
throw err;
|
||||
@ -88,14 +87,15 @@ export const checkIsPasswordBreached = async (password: string): Promise<boolean
|
||||
}
|
||||
|
||||
console.error(
|
||||
`Received a non-200 response (${response ? response.status : "unknown"}) from the Pwnd Passwords API`
|
||||
`Received a non-200 response (${
|
||||
response ? response.status : "unknown"
|
||||
}) from the Pwnd Passwords API`
|
||||
);
|
||||
return false;
|
||||
} catch (err: any) {
|
||||
console.error("An unexpected error has occurred:", err.message);
|
||||
return false;
|
||||
} finally {
|
||||
|
||||
// Clear the UTF-8 encoded password from memory
|
||||
|
||||
if (encodedPwd) {
|
||||
|
@ -1,5 +1,11 @@
|
||||
import { checkIsPasswordBreached } from "./checkIsPasswordBreached";
|
||||
import { escapeCharRegex, letterCharRegex, lowEntropyRegexes,numAndSpecialCharRegex, repeatedCharRegex } from "./passwordRegexes";
|
||||
import {
|
||||
escapeCharRegex,
|
||||
letterCharRegex,
|
||||
lowEntropyRegexes,
|
||||
numAndSpecialCharRegex,
|
||||
repeatedCharRegex
|
||||
} from "./passwordRegexes";
|
||||
|
||||
type Errors = {
|
||||
tooShort?: string;
|
||||
@ -44,40 +50,38 @@ const checkPassword = async ({ password, setErrors }: CheckPasswordParams): Prom
|
||||
{
|
||||
name: "tooShort",
|
||||
validator: (pwd: string) => pwd.length >= 14,
|
||||
errorText: "at least 14 characters",
|
||||
errorText: "at least 14 characters"
|
||||
},
|
||||
{
|
||||
name: "tooLong",
|
||||
validator: (pwd: string) => pwd.length < 101,
|
||||
errorText: "at most 100 characters",
|
||||
errorText: "at most 100 characters"
|
||||
},
|
||||
{
|
||||
name: "noLetterChar",
|
||||
validator: (pwd: string) => letterCharRegex.test(pwd),
|
||||
errorText: "at least 1 letter character",
|
||||
errorText: "at least 1 letter character"
|
||||
},
|
||||
{
|
||||
name: "noNumOrSpecialChar",
|
||||
validator: (pwd: string) => numAndSpecialCharRegex.test(pwd),
|
||||
errorText: "at least 1 number or special character",
|
||||
errorText: "at least 1 number or special character"
|
||||
},
|
||||
{
|
||||
name: "repeatedChar",
|
||||
validator: (pwd: string) => !repeatedCharRegex.test(pwd),
|
||||
errorText: "at most 3 repeated, consecutive characters",
|
||||
errorText: "at most 3 repeated, consecutive characters"
|
||||
},
|
||||
{
|
||||
name: "escapeChar",
|
||||
validator: (pwd: string) => !escapeCharRegex.test(pwd),
|
||||
errorText: "No escape characters allowed.",
|
||||
errorText: "No escape characters allowed."
|
||||
},
|
||||
{
|
||||
name: "lowEntropy",
|
||||
validator: (pwd: string) => (
|
||||
!lowEntropyRegexes.some(regex => regex.test(pwd))
|
||||
),
|
||||
errorText: "Password contains personal info.",
|
||||
},
|
||||
validator: (pwd: string) => !lowEntropyRegexes.some((regex) => regex.test(pwd)),
|
||||
errorText: "Password contains personal info."
|
||||
}
|
||||
];
|
||||
|
||||
const isBreached = await checkIsPasswordBreached(password);
|
||||
@ -96,4 +100,4 @@ const checkPassword = async ({ password, setErrors }: CheckPasswordParams): Prom
|
||||
return Object.keys(errors).length > 0;
|
||||
};
|
||||
|
||||
export default checkPassword;
|
||||
export default checkPassword;
|
||||
|
@ -1,6 +1,7 @@
|
||||
// This regex covers letters (case insensitive) for the top 50 most spoken languages
|
||||
/* eslint-disable no-misleading-character-class */
|
||||
export const letterCharRegex = /[A-Za-z\u00C0-\u00D6\u00D8-\u00DE\u00DF-\u00F6\u00F8-\u00FF\u3040-\u309F\u30A0-\u30FF\u4E00-\u9FFF\u0600-\u06FF\u0400-\u04FF\u0500-\u052F\u2DE0-\u2DFF\uA640-\uA69F\u05B0-\u05FF\u0980-\u09FF\u1F00-\u1FFF\u0130\u015E\u011E\u00C7\u00FC\u00FB\u00EB\u00E7]/u;
|
||||
export const letterCharRegex =
|
||||
/[A-Za-z\u00C0-\u00D6\u00D8-\u00DE\u00DF-\u00F6\u00F8-\u00FF\u3040-\u309F\u30A0-\u30FF\u4E00-\u9FFF\u0600-\u06FF\u0400-\u04FF\u0500-\u052F\u2DE0-\u2DFF\uA640-\uA69F\u05B0-\u05FF\u0980-\u09FF\u1F00-\u1FFF\u0130\u015E\u011E\u00C7\u00FC\u00FB\u00EB\u00E7]/u;
|
||||
|
||||
// This regex covers digits, special characters, symbols, and emojis.
|
||||
export const numAndSpecialCharRegex = /[\d!@#$%^&*(),.?":{}|<>]|[^\p{L}\p{N}\s]/u;
|
||||
@ -32,5 +33,5 @@ export const lowEntropyRegexes = [
|
||||
/\b(?:[A-Z0-9]{7,10}|[A-Z0-9]{10,11}|[A-Z0-9]{7,10})\b/,
|
||||
|
||||
// US social security number
|
||||
/\b\d{3}[-\s]?\d{2}[-\s]?\d{4}\b/,
|
||||
];
|
||||
/\b\d{3}[-\s]?\d{2}[-\s]?\d{4}\b/
|
||||
];
|
||||
|
@ -3,9 +3,7 @@ import crypto from "crypto";
|
||||
|
||||
import jsrp from "jsrp";
|
||||
|
||||
import { issueBackupPrivateKey ,
|
||||
srp1
|
||||
} from "@app/hooks/api/auth/queries";
|
||||
import { issueBackupPrivateKey, srp1 } from "@app/hooks/api/auth/queries";
|
||||
|
||||
import generateBackupPDF from "../generateBackupPDF";
|
||||
import Aes256Gcm from "./aes-256-gcm";
|
||||
@ -97,7 +95,6 @@ const issueBackupKey = async ({
|
||||
generatedKey
|
||||
});
|
||||
setBackupKeyIssued(true);
|
||||
|
||||
} catch {
|
||||
setBackupKeyError(true);
|
||||
}
|
||||
|
@ -3,11 +3,7 @@ import { useRouter } from "next/router";
|
||||
|
||||
import { useUser } from "@app/context";
|
||||
|
||||
import {
|
||||
boot as bootIntercom,
|
||||
load as loadIntercom,
|
||||
update as updateIntercom,
|
||||
} from "./intercom";
|
||||
import { boot as bootIntercom, load as loadIntercom, update as updateIntercom } from "./intercom";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
export const IntercomProvider = ({ children }: { children: any }) => {
|
||||
@ -16,7 +12,11 @@ export const IntercomProvider = ({ children }: { children: any }) => {
|
||||
|
||||
if (typeof window !== "undefined") {
|
||||
loadIntercom();
|
||||
bootIntercom({name: `${user?.firstName || ""} ${user?.lastName || ""}`, email: user?.email || "", created_at: Math.floor(((new Date(user?.createdAt))?.getTime() || 0) / 1000)});
|
||||
bootIntercom({
|
||||
name: `${user?.firstName || ""} ${user?.lastName || ""}`,
|
||||
email: user?.email || "",
|
||||
created_at: Math.floor((new Date(user?.createdAt)?.getTime() || 0) / 1000)
|
||||
});
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
@ -37,4 +37,4 @@ export const IntercomProvider = ({ children }: { children: any }) => {
|
||||
}, [router.events]);
|
||||
|
||||
return children;
|
||||
};
|
||||
};
|
||||
|
@ -1,5 +1,5 @@
|
||||
export const isValidHexColor = (hexColor: string) => {
|
||||
const hexColorPattern = /^#?([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$/;
|
||||
|
||||
return hexColorPattern.test(hexColor);
|
||||
}
|
||||
export const isValidHexColor = (hexColor: string) => {
|
||||
const hexColorPattern = /^#?([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$/;
|
||||
|
||||
return hexColorPattern.test(hexColor);
|
||||
};
|
||||
|
@ -17,10 +17,9 @@ export const saveTokenToLocalStorage = ({
|
||||
encryptedPrivateKey,
|
||||
iv,
|
||||
tag,
|
||||
privateKey,
|
||||
privateKey
|
||||
}: Props) => {
|
||||
try {
|
||||
|
||||
if (protectedKey) {
|
||||
localStorage.removeItem("protectedKey");
|
||||
localStorage.setItem("protectedKey", protectedKey);
|
||||
@ -62,9 +61,7 @@ export const saveTokenToLocalStorage = ({
|
||||
}
|
||||
} catch (err) {
|
||||
if (err instanceof Error) {
|
||||
throw new Error(
|
||||
`Unable to send the tokens in local storage:${ err.message}`
|
||||
);
|
||||
throw new Error(`Unable to send the tokens in local storage:${err.message}`);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1,7 +1,7 @@
|
||||
/* eslint-disable */
|
||||
import { PostHog } from 'posthog-js';
|
||||
import { initPostHog } from '@app/components/analytics/posthog';
|
||||
import { ENV } from '@app/components/utilities/config';
|
||||
import { PostHog } from "posthog-js";
|
||||
import { initPostHog } from "@app/components/analytics/posthog";
|
||||
import { ENV } from "@app/components/utilities/config";
|
||||
|
||||
declare let TELEMETRY_CAPTURING_ENABLED: any;
|
||||
|
||||
@ -13,23 +13,23 @@ class Capturer {
|
||||
}
|
||||
|
||||
capture(item: string) {
|
||||
if (ENV === 'production' && TELEMETRY_CAPTURING_ENABLED === "true") {
|
||||
if (ENV === "production" && TELEMETRY_CAPTURING_ENABLED === "true") {
|
||||
try {
|
||||
this.api.capture(item);
|
||||
} catch (error) {
|
||||
console.error('PostHog', error);
|
||||
console.error("PostHog", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
identify(id: string, email?: string) {
|
||||
if (ENV === 'production' && TELEMETRY_CAPTURING_ENABLED === "true") {
|
||||
if (ENV === "production" && TELEMETRY_CAPTURING_ENABLED === "true") {
|
||||
try {
|
||||
this.api.identify(id, {
|
||||
email: email
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('PostHog', error);
|
||||
console.error("PostHog", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ type Story = StoryObj<typeof Accordion>;
|
||||
|
||||
export const Basic: Story = {
|
||||
render: (args) => (
|
||||
<div className="flex justify-center w-full">
|
||||
<div className="flex w-full justify-center">
|
||||
<Accordion {...args}>
|
||||
<AccordionItem value="section-1">
|
||||
<AccordionTrigger>Section 1</AccordionTrigger>
|
||||
|
@ -8,7 +8,7 @@ export const AccordionItem = forwardRef<HTMLDivElement, AccordionPrimitive.Accor
|
||||
({ children, className, ...props }, forwardedRef) => (
|
||||
<AccordionPrimitive.Item
|
||||
className={twMerge(
|
||||
"mt-px overflow-hidden first:mt-0 data-[state=open]:border-l data-[state=open]:border-primary transition-all border-transparent",
|
||||
"mt-px overflow-hidden border-transparent transition-all first:mt-0 data-[state=open]:border-l data-[state=open]:border-primary",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
@ -27,7 +27,7 @@ export const AccordionTrigger = forwardRef<
|
||||
<AccordionPrimitive.Header className="flex">
|
||||
<AccordionPrimitive.Trigger
|
||||
className={twMerge(
|
||||
"py-2 px-4 group data-[state=open]:text-primary h-11 hover:text-primary flex flex-1 outline-none items-center justify-between ",
|
||||
"group flex h-11 flex-1 items-center justify-between py-2 px-4 outline-none hover:text-primary data-[state=open]:text-primary ",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
@ -36,7 +36,7 @@ export const AccordionTrigger = forwardRef<
|
||||
{children}
|
||||
<FontAwesomeIcon
|
||||
icon={faChevronDown}
|
||||
className="ease-[cubic-bezier(0.87,_0,_0.13,_1)] transition-transform duration-300 group-data-[state=open]:rotate-180 text-sm"
|
||||
className="text-sm transition-transform duration-300 ease-[cubic-bezier(0.87,_0,_0.13,_1)] group-data-[state=open]:rotate-180"
|
||||
aria-hidden
|
||||
/>
|
||||
</AccordionPrimitive.Trigger>
|
||||
@ -51,13 +51,13 @@ export const AccordionContent = forwardRef<
|
||||
>(({ children, className, ...props }, forwardedRef) => (
|
||||
<AccordionPrimitive.Content
|
||||
className={twMerge(
|
||||
"data-[state=open]:animate-slideDown data-[state=closed]:animate-slideUp overflow-hidden",
|
||||
"overflow-hidden data-[state=open]:animate-slideDown data-[state=closed]:animate-slideUp",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
ref={forwardedRef}
|
||||
>
|
||||
<div className="text-sm py-2 px-4">{children}</div>
|
||||
<div className="py-2 px-4 text-sm">{children}</div>
|
||||
</AccordionPrimitive.Content>
|
||||
));
|
||||
|
||||
|
@ -1 +1 @@
|
||||
export { Accordion, AccordionContent, AccordionItem,AccordionTrigger } from "./Accordion";
|
||||
export { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "./Accordion";
|
||||
|
@ -10,7 +10,7 @@ export type CardTitleProps = {
|
||||
export const CardTitle = ({ children, className, subTitle }: CardTitleProps) => (
|
||||
<div
|
||||
className={twMerge(
|
||||
"px-6 py-4 mb-5 font-sans text-lg font-normal border-b border-mineshaft-600 break-words",
|
||||
"mb-5 break-words border-b border-mineshaft-600 px-6 py-4 font-sans text-lg font-normal",
|
||||
className
|
||||
)}
|
||||
>
|
||||
|
@ -30,7 +30,7 @@ export const Checkbox = ({
|
||||
<div className="flex items-center font-inter text-bunker-300">
|
||||
<CheckboxPrimitive.Root
|
||||
className={twMerge(
|
||||
"flex items-center flex-shrink-0 justify-center w-4 h-4 transition-all rounded shadow border border-mineshaft-400 hover:bg-mineshaft-500 bg-mineshaft-600",
|
||||
"flex h-4 w-4 flex-shrink-0 items-center justify-center rounded border border-mineshaft-400 bg-mineshaft-600 shadow transition-all hover:bg-mineshaft-500",
|
||||
isDisabled && "bg-bunker-400 hover:bg-bunker-400",
|
||||
isChecked && "bg-primary hover:bg-primary",
|
||||
Boolean(children) && "mr-3",
|
||||
@ -46,7 +46,7 @@ export const Checkbox = ({
|
||||
<FontAwesomeIcon icon={faCheck} size="sm" />
|
||||
</CheckboxPrimitive.Indicator>
|
||||
</CheckboxPrimitive.Root>
|
||||
<label className="text-sm whitespace-nowrap truncate" htmlFor={id}>
|
||||
<label className="truncate whitespace-nowrap text-sm" htmlFor={id}>
|
||||
{children}
|
||||
{isRequired && <span className="pl-1 text-red">*</span>}
|
||||
</label>
|
||||
|
@ -58,7 +58,7 @@ export const DeleteActionModal = ({
|
||||
title={title}
|
||||
subTitle={subTitle}
|
||||
footerContent={
|
||||
<div className="flex items-center mx-2">
|
||||
<div className="mx-2 flex items-center">
|
||||
<Button
|
||||
className="mr-4"
|
||||
colorSchema="danger"
|
||||
@ -91,7 +91,11 @@ export const DeleteActionModal = ({
|
||||
}
|
||||
className="mb-0"
|
||||
>
|
||||
<Input value={inputData} onChange={(e) => setInputData(e.target.value)} placeholder="Type to delete..." />
|
||||
<Input
|
||||
value={inputData}
|
||||
onChange={(e) => setInputData(e.target.value)}
|
||||
placeholder="Type to delete..."
|
||||
/>
|
||||
</FormControl>
|
||||
</form>
|
||||
</ModalContent>
|
||||
|
@ -45,7 +45,7 @@ export const DrawerContent = forwardRef<HTMLDivElement, DrawerContentProps>(
|
||||
ref={forwardedRef}
|
||||
className={twMerge(drawerContentVariation({ direction, className }))}
|
||||
>
|
||||
<Card isRounded={false} className="h-full w-full dark">
|
||||
<Card isRounded={false} className="dark h-full w-full">
|
||||
{title && (
|
||||
<CardTitle subTitle={subTitle} className="px-4">
|
||||
{title}
|
||||
|
@ -1 +1 @@
|
||||
export { Drawer, DrawerClose,DrawerContent, DrawerTrigger } from "./Drawer";
|
||||
export { Drawer, DrawerClose, DrawerContent, DrawerTrigger } from "./Drawer";
|
||||
|
@ -25,7 +25,7 @@ type Story = StoryObj<typeof DropdownMenuContent>;
|
||||
|
||||
export const Basic: Story = {
|
||||
render: (args) => (
|
||||
<div className="flex justify-center w-full">
|
||||
<div className="flex w-full justify-center">
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<IconButton ariaLabel="add">
|
||||
@ -43,7 +43,7 @@ export const Basic: Story = {
|
||||
|
||||
export const Icons: Story = {
|
||||
render: (args) => (
|
||||
<div className="flex justify-center w-full">
|
||||
<div className="flex w-full justify-center">
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<IconButton ariaLabel="add">
|
||||
@ -65,7 +65,7 @@ export const Icons: Story = {
|
||||
|
||||
export const WithDivider: Story = {
|
||||
render: (args) => (
|
||||
<div className="flex justify-center w-full">
|
||||
<div className="flex w-full justify-center">
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<IconButton ariaLabel="add">
|
||||
@ -86,7 +86,7 @@ export const WithDivider: Story = {
|
||||
|
||||
export const Group: Story = {
|
||||
render: (args) => (
|
||||
<div className="flex justify-center w-full">
|
||||
<div className="flex w-full justify-center">
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<IconButton ariaLabel="add">
|
||||
|
@ -24,7 +24,7 @@ export const DropdownMenuContent = forwardRef<HTMLDivElement, DropdownMenuConten
|
||||
{...props}
|
||||
ref={forwardedRef}
|
||||
className={twMerge(
|
||||
"min-w-[220px] z-30 bg-mineshaft-900 border border-mineshaft-600 will-change-auto text-bunker-300 rounded-md shadow data-[side=top]:animate-slideDownAndFade data-[side=left]:animate-slideRightAndFade data-[side=right]:animate-slideLeftAndFade data-[side=bottom]:animate-slideUpAndFade",
|
||||
"z-30 min-w-[220px] rounded-md border border-mineshaft-600 bg-mineshaft-900 text-bunker-300 shadow will-change-auto data-[side=top]:animate-slideDownAndFade data-[side=left]:animate-slideRightAndFade data-[side=right]:animate-slideLeftAndFade data-[side=bottom]:animate-slideUpAndFade",
|
||||
className
|
||||
)}
|
||||
>
|
||||
@ -48,7 +48,7 @@ export const DropdownSubMenuContent = forwardRef<HTMLDivElement, DropdownSubMenu
|
||||
{...props}
|
||||
ref={forwardedRef}
|
||||
className={twMerge(
|
||||
"min-w-[220px] z-30 bg-mineshaft-900 border border-mineshaft-600 will-change-auto text-bunker-300 rounded-md shadow data-[side=top]:animate-slideDownAndFade data-[side=left]:animate-slideRightAndFade data-[side=right]:animate-slideLeftAndFade data-[side=bottom]:animate-slideUpAndFade",
|
||||
"z-30 min-w-[220px] rounded-md border border-mineshaft-600 bg-mineshaft-900 text-bunker-300 shadow will-change-auto data-[side=top]:animate-slideDownAndFade data-[side=left]:animate-slideRightAndFade data-[side=right]:animate-slideLeftAndFade data-[side=bottom]:animate-slideUpAndFade",
|
||||
className
|
||||
)}
|
||||
>
|
||||
@ -66,7 +66,7 @@ export type DropdownLabelProps = DropdownMenuPrimitive.MenuLabelProps;
|
||||
export const DropdownMenuLabel = ({ className, ...props }: DropdownLabelProps) => (
|
||||
<DropdownMenuPrimitive.Label
|
||||
{...props}
|
||||
className={twMerge("text-xs text-bunker-400 px-4 pt-2 pb-1", className)}
|
||||
className={twMerge("px-4 pt-2 pb-1 text-xs text-bunker-400", className)}
|
||||
/>
|
||||
);
|
||||
|
||||
@ -91,14 +91,14 @@ export const DropdownMenuItem = <T extends ElementType = "button">({
|
||||
<DropdownMenuPrimitive.Item
|
||||
{...props}
|
||||
className={twMerge(
|
||||
"text-xs text-mineshaft-200 block font-inter px-4 py-2 data-[highlighted]:bg-mineshaft-700 rounded-sm outline-none cursor-pointer",
|
||||
"block cursor-pointer rounded-sm px-4 py-2 font-inter text-xs text-mineshaft-200 outline-none data-[highlighted]:bg-mineshaft-700",
|
||||
className
|
||||
)}
|
||||
>
|
||||
<Item type="button" role="menuitem" className="flex w-full items-center" ref={inputRef}>
|
||||
{icon && iconPos === "left" && <span className="flex items-center mr-2">{icon}</span>}
|
||||
{icon && iconPos === "left" && <span className="mr-2 flex items-center">{icon}</span>}
|
||||
<span className="flex-grow text-left">{children}</span>
|
||||
{icon && iconPos === "right" && <span className="flex items-center ml-2">{icon}</span>}
|
||||
{icon && iconPos === "right" && <span className="ml-2 flex items-center">{icon}</span>}
|
||||
</Item>
|
||||
</DropdownMenuPrimitive.Item>
|
||||
);
|
||||
@ -124,14 +124,14 @@ export const DropdownSubMenuTrigger = <T extends ElementType = "button">({
|
||||
<DropdownMenuPrimitive.SubTrigger
|
||||
{...props}
|
||||
className={twMerge(
|
||||
"text-xs text-mineshaft-200 block font-inter px-4 py-2 data-[highlighted]:bg-mineshaft-700 rounded-sm outline-none cursor-pointer",
|
||||
"block cursor-pointer rounded-sm px-4 py-2 font-inter text-xs text-mineshaft-200 outline-none data-[highlighted]:bg-mineshaft-700",
|
||||
className
|
||||
)}
|
||||
>
|
||||
<Item type="button" role="menuitem" className="flex w-full items-center" ref={inputRef}>
|
||||
{icon && iconPos === "left" && <span className="flex items-center mr-2">{icon}</span>}
|
||||
{icon && iconPos === "left" && <span className="mr-2 flex items-center">{icon}</span>}
|
||||
<span className="flex-grow text-left">{children}</span>
|
||||
{icon && iconPos === "right" && <span className="flex items-center ml-2">{icon}</span>}
|
||||
{icon && iconPos === "right" && <span className="ml-2 flex items-center">{icon}</span>}
|
||||
</Item>
|
||||
</DropdownMenuPrimitive.SubTrigger>
|
||||
);
|
||||
@ -143,7 +143,7 @@ export const DropdownMenuGroup = forwardRef<HTMLDivElement, DropdownMenuGroupPro
|
||||
({ ...props }, ref) => (
|
||||
<DropdownMenuPrimitive.Group
|
||||
{...props}
|
||||
className={twMerge("text-xs py-2 pl-3", props.className)}
|
||||
className={twMerge("py-2 pl-3 text-xs", props.className)}
|
||||
ref={ref}
|
||||
/>
|
||||
)
|
||||
@ -159,7 +159,7 @@ export const DropdownMenuSeparator = forwardRef<
|
||||
<DropdownMenuPrimitive.Separator
|
||||
ref={ref}
|
||||
{...props}
|
||||
className={twMerge("h-[1px] bg-gray-700 m-1", className)}
|
||||
className={twMerge("m-1 h-[1px] bg-gray-700", className)}
|
||||
/>
|
||||
));
|
||||
|
||||
|
@ -10,9 +10,10 @@ export const EmailServiceSetupModal = ({ isOpen, onOpenChange }: Props): JSX.Ele
|
||||
<Modal isOpen={isOpen} onOpenChange={onOpenChange}>
|
||||
<ModalContent title="Email service not configured">
|
||||
<p className="mb-4 text-bunker-300">
|
||||
The administrators of this Infisical instance have not yet set up an email service provider required to perform this action
|
||||
The administrators of this Infisical instance have not yet set up an email service provider
|
||||
required to perform this action
|
||||
</p>
|
||||
|
||||
|
||||
<a href="https://infisical.com/docs/self-hosting/configuration/email">
|
||||
<Button className="mr-4">Learn more</Button>
|
||||
</a>
|
||||
|
@ -23,7 +23,7 @@ export const FormLabel = ({ id, label, isRequired, icon, className }: FormLabelP
|
||||
{label}
|
||||
{isRequired && <span className="ml-1 text-red">*</span>}
|
||||
{icon && (
|
||||
<span className="ml-2 text-mineshaft-300 hover:text-mineshaft-200 cursor-default">
|
||||
<span className="ml-2 cursor-default text-mineshaft-300 hover:text-mineshaft-200">
|
||||
{icon}
|
||||
</span>
|
||||
)}
|
||||
|
@ -10,22 +10,16 @@ type Props = {
|
||||
|
||||
export type HoverCardProps = Props;
|
||||
|
||||
export const HoverObject = ({
|
||||
text,
|
||||
icon,
|
||||
color
|
||||
}: Props): JSX.Element => (
|
||||
export const HoverObject = ({ text, icon, color }: Props): JSX.Element => (
|
||||
<HoverCard.Root openDelay={50}>
|
||||
<HoverCard.Trigger asChild>
|
||||
<a
|
||||
className="ImageTrigger z-20"
|
||||
>
|
||||
<a className="ImageTrigger z-20">
|
||||
<FontAwesomeIcon icon={icon} className={`text-${color}`} />
|
||||
</a>
|
||||
</HoverCard.Trigger>
|
||||
<HoverCard.Portal>
|
||||
<HoverCard.Content className="HoverCardContent z-[300]" sideOffset={5}>
|
||||
<div className='bg-bunker-700 border border-mineshaft-600 p-2 rounded-md drop-shadow-xl text-bunker-300'>
|
||||
<div className="rounded-md border border-mineshaft-600 bg-bunker-700 p-2 text-bunker-300 drop-shadow-xl">
|
||||
<div style={{ display: "flex", flexDirection: "column", gap: 15 }}>
|
||||
<div>
|
||||
<div className="Text bold">{text}</div>
|
||||
|
@ -1 +1 @@
|
||||
export { HoverCard,HoverCardContent, HoverCardTrigger } from "./HoverCardv2";
|
||||
export { HoverCard, HoverCardContent, HoverCardTrigger } from "./HoverCardv2";
|
||||
|
@ -29,7 +29,7 @@ export const ModalContent = forwardRef<HTMLDivElement, ModalContentProps>(
|
||||
<Card
|
||||
isRounded
|
||||
className={twMerge(
|
||||
"fixed top-1/2 left-1/2 z-30 dark:[color-scheme:dark] max-h-screen thin-scrollbar max-w-xl -translate-y-2/4 -translate-x-2/4 animate-popIn border border-mineshaft-600 drop-shadow-2xl",
|
||||
"thin-scrollbar fixed top-1/2 left-1/2 z-30 max-h-screen max-w-xl -translate-y-2/4 -translate-x-2/4 animate-popIn border border-mineshaft-600 drop-shadow-2xl dark:[color-scheme:dark]",
|
||||
className
|
||||
)}
|
||||
>
|
||||
|
@ -44,7 +44,7 @@ export const Pagination = ({
|
||||
return (
|
||||
<div
|
||||
className={twMerge(
|
||||
"flex items-center justify-end text-white w-full py-3 px-4 bg-mineshaft-800",
|
||||
"flex w-full items-center justify-end bg-mineshaft-800 py-3 px-4 text-white",
|
||||
className
|
||||
)}
|
||||
>
|
||||
|
@ -3,39 +3,42 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import * as Popover from "@radix-ui/react-popover";
|
||||
|
||||
type Props = {
|
||||
children: any;
|
||||
text: string;
|
||||
onChangeHandler: (value: string, id: string) => void;
|
||||
children: any;
|
||||
text: string;
|
||||
onChangeHandler: (value: string, id: string) => void;
|
||||
id: string;
|
||||
};
|
||||
|
||||
export type PopoverProps = Props;
|
||||
|
||||
export const PopoverObject = ({children, text, onChangeHandler, id}: Props) => (
|
||||
export const PopoverObject = ({ children, text, onChangeHandler, id }: Props) => (
|
||||
<Popover.Root>
|
||||
<Popover.Trigger asChild className='data-[state=open]:outline data-[state=open]:outline-primary data-[state=closed]:hover:outline data-[state=closed]:hover:outline-mineshaft-400'>
|
||||
<Popover.Trigger
|
||||
asChild
|
||||
className="data-[state=open]:outline data-[state=open]:outline-primary data-[state=closed]:hover:outline data-[state=closed]:hover:outline-mineshaft-400"
|
||||
>
|
||||
{children}
|
||||
</Popover.Trigger>
|
||||
<Popover.Portal>
|
||||
<Popover.Content
|
||||
className="rounded z-[100] p-3 w-[460px] min-h-fit border border-chicago-700 bg-mineshaft-600 shadow-[0_10px_38px_-10px_hsla(206,22%,7%,.35),0_10px_20px_-15px_hsla(206,22%,7%,.2)] focus:shadow-[0_10px_38px_-10px_hsla(206,22%,7%,.35),0_10px_20px_-15px_hsla(206,22%,7%,.2),0_0_0_2px_theme(colors.violet7)] will-change-[transform,opacity] data-[state=open]:data-[side=top]:animate-slideDownAndFade data-[state=open]:data-[side=right]:animate-slideLeftAndFade data-[state=open]:data-[side=bottom]:animate-slideUpAndFade data-[state=open]:data-[side=left]:animate-slideRightAndFade"
|
||||
className="z-[100] min-h-fit w-[460px] rounded border border-chicago-700 bg-mineshaft-600 p-3 shadow-[0_10px_38px_-10px_hsla(206,22%,7%,.35),0_10px_20px_-15px_hsla(206,22%,7%,.2)] will-change-[transform,opacity] focus:shadow-[0_10px_38px_-10px_hsla(206,22%,7%,.35),0_10px_20px_-15px_hsla(206,22%,7%,.2),0_0_0_2px_theme(colors.violet7)] data-[state=open]:data-[side=top]:animate-slideDownAndFade data-[state=open]:data-[side=right]:animate-slideLeftAndFade data-[state=open]:data-[side=bottom]:animate-slideUpAndFade data-[state=open]:data-[side=left]:animate-slideRightAndFade"
|
||||
sideOffset={5}
|
||||
hideWhenDetached
|
||||
side="left"
|
||||
>
|
||||
<div className="flex flex-col pt-2 dark">
|
||||
<p className="text-bunker-200 text-[15px] leading-[0px] font-medium mb-5">Comment</p>
|
||||
<div className="dark flex flex-col pt-2">
|
||||
<p className="mb-5 text-[15px] font-medium leading-[0px] text-bunker-200">Comment</p>
|
||||
<textarea
|
||||
onChange={(e) => onChangeHandler(e.target.value, id)}
|
||||
// type={type}
|
||||
value={text}
|
||||
className='z-10 dark:[color-scheme:dark] peer h-[20rem] ph-no-capture bg-bunker-600 border border-mineshaft-500 rounded-md py-2.5 caret-bunker-200 text-sm px-2 w-full outline-none text-bunker-300 focus:text-bunker-100 placeholder:text-bunker-400 placeholder:focus:text-transparent placeholder duration-200'
|
||||
className="ph-no-capture placeholder peer z-10 h-[20rem] w-full rounded-md border border-mineshaft-500 bg-bunker-600 py-2.5 px-2 text-sm text-bunker-300 caret-bunker-200 outline-none duration-200 placeholder:text-bunker-400 focus:text-bunker-100 placeholder:focus:text-transparent dark:[color-scheme:dark]"
|
||||
spellCheck="false"
|
||||
placeholder='–'
|
||||
placeholder="–"
|
||||
/>
|
||||
</div>
|
||||
<Popover.Close
|
||||
className="rounded-full h-[25px] w-[25px] inline-flex items-center justify-center text-bunker-300 hover:text-white absolute top-[5px] right-[5px] hover:bg-violet4 focus:shadow-[0_0_0_2px] focus:shadow-violet7 outline-none cursor-default"
|
||||
className="hover:bg-violet4 focus:shadow-violet7 absolute top-[5px] right-[5px] inline-flex h-[25px] w-[25px] cursor-default items-center justify-center rounded-full text-bunker-300 outline-none hover:text-white focus:shadow-[0_0_0_2px]"
|
||||
aria-label="Close"
|
||||
>
|
||||
<FontAwesomeIcon icon={faXmark} />
|
||||
|
@ -1 +1 @@
|
||||
export { Popover,PopoverContent, PopoverTrigger } from "./Popoverv2";
|
||||
export { Popover, PopoverContent, PopoverTrigger } from "./Popoverv2";
|
||||
|
@ -6,35 +6,35 @@ export type RadioGroupProps = RadioGroupPrimitive.RadioGroupProps;
|
||||
|
||||
// Note this component is not customizable (Heroku integration and potentially other pages depend on it)
|
||||
export const RadioGroup = ({ className, children, ...props }: RadioGroupProps) => (
|
||||
<RadioGroupPrimitive.Root
|
||||
className={twMerge("flex flex-row gap-5 px-6 mb-6", className)}
|
||||
<RadioGroupPrimitive.Root
|
||||
className={twMerge("mb-6 flex flex-row gap-5 px-6", className)}
|
||||
defaultValue="App"
|
||||
aria-label="View density"
|
||||
{...props}
|
||||
>
|
||||
>
|
||||
<div className="flex items-center">
|
||||
<RadioGroupPrimitive.Item
|
||||
className="bg-bunker-400/20 w-[20px] h-[20px] rounded-full hover:bg-bunker-400/40 border border-bunker-400/60 duration-200 outline-none cursor-default"
|
||||
className="h-[20px] w-[20px] cursor-default rounded-full border border-bunker-400/60 bg-bunker-400/20 outline-none duration-200 hover:bg-bunker-400/40"
|
||||
value="App"
|
||||
id="r1"
|
||||
>
|
||||
<RadioGroupPrimitive.Indicator className="flex items-center justify-center w-full h-full relative after:content-[''] after:block after:w-[11px] after:h-[11px] after:rounded-[50%] after:bg-primary" />
|
||||
<RadioGroupPrimitive.Indicator className="relative flex h-full w-full items-center justify-center after:block after:h-[11px] after:w-[11px] after:rounded-[50%] after:bg-primary after:content-['']" />
|
||||
</RadioGroupPrimitive.Item>
|
||||
<label className="text-bunker-200 text-sm leading-none pl-2" htmlFor="r1">
|
||||
<label className="pl-2 text-sm leading-none text-bunker-200" htmlFor="r1">
|
||||
App
|
||||
</label>
|
||||
</div>
|
||||
<div className="flex items-center">
|
||||
<RadioGroupPrimitive.Item
|
||||
className="bg-bunker-400/20 w-[22px] h-[22px] rounded-full hover:bg-bunker-400/40 border border-bunker-400/60 duration-200 outline-none cursor-default"
|
||||
className="h-[22px] w-[22px] cursor-default rounded-full border border-bunker-400/60 bg-bunker-400/20 outline-none duration-200 hover:bg-bunker-400/40"
|
||||
value="Pipeline"
|
||||
id="r2"
|
||||
>
|
||||
<RadioGroupPrimitive.Indicator className="flex items-center justify-center w-full h-full relative after:content-[''] after:block after:w-[13px] after:h-[13px] after:rounded-[50%] after:bg-primary" />
|
||||
<RadioGroupPrimitive.Indicator className="relative flex h-full w-full items-center justify-center after:block after:h-[13px] after:w-[13px] after:rounded-[50%] after:bg-primary after:content-['']" />
|
||||
</RadioGroupPrimitive.Item>
|
||||
<label className="text-bunker-200 text-sm leading-none pl-2" htmlFor="r2">
|
||||
<label className="pl-2 text-sm leading-none text-bunker-200" htmlFor="r2">
|
||||
Pipeline
|
||||
</label>
|
||||
</div>
|
||||
</RadioGroupPrimitive.Root>
|
||||
);
|
||||
);
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { forwardRef, ReactNode } from "react";
|
||||
import { IconProp } from "@fortawesome/fontawesome-svg-core";
|
||||
import { faCaretDown, faCaretUp,faCheck } from "@fortawesome/free-solid-svg-icons";
|
||||
import { faCaretDown, faCaretUp, faCheck } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import * as SelectPrimitive from "@radix-ui/react-select";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
@ -57,7 +57,7 @@ export const Select = forwardRef<HTMLButtonElement, SelectProps>(
|
||||
<SelectPrimitive.Portal>
|
||||
<SelectPrimitive.Content
|
||||
className={twMerge(
|
||||
"relative top-1 z-[100] overflow-hidden rounded-md bg-mineshaft-900 border border-mineshaft-600 font-inter text-bunker-100 shadow-md",
|
||||
"relative top-1 z-[100] overflow-hidden rounded-md border border-mineshaft-600 bg-mineshaft-900 font-inter text-bunker-100 shadow-md",
|
||||
dropdownContainerClassName
|
||||
)}
|
||||
position={position}
|
||||
|
@ -20,7 +20,7 @@ export const Spinner = ({ className, size = "md" }: Props): JSX.Element => {
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
className={twMerge(
|
||||
"text-gray-200 animate-spin dark:text-gray-600 fill-primary m-1",
|
||||
"m-1 animate-spin fill-primary text-gray-200 dark:text-gray-600",
|
||||
sizeChart[size],
|
||||
className
|
||||
)}
|
||||
|
@ -14,7 +14,7 @@ export const Stepper = ({ activeStep, children, direction, className }: StepperP
|
||||
return (
|
||||
<div
|
||||
className={twMerge(
|
||||
"flex items-center w-full space-x-3 p-2 border border-bunker-300/30 rounded-md",
|
||||
"flex w-full items-center space-x-3 rounded-md border border-bunker-300/30 p-2",
|
||||
className
|
||||
)}
|
||||
>
|
||||
@ -25,15 +25,15 @@ export const Stepper = ({ activeStep, children, direction, className }: StepperP
|
||||
return (
|
||||
<div
|
||||
className={twMerge(
|
||||
"flex items-center space-x-3 flex-shrink-0",
|
||||
"flex flex-shrink-0 items-center space-x-3",
|
||||
isNotLast && "flex-grow"
|
||||
)}
|
||||
>
|
||||
<div className="flex items-center space-x-2 flex-shrink-0">
|
||||
<div className="flex flex-shrink-0 items-center space-x-2">
|
||||
<div
|
||||
className={twMerge(
|
||||
"w-7 h-7 flex items-center justify-center font-medium text-mineshaft-800 text-sm rounded-full transition-all",
|
||||
isCompleted ? "bg-primary" : "border text-bunker-300 border-primary/30",
|
||||
"flex h-7 w-7 items-center justify-center rounded-full text-sm font-medium text-mineshaft-800 transition-all",
|
||||
isCompleted ? "bg-primary" : "border border-primary/30 text-bunker-300",
|
||||
isActive && "bg-primary text-mineshaft-800"
|
||||
)}
|
||||
>
|
||||
@ -71,7 +71,7 @@ export type StepProps = {
|
||||
export const Step = ({ title, description }: StepProps) => {
|
||||
return (
|
||||
<div className="flex flex-col text-gray-300">
|
||||
<div className="font-medium text-sm">{title}</div>
|
||||
<div className="text-sm font-medium">{title}</div>
|
||||
{description && <div className="text-xs">{description}</div>}
|
||||
</div>
|
||||
);
|
||||
|
@ -1,2 +1,2 @@
|
||||
export type { StepperProps,StepProps } from "./Stepper";
|
||||
export { Step,Stepper } from "./Stepper";
|
||||
export type { StepperProps, StepProps } from "./Stepper";
|
||||
export { Step, Stepper } from "./Stepper";
|
||||
|
@ -6,5 +6,6 @@ export type {
|
||||
TFootProps,
|
||||
THeadProps,
|
||||
ThProps,
|
||||
TrProps} from "./Table";
|
||||
export { Table, TableContainer, TableSkeleton, TBody, Td, TFoot,Th, THead, Tr } from "./Table";
|
||||
TrProps
|
||||
} from "./Table";
|
||||
export { Table, TableContainer, TableSkeleton, TBody, Td, TFoot, Th, THead, Tr } from "./Table";
|
||||
|
@ -13,7 +13,7 @@ export type TabListProps = TabsPrimitive.TabsListProps;
|
||||
|
||||
export const TabList = ({ className, children, ...props }: TabListProps) => (
|
||||
<TabsPrimitive.List
|
||||
className={twMerge("flex-shrink-0 flex border-b-2 border-mineshaft-800", className)}
|
||||
className={twMerge("flex flex-shrink-0 border-b-2 border-mineshaft-800", className)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
@ -25,7 +25,7 @@ export type TabProps = TabsPrimitive.TabsTriggerProps;
|
||||
export const Tab = ({ className, children, ...props }: TabProps) => (
|
||||
<TabsPrimitive.Trigger
|
||||
className={twMerge(
|
||||
"px-3 h-10 font-medium text-sm flex items-center justify-center select-none first:rounded-tl-md last:rounded-tr-md hover:text-mineshaft-200 text-mineshaft-400 transition-all data-[state=active]:text-white data-[state=active]:border-b data-[state=active]:border-primary",
|
||||
"flex h-10 select-none items-center justify-center px-3 text-sm font-medium text-mineshaft-400 transition-all first:rounded-tl-md last:rounded-tr-md hover:text-mineshaft-200 data-[state=active]:border-b data-[state=active]:border-primary data-[state=active]:text-white",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
@ -38,7 +38,7 @@ export type TabPanelProps = TabsPrimitive.TabsContentProps;
|
||||
|
||||
export const TabPanel = ({ className, children, ...props }: TabPanelProps) => (
|
||||
<TabsPrimitive.Content
|
||||
className={twMerge("outline-none flex-grow py-5 rounded-bl-md rounded-br-md", className)}
|
||||
className={twMerge("flex-grow rounded-bl-md rounded-br-md py-5 outline-none", className)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
|
@ -1,2 +1,2 @@
|
||||
export type { TabListProps,TabPanelProps, TabProps, TabsProps } from "./Tabs";
|
||||
export type { TabListProps, TabPanelProps, TabProps, TabsProps } from "./Tabs";
|
||||
export { Tab, TabList, TabPanel, Tabs } from "./Tabs";
|
||||
|
@ -39,7 +39,7 @@ export const AuthProvider = ({ children }: Props): JSX.Element => {
|
||||
// wait for app to load the auth state
|
||||
if (isLoading || !isReady) {
|
||||
return (
|
||||
<div className="flex items-center justify-center w-screen h-screen bg-bunker-800">
|
||||
<div className="flex h-screen w-screen items-center justify-center bg-bunker-800">
|
||||
<img
|
||||
src="/images/loading/loading.gif"
|
||||
height={70}
|
||||
|
@ -1,3 +1,3 @@
|
||||
export { ProjectPermissionProvider, useProjectPermission } from "./ProjectPermissionContext";
|
||||
export type { ProjectPermissionSet, TProjectPermission } from "./types";
|
||||
export { ProjectPermissionActions,ProjectPermissionSub } from "./types";
|
||||
export { ProjectPermissionActions, ProjectPermissionSub } from "./types";
|
||||
|
@ -38,22 +38,31 @@ export const ServerConfigProvider = ({ children }: Props): JSX.Element => {
|
||||
<div className="relative mx-auto flex h-screen w-full flex-col items-center justify-center space-y-8 bg-bunker-800 px-8 text-mineshaft-50 dark:[color-scheme:dark]">
|
||||
<Head>
|
||||
<title>Infisical Maintenance Mode</title>
|
||||
<link rel='icon' href='/infisical.ico' />
|
||||
<link rel="icon" href="/infisical.ico" />
|
||||
</Head>
|
||||
<img src="/images/maintenance.png" height={175} width={300} alt="maintenance mode" className="w-[40rem]"/>
|
||||
<img
|
||||
src="/images/maintenance.png"
|
||||
height={175}
|
||||
width={300}
|
||||
alt="maintenance mode"
|
||||
className="w-[40rem]"
|
||||
/>
|
||||
<p className="mx-8 mb-4 flex justify-center bg-gradient-to-tr from-mineshaft-300 to-white bg-clip-text text-4xl font-bold text-transparent md:mx-16">
|
||||
Scheduled Maintenance
|
||||
</p>
|
||||
<div className="mt-2 text-center text-lg text-bunker-300">
|
||||
Infisical is undergoing planned maintenance. <br /> No action is required on your end — your applications will continue to fetch secrets.
|
||||
<br /> If you have questions, please <a
|
||||
className="text-bunker-300 underline underline-offset-4 decoration-primary-800 hover:decoration-primary-600 hover:text-mineshaft-100 duration-200"
|
||||
Infisical is undergoing planned maintenance. <br /> No action is required on your end —
|
||||
your applications will continue to fetch secrets.
|
||||
<br /> If you have questions, please{" "}
|
||||
<a
|
||||
className="text-bunker-300 underline decoration-primary-800 underline-offset-4 duration-200 hover:text-mineshaft-100 hover:decoration-primary-600"
|
||||
href="https://infisical.com/slack"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
join our Slack community
|
||||
</a>.
|
||||
</a>
|
||||
.
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -1 +1 @@
|
||||
export { ServerConfigProvider,useServerConfig } from "./ServerConfigContext";
|
||||
export { ServerConfigProvider, useServerConfig } from "./ServerConfigContext";
|
||||
|
@ -14,7 +14,7 @@ export {
|
||||
ProjectPermissionSub,
|
||||
useProjectPermission
|
||||
} from "./ProjectPermissionContext";
|
||||
export { ServerConfigProvider,useServerConfig } from "./ServerConfigContext";
|
||||
export { ServerConfigProvider, useServerConfig } from "./ServerConfigContext";
|
||||
export { SubscriptionProvider, useSubscription } from "./SubscriptionContext";
|
||||
export { UserProvider, useUser } from "./UserContext";
|
||||
export { useWorkspace, WorkspaceProvider } from "./WorkspaceContext";
|
||||
|
@ -15,7 +15,7 @@ const updateUserProjectPermission = async ({
|
||||
denials: {
|
||||
ability: string;
|
||||
environmentSlug: string;
|
||||
}[]
|
||||
}[];
|
||||
}) =>
|
||||
SecurityClient.fetchCall(`/api/v1/membership/${membershipId}/deny-permissions`, {
|
||||
method: "POST",
|
||||
|
@ -4,32 +4,30 @@
|
||||
* @returns {String} text - how much time has passed since a certain timestamp
|
||||
*/
|
||||
function timeSince(date: Date) {
|
||||
const seconds = Math.floor(
|
||||
((new Date() as any) - (date as any)) / 1000
|
||||
) as number;
|
||||
const seconds = Math.floor(((new Date() as any) - (date as any)) / 1000) as number;
|
||||
|
||||
let interval = seconds / 31536000;
|
||||
|
||||
if (interval > 1) {
|
||||
return `${Math.floor(interval) } years ago`;
|
||||
return `${Math.floor(interval)} years ago`;
|
||||
}
|
||||
interval = seconds / 2592000;
|
||||
if (interval > 1) {
|
||||
return `${Math.floor(interval) } months ago`;
|
||||
return `${Math.floor(interval)} months ago`;
|
||||
}
|
||||
interval = seconds / 86400;
|
||||
if (interval > 1) {
|
||||
return `${Math.floor(interval) } days ago`;
|
||||
return `${Math.floor(interval)} days ago`;
|
||||
}
|
||||
interval = seconds / 3600;
|
||||
if (interval > 1) {
|
||||
return `${Math.floor(interval) } hours ago`;
|
||||
return `${Math.floor(interval)} hours ago`;
|
||||
}
|
||||
interval = seconds / 60;
|
||||
if (interval > 1) {
|
||||
return `${Math.floor(interval) } minutes ago`;
|
||||
return `${Math.floor(interval)} minutes ago`;
|
||||
}
|
||||
return `${Math.floor(seconds) } seconds ago`;
|
||||
return `${Math.floor(seconds)} seconds ago`;
|
||||
}
|
||||
|
||||
export default timeSince;
|
||||
|
@ -14,73 +14,71 @@ import { deriveArgonKey } from "@app/components/utilities/cryptography/crypto";
|
||||
* @param {String} obj.protectedKeyTag
|
||||
*/
|
||||
const decryptPrivateKeyHelper = async ({
|
||||
encryptionVersion,
|
||||
encryptedPrivateKey,
|
||||
iv,
|
||||
tag,
|
||||
password,
|
||||
salt,
|
||||
protectedKey,
|
||||
protectedKeyIV,
|
||||
protectedKeyTag,
|
||||
encryptionVersion,
|
||||
encryptedPrivateKey,
|
||||
iv,
|
||||
tag,
|
||||
password,
|
||||
salt,
|
||||
protectedKey,
|
||||
protectedKeyIV,
|
||||
protectedKeyTag
|
||||
}: {
|
||||
encryptionVersion: number;
|
||||
encryptedPrivateKey: string;
|
||||
iv: string;
|
||||
tag: string;
|
||||
password: string;
|
||||
salt: string;
|
||||
protectedKey?: string;
|
||||
protectedKeyIV?: string;
|
||||
protectedKeyTag?: string;
|
||||
encryptionVersion: number;
|
||||
encryptedPrivateKey: string;
|
||||
iv: string;
|
||||
tag: string;
|
||||
password: string;
|
||||
salt: string;
|
||||
protectedKey?: string;
|
||||
protectedKeyIV?: string;
|
||||
protectedKeyTag?: string;
|
||||
}) => {
|
||||
let privateKey;
|
||||
try {
|
||||
if (encryptionVersion === 1) {
|
||||
privateKey = Aes256Gcm.decrypt({
|
||||
ciphertext: encryptedPrivateKey,
|
||||
iv,
|
||||
tag,
|
||||
secret: password
|
||||
.slice(0, 32)
|
||||
.padStart(32 + (password.slice(0, 32).length - new Blob([password]).size), "0")
|
||||
});
|
||||
} else if (encryptionVersion === 2 && protectedKey && protectedKeyIV && protectedKeyTag) {
|
||||
const derivedKey = await deriveArgonKey({
|
||||
password,
|
||||
salt,
|
||||
mem: 65536,
|
||||
time: 3,
|
||||
parallelism: 1,
|
||||
hashLen: 32
|
||||
});
|
||||
|
||||
if (!derivedKey) throw new Error("Failed to generate derived key");
|
||||
let privateKey;
|
||||
try {
|
||||
if (encryptionVersion === 1) {
|
||||
privateKey = Aes256Gcm.decrypt({
|
||||
ciphertext: encryptedPrivateKey,
|
||||
iv,
|
||||
tag,
|
||||
secret: password
|
||||
.slice(0, 32)
|
||||
.padStart(32 + (password.slice(0, 32).length - new Blob([password]).size), "0")
|
||||
});
|
||||
} else if (encryptionVersion === 2 && protectedKey && protectedKeyIV && protectedKeyTag) {
|
||||
const derivedKey = await deriveArgonKey({
|
||||
password,
|
||||
salt,
|
||||
mem: 65536,
|
||||
time: 3,
|
||||
parallelism: 1,
|
||||
hashLen: 32
|
||||
});
|
||||
|
||||
const key = Aes256Gcm.decrypt({
|
||||
ciphertext: protectedKey,
|
||||
iv: protectedKeyIV,
|
||||
tag: protectedKeyTag,
|
||||
secret: Buffer.from(derivedKey.hash)
|
||||
});
|
||||
|
||||
// decrypt back the private key
|
||||
privateKey = Aes256Gcm.decrypt({
|
||||
ciphertext: encryptedPrivateKey,
|
||||
iv,
|
||||
tag,
|
||||
secret: Buffer.from(key, "hex")
|
||||
});
|
||||
} else {
|
||||
throw new Error("Insufficient details to decrypt private key");
|
||||
}
|
||||
} catch (err) {
|
||||
throw new Error("Failed to decrypt private key");
|
||||
if (!derivedKey) throw new Error("Failed to generate derived key");
|
||||
|
||||
const key = Aes256Gcm.decrypt({
|
||||
ciphertext: protectedKey,
|
||||
iv: protectedKeyIV,
|
||||
tag: protectedKeyTag,
|
||||
secret: Buffer.from(derivedKey.hash)
|
||||
});
|
||||
|
||||
// decrypt back the private key
|
||||
privateKey = Aes256Gcm.decrypt({
|
||||
ciphertext: encryptedPrivateKey,
|
||||
iv,
|
||||
tag,
|
||||
secret: Buffer.from(key, "hex")
|
||||
});
|
||||
} else {
|
||||
throw new Error("Insufficient details to decrypt private key");
|
||||
}
|
||||
} catch (err) {
|
||||
throw new Error("Failed to decrypt private key");
|
||||
}
|
||||
|
||||
return privateKey;
|
||||
}
|
||||
return privateKey;
|
||||
};
|
||||
|
||||
export {
|
||||
decryptPrivateKeyHelper
|
||||
};
|
||||
export { decryptPrivateKeyHelper };
|
||||
|
@ -29,13 +29,13 @@ export const withPermission = <T extends {}, J extends TOrgPermission>(
|
||||
return (
|
||||
<div
|
||||
className={twMerge(
|
||||
"container h-full mx-auto flex justify-center items-center",
|
||||
"container mx-auto flex h-full items-center justify-center",
|
||||
containerClassName
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className={twMerge(
|
||||
"rounded-md bg-mineshaft-800 text-bunker-300 p-16 flex space-x-12 items-end",
|
||||
"flex items-end space-x-12 rounded-md bg-mineshaft-800 p-16 text-bunker-300",
|
||||
className
|
||||
)}
|
||||
>
|
||||
@ -43,7 +43,7 @@ export const withPermission = <T extends {}, J extends TOrgPermission>(
|
||||
<FontAwesomeIcon icon={faLock} size="6x" />
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-4xl font-medium mb-2">Access Restricted</div>
|
||||
<div className="mb-2 text-4xl font-medium">Access Restricted</div>
|
||||
<div className="text-sm">
|
||||
Your role has limited permissions, please <br /> contact your admin to gain access
|
||||
</div>
|
||||
|
@ -1,4 +1 @@
|
||||
export {
|
||||
useCreateAPIKeyV2,
|
||||
useDeleteAPIKeyV2,
|
||||
useUpdateAPIKeyV2} from "./queries";
|
||||
export { useCreateAPIKeyV2, useDeleteAPIKeyV2, useUpdateAPIKeyV2 } from "./queries";
|
||||
|
@ -8,14 +8,13 @@ import {
|
||||
CreateAPIKeyDataV2DTO,
|
||||
CreateServiceTokenDataV3Res,
|
||||
DeleteAPIKeyDataV2DTO,
|
||||
UpdateAPIKeyDataV2DTO} from "./types";
|
||||
UpdateAPIKeyDataV2DTO
|
||||
} from "./types";
|
||||
|
||||
export const useCreateAPIKeyV2 = () => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation<CreateServiceTokenDataV3Res, {}, CreateAPIKeyDataV2DTO>({
|
||||
mutationFn: async ({
|
||||
name
|
||||
}) => {
|
||||
mutationFn: async ({ name }) => {
|
||||
const { data } = await apiRequest.post("/api/v3/api-key", {
|
||||
name
|
||||
});
|
||||
@ -31,11 +30,10 @@ export const useCreateAPIKeyV2 = () => {
|
||||
export const useUpdateAPIKeyV2 = () => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation<APIKeyDataV2, {}, UpdateAPIKeyDataV2DTO>({
|
||||
mutationFn: async ({
|
||||
apiKeyDataId,
|
||||
name
|
||||
}) => {
|
||||
const { data: { apiKeyData } } = await apiRequest.patch(`/api/v3/api-key/${apiKeyDataId}`, {
|
||||
mutationFn: async ({ apiKeyDataId, name }) => {
|
||||
const {
|
||||
data: { apiKeyData }
|
||||
} = await apiRequest.patch(`/api/v3/api-key/${apiKeyDataId}`, {
|
||||
name
|
||||
});
|
||||
return apiKeyData;
|
||||
@ -49,14 +47,14 @@ export const useUpdateAPIKeyV2 = () => {
|
||||
export const useDeleteAPIKeyV2 = () => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation<APIKeyDataV2, {}, DeleteAPIKeyDataV2DTO>({
|
||||
mutationFn: async ({
|
||||
apiKeyDataId
|
||||
}) => {
|
||||
const { data: { apiKeyData } } = await apiRequest.delete(`/api/v3/api-key/${apiKeyDataId}`);
|
||||
mutationFn: async ({ apiKeyDataId }) => {
|
||||
const {
|
||||
data: { apiKeyData }
|
||||
} = await apiRequest.delete(`/api/v3/api-key/${apiKeyDataId}`);
|
||||
return apiKeyData;
|
||||
},
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries(userKeys.myAPIKeysV2);
|
||||
}
|
||||
});
|
||||
};
|
||||
};
|
||||
|
@ -1,57 +1,57 @@
|
||||
import { EventType, UserAgentType } from "./enums";
|
||||
|
||||
export const eventToNameMap: { [K in EventType]: string } = {
|
||||
[EventType.GET_SECRETS]: "List secrets",
|
||||
[EventType.GET_SECRET]: "Read secret",
|
||||
[EventType.CREATE_SECRET]: "Create secret",
|
||||
[EventType.UPDATE_SECRET]: "Update secret",
|
||||
[EventType.DELETE_SECRET]: "Delete secret",
|
||||
[EventType.GET_WORKSPACE_KEY]: "Read project key",
|
||||
[EventType.AUTHORIZE_INTEGRATION]: "Authorize integration",
|
||||
[EventType.UNAUTHORIZE_INTEGRATION]: "Unauthorize integration",
|
||||
[EventType.CREATE_INTEGRATION]: "Create integration",
|
||||
[EventType.DELETE_INTEGRATION]: "Delete integration",
|
||||
[EventType.ADD_TRUSTED_IP]: "Add trusted IP",
|
||||
[EventType.UPDATE_TRUSTED_IP]: "Update trusted IP",
|
||||
[EventType.DELETE_TRUSTED_IP]: "Delete trusted IP",
|
||||
[EventType.CREATE_SERVICE_TOKEN]: "Create service token",
|
||||
[EventType.DELETE_SERVICE_TOKEN]: "Delete service token",
|
||||
[EventType.CREATE_IDENTITY]: "Create identity",
|
||||
[EventType.UPDATE_IDENTITY]: "Update identity",
|
||||
[EventType.DELETE_IDENTITY]: "Delete identity",
|
||||
[EventType.LOGIN_IDENTITY_UNIVERSAL_AUTH]: "Login via universal auth",
|
||||
[EventType.ADD_IDENTITY_UNIVERSAL_AUTH]: "Add universal auth",
|
||||
[EventType.UPDATE_IDENTITY_UNIVERSAL_AUTH]: "Update universal auth",
|
||||
[EventType.GET_IDENTITY_UNIVERSAL_AUTH]: "Get universal auth",
|
||||
[EventType.CREATE_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRET]: "Create universal auth client secret",
|
||||
[EventType.REVOKE_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRET]: "Revoke universal auth client secret",
|
||||
[EventType.GET_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRETS]: "Get universal auth client secrets",
|
||||
[EventType.GET_IDENTITY_UNIVERSAL_AUTH]: "Get universal auth",
|
||||
[EventType.CREATE_ENVIRONMENT]: "Create environment",
|
||||
[EventType.UPDATE_ENVIRONMENT]: "Update environment",
|
||||
[EventType.DELETE_ENVIRONMENT]: "Delete environment",
|
||||
[EventType.ADD_WORKSPACE_MEMBER]: "Add member",
|
||||
[EventType.REMOVE_WORKSPACE_MEMBER]: "Remove member",
|
||||
[EventType.CREATE_FOLDER]: "Create folder",
|
||||
[EventType.UPDATE_FOLDER]: "Update folder",
|
||||
[EventType.DELETE_FOLDER]: "Delete folder",
|
||||
[EventType.CREATE_WEBHOOK]: "Create webhook",
|
||||
[EventType.UPDATE_WEBHOOK_STATUS]: "Update webhook status",
|
||||
[EventType.DELETE_WEBHOOK]: "Delete webhook",
|
||||
[EventType.GET_SECRET_IMPORTS]: "List secret imports",
|
||||
[EventType.CREATE_SECRET_IMPORT]: "Create secret import",
|
||||
[EventType.UPDATE_SECRET_IMPORT]: "Update secret import",
|
||||
[EventType.DELETE_SECRET_IMPORT]: "Delete secret import",
|
||||
[EventType.UPDATE_USER_WORKSPACE_DENIED_PERMISSIONS]: "Update denied permissions",
|
||||
[EventType.UPDATE_USER_WORKSPACE_ROLE]: "Update user role"
|
||||
[EventType.GET_SECRETS]: "List secrets",
|
||||
[EventType.GET_SECRET]: "Read secret",
|
||||
[EventType.CREATE_SECRET]: "Create secret",
|
||||
[EventType.UPDATE_SECRET]: "Update secret",
|
||||
[EventType.DELETE_SECRET]: "Delete secret",
|
||||
[EventType.GET_WORKSPACE_KEY]: "Read project key",
|
||||
[EventType.AUTHORIZE_INTEGRATION]: "Authorize integration",
|
||||
[EventType.UNAUTHORIZE_INTEGRATION]: "Unauthorize integration",
|
||||
[EventType.CREATE_INTEGRATION]: "Create integration",
|
||||
[EventType.DELETE_INTEGRATION]: "Delete integration",
|
||||
[EventType.ADD_TRUSTED_IP]: "Add trusted IP",
|
||||
[EventType.UPDATE_TRUSTED_IP]: "Update trusted IP",
|
||||
[EventType.DELETE_TRUSTED_IP]: "Delete trusted IP",
|
||||
[EventType.CREATE_SERVICE_TOKEN]: "Create service token",
|
||||
[EventType.DELETE_SERVICE_TOKEN]: "Delete service token",
|
||||
[EventType.CREATE_IDENTITY]: "Create identity",
|
||||
[EventType.UPDATE_IDENTITY]: "Update identity",
|
||||
[EventType.DELETE_IDENTITY]: "Delete identity",
|
||||
[EventType.LOGIN_IDENTITY_UNIVERSAL_AUTH]: "Login via universal auth",
|
||||
[EventType.ADD_IDENTITY_UNIVERSAL_AUTH]: "Add universal auth",
|
||||
[EventType.UPDATE_IDENTITY_UNIVERSAL_AUTH]: "Update universal auth",
|
||||
[EventType.GET_IDENTITY_UNIVERSAL_AUTH]: "Get universal auth",
|
||||
[EventType.CREATE_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRET]: "Create universal auth client secret",
|
||||
[EventType.REVOKE_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRET]: "Revoke universal auth client secret",
|
||||
[EventType.GET_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRETS]: "Get universal auth client secrets",
|
||||
[EventType.GET_IDENTITY_UNIVERSAL_AUTH]: "Get universal auth",
|
||||
[EventType.CREATE_ENVIRONMENT]: "Create environment",
|
||||
[EventType.UPDATE_ENVIRONMENT]: "Update environment",
|
||||
[EventType.DELETE_ENVIRONMENT]: "Delete environment",
|
||||
[EventType.ADD_WORKSPACE_MEMBER]: "Add member",
|
||||
[EventType.REMOVE_WORKSPACE_MEMBER]: "Remove member",
|
||||
[EventType.CREATE_FOLDER]: "Create folder",
|
||||
[EventType.UPDATE_FOLDER]: "Update folder",
|
||||
[EventType.DELETE_FOLDER]: "Delete folder",
|
||||
[EventType.CREATE_WEBHOOK]: "Create webhook",
|
||||
[EventType.UPDATE_WEBHOOK_STATUS]: "Update webhook status",
|
||||
[EventType.DELETE_WEBHOOK]: "Delete webhook",
|
||||
[EventType.GET_SECRET_IMPORTS]: "List secret imports",
|
||||
[EventType.CREATE_SECRET_IMPORT]: "Create secret import",
|
||||
[EventType.UPDATE_SECRET_IMPORT]: "Update secret import",
|
||||
[EventType.DELETE_SECRET_IMPORT]: "Delete secret import",
|
||||
[EventType.UPDATE_USER_WORKSPACE_DENIED_PERMISSIONS]: "Update denied permissions",
|
||||
[EventType.UPDATE_USER_WORKSPACE_ROLE]: "Update user role"
|
||||
};
|
||||
|
||||
export const userAgentTTypeoNameMap: { [K in UserAgentType]: string } = {
|
||||
[UserAgentType.WEB]: "Web",
|
||||
[UserAgentType.CLI]: "CLI",
|
||||
[UserAgentType.K8_OPERATOR]: "K8s operator",
|
||||
[UserAgentType.TERRAFORM]: "Terraform",
|
||||
[UserAgentType.NODE_SDK]: "InfisicalNodeSDK",
|
||||
[UserAgentType.PYTHON_SDK]: "InfisicalPythonSDK",
|
||||
[UserAgentType.OTHER]: "Other",
|
||||
};
|
||||
[UserAgentType.WEB]: "Web",
|
||||
[UserAgentType.CLI]: "CLI",
|
||||
[UserAgentType.K8_OPERATOR]: "K8s operator",
|
||||
[UserAgentType.TERRAFORM]: "Terraform",
|
||||
[UserAgentType.NODE_SDK]: "InfisicalNodeSDK",
|
||||
[UserAgentType.PYTHON_SDK]: "InfisicalPythonSDK",
|
||||
[UserAgentType.OTHER]: "Other"
|
||||
};
|
||||
|
@ -38,7 +38,7 @@ export enum EventType {
|
||||
UPDATE_IDENTITY_UNIVERSAL_AUTH = "update-identity-universal-auth",
|
||||
GET_IDENTITY_UNIVERSAL_AUTH = "get-identity-universal-auth",
|
||||
CREATE_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRET = "create-identity-universal-auth-client-secret",
|
||||
REVOKE_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRET = "revoke-identity-universal-auth-client-secret",
|
||||
REVOKE_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRET = "revoke-identity-universal-auth-client-secret",
|
||||
GET_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRETS = "get-identity-universal-auth-client-secret",
|
||||
CREATE_ENVIRONMENT = "create-environment",
|
||||
UPDATE_ENVIRONMENT = "update-environment",
|
||||
@ -57,4 +57,4 @@ export enum EventType {
|
||||
DELETE_SECRET_IMPORT = "delete-secret-import",
|
||||
UPDATE_USER_WORKSPACE_ROLE = "update-user-workspace-role",
|
||||
UPDATE_USER_WORKSPACE_DENIED_PERMISSIONS = "update-user-workspace-denied-permissions"
|
||||
}
|
||||
}
|
||||
|
@ -1 +1 @@
|
||||
export * from "./queries";
|
||||
export * from "./queries";
|
||||
|
@ -4,12 +4,12 @@ export type GetAuthTokenAPI = {
|
||||
|
||||
export type SendMfaTokenDTO = {
|
||||
email: string;
|
||||
}
|
||||
};
|
||||
|
||||
export type VerifyMfaTokenDTO = {
|
||||
email: string;
|
||||
mfaCode: string;
|
||||
}
|
||||
};
|
||||
|
||||
export type VerifyMfaTokenRes = {
|
||||
encryptionVersion: number;
|
||||
@ -21,24 +21,24 @@ export type VerifyMfaTokenRes = {
|
||||
encryptedPrivateKey: string;
|
||||
iv: string;
|
||||
tag: string;
|
||||
}
|
||||
};
|
||||
|
||||
export type Login1DTO = {
|
||||
email: string;
|
||||
clientPublicKey: string;
|
||||
providerAuthToken?: string;
|
||||
}
|
||||
};
|
||||
|
||||
export type Login2DTO = {
|
||||
email: string;
|
||||
clientProof: string;
|
||||
providerAuthToken?: string;
|
||||
}
|
||||
};
|
||||
|
||||
export type Login1Res = {
|
||||
serverPublicKey: string;
|
||||
salt: string;
|
||||
}
|
||||
};
|
||||
|
||||
export type Login2Res = {
|
||||
mfaEnabled: boolean;
|
||||
@ -51,26 +51,26 @@ export type Login2Res = {
|
||||
encryptedPrivateKey?: string;
|
||||
iv?: string;
|
||||
tag?: string;
|
||||
}
|
||||
};
|
||||
|
||||
export type LoginLDAPDTO = {
|
||||
organizationSlug: string;
|
||||
username: string;
|
||||
password: string;
|
||||
}
|
||||
};
|
||||
|
||||
export type LoginLDAPRes = {
|
||||
nextUrl: string;
|
||||
}
|
||||
};
|
||||
|
||||
export type SRP1DTO = {
|
||||
clientPublicKey: string;
|
||||
}
|
||||
};
|
||||
|
||||
export type SRPR1Res = {
|
||||
serverPublicKey: string;
|
||||
salt: string;
|
||||
}
|
||||
};
|
||||
|
||||
export type CompleteAccountDTO = {
|
||||
email: string;
|
||||
@ -85,19 +85,19 @@ export type CompleteAccountDTO = {
|
||||
encryptedPrivateKeyTag: string;
|
||||
salt: string;
|
||||
verifier: string;
|
||||
}
|
||||
};
|
||||
|
||||
export type CompleteAccountSignupDTO = CompleteAccountDTO & {
|
||||
providerAuthToken?: string;
|
||||
attributionSource?: string;
|
||||
organizationName: string;
|
||||
}
|
||||
};
|
||||
|
||||
export type VerifySignupInviteDTO = {
|
||||
email: string;
|
||||
code: string;
|
||||
organizationId: string;
|
||||
}
|
||||
};
|
||||
|
||||
export type ChangePasswordDTO = {
|
||||
clientProof: string;
|
||||
@ -109,7 +109,7 @@ export type ChangePasswordDTO = {
|
||||
encryptedPrivateKeyTag: string;
|
||||
salt: string;
|
||||
verifier: string;
|
||||
}
|
||||
};
|
||||
|
||||
export type ResetPasswordDTO = {
|
||||
protectedKey: string;
|
||||
@ -121,7 +121,7 @@ export type ResetPasswordDTO = {
|
||||
salt: string;
|
||||
verifier: string;
|
||||
verificationToken: string;
|
||||
}
|
||||
};
|
||||
|
||||
export type IssueBackupPrivateKeyDTO = {
|
||||
encryptedPrivateKey: string;
|
||||
@ -130,8 +130,8 @@ export type IssueBackupPrivateKeyDTO = {
|
||||
salt: string;
|
||||
verifier: string;
|
||||
clientProof: string;
|
||||
}
|
||||
};
|
||||
|
||||
export type GetBackupEncryptedPrivateKeyDTO = {
|
||||
verificationToken: string;
|
||||
}
|
||||
};
|
||||
|
@ -12,7 +12,9 @@ export const useGetWorkspaceBot = (workspaceId: string) =>
|
||||
useQuery({
|
||||
queryKey: queryKeys.getBot(workspaceId),
|
||||
queryFn: async () => {
|
||||
const { data: { bot } } = await apiRequest.get<{ bot: TBot }>(`/api/v1/bot/${workspaceId}`);
|
||||
const {
|
||||
data: { bot }
|
||||
} = await apiRequest.get<{ bot: TBot }>(`/api/v1/bot/${workspaceId}`);
|
||||
return bot;
|
||||
},
|
||||
enabled: Boolean(workspaceId)
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { IdentityAuthMethod } from "./enums";
|
||||
|
||||
export const identityAuthToNameMap: { [I in IdentityAuthMethod]: string } = {
|
||||
[IdentityAuthMethod.UNIVERSAL_AUTH]: "Universal Auth"
|
||||
};
|
||||
[IdentityAuthMethod.UNIVERSAL_AUTH]: "Universal Auth"
|
||||
};
|
||||
|
@ -1,14 +1,12 @@
|
||||
export { identityAuthToNameMap } from "./constants";
|
||||
export { IdentityAuthMethod } from "./enums";
|
||||
export {
|
||||
useAddIdentityUniversalAuth,
|
||||
useCreateIdentity,
|
||||
useCreateIdentityUniversalAuthClientSecret,
|
||||
useDeleteIdentity,
|
||||
useRevokeIdentityUniversalAuthClientSecret,
|
||||
useUpdateIdentity,
|
||||
useUpdateIdentityUniversalAuth} from "./mutations";
|
||||
export {
|
||||
useGetIdentityUniversalAuth,
|
||||
useGetIdentityUniversalAuthClientSecrets
|
||||
} from "./queries";
|
||||
useAddIdentityUniversalAuth,
|
||||
useCreateIdentity,
|
||||
useCreateIdentityUniversalAuthClientSecret,
|
||||
useDeleteIdentity,
|
||||
useRevokeIdentityUniversalAuthClientSecret,
|
||||
useUpdateIdentity,
|
||||
useUpdateIdentityUniversalAuth
|
||||
} from "./mutations";
|
||||
export { useGetIdentityUniversalAuth, useGetIdentityUniversalAuthClientSecrets } from "./queries";
|
||||
|
@ -4,161 +4,168 @@ import { apiRequest } from "@app/config/request";
|
||||
|
||||
import { organizationKeys } from "../organization/queries";
|
||||
import { identitiesKeys } from "./queries";
|
||||
import {
|
||||
AddIdentityUniversalAuthDTO,
|
||||
ClientSecretData,
|
||||
CreateIdentityDTO,
|
||||
CreateIdentityUniversalAuthClientSecretDTO,
|
||||
CreateIdentityUniversalAuthClientSecretRes,
|
||||
DeleteIdentityDTO,
|
||||
DeleteIdentityUniversalAuthClientSecretDTO,
|
||||
Identity,
|
||||
IdentityUniversalAuth,
|
||||
UpdateIdentityDTO,
|
||||
UpdateIdentityUniversalAuthDTO} from "./types";
|
||||
import {
|
||||
AddIdentityUniversalAuthDTO,
|
||||
ClientSecretData,
|
||||
CreateIdentityDTO,
|
||||
CreateIdentityUniversalAuthClientSecretDTO,
|
||||
CreateIdentityUniversalAuthClientSecretRes,
|
||||
DeleteIdentityDTO,
|
||||
DeleteIdentityUniversalAuthClientSecretDTO,
|
||||
Identity,
|
||||
IdentityUniversalAuth,
|
||||
UpdateIdentityDTO,
|
||||
UpdateIdentityUniversalAuthDTO
|
||||
} from "./types";
|
||||
|
||||
export const useCreateIdentity = () => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation<Identity, {}, CreateIdentityDTO>({
|
||||
mutationFn: async (body) => {
|
||||
const { data: { identity } } = await apiRequest.post("/api/v1/identities/", body);
|
||||
return identity;
|
||||
},
|
||||
onSuccess: (_, { organizationId }) => {
|
||||
queryClient.invalidateQueries(organizationKeys.getOrgIdentityMemberships(organizationId));
|
||||
}
|
||||
});
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation<Identity, {}, CreateIdentityDTO>({
|
||||
mutationFn: async (body) => {
|
||||
const {
|
||||
data: { identity }
|
||||
} = await apiRequest.post("/api/v1/identities/", body);
|
||||
return identity;
|
||||
},
|
||||
onSuccess: (_, { organizationId }) => {
|
||||
queryClient.invalidateQueries(organizationKeys.getOrgIdentityMemberships(organizationId));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const useUpdateIdentity = () => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation<Identity, {}, UpdateIdentityDTO>({
|
||||
mutationFn: async ({
|
||||
identityId,
|
||||
name,
|
||||
role
|
||||
}) => {
|
||||
|
||||
const { data: { identity } } = await apiRequest.patch(`/api/v1/identities/${identityId}`, {
|
||||
name,
|
||||
role
|
||||
});
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation<Identity, {}, UpdateIdentityDTO>({
|
||||
mutationFn: async ({ identityId, name, role }) => {
|
||||
const {
|
||||
data: { identity }
|
||||
} = await apiRequest.patch(`/api/v1/identities/${identityId}`, {
|
||||
name,
|
||||
role
|
||||
});
|
||||
|
||||
return identity;
|
||||
},
|
||||
onSuccess: (_, { organizationId }) => {
|
||||
queryClient.invalidateQueries(organizationKeys.getOrgIdentityMemberships(organizationId));
|
||||
}
|
||||
});
|
||||
}
|
||||
return identity;
|
||||
},
|
||||
onSuccess: (_, { organizationId }) => {
|
||||
queryClient.invalidateQueries(organizationKeys.getOrgIdentityMemberships(organizationId));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const useDeleteIdentity = () => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation<Identity, {}, DeleteIdentityDTO>({
|
||||
mutationFn: async ({
|
||||
identityId,
|
||||
}) => {
|
||||
const { data: { identity } } = await apiRequest.delete(`/api/v1/identities/${identityId}`);
|
||||
return identity;
|
||||
},
|
||||
onSuccess: (_, { organizationId }) => {
|
||||
queryClient.invalidateQueries(organizationKeys.getOrgIdentityMemberships(organizationId));
|
||||
}
|
||||
});
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation<Identity, {}, DeleteIdentityDTO>({
|
||||
mutationFn: async ({ identityId }) => {
|
||||
const {
|
||||
data: { identity }
|
||||
} = await apiRequest.delete(`/api/v1/identities/${identityId}`);
|
||||
return identity;
|
||||
},
|
||||
onSuccess: (_, { organizationId }) => {
|
||||
queryClient.invalidateQueries(organizationKeys.getOrgIdentityMemberships(organizationId));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// TODO: move these to /auth
|
||||
|
||||
export const useAddIdentityUniversalAuth = () => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation<IdentityUniversalAuth, {}, AddIdentityUniversalAuthDTO>({
|
||||
mutationFn: async ({
|
||||
identityId,
|
||||
clientSecretTrustedIps,
|
||||
accessTokenTTL,
|
||||
accessTokenMaxTTL,
|
||||
accessTokenNumUsesLimit,
|
||||
accessTokenTrustedIps,
|
||||
}) => {
|
||||
const { data: { identityUniversalAuth } } = await apiRequest.post(`/api/v1/auth/universal-auth/identities/${identityId}`,
|
||||
{
|
||||
clientSecretTrustedIps,
|
||||
accessTokenTTL,
|
||||
accessTokenMaxTTL,
|
||||
accessTokenNumUsesLimit,
|
||||
accessTokenTrustedIps,
|
||||
}
|
||||
);
|
||||
return identityUniversalAuth;
|
||||
},
|
||||
onSuccess: (_, { organizationId }) => {
|
||||
queryClient.invalidateQueries(organizationKeys.getOrgIdentityMemberships(organizationId));
|
||||
}
|
||||
});
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation<IdentityUniversalAuth, {}, AddIdentityUniversalAuthDTO>({
|
||||
mutationFn: async ({
|
||||
identityId,
|
||||
clientSecretTrustedIps,
|
||||
accessTokenTTL,
|
||||
accessTokenMaxTTL,
|
||||
accessTokenNumUsesLimit,
|
||||
accessTokenTrustedIps
|
||||
}) => {
|
||||
const {
|
||||
data: { identityUniversalAuth }
|
||||
} = await apiRequest.post(`/api/v1/auth/universal-auth/identities/${identityId}`, {
|
||||
clientSecretTrustedIps,
|
||||
accessTokenTTL,
|
||||
accessTokenMaxTTL,
|
||||
accessTokenNumUsesLimit,
|
||||
accessTokenTrustedIps
|
||||
});
|
||||
return identityUniversalAuth;
|
||||
},
|
||||
onSuccess: (_, { organizationId }) => {
|
||||
queryClient.invalidateQueries(organizationKeys.getOrgIdentityMemberships(organizationId));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const useUpdateIdentityUniversalAuth = () => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation<IdentityUniversalAuth, {}, UpdateIdentityUniversalAuthDTO>({
|
||||
mutationFn: async ({
|
||||
identityId,
|
||||
clientSecretTrustedIps,
|
||||
accessTokenTTL,
|
||||
accessTokenMaxTTL,
|
||||
accessTokenNumUsesLimit,
|
||||
accessTokenTrustedIps,
|
||||
}) => {
|
||||
const { data: { identityUniversalAuth } } = await apiRequest.patch(`/api/v1/auth/universal-auth/identities/${identityId}`,
|
||||
{
|
||||
clientSecretTrustedIps,
|
||||
accessTokenTTL,
|
||||
accessTokenMaxTTL,
|
||||
accessTokenNumUsesLimit,
|
||||
accessTokenTrustedIps,
|
||||
}
|
||||
);
|
||||
return identityUniversalAuth;
|
||||
},
|
||||
onSuccess: (_, { organizationId }) => {
|
||||
queryClient.invalidateQueries(organizationKeys.getOrgIdentityMemberships(organizationId));
|
||||
}
|
||||
});
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation<IdentityUniversalAuth, {}, UpdateIdentityUniversalAuthDTO>({
|
||||
mutationFn: async ({
|
||||
identityId,
|
||||
clientSecretTrustedIps,
|
||||
accessTokenTTL,
|
||||
accessTokenMaxTTL,
|
||||
accessTokenNumUsesLimit,
|
||||
accessTokenTrustedIps
|
||||
}) => {
|
||||
const {
|
||||
data: { identityUniversalAuth }
|
||||
} = await apiRequest.patch(`/api/v1/auth/universal-auth/identities/${identityId}`, {
|
||||
clientSecretTrustedIps,
|
||||
accessTokenTTL,
|
||||
accessTokenMaxTTL,
|
||||
accessTokenNumUsesLimit,
|
||||
accessTokenTrustedIps
|
||||
});
|
||||
return identityUniversalAuth;
|
||||
},
|
||||
onSuccess: (_, { organizationId }) => {
|
||||
queryClient.invalidateQueries(organizationKeys.getOrgIdentityMemberships(organizationId));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const useCreateIdentityUniversalAuthClientSecret = () => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation<CreateIdentityUniversalAuthClientSecretRes, {}, CreateIdentityUniversalAuthClientSecretDTO>({
|
||||
mutationFn: async ({
|
||||
identityId,
|
||||
description,
|
||||
ttl,
|
||||
numUsesLimit
|
||||
}) => {
|
||||
const { data } = await apiRequest.post(`/api/v1/auth/universal-auth/identities/${identityId}/client-secrets`, {
|
||||
description,
|
||||
ttl,
|
||||
numUsesLimit
|
||||
});
|
||||
return data;
|
||||
},
|
||||
onSuccess: (_, { identityId }) => {
|
||||
queryClient.invalidateQueries(identitiesKeys.getIdentityUniversalAuthClientSecrets(identityId));
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation<
|
||||
CreateIdentityUniversalAuthClientSecretRes,
|
||||
{},
|
||||
CreateIdentityUniversalAuthClientSecretDTO
|
||||
>({
|
||||
mutationFn: async ({ identityId, description, ttl, numUsesLimit }) => {
|
||||
const { data } = await apiRequest.post(
|
||||
`/api/v1/auth/universal-auth/identities/${identityId}/client-secrets`,
|
||||
{
|
||||
description,
|
||||
ttl,
|
||||
numUsesLimit
|
||||
}
|
||||
});
|
||||
);
|
||||
return data;
|
||||
},
|
||||
onSuccess: (_, { identityId }) => {
|
||||
queryClient.invalidateQueries(
|
||||
identitiesKeys.getIdentityUniversalAuthClientSecrets(identityId)
|
||||
);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const useRevokeIdentityUniversalAuthClientSecret = () => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation<ClientSecretData, {}, DeleteIdentityUniversalAuthClientSecretDTO>({
|
||||
mutationFn: async ({
|
||||
identityId,
|
||||
clientSecretId
|
||||
}) => {
|
||||
const { data: { clientSecretData } } = await apiRequest.post<{ clientSecretData: ClientSecretData }>(`/api/v1/auth/universal-auth/identities/${identityId}/client-secrets/${clientSecretId}/revoke`);
|
||||
return clientSecretData;
|
||||
},
|
||||
onSuccess: (_, { identityId }) => {
|
||||
queryClient.invalidateQueries(identitiesKeys.getIdentityUniversalAuthClientSecrets(identityId));
|
||||
}
|
||||
});
|
||||
};
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation<ClientSecretData, {}, DeleteIdentityUniversalAuthClientSecretDTO>({
|
||||
mutationFn: async ({ identityId, clientSecretId }) => {
|
||||
const {
|
||||
data: { clientSecretData }
|
||||
} = await apiRequest.post<{ clientSecretData: ClientSecretData }>(
|
||||
`/api/v1/auth/universal-auth/identities/${identityId}/client-secrets/${clientSecretId}/revoke`
|
||||
);
|
||||
return clientSecretData;
|
||||
},
|
||||
onSuccess: (_, { identityId }) => {
|
||||
queryClient.invalidateQueries(
|
||||
identitiesKeys.getIdentityUniversalAuthClientSecrets(identityId)
|
||||
);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
@ -2,39 +2,45 @@ import { useQuery } from "@tanstack/react-query";
|
||||
|
||||
import { apiRequest } from "@app/config/request";
|
||||
|
||||
import { ClientSecretData , IdentityUniversalAuth } from "./types";
|
||||
import { ClientSecretData, IdentityUniversalAuth } from "./types";
|
||||
|
||||
export const identitiesKeys = {
|
||||
getIdentityUniversalAuth: (identityId: string) => [{ identityId }, "identity-universal-auth"] as const,
|
||||
getIdentityUniversalAuthClientSecrets: (identityId: string) => [{ identityId }, "identity-universal-auth-client-secrets"] as const
|
||||
}
|
||||
getIdentityUniversalAuth: (identityId: string) =>
|
||||
[{ identityId }, "identity-universal-auth"] as const,
|
||||
getIdentityUniversalAuthClientSecrets: (identityId: string) =>
|
||||
[{ identityId }, "identity-universal-auth-client-secrets"] as const
|
||||
};
|
||||
|
||||
export const useGetIdentityUniversalAuth = (identityId: string) => {
|
||||
return useQuery({
|
||||
queryKey: identitiesKeys.getIdentityUniversalAuth(identityId),
|
||||
queryFn: async () => {
|
||||
if (identityId === "") throw new Error("Identity ID is required");
|
||||
return useQuery({
|
||||
queryKey: identitiesKeys.getIdentityUniversalAuth(identityId),
|
||||
queryFn: async () => {
|
||||
if (identityId === "") throw new Error("Identity ID is required");
|
||||
|
||||
const { data: { identityUniversalAuth } } = await apiRequest.get<{ identityUniversalAuth: IdentityUniversalAuth }>(
|
||||
`/api/v1/auth/universal-auth/identities/${identityId}`
|
||||
);
|
||||
|
||||
return identityUniversalAuth;
|
||||
}
|
||||
});
|
||||
}
|
||||
const {
|
||||
data: { identityUniversalAuth }
|
||||
} = await apiRequest.get<{ identityUniversalAuth: IdentityUniversalAuth }>(
|
||||
`/api/v1/auth/universal-auth/identities/${identityId}`
|
||||
);
|
||||
|
||||
return identityUniversalAuth;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const useGetIdentityUniversalAuthClientSecrets = (identityId: string) => {
|
||||
return useQuery({
|
||||
queryKey: identitiesKeys.getIdentityUniversalAuthClientSecrets(identityId),
|
||||
queryFn: async () => {
|
||||
if (identityId === "") return [];
|
||||
return useQuery({
|
||||
queryKey: identitiesKeys.getIdentityUniversalAuthClientSecrets(identityId),
|
||||
queryFn: async () => {
|
||||
if (identityId === "") return [];
|
||||
|
||||
const { data: { clientSecretData } } = await apiRequest.get<{ clientSecretData: ClientSecretData[] }>(
|
||||
`/api/v1/auth/universal-auth/identities/${identityId}/client-secrets`
|
||||
);
|
||||
|
||||
return clientSecretData;
|
||||
}
|
||||
});
|
||||
}
|
||||
const {
|
||||
data: { clientSecretData }
|
||||
} = await apiRequest.get<{ clientSecretData: ClientSecretData[] }>(
|
||||
`/api/v1/auth/universal-auth/identities/${identityId}/client-secrets`
|
||||
);
|
||||
|
||||
return clientSecretData;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
@ -1,4 +1,4 @@
|
||||
export * from "./admin"
|
||||
export * from "./admin";
|
||||
export * from "./apiKeys";
|
||||
export * from "./auditLogs";
|
||||
export * from "./auth";
|
||||
@ -27,4 +27,4 @@ export * from "./tags";
|
||||
export * from "./trustedIps";
|
||||
export * from "./users";
|
||||
export * from "./webhooks";
|
||||
export * from "./workspace";
|
||||
export * from "./workspace";
|
||||
|
@ -15,4 +15,4 @@ export {
|
||||
useGetIntegrationAuthTeams,
|
||||
useGetIntegrationAuthVercelBranches,
|
||||
useSaveIntegrationAccessToken
|
||||
} from "./queries";
|
||||
} from "./queries";
|
||||
|
@ -68,8 +68,8 @@ const integrationAuthKeys = {
|
||||
environmentId: string;
|
||||
scope: "job" | "application" | "container";
|
||||
}) => [{ integrationAuthId, environmentId, scope }, "integrationAuthQoveryScopes"] as const,
|
||||
getIntegrationAuthHerokuPipelines: ({ integrationAuthId }: { integrationAuthId: string; }) =>
|
||||
[{ integrationAuthId}, "integrationAuthHerokuPipelines"] as const,
|
||||
getIntegrationAuthHerokuPipelines: ({ integrationAuthId }: { integrationAuthId: string }) =>
|
||||
[{ integrationAuthId }, "integrationAuthHerokuPipelines"] as const,
|
||||
getIntegrationAuthRailwayEnvironments: ({
|
||||
integrationAuthId,
|
||||
appId
|
||||
@ -322,8 +322,10 @@ const fetchIntegrationAuthQoveryScopes = async ({
|
||||
return undefined;
|
||||
};
|
||||
|
||||
const fetchIntegrationAuthHerokuPipelines = async ({ integrationAuthId }: {
|
||||
integrationAuthId: string;
|
||||
const fetchIntegrationAuthHerokuPipelines = async ({
|
||||
integrationAuthId
|
||||
}: {
|
||||
integrationAuthId: string;
|
||||
}) => {
|
||||
const {
|
||||
data: { pipelines }
|
||||
|
@ -1,5 +1 @@
|
||||
export {
|
||||
useCreateIntegration,
|
||||
useDeleteIntegration,
|
||||
useGetCloudIntegrations
|
||||
} from "./queries";
|
||||
export { useCreateIntegration, useDeleteIntegration, useGetCloudIntegrations } from "./queries";
|
||||
|
@ -63,9 +63,11 @@ export const useCreateIntegration = () => {
|
||||
secretSuffix?: string;
|
||||
initialSyncBehavior?: string;
|
||||
shouldAutoRedeploy?: boolean;
|
||||
}
|
||||
};
|
||||
}) => {
|
||||
const { data: { integration } } = await apiRequest.post("/api/v1/integration", {
|
||||
const {
|
||||
data: { integration }
|
||||
} = await apiRequest.post("/api/v1/integration", {
|
||||
integrationAuthId,
|
||||
isActive,
|
||||
app,
|
||||
@ -101,4 +103,4 @@ export const useDeleteIntegration = () => {
|
||||
queryClient.invalidateQueries(workspaceKeys.getWorkspaceAuthorization(workspaceId));
|
||||
}
|
||||
});
|
||||
};
|
||||
};
|
||||
|
@ -1,5 +1 @@
|
||||
export {
|
||||
useCreateLDAPConfig,
|
||||
useGetLDAPConfig,
|
||||
useUpdateLDAPConfig
|
||||
} from "./queries";
|
||||
export { useCreateLDAPConfig, useGetLDAPConfig, useUpdateLDAPConfig } from "./queries";
|
||||
|
@ -3,27 +3,42 @@ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { apiRequest } from "@app/config/request";
|
||||
|
||||
const ldapConfigKeys = {
|
||||
getLDAPConfig: (orgId: string) => [{ orgId }, "organization-ldap"] as const,
|
||||
}
|
||||
getLDAPConfig: (orgId: string) => [{ orgId }, "organization-ldap"] as const
|
||||
};
|
||||
|
||||
export const useGetLDAPConfig = (organizationId: string) => {
|
||||
return useQuery({
|
||||
queryKey: ldapConfigKeys.getLDAPConfig(organizationId),
|
||||
queryFn: async () => {
|
||||
const { data } = await apiRequest.get(
|
||||
`/api/v1/ldap/config?organizationId=${organizationId}`
|
||||
);
|
||||
const { data } = await apiRequest.get(`/api/v1/ldap/config?organizationId=${organizationId}`);
|
||||
|
||||
return data;
|
||||
},
|
||||
enabled: true
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export const useCreateLDAPConfig = () => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: async ({
|
||||
organizationId,
|
||||
isActive,
|
||||
url,
|
||||
bindDN,
|
||||
bindPass,
|
||||
searchBase,
|
||||
caCert
|
||||
}: {
|
||||
organizationId: string;
|
||||
isActive: boolean;
|
||||
url: string;
|
||||
bindDN: string;
|
||||
bindPass: string;
|
||||
searchBase: string;
|
||||
caCert?: string;
|
||||
}) => {
|
||||
const { data } = await apiRequest.post("/api/v1/ldap/config", {
|
||||
organizationId,
|
||||
isActive,
|
||||
url,
|
||||
@ -31,28 +46,8 @@ export const useCreateLDAPConfig = () => {
|
||||
bindPass,
|
||||
searchBase,
|
||||
caCert
|
||||
}: {
|
||||
organizationId: string;
|
||||
isActive: boolean;
|
||||
url: string;
|
||||
bindDN: string;
|
||||
bindPass: string;
|
||||
searchBase: string;
|
||||
caCert?: string;
|
||||
}) => {
|
||||
const { data } = await apiRequest.post(
|
||||
"/api/v1/ldap/config",
|
||||
{
|
||||
organizationId,
|
||||
isActive,
|
||||
url,
|
||||
bindDN,
|
||||
bindPass,
|
||||
searchBase,
|
||||
caCert
|
||||
}
|
||||
);
|
||||
|
||||
});
|
||||
|
||||
return data;
|
||||
},
|
||||
onSuccess(_, dto) {
|
||||
@ -65,6 +60,23 @@ export const useUpdateLDAPConfig = () => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: async ({
|
||||
organizationId,
|
||||
isActive,
|
||||
url,
|
||||
bindDN,
|
||||
bindPass,
|
||||
searchBase,
|
||||
caCert
|
||||
}: {
|
||||
organizationId: string;
|
||||
isActive?: boolean;
|
||||
url?: string;
|
||||
bindDN?: string;
|
||||
bindPass?: string;
|
||||
searchBase?: string;
|
||||
caCert?: string;
|
||||
}) => {
|
||||
const { data } = await apiRequest.patch("/api/v1/ldap/config", {
|
||||
organizationId,
|
||||
isActive,
|
||||
url,
|
||||
@ -72,32 +84,12 @@ export const useUpdateLDAPConfig = () => {
|
||||
bindPass,
|
||||
searchBase,
|
||||
caCert
|
||||
}: {
|
||||
organizationId: string;
|
||||
isActive?: boolean;
|
||||
url?: string;
|
||||
bindDN?: string;
|
||||
bindPass?: string;
|
||||
searchBase?: string;
|
||||
caCert?: string;
|
||||
}) => {
|
||||
const { data } = await apiRequest.patch(
|
||||
"/api/v1/ldap/config",
|
||||
{
|
||||
organizationId,
|
||||
isActive,
|
||||
url,
|
||||
bindDN,
|
||||
bindPass,
|
||||
searchBase,
|
||||
caCert
|
||||
}
|
||||
);
|
||||
|
||||
});
|
||||
|
||||
return data;
|
||||
},
|
||||
onSuccess(_, dto) {
|
||||
queryClient.invalidateQueries(ldapConfigKeys.getLDAPConfig(dto.organizationId));
|
||||
}
|
||||
});
|
||||
};
|
||||
};
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user