mirror of
https://github.com/Infisical/infisical.git
synced 2025-07-05 04:29:09 +00:00
Compare commits
36 Commits
docker-com
...
infisical/
Author | SHA1 | Date | |
---|---|---|---|
3ebd2fdc6d | |||
8d06a6c969 | |||
43879f6813 | |||
8d77f2d8f3 | |||
79206efcd0 | |||
06d30fc10f | |||
5481b84a94 | |||
ab878e00c9 | |||
6773996d40 | |||
2e20b38bce | |||
2d088a865f | |||
0a8ec6b9da | |||
01b29c3917 | |||
5439ddeadf | |||
9d17d5277b | |||
c70fc7826a | |||
9ed2bb38c3 | |||
f458cf0d40 | |||
ce3dc86f78 | |||
d1927cb9cf | |||
e80426f72e | |||
f8a8ea2118 | |||
f5cd68168b | |||
b74ce14d80 | |||
afdc5e8531 | |||
57daeb71e6 | |||
98b5f713a5 | |||
120d7e42bf | |||
c2bd259c12 | |||
242d770098 | |||
1855fc769d | |||
217fef65e8 | |||
00650df501 | |||
6ff5fb69d4 | |||
9fe2021d9f | |||
fe2f2f972e |
@ -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
1464
backend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -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",
|
||||
|
@ -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: [
|
||||
|
@ -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 };
|
||||
|
@ -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 || "");
|
||||
|
@ -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
|
||||
|
@ -34,4 +34,5 @@ export type TCompleteAccountInviteDTO = {
|
||||
verifier: string;
|
||||
ip: string;
|
||||
userAgent: string;
|
||||
authorization: string;
|
||||
};
|
||||
|
@ -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.
|
||||
}))
|
||||
: []
|
||||
}));
|
||||
};
|
||||
|
@ -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`.
|
||||
|
@ -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>
|
||||
|
@ -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.
|
||||
|
12
frontend/package-lock.json
generated
12
frontend/package-lock.json
generated
@ -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": {
|
||||
|
@ -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",
|
||||
|
@ -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)}
|
||||
|
@ -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>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
Reference in New Issue
Block a user