Fix merge conflicts

This commit is contained in:
Tuan Dang
2024-04-11 20:46:14 -07:00
10 changed files with 109 additions and 37 deletions
+7 -4
View File
@@ -192,6 +192,12 @@ export const PROJECTS = {
}, },
LIST_GROUPS_IN_PROJECT: { LIST_GROUPS_IN_PROJECT: {
projectSlug: "The slug of the project to list groups for." projectSlug: "The slug of the project to list groups for."
},
LIST_INTEGRATION: {
workspaceId: "The ID of the project to list integrations for."
},
LIST_INTEGRATION_AUTHORIZATION: {
workspaceId: "The ID of the project to list integration auths for."
} }
} as const; } as const;
@@ -553,11 +559,8 @@ export const INTEGRATION_AUTH = {
url: "", url: "",
namespace: "", namespace: "",
refreshToken: "The refresh token for integration authorization." refreshToken: "The refresh token for integration authorization."
},
LIST_AUTHORIZATION: {
workspaceId: "The ID of the project to list integration auths for."
} }
}; } as const;
export const INTEGRATION = { export const INTEGRATION = {
CREATE: { CREATE: {
@@ -7,7 +7,7 @@ import {
UserEncryptionKeysSchema, UserEncryptionKeysSchema,
UsersSchema UsersSchema
} from "@app/db/schemas"; } from "@app/db/schemas";
import { INTEGRATION_AUTH, PROJECTS } from "@app/lib/api-docs"; import { PROJECTS } from "@app/lib/api-docs";
import { readLimit, writeLimit } from "@app/server/config/rateLimiter"; import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type"; import { AuthMode } from "@app/services/auth/auth-type";
@@ -326,8 +326,14 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
rateLimit: readLimit rateLimit: readLimit
}, },
schema: { schema: {
description: "List integrations for a project.",
security: [
{
bearerAuth: []
}
],
params: z.object({ params: z.object({
workspaceId: z.string().trim() workspaceId: z.string().trim().describe(PROJECTS.LIST_INTEGRATION.workspaceId)
}), }),
response: { response: {
200: z.object({ 200: z.object({
@@ -370,7 +376,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
} }
], ],
params: z.object({ params: z.object({
workspaceId: z.string().trim().describe(INTEGRATION_AUTH.LIST_AUTHORIZATION.workspaceId) workspaceId: z.string().trim().describe(PROJECTS.LIST_INTEGRATION_AUTHORIZATION.workspaceId)
}), }),
response: { response: {
200: z.object({ 200: z.object({
+7 -10
View File
@@ -151,13 +151,11 @@ export const projectDALFactory = (db: TDbClient) => {
const findProjectById = async (id: string) => { const findProjectById = async (id: string) => {
try { try {
const workspaces = await db(TableName.ProjectMembership) const workspaces = await db(TableName.Project)
.where(`${TableName.Project}.id`, id) .where(`${TableName.Project}.id`, id)
.join(TableName.Project, `${TableName.ProjectMembership}.projectId`, `${TableName.Project}.id`) .leftJoin(TableName.Environment, `${TableName.Environment}.projectId`, `${TableName.Project}.id`)
.join(TableName.Environment, `${TableName.Environment}.projectId`, `${TableName.Project}.id`)
.select( .select(
selectAllTableCols(TableName.Project), selectAllTableCols(TableName.Project),
db.ref("id").withSchema(TableName.Project).as("_id"),
db.ref("id").withSchema(TableName.Environment).as("envId"), db.ref("id").withSchema(TableName.Environment).as("envId"),
db.ref("slug").withSchema(TableName.Environment).as("envSlug"), db.ref("slug").withSchema(TableName.Environment).as("envSlug"),
db.ref("name").withSchema(TableName.Environment).as("envName") db.ref("name").withSchema(TableName.Environment).as("envName")
@@ -166,10 +164,11 @@ export const projectDALFactory = (db: TDbClient) => {
{ column: `${TableName.Project}.name`, order: "asc" }, { column: `${TableName.Project}.name`, order: "asc" },
{ column: `${TableName.Environment}.position`, order: "asc" } { column: `${TableName.Environment}.position`, order: "asc" }
]); ]);
const project = sqlNestRelationships({ const project = sqlNestRelationships({
data: workspaces, data: workspaces,
key: "id", key: "id",
parentMapper: ({ _id, ...el }) => ({ _id, ...ProjectsSchema.parse(el) }), parentMapper: ({ ...el }) => ({ _id: el.id, ...ProjectsSchema.parse(el) }),
childrenMapper: [ childrenMapper: [
{ {
key: "envId", key: "envId",
@@ -199,14 +198,12 @@ export const projectDALFactory = (db: TDbClient) => {
throw new BadRequestError({ message: "Organization ID is required when querying with slugs" }); throw new BadRequestError({ message: "Organization ID is required when querying with slugs" });
} }
const projects = await db(TableName.ProjectMembership) const projects = await db(TableName.Project)
.where(`${TableName.Project}.slug`, slug) .where(`${TableName.Project}.slug`, slug)
.where(`${TableName.Project}.orgId`, orgId) .where(`${TableName.Project}.orgId`, orgId)
.join(TableName.Project, `${TableName.ProjectMembership}.projectId`, `${TableName.Project}.id`) .leftJoin(TableName.Environment, `${TableName.Environment}.projectId`, `${TableName.Project}.id`)
.join(TableName.Environment, `${TableName.Environment}.projectId`, `${TableName.Project}.id`)
.select( .select(
selectAllTableCols(TableName.Project), selectAllTableCols(TableName.Project),
db.ref("id").withSchema(TableName.Project).as("_id"),
db.ref("id").withSchema(TableName.Environment).as("envId"), db.ref("id").withSchema(TableName.Environment).as("envId"),
db.ref("slug").withSchema(TableName.Environment).as("envSlug"), db.ref("slug").withSchema(TableName.Environment).as("envSlug"),
db.ref("name").withSchema(TableName.Environment).as("envName") db.ref("name").withSchema(TableName.Environment).as("envName")
@@ -219,7 +216,7 @@ export const projectDALFactory = (db: TDbClient) => {
const project = sqlNestRelationships({ const project = sqlNestRelationships({
data: projects, data: projects,
key: "id", key: "id",
parentMapper: ({ _id, ...el }) => ({ _id, ...ProjectsSchema.parse(el) }), parentMapper: ({ ...el }) => ({ _id: el.id, ...ProjectsSchema.parse(el) }),
childrenMapper: [ childrenMapper: [
{ {
key: "envId", key: "envId",
@@ -0,0 +1,4 @@
---
title: "List project integrations "
openapi: "GET /api/v1/workspace/{workspaceId}/integrations"
---
Binary file not shown.

After

Width:  |  Height:  |  Size: 300 KiB

+2 -1
View File
@@ -520,7 +520,8 @@
"api-reference/endpoints/integrations/delete-auth-by-id", "api-reference/endpoints/integrations/delete-auth-by-id",
"api-reference/endpoints/integrations/create", "api-reference/endpoints/integrations/create",
"api-reference/endpoints/integrations/update", "api-reference/endpoints/integrations/update",
"api-reference/endpoints/integrations/delete" "api-reference/endpoints/integrations/delete",
"api-reference/endpoints/integrations/list-project-integrations"
] ]
}, },
{ {
+26 -15
View File
@@ -121,24 +121,35 @@ Without email configuration, Infisical's core functions like sign-up/login and s
</Accordion> </Accordion>
<Accordion title="AWS SES"> <Accordion title="AWS SES">
1. Create an account and [configure AWS SES](https://aws.amazon.com/premiumsupport/knowledge-center/ses-set-up-connect-smtp/) to send emails in the Amazon SES console. <Steps>
2. Create an IAM user for SMTP authentication and obtain SMTP credentials in SMTP settings > Create SMTP credentials <Step title="Create a verifed identity">
This will be used to verify the email you are sending from.
![Create SES identity](../../images/self-hosting/configuration/email/ses-create-identity.png)
<Info>
If you AWS SES is under sandbox mode, you will only be able to send emails to verified identies.
</Info>
</Step>
<Step title="Create an account and configure AWS SES">
Create an IAM user for SMTP authentication and obtain SMTP credentials in SMTP settings > Create SMTP credentials
![opening AWS SES console](../../images/self-hosting/configuration/email/email-aws-ses-console.png) ![opening AWS SES console](../../images/self-hosting/configuration/email/email-aws-ses-console.png)
![creating AWS IAM SES user](../../images/self-hosting/configuration/email/email-aws-ses-user.png) ![creating AWS IAM SES user](../../images/self-hosting/configuration/email/email-aws-ses-user.png)
</Step>
<Step title="Set up your SMTP environment variables">
With your AWS SES SMTP credentials, you can now set up your SMTP environment variables for your Infisical instance.
3. With your AWS SES SMTP credentials, you can now set up your SMTP environment variables: ```
SMTP_HOST=email-smtp.ap-northeast-1.amazonaws.com # SMTP endpoint obtained from SMTP settings
``` SMTP_USERNAME=xxx # your SMTP username
SMTP_HOST=email-smtp.ap-northeast-1.amazonaws.com # SMTP endpoint obtained from SMTP settings SMTP_PASSWORD=xxx # your SMTP password
SMTP_USERNAME=xxx # your SMTP username SMTP_PORT=587
SMTP_PASSWORD=xxx # your SMTP password SMTP_SECURE=false
SMTP_PORT=587 SMTP_[email protected] # your email address being used to send out emails
SMTP_SECURE=true SMTP_FROM_NAME=Infisical
[email protected] # your email address being used to send out emails ```
SMTP_FROM_NAME=Infisical </Step>
``` </Steps>
<Info> <Info>
Remember that you will need to restart Infisical for this to work properly. Remember that you will need to restart Infisical for this to work properly.
@@ -0,0 +1,29 @@
import { useRouter } from "next/router";
import { Button } from "../v2";
interface IProps {
projectId: string;
}
export const NoEnvironmentsBanner = ({ projectId }: IProps) => {
const router = useRouter();
return (
<div className="mt-4 flex w-full flex-row items-center rounded-md border border-primary-600/70 bg-primary/[.07] p-4 text-base text-white">
<div className="flex w-full flex-col text-sm">
<span className="mb-2 text-lg font-semibold">
No environments in your project was found
</span>
<p className="prose">
In order to use integrations, you need to create at least one environment in your project.
</p>
</div>
<div className="my-2">
<Button onClick={() => router.push(`/project/${projectId}/settings#environments`)}>
Add environments
</Button>
</div>
</div>
);
};
@@ -1,10 +1,17 @@
import { useMemo } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { faCheck, faXmark } from "@fortawesome/free-solid-svg-icons"; import { faCheck, faXmark } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { NoEnvironmentsBanner } from "@app/components/integrations/NoEnvironmentsBanner";
import { createNotification } from "@app/components/notifications"; import { createNotification } from "@app/components/notifications";
import { DeleteActionModal, Skeleton, Tooltip } from "@app/components/v2"; import { DeleteActionModal, Skeleton, Tooltip } from "@app/components/v2";
import { ProjectPermissionActions, ProjectPermissionSub, useProjectPermission } from "@app/context"; import {
ProjectPermissionActions,
ProjectPermissionSub,
useProjectPermission,
useWorkspace
} from "@app/context";
import { usePopUp } from "@app/hooks"; import { usePopUp } from "@app/hooks";
import { IntegrationAuth, TCloudIntegration } from "@app/hooks/api/types"; import { IntegrationAuth, TCloudIntegration } from "@app/hooks/api/types";
@@ -31,18 +38,32 @@ export const CloudIntegrationSection = ({
"deleteConfirmation" "deleteConfirmation"
] as const); ] as const);
const { permission } = useProjectPermission(); const { permission } = useProjectPermission();
const { currentWorkspace } = useWorkspace();
const isEmpty = !isLoading && !cloudIntegrations?.length; const isEmpty = !isLoading && !cloudIntegrations?.length;
const sortedCloudIntegrations = cloudIntegrations.sort((a, b) => a.name.localeCompare(b.name)); const sortedCloudIntegrations = useMemo(() => {
const sortedIntegrations = cloudIntegrations.sort((a, b) => a.name.localeCompare(b.name));
if (currentWorkspace?.environments.length === 0) {
return sortedIntegrations.map((integration) => ({ ...integration, isAvailable: false }));
}
return sortedIntegrations;
}, [cloudIntegrations, currentWorkspace?.environments]);
return ( return (
<div> <div>
<div className="px-5">
{currentWorkspace?.environments.length === 0 && (
<NoEnvironmentsBanner projectId={currentWorkspace.id} />
)}
</div>
<div className="m-4 mt-7 flex max-w-5xl flex-col items-start justify-between px-2 text-xl"> <div className="m-4 mt-7 flex max-w-5xl flex-col items-start justify-between px-2 text-xl">
<h1 className="text-3xl font-semibold">{t("integrations.cloud-integrations")}</h1> <h1 className="text-3xl font-semibold">{t("integrations.cloud-integrations")}</h1>
<p className="text-base text-gray-400">{t("integrations.click-to-start")}</p> <p className="text-base text-gray-400">{t("integrations.click-to-start")}</p>
</div> </div>
<div className="mx-6 grid grid-cols-2 gap-4 lg:grid-cols-3 2xl:grid-cols-4"> <div className="mx-6 grid grid-cols-2 gap-4 lg:grid-cols-3 2xl:grid-cols-4">
{isLoading && {isLoading &&
Array.from({ length: 12 }).map((_, index) => ( Array.from({ length: 12 }).map((_, index) => (
@@ -251,7 +251,7 @@ export const InitialStep = ({ setStep, email, setEmail, password, setPassword }:
<div className="mt-6 flex flex-row text-sm text-bunker-400"> <div className="mt-6 flex flex-row text-sm text-bunker-400">
<Link href="/signup"> <Link href="/signup">
<span className="cursor-pointer duration-200 hover:text-bunker-200 hover:underline hover:decoration-primary-700 hover:underline-offset-4"> <span className="cursor-pointer duration-200 hover:text-bunker-200 hover:underline hover:decoration-primary-700 hover:underline-offset-4">
Don&apos;t have an acount yet? {t("login.create-account")} Don&apos;t have an account yet? {t("login.create-account")}
</span> </span>
</Link> </Link>
</div> </div>