Compare commits

...

36 Commits

Author SHA1 Message Date
3ebd2fdc6d Merge pull request #1426 from akhilmhdh/fix/identity-auth-identity-ops
feat: made identity crud with identity auth mode and api key for user me
2024-02-20 11:05:26 -05:00
8d06a6c969 feat: made identity crud with identity auth mode and api key for user me 2024-02-20 21:08:34 +05:30
43879f6813 Update scanning-overview.mdx 2024-02-19 17:05:36 -08:00
8d77f2d8f3 Merge pull request #1420 from Infisical/snyk-upgrade-5b2dd8f68969929536cb2ba586aae1b7
[Snyk] Upgrade aws-sdk from 2.1532.0 to 2.1545.0
2024-02-19 13:55:11 -05:00
79206efcd0 fix: upgrade aws-sdk from 2.1532.0 to 2.1545.0
Snyk has created this PR to upgrade aws-sdk from 2.1532.0 to 2.1545.0.

See this package in npm:
https://www.npmjs.com/package/aws-sdk

See this project in Snyk:
https://app.snyk.io/org/maidul98/project/35057e82-ed7d-4e19-ba4d-719a42135cd6?utm_source=github&utm_medium=referral&page=upgrade-pr
2024-02-18 06:17:23 +00:00
06d30fc10f Merge pull request #1417 from Infisical/snyk-upgrade-7fe9aedc3b5777266707225885372828
[Snyk] Upgrade zustand from 4.4.7 to 4.5.0
2024-02-17 16:56:29 -05:00
5481b84a94 patch package json 2024-02-17 16:34:27 +00:00
ab878e00c9 Merge pull request #1418 from akhilmhdh/fix/import-sec-500
fix: resolved secret import secrets failing when folder has empty secrets
2024-02-17 11:11:58 -05:00
6773996d40 fix: resolved secret import secrets failing when folder has empty secrets 2024-02-17 20:45:01 +05:30
2e20b38bce fix: upgrade zustand from 4.4.7 to 4.5.0
Snyk has created this PR to upgrade zustand from 4.4.7 to 4.5.0.

See this package in npm:
https://www.npmjs.com/package/zustand

See this project in Snyk:
https://app.snyk.io/org/maidul98/project/53d4ecb6-6cc1-4918-aa73-bf9cae4ffd13?utm_source=github&utm_medium=referral&page=upgrade-pr
2024-02-17 04:00:15 +00:00
2d088a865f Merge pull request #1412 from ruflair/patch-1
Update docker.mdx
2024-02-16 13:17:28 -05:00
0a8ec6b9da Update docker.mdx
Fix typo in docker.mdx
2024-02-16 10:13:29 -08:00
01b29c3917 Merge pull request #1336 from Infisical/snyk-fix-71fbba54e9eda81c90db390106760c64
[Snyk] Fix for 4 vulnerabilities
2024-02-16 12:15:16 -05:00
5439ddeadf Merge branch 'main' into snyk-fix-71fbba54e9eda81c90db390106760c64 2024-02-16 12:12:48 -05:00
9d17d5277b Merge pull request #1403 from Infisical/daniel/improve-reminders
(Fix): Improve reminders
2024-02-16 12:00:09 -05:00
c70fc7826a Merge pull request #1361 from Infisical/snyk-fix-4a9e6187125617ed814113514828d4f5
[Snyk] Security upgrade nodemailer from 6.9.7 to 6.9.9
2024-02-16 11:59:43 -05:00
9ed2bb38c3 Merge branch 'main' into snyk-fix-4a9e6187125617ed814113514828d4f5 2024-02-16 11:59:31 -05:00
f458cf0d40 Merge pull request #1123 from jinsley8/fix-webhook-settings-ui
fix(frontend): Remove max-width to match other views
2024-02-16 11:40:25 -05:00
ce3dc86f78 add troubleshoot for ansible 2024-02-16 10:37:34 -05:00
d1927cb9cf Merge pull request #1299 from ORCID/docs/ansible-workaround
docs: cover ansible forking error
2024-02-16 10:36:37 -05:00
e80426f72e update signup complete route 2024-02-16 09:15:52 -05:00
f8a8ea2118 update interval text 2024-02-15 14:50:42 -05:00
f5cd68168b Merge pull request #1409 from Infisical/docker-compose-selfhost-docs
Docker compose docs with postgres
2024-02-15 12:58:06 -05:00
b74ce14d80 Update SecretItem.tsx 2024-02-15 18:52:25 +01:00
afdc5e8531 Merge pull request #1406 from abdulhakkeempa/bug-fix-model-close-on-cancel
(Fix): Create API Modal closes on Cancel button
2024-02-15 22:39:21 +05:30
57daeb71e6 Merge remote-tracking branch 'origin/main' into bug-fix-model-close-on-cancel 2024-02-15 10:47:50 +05:30
98b5f713a5 Fix: Cancel button working on Create API in Personal Settings 2024-02-15 10:09:45 +05:30
120d7e42bf Added secret reminder updating, and viewing existing values 2024-02-15 01:14:19 +01:00
c2bd259c12 Added secret reminders to sidebar (and formatting) 2024-02-15 01:14:05 +01:00
242d770098 Improved UX when trying to find reminder value 2024-02-15 01:12:48 +01:00
1855fc769d Update check-api-for-breaking-changes.yml 2024-02-14 15:42:45 -05:00
217fef65e8 update breaking change workflow to dev.yml 2024-02-14 15:37:21 -05:00
00650df501 fix: backend/package.json & backend/package-lock.json to reduce vulnerabilities
The following vulnerabilities are fixed with an upgrade:
- https://snyk.io/vuln/SNYK-JS-NODEMAILER-6219989
2024-02-01 20:01:54 +00:00
6ff5fb69d4 fix: backend/package.json & backend/package-lock.json to reduce vulnerabilities
The following vulnerabilities are fixed with an upgrade:
- https://snyk.io/vuln/SNYK-JS-AXIOS-6124857
- https://snyk.io/vuln/SNYK-JS-AXIOS-6144788
- https://snyk.io/vuln/SNYK-JS-FASTIFYSWAGGERUI-6157561
- https://snyk.io/vuln/SNYK-JS-INFLIGHT-6095116
2024-01-27 15:06:50 +00:00
9fe2021d9f docs: cover ansible forking error 2024-01-10 22:33:38 +00:00
fe2f2f972e fix(frontend): Remove max-width to match other views
This commit removes the max-width constraint on the WebhooksTab.tsx component, aligning it with the full-width layout consistency seen in other views. The previous max-width of 1024px resulted in unused space on larger screens.
2023-10-25 13:54:09 -04:00
19 changed files with 810 additions and 1369 deletions

View File

@ -28,7 +28,7 @@ jobs:
run: docker build --tag infisical-api .
working-directory: backend
- name: Start postgres and redis
run: touch .env && docker-compose -f docker-compose.prod.yml up -d db redis
run: touch .env && docker-compose -f docker-compose.dev.yml up -d db redis
- name: Start the server
run: |
echo "SECRET_SCANNING_GIT_APP_ID=793712" >> .env
@ -70,6 +70,6 @@ jobs:
run: oasdiff breaking https://app.infisical.com/api/docs/json http://localhost:4000/api/docs/json --fail-on ERR
- name: cleanup
run: |
docker-compose -f "docker-compose.pg.yml" down
docker-compose -f "docker-compose.dev.yml" down
docker stop infisical-api
docker remove infisical-api

1464
backend/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -81,7 +81,7 @@
"@fastify/rate-limit": "^9.0.0",
"@fastify/session": "^10.7.0",
"@fastify/swagger": "^8.12.0",
"@fastify/swagger-ui": "^1.10.1",
"@fastify/swagger-ui": "^2.1.0",
"@node-saml/passport-saml": "^4.0.4",
"@octokit/rest": "^20.0.2",
"@octokit/webhooks-types": "^7.3.1",
@ -90,8 +90,8 @@
"@ucast/mongo2js": "^1.3.4",
"ajv": "^8.12.0",
"argon2": "^0.31.2",
"aws-sdk": "^2.1532.0",
"axios": "^1.6.2",
"aws-sdk": "^2.1545.0",
"axios": "^1.6.4",
"axios-retry": "^4.0.0",
"bcrypt": "^5.1.1",
"bullmq": "^5.1.1",
@ -109,7 +109,8 @@
"mysql2": "^3.6.5",
"nanoid": "^5.0.4",
"node-cache": "^5.1.2",
"nodemailer": "^6.9.7",
"nodemailer": "^6.9.9",
"ora": "^7.0.1",
"passport-github": "^1.1.0",
"passport-gitlab2": "^5.0.0",
"passport-google-oauth20": "^2.0.0",
@ -117,7 +118,7 @@
"picomatch": "^3.0.1",
"pino": "^8.16.2",
"posthog-node": "^3.6.0",
"probot": "^12.3.3",
"probot": "^13.0.0",
"smee-client": "^2.0.0",
"tweetnacl": "^1.0.3",
"tweetnacl-util": "^0.15.1",

View File

@ -9,7 +9,7 @@ export const registerIdentityRouter = async (server: FastifyZodProvider) => {
server.route({
method: "POST",
url: "/",
onRequest: verifyAuth([AuthMode.JWT]),
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
description: "Create identity",
security: [
@ -56,7 +56,7 @@ export const registerIdentityRouter = async (server: FastifyZodProvider) => {
server.route({
method: "PATCH",
url: "/:identityId",
onRequest: verifyAuth([AuthMode.JWT]),
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
description: "Update identity",
security: [
@ -105,7 +105,7 @@ export const registerIdentityRouter = async (server: FastifyZodProvider) => {
server.route({
method: "DELETE",
url: "/:identityId",
onRequest: verifyAuth([AuthMode.JWT]),
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
description: "Delete identity",
security: [

View File

@ -197,7 +197,7 @@ export const registerUserRouter = async (server: FastifyZodProvider) => {
})
}
},
onRequest: verifyAuth([AuthMode.JWT]),
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY]),
handler: async (req) => {
const user = await server.services.user.getMe(req.permission.id);
return { user };

View File

@ -156,7 +156,8 @@ export const registerSignupRouter = async (server: FastifyZodProvider) => {
const { user, accessToken, refreshToken } = await server.services.signup.completeAccountInvite({
...req.body,
ip: req.realIp,
userAgent
userAgent,
authorization: req.headers.authorization as string
});
void server.services.telemetry.sendLoopsEvent(user.email, user.firstName || "", user.lastName || "");

View File

@ -212,13 +212,16 @@ export const authSignupServiceFactory = ({
protectedKeyTag,
encryptedPrivateKey,
encryptedPrivateKeyIV,
encryptedPrivateKeyTag
encryptedPrivateKeyTag,
authorization
}: TCompleteAccountInviteDTO) => {
const user = await userDAL.findUserByEmail(email);
if (!user || (user && user.isAccepted)) {
throw new Error("Failed to complete account for complete user");
}
validateSignUpAuthorization(authorization, user.id);
const [orgMembership] = await orgDAL.findMembership({
inviteEmail: email,
status: OrgMembershipStatus.Invited

View File

@ -34,4 +34,5 @@ export type TCompleteAccountInviteDTO = {
verifier: string;
ip: string;
userAgent: string;
authorization: string;
};

View File

@ -41,13 +41,12 @@ export const fnSecretsFromImports = async ({
environment: importEnv.slug,
environmentInfo: importEnv,
folderId: importedFolders?.[i]?.id,
secrets: importedFolders?.[i]?.id
? importedSecsGroupByFolderId[importedFolders?.[i]?.id as string].map((item) => ({
// this will ensure for cases when secrets are empty. Could be due to missing folder for a path or when emtpy secrets inside a given path
secrets: (importedSecsGroupByFolderId?.[importedFolders?.[i]?.id as string] || []).map((item) => ({
...item,
environment: importEnv.slug,
workspace: "", // This field should not be used, it's only here to keep the older Python SDK versions backwards compatible with the new Postgres backend.
_id: item.id // The old Python SDK depends on the _id field being returned. We return this to keep the older Python SDK versions backwards compatible with the new Postgres backend.
}))
: []
}));
};

View File

@ -34,7 +34,7 @@ In addition to scanning for past leaks, this new addition also actively aids in
infisical scan git-changes
# Display the full secret findings
infisical git-changes --verbose
infisical scan git-changes --verbose
```
Scanning for secrets before you commit your changes is great way to prevent leaks. Infisical makes this easy with the sub command `git-changes`.

View File

@ -5,6 +5,19 @@ description: "How to use Infisical for secret management in Ansible"
The documentation for using Infisical to manage secrets in Ansible is currently available [here](https://galaxy.ansible.com/ui/repo/published/infisical/vault/).
<Info>
Have any questions? Join Infisical's [community Slack](https://infisical.com/slack) for quick support.
</Info>
## Troubleshoot
<Accordion title="I'm getting a error related to objc[72832]: +[__NSCFConstantString initialize]">
If you get this Python error when you running the lookup plugin:-
```
objc[72832]: +[__NSCFConstantString initialize] may have been in progress in another thread when fork() was called. We cannot safely call it or ignore it in the fork() child process. Crashing instead. Set a breakpoint on objc_initializeAfterForkError to debug.
Fatal Python error: Aborted
```
You will need to add this to your shell environment or ansible wrapper script:-
```
export OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES
```
</Accordion>

View File

@ -51,7 +51,7 @@ CMD ["infisical", "run", "--", "npm", "run", "start"]
CMD ["infisical", "run", "--command", "npm run start && ..."]
```
## Generate an service token
## Generate a service token
Head to your project settings in the Infisical dashboard to generate an [service token](/documentation/platform/token).
This service token will allow you to authenticate and fetch secrets from Infisical.

View File

@ -1,5 +1,5 @@
{
"name": "frontend",
"name": "npm-proj-1708142396879-0.7491636790514078NCvb8i",
"lockfileVersion": 3,
"requires": true,
"packages": {
@ -94,7 +94,7 @@
"yaml": "^2.2.2",
"yup": "^0.32.11",
"zod": "^3.22.3",
"zustand": "^4.4.1"
"zustand": "^4.5.0"
},
"devDependencies": {
"@storybook/addon-essentials": "^7.5.2",
@ -23536,9 +23536,9 @@
}
},
"node_modules/zustand": {
"version": "4.4.7",
"resolved": "https://registry.npmjs.org/zustand/-/zustand-4.4.7.tgz",
"integrity": "sha512-QFJWJMdlETcI69paJwhSMJz7PPWjVP8Sjhclxmxmxv/RYI7ZOvR5BHX+ktH0we9gTWQMxcne8q1OY8xxz604gw==",
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.0.tgz",
"integrity": "sha512-zlVFqS5TQ21nwijjhJlx4f9iGrXSL0o/+Dpy4txAP22miJ8Ti6c1Ol1RLNN98BMib83lmDH/2KmLwaNXpjrO1A==",
"dependencies": {
"use-sync-external-store": "1.2.0"
},
@ -23547,7 +23547,7 @@
},
"peerDependencies": {
"@types/react": ">=16.8",
"immer": ">=9.0",
"immer": ">=9.0.6",
"react": ">=16.8"
},
"peerDependenciesMeta": {

View File

@ -102,7 +102,7 @@
"yaml": "^2.2.2",
"yup": "^0.32.11",
"zod": "^3.22.3",
"zustand": "^4.4.1"
"zustand": "^4.5.0"
},
"devDependencies": {
"@storybook/addon-essentials": "^7.5.2",

View File

@ -1,6 +1,6 @@
import { useEffect } from "react";
import { Controller, useForm } from "react-hook-form";
import { faClock } from "@fortawesome/free-solid-svg-icons";
import { faClock, faTrash } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { zodResolver } from "@hookform/resolvers/zod";
import { twMerge } from "tailwind-merge";
@ -9,20 +9,28 @@ import { z } from "zod";
import { Button, FormControl, Input, Modal, ModalContent, TextArea } from "@app/components/v2";
const ReminderFormSchema = z.object({
note: z.string().optional(),
note: z.string().optional().nullable(),
days: z
.number()
.min(1, { message: "Must be at least 1 day" })
.max(365, { message: "Must be less than 365 days" })
.nullable()
});
export type TReminderFormSchema = z.infer<typeof ReminderFormSchema>;
interface ReminderFormProps {
isOpen: boolean;
repeatDays?: number | null;
note?: string | null;
onOpenChange: (isOpen: boolean, data?: TReminderFormSchema) => void;
}
export const CreateReminderForm = ({ isOpen, onOpenChange }: ReminderFormProps) => {
export const CreateReminderForm = ({
isOpen,
onOpenChange,
repeatDays,
note
}: ReminderFormProps) => {
const {
register,
control,
@ -31,32 +39,31 @@ export const CreateReminderForm = ({ isOpen, onOpenChange }: ReminderFormProps)
handleSubmit,
formState: { isSubmitting }
} = useForm<TReminderFormSchema>({
defaultValues: {
days: repeatDays || undefined,
note: note || ""
},
resolver: zodResolver(ReminderFormSchema)
});
const handleFormSubmit = async (data: TReminderFormSchema) => {
console.log(data);
onOpenChange(false, data);
};
useEffect(() => {
if (isOpen) {
reset();
reset({
days: repeatDays || undefined,
note: note || ""
});
}
}, [isOpen]);
return (
<Modal isOpen={isOpen} onOpenChange={onOpenChange}>
<ModalContent
title="Create secret reminder"
// ? QUESTION: Should this specifically say its for secret rotation?
// ? Or should we be call it something more generic?
subTitle={
<div>
Set up a reminder for when this secret should be rotated. Everyone with access to this
project will be notified when the reminder is triggered.
</div>
}
title={`${repeatDays ? "Update" : "Create"} reminder`}
subTitle="Set up a reminder for when this secret should be rotated. Everyone with access to this project will be notified when the reminder is triggered."
>
<form onSubmit={handleSubmit(handleFormSubmit)}>
<div className="space-y-2">
@ -68,7 +75,7 @@ export const CreateReminderForm = ({ isOpen, onOpenChange }: ReminderFormProps)
<>
<FormControl
className="mb-0"
label="How many days between"
label="Reminder Interval (in days)"
isError={Boolean(fieldState.error)}
errorText={fieldState.error?.message || ""}
>
@ -76,6 +83,7 @@ export const CreateReminderForm = ({ isOpen, onOpenChange }: ReminderFormProps)
onChange={(el) => setValue("days", parseInt(el.target.value, 10))}
type="number"
placeholder="31"
value={field.value || undefined}
/>
</FormControl>
<div
@ -84,7 +92,8 @@ export const CreateReminderForm = ({ isOpen, onOpenChange }: ReminderFormProps)
field.value ? "opacity-60" : "opacity-0"
)}
>
Every {field.value > 1 ? `${field.value} days` : "day"}
A reminder will be sent every{" "}
{field.value && field.value > 1 ? `${field.value} days` : "day"}
</div>
</>
)}
@ -102,17 +111,27 @@ export const CreateReminderForm = ({ isOpen, onOpenChange }: ReminderFormProps)
/>
</FormControl>
</div>
<div className="mt-7 flex items-center">
<div className="mt-7 flex items-center space-x-4">
<Button
isDisabled={isSubmitting}
isLoading={isSubmitting}
key="layout-create-project-submit"
className="mr-4"
className=""
leftIcon={<FontAwesomeIcon icon={faClock} />}
type="submit"
>
Create reminder
{repeatDays ? "Update" : "Create"} reminder
</Button>
{repeatDays && (
<Button
key="layout-cancel-create-project"
onClick={() => onOpenChange(false, { days: null, note: null })}
colorSchema="danger"
leftIcon={<FontAwesomeIcon icon={faTrash} />}
>
Delete reminder
</Button>
)}
<Button
key="layout-cancel-create-project"
onClick={() => onOpenChange(false)}

View File

@ -5,6 +5,7 @@ import {
faCheckCircle,
faCircle,
faCircleDot,
faClock,
faPlus,
faTag
} from "@fortawesome/free-solid-svg-icons";
@ -33,9 +34,11 @@ import {
Tooltip
} from "@app/components/v2";
import { ProjectPermissionActions, ProjectPermissionSub, useProjectPermission } from "@app/context";
import { useToggle } from "@app/hooks";
import { useGetSecretVersion } from "@app/hooks/api";
import { DecryptedSecret, UserWsKeyPair, WsTag } from "@app/hooks/api/types";
import { CreateReminderForm } from "./CreateReminderForm";
import { formSchema, SecretActionType, TFormSchema } from "./SecretListView.utils";
type Props = {
@ -147,13 +150,34 @@ export const SecretDetailSidebar = ({
await onSaveSecret(secret, { ...secret, ...data }, () => reset());
};
const [createReminderFormOpen, setCreateReminderFormOpen] = useToggle(false);
const secretReminderRepeatDays = watch("reminderRepeatDays");
const secretReminderNote = watch("reminderNote");
return (
<>
<CreateReminderForm
repeatDays={secretReminderRepeatDays}
note={secretReminderNote}
isOpen={createReminderFormOpen}
onOpenChange={(_, data) => {
setCreateReminderFormOpen.toggle();
if (data) {
setValue("reminderRepeatDays", data.days, { shouldDirty: true });
setValue("reminderNote", data.note, { shouldDirty: true });
}
}}
/>
<Drawer
onOpenChange={(state) => {
if (isOpen && isDirty) {
if (
// eslint-disable-next-line no-alert
window.confirm("You have edited the secret. Are you sure you want to reset the change?")
window.confirm(
"You have edited the secret. Are you sure you want to reset the change?"
)
) {
onToggle(false);
reset();
@ -313,6 +337,39 @@ export const SecretDetailSidebar = ({
</DropdownMenu>
</div>
</FormControl>
<FormControl label="Reminder">
{secretReminderRepeatDays && secretReminderRepeatDays > 0 ? (
<div className="mt-2 ml-1 flex items-center justify-between">
<div className="flex items-center space-x-2">
<FontAwesomeIcon className="text-primary-500" icon={faClock} />
<span className="text-sm text-bunker-300">
Reminder every {secretReminderRepeatDays}{" "}
{secretReminderRepeatDays > 1 ? "days" : "day"}
</span>
</div>
<div>
<Button
className="px-2 py-1"
variant="outline_bg"
onClick={() => setCreateReminderFormOpen.on()}
>
Update
</Button>
</div>
</div>
) : (
<div className="mt-2 ml-1 flex items-center space-x-2">
<Button
className="px-2 py-1"
variant="outline_bg"
leftIcon={<FontAwesomeIcon icon={faClock} />}
onClick={() => setCreateReminderFormOpen.on()}
>
Create Reminder
</Button>
</div>
)}
</FormControl>
<FormControl label="Comments & Notes">
<TextArea
className="border border-mineshaft-600 text-sm"
@ -404,5 +461,6 @@ export const SecretDetailSidebar = ({
</form>
</DrawerContent>
</Drawer>
</>
);
};

View File

@ -111,9 +111,11 @@ export const SecretItem = memo(
resolver: zodResolver(formSchema)
});
const secretReminderRepeatDays = watch("reminderRepeatDays");
const secretReminderNote = watch("reminderNote");
const overrideAction = watch("overrideAction");
const hasComment = Boolean(watch("comment"));
const hasReminder = Boolean(watch("reminderRepeatDays"));
const selectedTags = watch("tags", []);
const selectedTagsGroupById = selectedTags.reduce<Record<string, boolean>>(
@ -191,6 +193,8 @@ export const SecretItem = memo(
return (
<>
<CreateReminderForm
repeatDays={secretReminderRepeatDays}
note={secretReminderNote}
isOpen={createReminderFormOpen}
onOpenChange={(_, data) => {
setCreateReminderFormOpen.toggle();
@ -380,22 +384,24 @@ export const SecretItem = memo(
<IconButton
className={twMerge(
"w-0 overflow-hidden p-0 group-hover:mr-2 group-hover:w-5 data-[state=open]:w-6",
hasReminder && "w-5 text-primary"
Boolean(secretReminderRepeatDays) && "w-5 text-primary"
)}
variant="plain"
size="md"
ariaLabel="add-reminder"
>
<Tooltip content="Reminder">
<FontAwesomeIcon
onClick={() => {
if (!hasReminder) {
setCreateReminderFormOpen.on();
} else {
setValue("reminderRepeatDays", null, { shouldDirty: true });
setValue("reminderNote", null, { shouldDirty: true });
<Tooltip
content={
secretReminderRepeatDays && secretReminderRepeatDays > 0
? `Every ${secretReminderRepeatDays} day${
Number(secretReminderRepeatDays) > 1 ? "s" : ""
}
}}
`
: "Reminder"
}
>
<FontAwesomeIcon
onClick={() => setCreateReminderFormOpen.on()}
icon={faClock}
/>
</Tooltip>

View File

@ -170,7 +170,11 @@ export const AddAPIKeyModal = ({
>
Add
</Button>
<Button colorSchema="secondary" variant="plain">
<Button
colorSchema="secondary"
variant="plain"
onClick={() => handlePopUpToggle("addAPIKey", false)}
>
Cancel
</Button>
</div>

View File

@ -139,7 +139,7 @@ export const WebhooksTab = withProjectPermission(
};
return (
<div className="mb-6 max-w-screen-lg rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
<div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
<div className="flex justify-between">
<p className="text-xl font-semibold text-mineshaft-100">{t("settings.webhooks.title")}</p>
<ProjectPermissionCan