mirror of
https://github.com/Infisical/infisical.git
synced 2025-07-15 09:42:14 +00:00
Compare commits
129 Commits
infisical/
...
daniel/cli
Author | SHA1 | Date | |
---|---|---|---|
37169d0e20 | |||
ca39901601 | |||
44cae9d52e | |||
6b81eb5aa6 | |||
be6012d03f | |||
dff0318cc2 | |||
eba7b6a3ce | |||
ff44807605 | |||
bfb4e1ac14 | |||
5c2d61432e | |||
7ea0730621 | |||
d9d5ba055e | |||
8d318881b8 | |||
2b7d1a2e36 | |||
5d3b04be29 | |||
42f10b2bfd | |||
80470e96e5 | |||
8cc5c766e3 | |||
a1fe0e9676 | |||
e1bbe17526 | |||
9475755d40 | |||
30ba304382 | |||
974e6e56b4 | |||
b7bbc513e5 | |||
f6956130bb | |||
01f373798f | |||
fe82231574 | |||
d6eee8a7b3 | |||
88827d5060 | |||
9b0299ff9c | |||
f9e59cf35e | |||
afc92d6ec0 | |||
74479fb950 | |||
06eef21e1c | |||
e687de0a97 | |||
7192abfc4c | |||
c7744ca371 | |||
ed45daf34b | |||
3baeddc426 | |||
1e0995e5fc | |||
9c724ff064 | |||
5950369101 | |||
add15bacd3 | |||
a6aa370349 | |||
a50391889a | |||
fb4b35fa09 | |||
4dafe14d90 | |||
8c1745f73e | |||
c829dcf4a3 | |||
973bfe2407 | |||
e2a3dc4a0a | |||
f4df97b968 | |||
278666ca96 | |||
e2f72de2d8 | |||
a24b8a858c | |||
2b7f7e82c7 | |||
9b0cea23c7 | |||
49f6d6f77b | |||
611704d0d4 | |||
e6143dc21f | |||
28caafa248 | |||
41df9880ce | |||
c3ea73e461 | |||
19841bbf7d | |||
748ffb4008 | |||
75338f7c81 | |||
f1fe4f5b5a | |||
53f83b6883 | |||
67e45c086f | |||
7bf57e6d46 | |||
a7d62848f9 | |||
9a6d0d2048 | |||
5bb7e9163a | |||
41af790073 | |||
3e0d4ce6cf | |||
0d22320d61 | |||
fafdff7de1 | |||
bbe245477b | |||
ea9659ba64 | |||
4246a07c1a | |||
d410b85fe1 | |||
d1adc4cfad | |||
2e4a7c5e7f | |||
bf05366c5f | |||
ba51ede553 | |||
4d1b0790d4 | |||
76dd1d9fca | |||
2c7237411e | |||
78f9981139 | |||
fcd810ee07 | |||
e4084facaa | |||
ebbee48adf | |||
5b09212f27 | |||
17a458cc26 | |||
96f381d61b | |||
df39add05a | |||
da50807919 | |||
8652e09546 | |||
6826f9058e | |||
5d932c021f | |||
c74566cefd | |||
9daf73d71c | |||
2ca3e05f37 | |||
ebc03c20ce | |||
29cc94f756 | |||
69e8b3d242 | |||
5d18abc67f | |||
233e12189a | |||
cbdbc8790e | |||
7975e86161 | |||
d767995b45 | |||
98762e53f1 | |||
acee2314f4 | |||
2f23381a80 | |||
2d5006eeb1 | |||
82083f5df8 | |||
ddc4cf5a74 | |||
ff4b7ba52e | |||
e2bf3546a0 | |||
da710c65db | |||
f3ca2c2ba4 | |||
e3aa23a317 | |||
815a497ac9 | |||
0134ffde5e | |||
48444b4df9 | |||
d0a61ffdba | |||
f6dbce3603 | |||
b09398ac75 | |||
7ef22b2715 |
@ -2,7 +2,6 @@ import { z } from "zod";
|
|||||||
|
|
||||||
import { AuditLogsSchema, SecretSnapshotsSchema } from "@app/db/schemas";
|
import { AuditLogsSchema, SecretSnapshotsSchema } from "@app/db/schemas";
|
||||||
import { EventType, UserAgentType } from "@app/ee/services/audit-log/audit-log-types";
|
import { EventType, UserAgentType } from "@app/ee/services/audit-log/audit-log-types";
|
||||||
import { AUDIT_LOGS, PROJECTS } from "@app/lib/api-docs";
|
|
||||||
import { removeTrailingSlash } from "@app/lib/fn";
|
import { removeTrailingSlash } from "@app/lib/fn";
|
||||||
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";
|
||||||
@ -20,13 +19,13 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
params: z.object({
|
params: z.object({
|
||||||
workspaceId: z.string().trim().describe(PROJECTS.GET_SNAPSHOTS.workspaceId)
|
workspaceId: z.string().trim()
|
||||||
}),
|
}),
|
||||||
querystring: z.object({
|
querystring: z.object({
|
||||||
environment: z.string().trim().describe(PROJECTS.GET_SNAPSHOTS.environment),
|
environment: z.string().trim(),
|
||||||
path: z.string().trim().default("/").transform(removeTrailingSlash).describe(PROJECTS.GET_SNAPSHOTS.path),
|
path: z.string().trim().default("/").transform(removeTrailingSlash),
|
||||||
offset: z.coerce.number().default(0).describe(PROJECTS.GET_SNAPSHOTS.offset),
|
offset: z.coerce.number().default(0),
|
||||||
limit: z.coerce.number().default(20).describe(PROJECTS.GET_SNAPSHOTS.limit)
|
limit: z.coerce.number().default(20)
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
@ -92,16 +91,16 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
params: z.object({
|
params: z.object({
|
||||||
workspaceId: z.string().trim().describe(AUDIT_LOGS.EXPORT.workspaceId)
|
workspaceId: z.string().trim()
|
||||||
}),
|
}),
|
||||||
querystring: z.object({
|
querystring: z.object({
|
||||||
eventType: z.nativeEnum(EventType).optional().describe(AUDIT_LOGS.EXPORT.eventType),
|
eventType: z.nativeEnum(EventType).optional(),
|
||||||
userAgentType: z.nativeEnum(UserAgentType).optional().describe(AUDIT_LOGS.EXPORT.userAgentType),
|
userAgentType: z.nativeEnum(UserAgentType).optional(),
|
||||||
startDate: z.string().datetime().optional().describe(AUDIT_LOGS.EXPORT.startDate),
|
startDate: z.string().datetime().optional(),
|
||||||
endDate: z.string().datetime().optional().describe(AUDIT_LOGS.EXPORT.endDate),
|
endDate: z.string().datetime().optional(),
|
||||||
offset: z.coerce.number().default(0).describe(AUDIT_LOGS.EXPORT.offset),
|
offset: z.coerce.number().default(0),
|
||||||
limit: z.coerce.number().default(20).describe(AUDIT_LOGS.EXPORT.limit),
|
limit: z.coerce.number().default(20),
|
||||||
actor: z.string().optional().describe(AUDIT_LOGS.EXPORT.actor)
|
actor: z.string().optional()
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { SecretSnapshotsSchema, SecretTagsSchema, SecretVersionsSchema } from "@app/db/schemas";
|
import { SecretSnapshotsSchema, SecretTagsSchema, SecretVersionsSchema } from "@app/db/schemas";
|
||||||
import { PROJECTS } from "@app/lib/api-docs";
|
|
||||||
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";
|
||||||
|
|
||||||
@ -67,7 +66,7 @@ export const registerSnapshotRouter = async (server: FastifyZodProvider) => {
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
params: z.object({
|
params: z.object({
|
||||||
secretSnapshotId: z.string().trim().describe(PROJECTS.ROLLBACK_TO_SNAPSHOT.secretSnapshotId)
|
secretSnapshotId: z.string().trim()
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
|
@ -8,7 +8,6 @@ import { ForbiddenError } from "@casl/ability";
|
|||||||
|
|
||||||
import { TKeyStoreFactory } from "@app/keystore/keystore";
|
import { TKeyStoreFactory } from "@app/keystore/keystore";
|
||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
import { verifyOfflineLicense } from "@app/lib/crypto";
|
|
||||||
import { BadRequestError } from "@app/lib/errors";
|
import { BadRequestError } from "@app/lib/errors";
|
||||||
import { logger } from "@app/lib/logger";
|
import { logger } from "@app/lib/logger";
|
||||||
import { TOrgDALFactory } from "@app/services/org/org-dal";
|
import { TOrgDALFactory } from "@app/services/org/org-dal";
|
||||||
@ -27,7 +26,6 @@ import {
|
|||||||
TFeatureSet,
|
TFeatureSet,
|
||||||
TGetOrgBillInfoDTO,
|
TGetOrgBillInfoDTO,
|
||||||
TGetOrgTaxIdDTO,
|
TGetOrgTaxIdDTO,
|
||||||
TOfflineLicenseContents,
|
|
||||||
TOrgInvoiceDTO,
|
TOrgInvoiceDTO,
|
||||||
TOrgLicensesDTO,
|
TOrgLicensesDTO,
|
||||||
TOrgPlanDTO,
|
TOrgPlanDTO,
|
||||||
@ -98,36 +96,6 @@ export const licenseServiceFactory = ({
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (appCfg.LICENSE_KEY_OFFLINE) {
|
|
||||||
let isValidOfflineLicense = true;
|
|
||||||
const contents: TOfflineLicenseContents = JSON.parse(
|
|
||||||
Buffer.from(appCfg.LICENSE_KEY_OFFLINE, "base64").toString("utf8")
|
|
||||||
);
|
|
||||||
const isVerified = await verifyOfflineLicense(JSON.stringify(contents.license), contents.signature);
|
|
||||||
|
|
||||||
if (!isVerified) {
|
|
||||||
isValidOfflineLicense = false;
|
|
||||||
logger.warn(`Infisical EE offline license verification failed`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (contents.license.terminatesAt) {
|
|
||||||
const terminationDate = new Date(contents.license.terminatesAt);
|
|
||||||
if (terminationDate < new Date()) {
|
|
||||||
isValidOfflineLicense = false;
|
|
||||||
logger.warn(`Infisical EE offline license has expired`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isValidOfflineLicense) {
|
|
||||||
onPremFeatures = contents.license.features;
|
|
||||||
instanceType = InstanceType.EnterpriseOnPrem;
|
|
||||||
logger.info(`Instance type: ${InstanceType.EnterpriseOnPrem}`);
|
|
||||||
isValidLicense = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// this means this is self hosted oss version
|
// this means this is self hosted oss version
|
||||||
// else it would reach catch statement
|
// else it would reach catch statement
|
||||||
isValidLicense = true;
|
isValidLicense = true;
|
||||||
|
@ -6,21 +6,6 @@ export enum InstanceType {
|
|||||||
Cloud = "cloud"
|
Cloud = "cloud"
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TOfflineLicenseContents = {
|
|
||||||
license: TOfflineLicense;
|
|
||||||
signature: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type TOfflineLicense = {
|
|
||||||
issuedTo: string;
|
|
||||||
licenseId: string;
|
|
||||||
customerId: string | null;
|
|
||||||
issuedAt: string;
|
|
||||||
expiresAt: string | null;
|
|
||||||
terminatesAt: string | null;
|
|
||||||
features: TFeatureSet;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type TFeatureSet = {
|
export type TFeatureSet = {
|
||||||
_id: null;
|
_id: null;
|
||||||
slug: null;
|
slug: null;
|
||||||
|
@ -1,287 +0,0 @@
|
|||||||
export const IDENTITIES = {
|
|
||||||
CREATE: {
|
|
||||||
name: "The name of the identity to create.",
|
|
||||||
organizationId: "The organization ID to which the identity belongs.",
|
|
||||||
role: "The role of the identity. Possible values are 'no-access', 'member', and 'admin'."
|
|
||||||
},
|
|
||||||
UPDATE: {
|
|
||||||
identityId: "The ID of the identity to update.",
|
|
||||||
name: "The new name of the identity.",
|
|
||||||
role: "The new role of the identity."
|
|
||||||
},
|
|
||||||
DELETE: {
|
|
||||||
identityId: "The ID of the identity to delete."
|
|
||||||
}
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
export const UNIVERSAL_AUTH = {
|
|
||||||
LOGIN: {
|
|
||||||
clientId: "Your Machine Identity Client ID.",
|
|
||||||
clientSecret: "Your Machine Identity Client Secret."
|
|
||||||
},
|
|
||||||
ATTACH: {
|
|
||||||
identityId: "The ID of the identity to attach the configuration onto.",
|
|
||||||
clientSecretTrustedIps:
|
|
||||||
"A list of IPs or CIDR ranges that the Client Secret can be used from together with the Client ID to get back an access token. You can use 0.0.0.0/0, to allow usage from any network address.",
|
|
||||||
accessTokenTrustedIps:
|
|
||||||
"A list of IPs or CIDR ranges that access tokens can be used from. You can use 0.0.0.0/0, to allow usage from any network address.",
|
|
||||||
accessTokenTTL: "The lifetime for an access token in seconds. This value will be referenced at renewal time.",
|
|
||||||
accessTokenMaxTTL:
|
|
||||||
"The maximum lifetime for an access token in seconds. This value will be referenced at renewal time.",
|
|
||||||
accessTokenNumUsesLimit:
|
|
||||||
"The maximum number of times that an access token can be used; a value of 0 implies infinite number of uses."
|
|
||||||
},
|
|
||||||
RETRIEVE: {
|
|
||||||
identityId: "The ID of the identity to retrieve."
|
|
||||||
},
|
|
||||||
UPDATE: {
|
|
||||||
identityId: "The ID of the identity to update.",
|
|
||||||
clientSecretTrustedIps: "The new list of IPs or CIDR ranges that the Client Secret can be used from.",
|
|
||||||
accessTokenTrustedIps: "The new list of IPs or CIDR ranges that access tokens can be used from.",
|
|
||||||
accessTokenTTL: "The new lifetime for an access token in seconds.",
|
|
||||||
accessTokenMaxTTL: "The new maximum lifetime for an access token in seconds.",
|
|
||||||
accessTokenNumUsesLimit: "The new maximum number of times that an access token can be used."
|
|
||||||
},
|
|
||||||
CREATE_CLIENT_SECRET: {
|
|
||||||
identityId: "The ID of the identity to create a client secret for.",
|
|
||||||
description: "The description of the client secret.",
|
|
||||||
numUsesLimit:
|
|
||||||
"The maximum number of times that the client secret can be used; a value of 0 implies infinite number of uses.",
|
|
||||||
ttl: "The lifetime for the client secret in seconds."
|
|
||||||
},
|
|
||||||
LIST_CLIENT_SECRETS: {
|
|
||||||
identityId: "The ID of the identity to list client secrets for."
|
|
||||||
},
|
|
||||||
REVOKE_CLIENT_SECRET: {
|
|
||||||
identityId: "The ID of the identity to revoke the client secret from.",
|
|
||||||
clientSecretId: "The ID of the client secret to revoke."
|
|
||||||
},
|
|
||||||
RENEW_ACCESS_TOKEN: {
|
|
||||||
accessToken: "The access token to renew."
|
|
||||||
}
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
export const ORGANIZATIONS = {
|
|
||||||
LIST_USER_MEMBERSHIPS: {
|
|
||||||
organizationId: "The ID of the organization to get memberships from."
|
|
||||||
},
|
|
||||||
UPDATE_USER_MEMBERSHIP: {
|
|
||||||
organizationId: "The ID of the organization to update the membership for.",
|
|
||||||
membershipId: "The ID of the membership to update.",
|
|
||||||
role: "The new role of the membership."
|
|
||||||
},
|
|
||||||
DELETE_USER_MEMBERSHIP: {
|
|
||||||
organizationId: "The ID of the organization to delete the membership from.",
|
|
||||||
membershipId: "The ID of the membership to delete."
|
|
||||||
},
|
|
||||||
LIST_IDENTITY_MEMBERSHIPS: {
|
|
||||||
orgId: "The ID of the organization to get identity memberships from."
|
|
||||||
},
|
|
||||||
GET_PROJECTS: {
|
|
||||||
organizationId: "The ID of the organization to get projects from."
|
|
||||||
}
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
export const PROJECTS = {
|
|
||||||
CREATE: {
|
|
||||||
organizationSlug: "The slug of the organization to create the project in.",
|
|
||||||
projectName: "The name of the project to create.",
|
|
||||||
slug: "An optional slug for the project."
|
|
||||||
},
|
|
||||||
DELETE: {
|
|
||||||
workspaceId: "The ID of the project to delete."
|
|
||||||
},
|
|
||||||
GET: {
|
|
||||||
workspaceId: "The ID of the project."
|
|
||||||
},
|
|
||||||
UPDATE: {
|
|
||||||
workspaceId: "The ID of the project to update.",
|
|
||||||
name: "The new name of the project.",
|
|
||||||
autoCapitalization: "Disable or enable auto-capitalization for the project."
|
|
||||||
},
|
|
||||||
INVITE_MEMBER: {
|
|
||||||
projectId: "The ID of the project to invite the member to.",
|
|
||||||
emails: "A list of organization member emails to invite to the project.",
|
|
||||||
usernames: "A list of usernames to invite to the project."
|
|
||||||
},
|
|
||||||
REMOVE_MEMBER: {
|
|
||||||
projectId: "The ID of the project to remove the member from.",
|
|
||||||
emails: "A list of organization member emails to remove from the project.",
|
|
||||||
usernames: "A list of usernames to remove from the project."
|
|
||||||
},
|
|
||||||
GET_USER_MEMBERSHIPS: {
|
|
||||||
workspaceId: "The ID of the project to get memberships from."
|
|
||||||
},
|
|
||||||
UPDATE_USER_MEMBERSHIP: {
|
|
||||||
workspaceId: "The ID of the project to update the membership for.",
|
|
||||||
membershipId: "The ID of the membership to update.",
|
|
||||||
roles: "A list of roles to update the membership to."
|
|
||||||
},
|
|
||||||
LIST_IDENTITY_MEMBERSHIPS: {
|
|
||||||
projectId: "The ID of the project to get identity memberships from."
|
|
||||||
},
|
|
||||||
UPDATE_IDENTITY_MEMBERSHIP: {
|
|
||||||
projectId: "The ID of the project to update the identity membership for.",
|
|
||||||
identityId: "The ID of the identity to update the membership for.",
|
|
||||||
roles: "A list of roles to update the membership to."
|
|
||||||
},
|
|
||||||
DELETE_IDENTITY_MEMBERSHIP: {
|
|
||||||
projectId: "The ID of the project to delete the identity membership from.",
|
|
||||||
identityId: "The ID of the identity to delete the membership from."
|
|
||||||
},
|
|
||||||
GET_KEY: {
|
|
||||||
workspaceId: "The ID of the project to get the key from."
|
|
||||||
},
|
|
||||||
GET_SNAPSHOTS: {
|
|
||||||
workspaceId: "The ID of the project to get snapshots from.",
|
|
||||||
environment: "The environment to get snapshots from.",
|
|
||||||
path: "The secret path to get snapshots from.",
|
|
||||||
offset: "The offset to start from. If you enter 10, it will start from the 10th snapshot.",
|
|
||||||
limit: "The number of snapshots to return."
|
|
||||||
},
|
|
||||||
ROLLBACK_TO_SNAPSHOT: {
|
|
||||||
secretSnapshotId: "The ID of the snapshot to rollback to."
|
|
||||||
}
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
export const ENVIRONMENTS = {
|
|
||||||
CREATE: {
|
|
||||||
workspaceId: "The ID of the project to create the environment in.",
|
|
||||||
name: "The name of the environment to create.",
|
|
||||||
slug: "The slug of the environment to create."
|
|
||||||
},
|
|
||||||
UPDATE: {
|
|
||||||
workspaceId: "The ID of the project to update the environment in.",
|
|
||||||
id: "The ID of the environment to update.",
|
|
||||||
name: "The new name of the environment.",
|
|
||||||
slug: "The new slug of the environment.",
|
|
||||||
position: "The new position of the environment. The lowest number will be displayed as the first environment."
|
|
||||||
},
|
|
||||||
DELETE: {
|
|
||||||
workspaceId: "The ID of the project to delete the environment from.",
|
|
||||||
id: "The ID of the environment to delete."
|
|
||||||
}
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
export const FOLDERS = {
|
|
||||||
LIST: {
|
|
||||||
workspaceId: "The ID of the project to list folders from.",
|
|
||||||
environment: "The slug of the environment to list folders from.",
|
|
||||||
path: "The path to list folders from.",
|
|
||||||
directory: "The directory to list folders from. (Deprecated in favor of path)"
|
|
||||||
},
|
|
||||||
CREATE: {
|
|
||||||
workspaceId: "The ID of the project to create the folder in.",
|
|
||||||
environment: "The slug of the environment to create the folder in.",
|
|
||||||
name: "The name of the folder to create.",
|
|
||||||
path: "The path of the folder to create.",
|
|
||||||
directory: "The directory of the folder to create. (Deprecated in favor of path)"
|
|
||||||
},
|
|
||||||
UPDATE: {
|
|
||||||
folderId: "The ID of the folder to update.",
|
|
||||||
environment: "The slug of the environment where the folder is located.",
|
|
||||||
name: "The new name of the folder.",
|
|
||||||
path: "The path of the folder to update.",
|
|
||||||
directory: "The new directory of the folder to update. (Deprecated in favor of path)",
|
|
||||||
workspaceId: "The ID of the project where the folder is located."
|
|
||||||
},
|
|
||||||
DELETE: {
|
|
||||||
folderIdOrName: "The ID or name of the folder to delete.",
|
|
||||||
workspaceId: "The ID of the project to delete the folder from.",
|
|
||||||
environment: "The slug of the environment where the folder is located.",
|
|
||||||
directory: "The directory of the folder to delete. (Deprecated in favor of path)",
|
|
||||||
path: "The path of the folder to delete."
|
|
||||||
}
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
export const RAW_SECRETS = {
|
|
||||||
LIST: {
|
|
||||||
workspaceId: "The ID of the project to list secrets from.",
|
|
||||||
workspaceSlug: "The slug of the project to list secrets from. This parameter is only usable by machine identities.",
|
|
||||||
environment: "The slug of the environment to list secrets from.",
|
|
||||||
secretPath: "The secret path to list secrets from.",
|
|
||||||
includeImports: "Weather to include imported secrets or not."
|
|
||||||
},
|
|
||||||
CREATE: {
|
|
||||||
secretName: "The name of the secret to create.",
|
|
||||||
environment: "The slug of the environment to create the secret in.",
|
|
||||||
secretComment: "Attach a comment to the secret.",
|
|
||||||
secretPath: "The path to create the secret in.",
|
|
||||||
secretValue: "The value of the secret to create.",
|
|
||||||
skipMultilineEncoding: "Skip multiline encoding for the secret value.",
|
|
||||||
type: "The type of the secret to create.",
|
|
||||||
workspaceId: "The ID of the project to create the secret in."
|
|
||||||
},
|
|
||||||
GET: {
|
|
||||||
secretName: "The name of the secret to get.",
|
|
||||||
workspaceId: "The ID of the project to get the secret from.",
|
|
||||||
environment: "The slug of the environment to get the secret from.",
|
|
||||||
secretPath: "The path of the secret to get.",
|
|
||||||
version: "The version of the secret to get.",
|
|
||||||
type: "The type of the secret to get.",
|
|
||||||
includeImports: "Weather to include imported secrets or not."
|
|
||||||
},
|
|
||||||
UPDATE: {
|
|
||||||
secretName: "The name of the secret to update.",
|
|
||||||
environment: "The slug of the environment where the secret is located.",
|
|
||||||
secretPath: "The path of the secret to update",
|
|
||||||
secretValue: "The new value of the secret.",
|
|
||||||
skipMultilineEncoding: "Skip multiline encoding for the secret value.",
|
|
||||||
type: "The type of the secret to update.",
|
|
||||||
workspaceId: "The ID of the project to update the secret in."
|
|
||||||
},
|
|
||||||
DELETE: {
|
|
||||||
secretName: "The name of the secret to delete.",
|
|
||||||
environment: "The slug of the environment where the secret is located.",
|
|
||||||
secretPath: "The path of the secret.",
|
|
||||||
type: "The type of the secret to delete.",
|
|
||||||
workspaceId: "The ID of the project where the secret is located."
|
|
||||||
}
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
export const SECRET_IMPORTS = {
|
|
||||||
LIST: {
|
|
||||||
workspaceId: "The ID of the project to list secret imports from.",
|
|
||||||
environment: "The slug of the environment to list secret imports from.",
|
|
||||||
path: "The path to list secret imports from."
|
|
||||||
},
|
|
||||||
CREATE: {
|
|
||||||
environment: "The slug of the environment to import into.",
|
|
||||||
path: "The path to import into.",
|
|
||||||
workspaceId: "The ID of the project you are working in.",
|
|
||||||
import: {
|
|
||||||
environment: "The slug of the environment to import from.",
|
|
||||||
path: "The path to import from."
|
|
||||||
}
|
|
||||||
},
|
|
||||||
UPDATE: {
|
|
||||||
secretImportId: "The ID of the secret import to update.",
|
|
||||||
environment: "The slug of the environment where the secret import is located.",
|
|
||||||
import: {
|
|
||||||
environment: "The new environment slug to import from.",
|
|
||||||
path: "The new path to import from.",
|
|
||||||
position: "The new position of the secret import. The lowest number will be displayed as the first import."
|
|
||||||
},
|
|
||||||
path: "The path of the secret import to update.",
|
|
||||||
workspaceId: "The ID of the project where the secret import is located."
|
|
||||||
},
|
|
||||||
DELETE: {
|
|
||||||
workspaceId: "The ID of the project to delete the secret import from.",
|
|
||||||
secretImportId: "The ID of the secret import to delete.",
|
|
||||||
environment: "The slug of the environment where the secret import is located.",
|
|
||||||
path: "The path of the secret import to delete."
|
|
||||||
}
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
export const AUDIT_LOGS = {
|
|
||||||
EXPORT: {
|
|
||||||
workspaceId: "The ID of the project to export audit logs from.",
|
|
||||||
eventType: "The type of the event to export.",
|
|
||||||
userAgentType: "Choose which consuming application to export audit logs for.",
|
|
||||||
startDate: "The date to start the export from.",
|
|
||||||
endDate: "The date to end the export at.",
|
|
||||||
offset: "The offset to start from. If you enter 10, it will start from the 10th audit log.",
|
|
||||||
limit: "The number of audit logs to return.",
|
|
||||||
actor: "The actor to filter the audit logs by."
|
|
||||||
}
|
|
||||||
} as const;
|
|
@ -1 +0,0 @@
|
|||||||
export * from "./constants";
|
|
@ -106,7 +106,6 @@ const envSchema = z
|
|||||||
LICENSE_SERVER_URL: zpStr(z.string().optional().default("https://portal.infisical.com")),
|
LICENSE_SERVER_URL: zpStr(z.string().optional().default("https://portal.infisical.com")),
|
||||||
LICENSE_SERVER_KEY: zpStr(z.string().optional()),
|
LICENSE_SERVER_KEY: zpStr(z.string().optional()),
|
||||||
LICENSE_KEY: zpStr(z.string().optional()),
|
LICENSE_KEY: zpStr(z.string().optional()),
|
||||||
LICENSE_KEY_OFFLINE: zpStr(z.string().optional()),
|
|
||||||
|
|
||||||
// GENERIC
|
// GENERIC
|
||||||
STANDALONE_MODE: z
|
STANDALONE_MODE: z
|
||||||
|
@ -17,5 +17,4 @@ export {
|
|||||||
decryptSecrets,
|
decryptSecrets,
|
||||||
decryptSecretVersions
|
decryptSecretVersions
|
||||||
} from "./secret-encryption";
|
} from "./secret-encryption";
|
||||||
export { verifyOfflineLicense } from "./signing";
|
|
||||||
export { generateSrpServerKey, srpCheckClientProof } from "./srp";
|
export { generateSrpServerKey, srpCheckClientProof } from "./srp";
|
||||||
|
@ -1,8 +0,0 @@
|
|||||||
-----BEGIN RSA PUBLIC KEY-----
|
|
||||||
MIIBCgKCAQEApchBY3BXTu4zWGBguB7nM/pjpVLY3V7VGZOAxmR5ueQTJOwiGM13
|
|
||||||
5HN3EM9fDlQnZu9VSc0OFqRM/bUeUaI1oLPE6WzTHjdHyKjDI/S+TLx3VGEsvhM1
|
|
||||||
uukZpYX+3KX2w4wzRHBaBWyglFy0CVNth9UJhhpD+KKfv7dzcRmsbyoUWi9wGfJu
|
|
||||||
wLYCwaCwZRXIt1sLGmMncPz14vfwdnm2a5Tj1Jbt0GTyBl+1/ZqLbO6SsslLg2G+
|
|
||||||
o7FfGS9z8OUTkvDdu16qxL+p2wCEFZMnOz5BB4oakuT2gS9iOO2l5AOPcT4WzPzy
|
|
||||||
PYbX3d7cN9BkOY9I5z0cX4wzqHjQTvGNLQIDAQAB
|
|
||||||
-----END RSA PUBLIC KEY-----
|
|
@ -1,22 +0,0 @@
|
|||||||
import crypto, { KeyObject } from "crypto";
|
|
||||||
import fs from "fs/promises";
|
|
||||||
import path from "path";
|
|
||||||
|
|
||||||
export const verifySignature = (data: string, signature: Buffer, publicKey: KeyObject) => {
|
|
||||||
const verify = crypto.createVerify("SHA256");
|
|
||||||
verify.update(data);
|
|
||||||
verify.end();
|
|
||||||
return verify.verify(publicKey, signature);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const verifyOfflineLicense = async (licenseContents: string, signature: string) => {
|
|
||||||
const publicKeyPem = await fs.readFile(path.join(__dirname, "license_public_key.pem"), "utf8");
|
|
||||||
|
|
||||||
const publicKey = crypto.createPublicKey({
|
|
||||||
key: publicKeyPem,
|
|
||||||
format: "pem",
|
|
||||||
type: "pkcs1"
|
|
||||||
});
|
|
||||||
|
|
||||||
return verifySignature(licenseContents, Buffer.from(signature, "base64"), publicKey);
|
|
||||||
};
|
|
@ -1,7 +1,5 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { UNIVERSAL_AUTH } from "@app/lib/api-docs";
|
|
||||||
|
|
||||||
export const registerIdentityAccessTokenRouter = async (server: FastifyZodProvider) => {
|
export const registerIdentityAccessTokenRouter = async (server: FastifyZodProvider) => {
|
||||||
server.route({
|
server.route({
|
||||||
url: "/token/renew",
|
url: "/token/renew",
|
||||||
@ -9,7 +7,7 @@ export const registerIdentityAccessTokenRouter = async (server: FastifyZodProvid
|
|||||||
schema: {
|
schema: {
|
||||||
description: "Renew access token",
|
description: "Renew access token",
|
||||||
body: z.object({
|
body: z.object({
|
||||||
accessToken: z.string().trim().describe(UNIVERSAL_AUTH.RENEW_ACCESS_TOKEN.accessToken)
|
accessToken: z.string().trim()
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
|
@ -2,7 +2,6 @@ import { z } from "zod";
|
|||||||
|
|
||||||
import { IdentitiesSchema, OrgMembershipRole } from "@app/db/schemas";
|
import { IdentitiesSchema, OrgMembershipRole } from "@app/db/schemas";
|
||||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||||
import { IDENTITIES } from "@app/lib/api-docs";
|
|
||||||
import { getTelemetryDistinctId } from "@app/server/lib/telemetry";
|
import { getTelemetryDistinctId } from "@app/server/lib/telemetry";
|
||||||
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";
|
||||||
@ -21,9 +20,9 @@ export const registerIdentityRouter = async (server: FastifyZodProvider) => {
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
body: z.object({
|
body: z.object({
|
||||||
name: z.string().trim().describe(IDENTITIES.CREATE.name),
|
name: z.string().trim(),
|
||||||
organizationId: z.string().trim().describe(IDENTITIES.CREATE.organizationId),
|
organizationId: z.string().trim(),
|
||||||
role: z.string().trim().min(1).default(OrgMembershipRole.NoAccess).describe(IDENTITIES.CREATE.role)
|
role: z.string().trim().min(1).default(OrgMembershipRole.NoAccess)
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
@ -80,11 +79,11 @@ export const registerIdentityRouter = async (server: FastifyZodProvider) => {
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
params: z.object({
|
params: z.object({
|
||||||
identityId: z.string().describe(IDENTITIES.UPDATE.identityId)
|
identityId: z.string()
|
||||||
}),
|
}),
|
||||||
body: z.object({
|
body: z.object({
|
||||||
name: z.string().trim().optional().describe(IDENTITIES.UPDATE.name),
|
name: z.string().trim().optional(),
|
||||||
role: z.string().trim().min(1).optional().describe(IDENTITIES.UPDATE.role)
|
role: z.string().trim().min(1).optional()
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
@ -130,7 +129,7 @@ export const registerIdentityRouter = async (server: FastifyZodProvider) => {
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
params: z.object({
|
params: z.object({
|
||||||
identityId: z.string().describe(IDENTITIES.DELETE.identityId)
|
identityId: z.string()
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
|
@ -2,7 +2,6 @@ import { z } from "zod";
|
|||||||
|
|
||||||
import { IdentityUaClientSecretsSchema, IdentityUniversalAuthsSchema } from "@app/db/schemas";
|
import { IdentityUaClientSecretsSchema, IdentityUniversalAuthsSchema } from "@app/db/schemas";
|
||||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||||
import { UNIVERSAL_AUTH } from "@app/lib/api-docs";
|
|
||||||
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";
|
||||||
import { TIdentityTrustedIp } from "@app/services/identity/identity-types";
|
import { TIdentityTrustedIp } from "@app/services/identity/identity-types";
|
||||||
@ -27,8 +26,8 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
|
|||||||
schema: {
|
schema: {
|
||||||
description: "Login with Universal Auth",
|
description: "Login with Universal Auth",
|
||||||
body: z.object({
|
body: z.object({
|
||||||
clientId: z.string().trim().describe(UNIVERSAL_AUTH.LOGIN.clientId),
|
clientId: z.string().trim(),
|
||||||
clientSecret: z.string().trim().describe(UNIVERSAL_AUTH.LOGIN.clientSecret)
|
clientSecret: z.string().trim()
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
@ -77,7 +76,7 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
params: z.object({
|
params: z.object({
|
||||||
identityId: z.string().trim().describe(UNIVERSAL_AUTH.ATTACH.identityId)
|
identityId: z.string().trim()
|
||||||
}),
|
}),
|
||||||
body: z.object({
|
body: z.object({
|
||||||
clientSecretTrustedIps: z
|
clientSecretTrustedIps: z
|
||||||
@ -86,16 +85,14 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
|
|||||||
})
|
})
|
||||||
.array()
|
.array()
|
||||||
.min(1)
|
.min(1)
|
||||||
.default([{ ipAddress: "0.0.0.0/0" }, { ipAddress: "::/0" }])
|
.default([{ ipAddress: "0.0.0.0/0" }, { ipAddress: "::/0" }]),
|
||||||
.describe(UNIVERSAL_AUTH.ATTACH.clientSecretTrustedIps),
|
|
||||||
accessTokenTrustedIps: z
|
accessTokenTrustedIps: z
|
||||||
.object({
|
.object({
|
||||||
ipAddress: z.string().trim()
|
ipAddress: z.string().trim()
|
||||||
})
|
})
|
||||||
.array()
|
.array()
|
||||||
.min(1)
|
.min(1)
|
||||||
.default([{ ipAddress: "0.0.0.0/0" }, { ipAddress: "::/0" }])
|
.default([{ ipAddress: "0.0.0.0/0" }, { ipAddress: "::/0" }]),
|
||||||
.describe(UNIVERSAL_AUTH.ATTACH.accessTokenTrustedIps),
|
|
||||||
accessTokenTTL: z
|
accessTokenTTL: z
|
||||||
.number()
|
.number()
|
||||||
.int()
|
.int()
|
||||||
@ -103,22 +100,15 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
|
|||||||
.refine((value) => value !== 0, {
|
.refine((value) => value !== 0, {
|
||||||
message: "accessTokenTTL must have a non zero number"
|
message: "accessTokenTTL must have a non zero number"
|
||||||
})
|
})
|
||||||
.default(2592000)
|
.default(2592000),
|
||||||
.describe(UNIVERSAL_AUTH.ATTACH.accessTokenTTL), // 30 days
|
|
||||||
accessTokenMaxTTL: z
|
accessTokenMaxTTL: z
|
||||||
.number()
|
.number()
|
||||||
.int()
|
.int()
|
||||||
.refine((value) => value !== 0, {
|
.refine((value) => value !== 0, {
|
||||||
message: "accessTokenMaxTTL must have a non zero number"
|
message: "accessTokenMaxTTL must have a non zero number"
|
||||||
})
|
})
|
||||||
.default(2592000)
|
.default(2592000), // 30 days
|
||||||
.describe(UNIVERSAL_AUTH.ATTACH.accessTokenMaxTTL), // 30 days
|
accessTokenNumUsesLimit: z.number().int().min(0).default(0)
|
||||||
accessTokenNumUsesLimit: z
|
|
||||||
.number()
|
|
||||||
.int()
|
|
||||||
.min(0)
|
|
||||||
.default(0)
|
|
||||||
.describe(UNIVERSAL_AUTH.ATTACH.accessTokenNumUsesLimit)
|
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
@ -167,7 +157,7 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
params: z.object({
|
params: z.object({
|
||||||
identityId: z.string().describe(UNIVERSAL_AUTH.UPDATE.identityId)
|
identityId: z.string()
|
||||||
}),
|
}),
|
||||||
body: z.object({
|
body: z.object({
|
||||||
clientSecretTrustedIps: z
|
clientSecretTrustedIps: z
|
||||||
@ -176,23 +166,16 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
|
|||||||
})
|
})
|
||||||
.array()
|
.array()
|
||||||
.min(1)
|
.min(1)
|
||||||
.optional()
|
.optional(),
|
||||||
.describe(UNIVERSAL_AUTH.UPDATE.clientSecretTrustedIps),
|
|
||||||
accessTokenTrustedIps: z
|
accessTokenTrustedIps: z
|
||||||
.object({
|
.object({
|
||||||
ipAddress: z.string().trim()
|
ipAddress: z.string().trim()
|
||||||
})
|
})
|
||||||
.array()
|
.array()
|
||||||
.min(1)
|
.min(1)
|
||||||
.optional()
|
.optional(),
|
||||||
.describe(UNIVERSAL_AUTH.UPDATE.accessTokenTrustedIps),
|
accessTokenTTL: z.number().int().min(0).optional(),
|
||||||
accessTokenTTL: z.number().int().min(0).optional().describe(UNIVERSAL_AUTH.UPDATE.accessTokenTTL),
|
accessTokenNumUsesLimit: z.number().int().min(0).optional(),
|
||||||
accessTokenNumUsesLimit: z
|
|
||||||
.number()
|
|
||||||
.int()
|
|
||||||
.min(0)
|
|
||||||
.optional()
|
|
||||||
.describe(UNIVERSAL_AUTH.UPDATE.accessTokenNumUsesLimit),
|
|
||||||
accessTokenMaxTTL: z
|
accessTokenMaxTTL: z
|
||||||
.number()
|
.number()
|
||||||
.int()
|
.int()
|
||||||
@ -200,7 +183,6 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
|
|||||||
message: "accessTokenMaxTTL must have a non zero number"
|
message: "accessTokenMaxTTL must have a non zero number"
|
||||||
})
|
})
|
||||||
.optional()
|
.optional()
|
||||||
.describe(UNIVERSAL_AUTH.UPDATE.accessTokenMaxTTL)
|
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
@ -250,7 +232,7 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
params: z.object({
|
params: z.object({
|
||||||
identityId: z.string().describe(UNIVERSAL_AUTH.RETRIEVE.identityId)
|
identityId: z.string()
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
@ -294,12 +276,12 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
params: z.object({
|
params: z.object({
|
||||||
identityId: z.string().describe(UNIVERSAL_AUTH.CREATE_CLIENT_SECRET.identityId)
|
identityId: z.string()
|
||||||
}),
|
}),
|
||||||
body: z.object({
|
body: z.object({
|
||||||
description: z.string().trim().default("").describe(UNIVERSAL_AUTH.CREATE_CLIENT_SECRET.description),
|
description: z.string().trim().default(""),
|
||||||
numUsesLimit: z.number().min(0).default(0).describe(UNIVERSAL_AUTH.CREATE_CLIENT_SECRET.numUsesLimit),
|
numUsesLimit: z.number().min(0).default(0),
|
||||||
ttl: z.number().min(0).default(0).describe(UNIVERSAL_AUTH.CREATE_CLIENT_SECRET.ttl)
|
ttl: z.number().min(0).default(0)
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
@ -346,7 +328,7 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
params: z.object({
|
params: z.object({
|
||||||
identityId: z.string().describe(UNIVERSAL_AUTH.LIST_CLIENT_SECRETS.identityId)
|
identityId: z.string()
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
@ -389,8 +371,8 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
params: z.object({
|
params: z.object({
|
||||||
identityId: z.string().describe(UNIVERSAL_AUTH.REVOKE_CLIENT_SECRET.identityId),
|
identityId: z.string(),
|
||||||
clientSecretId: z.string().describe(UNIVERSAL_AUTH.REVOKE_CLIENT_SECRET.clientSecretId)
|
clientSecretId: z.string()
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
|
@ -352,68 +352,6 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
server.route({
|
|
||||||
url: "/:integrationAuthId/github/orgs",
|
|
||||||
method: "GET",
|
|
||||||
onRequest: verifyAuth([AuthMode.JWT]),
|
|
||||||
schema: {
|
|
||||||
params: z.object({
|
|
||||||
integrationAuthId: z.string().trim()
|
|
||||||
}),
|
|
||||||
response: {
|
|
||||||
200: z.object({
|
|
||||||
orgs: z.object({ name: z.string(), orgId: z.string() }).array()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
handler: async (req) => {
|
|
||||||
const orgs = await server.services.integrationAuth.getGithubOrgs({
|
|
||||||
actorId: req.permission.id,
|
|
||||||
actor: req.permission.type,
|
|
||||||
actorOrgId: req.permission.orgId,
|
|
||||||
actorAuthMethod: req.permission.authMethod,
|
|
||||||
id: req.params.integrationAuthId
|
|
||||||
});
|
|
||||||
if (!orgs) throw new Error("No organization found.");
|
|
||||||
|
|
||||||
return { orgs };
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
server.route({
|
|
||||||
url: "/:integrationAuthId/github/envs",
|
|
||||||
method: "GET",
|
|
||||||
onRequest: verifyAuth([AuthMode.JWT]),
|
|
||||||
schema: {
|
|
||||||
params: z.object({
|
|
||||||
integrationAuthId: z.string().trim()
|
|
||||||
}),
|
|
||||||
querystring: z.object({
|
|
||||||
repoOwner: z.string().trim(),
|
|
||||||
repoName: z.string().trim()
|
|
||||||
}),
|
|
||||||
response: {
|
|
||||||
200: z.object({
|
|
||||||
envs: z.object({ name: z.string(), envId: z.string() }).array()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
handler: async (req) => {
|
|
||||||
const envs = await server.services.integrationAuth.getGithubEnvs({
|
|
||||||
actorId: req.permission.id,
|
|
||||||
actor: req.permission.type,
|
|
||||||
actorOrgId: req.permission.orgId,
|
|
||||||
id: req.params.integrationAuthId,
|
|
||||||
actorAuthMethod: req.permission.authMethod,
|
|
||||||
repoName: req.query.repoName,
|
|
||||||
repoOwner: req.query.repoOwner
|
|
||||||
});
|
|
||||||
if (!envs) throw new Error("No organization found.");
|
|
||||||
|
|
||||||
return { envs };
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
url: "/:integrationAuthId/qovery/orgs",
|
url: "/:integrationAuthId/qovery/orgs",
|
||||||
method: "GET",
|
method: "GET",
|
||||||
|
@ -33,7 +33,6 @@ export const registerIntegrationRouter = async (server: FastifyZodProvider) => {
|
|||||||
secretPrefix: z.string().optional(),
|
secretPrefix: z.string().optional(),
|
||||||
secretSuffix: z.string().optional(),
|
secretSuffix: z.string().optional(),
|
||||||
initialSyncBehavior: z.string().optional(),
|
initialSyncBehavior: z.string().optional(),
|
||||||
shouldAutoRedeploy: z.boolean().optional(),
|
|
||||||
secretGCPLabel: z
|
secretGCPLabel: z
|
||||||
.object({
|
.object({
|
||||||
labelName: z.string(),
|
labelName: z.string(),
|
||||||
|
@ -2,7 +2,6 @@ import { z } from "zod";
|
|||||||
|
|
||||||
import { ProjectEnvironmentsSchema } from "@app/db/schemas";
|
import { ProjectEnvironmentsSchema } from "@app/db/schemas";
|
||||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||||
import { ENVIRONMENTS } from "@app/lib/api-docs";
|
|
||||||
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";
|
||||||
|
|
||||||
@ -19,11 +18,11 @@ export const registerProjectEnvRouter = async (server: FastifyZodProvider) => {
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
params: z.object({
|
params: z.object({
|
||||||
workspaceId: z.string().trim().describe(ENVIRONMENTS.CREATE.workspaceId)
|
workspaceId: z.string().trim()
|
||||||
}),
|
}),
|
||||||
body: z.object({
|
body: z.object({
|
||||||
name: z.string().trim().describe(ENVIRONMENTS.CREATE.name),
|
name: z.string().trim(),
|
||||||
slug: z.string().trim().describe(ENVIRONMENTS.CREATE.slug)
|
slug: z.string().trim()
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
@ -75,13 +74,13 @@ export const registerProjectEnvRouter = async (server: FastifyZodProvider) => {
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
params: z.object({
|
params: z.object({
|
||||||
workspaceId: z.string().trim().describe(ENVIRONMENTS.UPDATE.workspaceId),
|
workspaceId: z.string().trim(),
|
||||||
id: z.string().trim().describe(ENVIRONMENTS.UPDATE.id)
|
id: z.string().trim()
|
||||||
}),
|
}),
|
||||||
body: z.object({
|
body: z.object({
|
||||||
slug: z.string().trim().optional().describe(ENVIRONMENTS.UPDATE.slug),
|
slug: z.string().trim().optional(),
|
||||||
name: z.string().trim().optional().describe(ENVIRONMENTS.UPDATE.name),
|
name: z.string().trim().optional(),
|
||||||
position: z.number().optional().describe(ENVIRONMENTS.UPDATE.position)
|
position: z.number().optional()
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
@ -139,8 +138,8 @@ export const registerProjectEnvRouter = async (server: FastifyZodProvider) => {
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
params: z.object({
|
params: z.object({
|
||||||
workspaceId: z.string().trim().describe(ENVIRONMENTS.DELETE.workspaceId),
|
workspaceId: z.string().trim(),
|
||||||
id: z.string().trim().describe(ENVIRONMENTS.DELETE.id)
|
id: z.string().trim()
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
|
@ -9,7 +9,6 @@ import {
|
|||||||
UsersSchema
|
UsersSchema
|
||||||
} from "@app/db/schemas";
|
} from "@app/db/schemas";
|
||||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||||
import { PROJECTS } from "@app/lib/api-docs";
|
|
||||||
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";
|
||||||
import { ProjectUserMembershipTemporaryMode } from "@app/services/project-membership/project-membership-types";
|
import { ProjectUserMembershipTemporaryMode } from "@app/services/project-membership/project-membership-types";
|
||||||
@ -27,7 +26,7 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
params: z.object({
|
params: z.object({
|
||||||
workspaceId: z.string().trim().describe(PROJECTS.GET_USER_MEMBERSHIPS.workspaceId)
|
workspaceId: z.string().trim()
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
@ -137,8 +136,8 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
params: z.object({
|
params: z.object({
|
||||||
workspaceId: z.string().trim().describe(PROJECTS.UPDATE_USER_MEMBERSHIP.workspaceId),
|
workspaceId: z.string().trim(),
|
||||||
membershipId: z.string().trim().describe(PROJECTS.UPDATE_USER_MEMBERSHIP.membershipId)
|
membershipId: z.string().trim()
|
||||||
}),
|
}),
|
||||||
body: z.object({
|
body: z.object({
|
||||||
roles: z
|
roles: z
|
||||||
@ -159,7 +158,6 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider
|
|||||||
)
|
)
|
||||||
.min(1)
|
.min(1)
|
||||||
.refine((data) => data.some(({ isTemporary }) => !isTemporary), "At least long lived role is required")
|
.refine((data) => data.some(({ isTemporary }) => !isTemporary), "At least long lived role is required")
|
||||||
.describe(PROJECTS.UPDATE_USER_MEMBERSHIP.roles)
|
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
|
@ -7,7 +7,6 @@ import {
|
|||||||
UserEncryptionKeysSchema,
|
UserEncryptionKeysSchema,
|
||||||
UsersSchema
|
UsersSchema
|
||||||
} from "@app/db/schemas";
|
} from "@app/db/schemas";
|
||||||
import { PROJECTS } from "@app/lib/api-docs";
|
|
||||||
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";
|
||||||
import { ProjectFilterType } from "@app/services/project/project-types";
|
import { ProjectFilterType } from "@app/services/project/project-types";
|
||||||
@ -129,7 +128,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
|||||||
method: "GET",
|
method: "GET",
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
workspaceId: z.string().trim().describe(PROJECTS.GET.workspaceId)
|
workspaceId: z.string().trim()
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
@ -158,7 +157,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
|||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
workspaceId: z.string().trim().describe(PROJECTS.DELETE.workspaceId)
|
workspaceId: z.string().trim()
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
@ -221,16 +220,11 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
|||||||
method: "PATCH",
|
method: "PATCH",
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
workspaceId: z.string().trim().describe(PROJECTS.UPDATE.workspaceId)
|
workspaceId: z.string().trim()
|
||||||
}),
|
}),
|
||||||
body: z.object({
|
body: z.object({
|
||||||
name: z
|
name: z.string().trim().max(64, { message: "Name must be 64 or fewer characters" }).optional(),
|
||||||
.string()
|
autoCapitalization: z.boolean().optional()
|
||||||
.trim()
|
|
||||||
.max(64, { message: "Name must be 64 or fewer characters" })
|
|
||||||
.optional()
|
|
||||||
.describe(PROJECTS.UPDATE.name),
|
|
||||||
autoCapitalization: z.boolean().optional().describe(PROJECTS.UPDATE.autoCapitalization)
|
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
|
@ -2,7 +2,6 @@ import { z } from "zod";
|
|||||||
|
|
||||||
import { SecretFoldersSchema } from "@app/db/schemas";
|
import { SecretFoldersSchema } from "@app/db/schemas";
|
||||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||||
import { FOLDERS } from "@app/lib/api-docs";
|
|
||||||
import { removeTrailingSlash } from "@app/lib/fn";
|
import { removeTrailingSlash } from "@app/lib/fn";
|
||||||
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";
|
||||||
@ -20,12 +19,12 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
body: z.object({
|
body: z.object({
|
||||||
workspaceId: z.string().trim().describe(FOLDERS.CREATE.workspaceId),
|
workspaceId: z.string().trim(),
|
||||||
environment: z.string().trim().describe(FOLDERS.CREATE.environment),
|
environment: z.string().trim(),
|
||||||
name: z.string().trim().describe(FOLDERS.CREATE.name),
|
name: z.string().trim(),
|
||||||
path: z.string().trim().default("/").transform(removeTrailingSlash).describe(FOLDERS.CREATE.path),
|
path: z.string().trim().default("/").transform(removeTrailingSlash),
|
||||||
// backward compatiability with cli
|
// backward compatiability with cli
|
||||||
directory: z.string().trim().default("/").transform(removeTrailingSlash).describe(FOLDERS.CREATE.directory)
|
directory: z.string().trim().default("/").transform(removeTrailingSlash)
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
@ -75,15 +74,15 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
|
|||||||
],
|
],
|
||||||
params: z.object({
|
params: z.object({
|
||||||
// old way this was name
|
// old way this was name
|
||||||
folderId: z.string().describe(FOLDERS.UPDATE.folderId)
|
folderId: z.string()
|
||||||
}),
|
}),
|
||||||
body: z.object({
|
body: z.object({
|
||||||
workspaceId: z.string().trim().describe(FOLDERS.UPDATE.workspaceId),
|
workspaceId: z.string().trim(),
|
||||||
environment: z.string().trim().describe(FOLDERS.UPDATE.environment),
|
environment: z.string().trim(),
|
||||||
name: z.string().trim().describe(FOLDERS.UPDATE.name),
|
name: z.string().trim(),
|
||||||
path: z.string().trim().default("/").transform(removeTrailingSlash).describe(FOLDERS.UPDATE.path),
|
path: z.string().trim().default("/").transform(removeTrailingSlash),
|
||||||
// backward compatiability with cli
|
// backward compatiability with cli
|
||||||
directory: z.string().trim().default("/").transform(removeTrailingSlash).describe(FOLDERS.UPDATE.directory)
|
directory: z.string().trim().default("/").transform(removeTrailingSlash)
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
@ -122,7 +121,6 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO(daniel): Expose this route in api reference and write docs for it.
|
|
||||||
server.route({
|
server.route({
|
||||||
url: "/:folderIdOrName",
|
url: "/:folderIdOrName",
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
@ -135,14 +133,14 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
params: z.object({
|
params: z.object({
|
||||||
folderIdOrName: z.string().describe(FOLDERS.DELETE.folderIdOrName)
|
folderIdOrName: z.string()
|
||||||
}),
|
}),
|
||||||
body: z.object({
|
body: z.object({
|
||||||
workspaceId: z.string().trim().describe(FOLDERS.DELETE.workspaceId),
|
workspaceId: z.string().trim(),
|
||||||
environment: z.string().trim().describe(FOLDERS.DELETE.environment),
|
environment: z.string().trim(),
|
||||||
path: z.string().trim().default("/").transform(removeTrailingSlash).describe(FOLDERS.DELETE.path),
|
path: z.string().trim().default("/").transform(removeTrailingSlash),
|
||||||
// keep this here as cli need directory
|
// keep this here as cli need directory
|
||||||
directory: z.string().trim().default("/").transform(removeTrailingSlash).describe(FOLDERS.DELETE.directory)
|
directory: z.string().trim().default("/").transform(removeTrailingSlash)
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
@ -192,11 +190,11 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
querystring: z.object({
|
querystring: z.object({
|
||||||
workspaceId: z.string().trim().describe(FOLDERS.LIST.workspaceId),
|
workspaceId: z.string().trim(),
|
||||||
environment: z.string().trim().describe(FOLDERS.LIST.environment),
|
environment: z.string().trim(),
|
||||||
path: z.string().trim().default("/").transform(removeTrailingSlash).describe(FOLDERS.LIST.path),
|
path: z.string().trim().default("/").transform(removeTrailingSlash),
|
||||||
// backward compatiability with cli
|
// backward compatiability with cli
|
||||||
directory: z.string().trim().default("/").transform(removeTrailingSlash).describe(FOLDERS.LIST.directory)
|
directory: z.string().trim().default("/").transform(removeTrailingSlash)
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
|
@ -2,7 +2,6 @@ import { z } from "zod";
|
|||||||
|
|
||||||
import { SecretImportsSchema, SecretsSchema } from "@app/db/schemas";
|
import { SecretImportsSchema, SecretsSchema } from "@app/db/schemas";
|
||||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||||
import { SECRET_IMPORTS } from "@app/lib/api-docs";
|
|
||||||
import { removeTrailingSlash } from "@app/lib/fn";
|
import { removeTrailingSlash } from "@app/lib/fn";
|
||||||
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";
|
||||||
@ -20,12 +19,12 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) =>
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
body: z.object({
|
body: z.object({
|
||||||
workspaceId: z.string().trim().describe(SECRET_IMPORTS.CREATE.workspaceId),
|
workspaceId: z.string().trim(),
|
||||||
environment: z.string().trim().describe(SECRET_IMPORTS.CREATE.environment),
|
environment: z.string().trim(),
|
||||||
path: z.string().trim().default("/").transform(removeTrailingSlash).describe(SECRET_IMPORTS.CREATE.path),
|
path: z.string().trim().default("/").transform(removeTrailingSlash),
|
||||||
import: z.object({
|
import: z.object({
|
||||||
environment: z.string().trim().describe(SECRET_IMPORTS.CREATE.import.environment),
|
environment: z.string().trim(),
|
||||||
path: z.string().trim().transform(removeTrailingSlash).describe(SECRET_IMPORTS.CREATE.import.path)
|
path: z.string().trim().transform(removeTrailingSlash)
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
@ -82,21 +81,20 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) =>
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
params: z.object({
|
params: z.object({
|
||||||
secretImportId: z.string().trim().describe(SECRET_IMPORTS.UPDATE.secretImportId)
|
secretImportId: z.string().trim()
|
||||||
}),
|
}),
|
||||||
body: z.object({
|
body: z.object({
|
||||||
workspaceId: z.string().trim().describe(SECRET_IMPORTS.UPDATE.workspaceId),
|
workspaceId: z.string().trim(),
|
||||||
environment: z.string().trim().describe(SECRET_IMPORTS.UPDATE.environment),
|
environment: z.string().trim(),
|
||||||
path: z.string().trim().default("/").transform(removeTrailingSlash).describe(SECRET_IMPORTS.UPDATE.path),
|
path: z.string().trim().default("/").transform(removeTrailingSlash),
|
||||||
import: z.object({
|
import: z.object({
|
||||||
environment: z.string().trim().optional().describe(SECRET_IMPORTS.UPDATE.import.environment),
|
environment: z.string().trim().optional(),
|
||||||
path: z
|
path: z
|
||||||
.string()
|
.string()
|
||||||
.trim()
|
.trim()
|
||||||
.optional()
|
.optional()
|
||||||
.transform((val) => (val ? removeTrailingSlash(val) : val))
|
.transform((val) => (val ? removeTrailingSlash(val) : val)),
|
||||||
.describe(SECRET_IMPORTS.UPDATE.import.path),
|
position: z.number().optional()
|
||||||
position: z.number().optional().describe(SECRET_IMPORTS.UPDATE.import.position)
|
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
@ -154,12 +152,12 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) =>
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
params: z.object({
|
params: z.object({
|
||||||
secretImportId: z.string().trim().describe(SECRET_IMPORTS.DELETE.secretImportId)
|
secretImportId: z.string().trim()
|
||||||
}),
|
}),
|
||||||
body: z.object({
|
body: z.object({
|
||||||
workspaceId: z.string().trim().describe(SECRET_IMPORTS.DELETE.workspaceId),
|
workspaceId: z.string().trim(),
|
||||||
environment: z.string().trim().describe(SECRET_IMPORTS.DELETE.environment),
|
environment: z.string().trim(),
|
||||||
path: z.string().trim().default("/").transform(removeTrailingSlash).describe(SECRET_IMPORTS.DELETE.path)
|
path: z.string().trim().default("/").transform(removeTrailingSlash)
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
@ -215,9 +213,9 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) =>
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
querystring: z.object({
|
querystring: z.object({
|
||||||
workspaceId: z.string().trim().describe(SECRET_IMPORTS.LIST.workspaceId),
|
workspaceId: z.string().trim(),
|
||||||
environment: z.string().trim().describe(SECRET_IMPORTS.LIST.environment),
|
environment: z.string().trim(),
|
||||||
path: z.string().trim().default("/").transform(removeTrailingSlash).describe(SECRET_IMPORTS.LIST.path)
|
path: z.string().trim().default("/").transform(removeTrailingSlash)
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { IdentitiesSchema, IdentityOrgMembershipsSchema, OrgRolesSchema } from "@app/db/schemas";
|
import { IdentitiesSchema, IdentityOrgMembershipsSchema, OrgRolesSchema } from "@app/db/schemas";
|
||||||
import { ORGANIZATIONS } from "@app/lib/api-docs";
|
|
||||||
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";
|
||||||
|
|
||||||
@ -19,7 +18,7 @@ export const registerIdentityOrgRouter = async (server: FastifyZodProvider) => {
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
params: z.object({
|
params: z.object({
|
||||||
orgId: z.string().trim().describe(ORGANIZATIONS.LIST_IDENTITY_MEMBERSHIPS.orgId)
|
orgId: z.string().trim()
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
|
@ -7,7 +7,6 @@ import {
|
|||||||
ProjectMembershipRole,
|
ProjectMembershipRole,
|
||||||
ProjectUserMembershipRolesSchema
|
ProjectUserMembershipRolesSchema
|
||||||
} from "@app/db/schemas";
|
} from "@app/db/schemas";
|
||||||
import { PROJECTS } from "@app/lib/api-docs";
|
|
||||||
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";
|
||||||
import { ProjectUserMembershipTemporaryMode } from "@app/services/project-membership/project-membership-types";
|
import { ProjectUserMembershipTemporaryMode } from "@app/services/project-membership/project-membership-types";
|
||||||
@ -57,8 +56,8 @@ export const registerIdentityProjectRouter = async (server: FastifyZodProvider)
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
params: z.object({
|
params: z.object({
|
||||||
projectId: z.string().trim().describe(PROJECTS.UPDATE_IDENTITY_MEMBERSHIP.projectId),
|
projectId: z.string().trim(),
|
||||||
identityId: z.string().trim().describe(PROJECTS.UPDATE_IDENTITY_MEMBERSHIP.identityId)
|
identityId: z.string().trim()
|
||||||
}),
|
}),
|
||||||
body: z.object({
|
body: z.object({
|
||||||
roles: z
|
roles: z
|
||||||
@ -78,7 +77,6 @@ export const registerIdentityProjectRouter = async (server: FastifyZodProvider)
|
|||||||
])
|
])
|
||||||
)
|
)
|
||||||
.min(1)
|
.min(1)
|
||||||
.describe(PROJECTS.UPDATE_IDENTITY_MEMBERSHIP.roles)
|
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
@ -112,8 +110,8 @@ export const registerIdentityProjectRouter = async (server: FastifyZodProvider)
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
params: z.object({
|
params: z.object({
|
||||||
projectId: z.string().trim().describe(PROJECTS.DELETE_IDENTITY_MEMBERSHIP.projectId),
|
projectId: z.string().trim(),
|
||||||
identityId: z.string().trim().describe(PROJECTS.DELETE_IDENTITY_MEMBERSHIP.identityId)
|
identityId: z.string().trim()
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
@ -146,7 +144,7 @@ export const registerIdentityProjectRouter = async (server: FastifyZodProvider)
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
params: z.object({
|
params: z.object({
|
||||||
projectId: z.string().trim().describe(PROJECTS.LIST_IDENTITY_MEMBERSHIPS.projectId)
|
projectId: z.string().trim()
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { OrganizationsSchema, OrgMembershipsSchema, UserEncryptionKeysSchema, UsersSchema } from "@app/db/schemas";
|
import { OrganizationsSchema, OrgMembershipsSchema, UserEncryptionKeysSchema, UsersSchema } from "@app/db/schemas";
|
||||||
import { ORGANIZATIONS } from "@app/lib/api-docs";
|
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { ActorType, AuthMode } from "@app/services/auth/auth-type";
|
import { ActorType, AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
@ -18,7 +17,7 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
params: z.object({
|
params: z.object({
|
||||||
organizationId: z.string().trim().describe(ORGANIZATIONS.LIST_USER_MEMBERSHIPS.organizationId)
|
organizationId: z.string().trim()
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
@ -64,7 +63,7 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
params: z.object({
|
params: z.object({
|
||||||
organizationId: z.string().trim().describe(ORGANIZATIONS.GET_PROJECTS.organizationId)
|
organizationId: z.string().trim()
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
@ -109,12 +108,9 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
|
|||||||
apiKeyAuth: []
|
apiKeyAuth: []
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
params: z.object({
|
params: z.object({ organizationId: z.string().trim(), membershipId: z.string().trim() }),
|
||||||
organizationId: z.string().trim().describe(ORGANIZATIONS.UPDATE_USER_MEMBERSHIP.organizationId),
|
|
||||||
membershipId: z.string().trim().describe(ORGANIZATIONS.UPDATE_USER_MEMBERSHIP.membershipId)
|
|
||||||
}),
|
|
||||||
body: z.object({
|
body: z.object({
|
||||||
role: z.string().trim().describe(ORGANIZATIONS.UPDATE_USER_MEMBERSHIP.role)
|
role: z.string().trim()
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
@ -149,10 +145,7 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
|
|||||||
apiKeyAuth: []
|
apiKeyAuth: []
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
params: z.object({
|
params: z.object({ organizationId: z.string().trim(), membershipId: z.string().trim() }),
|
||||||
organizationId: z.string().trim().describe(ORGANIZATIONS.DELETE_USER_MEMBERSHIP.organizationId),
|
|
||||||
membershipId: z.string().trim().describe(ORGANIZATIONS.DELETE_USER_MEMBERSHIP.membershipId)
|
|
||||||
}),
|
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
membership: OrgMembershipsSchema
|
membership: OrgMembershipsSchema
|
||||||
|
@ -2,7 +2,6 @@ import { z } from "zod";
|
|||||||
|
|
||||||
import { ProjectMembershipsSchema } from "@app/db/schemas";
|
import { ProjectMembershipsSchema } from "@app/db/schemas";
|
||||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||||
import { PROJECTS } from "@app/lib/api-docs";
|
|
||||||
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";
|
||||||
|
|
||||||
@ -12,11 +11,11 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider
|
|||||||
url: "/:projectId/memberships",
|
url: "/:projectId/memberships",
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
projectId: z.string().describe(PROJECTS.INVITE_MEMBER.projectId)
|
projectId: z.string().describe("The ID of the project.")
|
||||||
}),
|
}),
|
||||||
body: z.object({
|
body: z.object({
|
||||||
emails: z.string().email().array().default([]).describe(PROJECTS.INVITE_MEMBER.emails),
|
emails: z.string().email().array().default([]).describe("Emails of the users to add to the project."),
|
||||||
usernames: z.string().array().default([]).describe(PROJECTS.INVITE_MEMBER.usernames)
|
usernames: z.string().array().default([]).describe("Usernames of the users to add to the project.")
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
@ -58,12 +57,12 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider
|
|||||||
url: "/:projectId/memberships",
|
url: "/:projectId/memberships",
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
projectId: z.string().describe(PROJECTS.REMOVE_MEMBER.projectId)
|
projectId: z.string().describe("The ID of the project.")
|
||||||
}),
|
}),
|
||||||
|
|
||||||
body: z.object({
|
body: z.object({
|
||||||
emails: z.string().email().array().default([]).describe(PROJECTS.REMOVE_MEMBER.emails),
|
emails: z.string().email().array().default([]).describe("Emails of the users to remove from the project."),
|
||||||
usernames: z.string().array().default([]).describe(PROJECTS.REMOVE_MEMBER.usernames)
|
usernames: z.string().array().default([]).describe("Usernames of the users to remove from the project.")
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
|
@ -3,7 +3,6 @@ import { z } from "zod";
|
|||||||
|
|
||||||
import { ProjectKeysSchema, ProjectsSchema } from "@app/db/schemas";
|
import { ProjectKeysSchema, ProjectsSchema } from "@app/db/schemas";
|
||||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||||
import { PROJECTS } from "@app/lib/api-docs";
|
|
||||||
import { authRateLimit } from "@app/server/config/rateLimiter";
|
import { authRateLimit } from "@app/server/config/rateLimiter";
|
||||||
import { getTelemetryDistinctId } from "@app/server/lib/telemetry";
|
import { getTelemetryDistinctId } from "@app/server/lib/telemetry";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
@ -39,7 +38,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
params: z.object({
|
params: z.object({
|
||||||
workspaceId: z.string().trim().describe(PROJECTS.GET_KEY.workspaceId)
|
workspaceId: z.string().trim()
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: ProjectKeysSchema.merge(
|
200: ProjectKeysSchema.merge(
|
||||||
@ -141,17 +140,11 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
|||||||
},
|
},
|
||||||
schema: {
|
schema: {
|
||||||
body: z.object({
|
body: z.object({
|
||||||
projectName: z.string().trim().describe(PROJECTS.CREATE.projectName),
|
projectName: z.string().trim().describe("Name of the project you're creating"),
|
||||||
slug: z
|
slug: slugSchema
|
||||||
.string()
|
|
||||||
.min(5)
|
|
||||||
.max(36)
|
|
||||||
.refine((v) => slugify(v) === v, {
|
|
||||||
message: "Slug must be a valid slug"
|
|
||||||
})
|
|
||||||
.optional()
|
.optional()
|
||||||
.describe(PROJECTS.CREATE.slug),
|
.describe("An optional slug for the project. If not provided, it will be auto-generated"),
|
||||||
organizationSlug: z.string().trim().describe(PROJECTS.CREATE.organizationSlug)
|
organizationSlug: z.string().trim().describe("The slug of the organization to create the project in")
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
|
@ -10,7 +10,6 @@ import {
|
|||||||
} from "@app/db/schemas";
|
} from "@app/db/schemas";
|
||||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||||
import { CommitType } from "@app/ee/services/secret-approval-request/secret-approval-request-types";
|
import { CommitType } from "@app/ee/services/secret-approval-request/secret-approval-request-types";
|
||||||
import { RAW_SECRETS } from "@app/lib/api-docs";
|
|
||||||
import { BadRequestError } from "@app/lib/errors";
|
import { BadRequestError } from "@app/lib/errors";
|
||||||
import { removeTrailingSlash } from "@app/lib/fn";
|
import { removeTrailingSlash } from "@app/lib/fn";
|
||||||
import { getTelemetryDistinctId } from "@app/server/lib/telemetry";
|
import { getTelemetryDistinctId } from "@app/server/lib/telemetry";
|
||||||
@ -35,15 +34,20 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
querystring: z.object({
|
querystring: z.object({
|
||||||
workspaceId: z.string().trim().optional().describe(RAW_SECRETS.LIST.workspaceId),
|
workspaceId: z.string().trim().optional(),
|
||||||
workspaceSlug: z.string().trim().optional().describe(RAW_SECRETS.LIST.workspaceSlug),
|
workspaceSlug: z
|
||||||
environment: z.string().trim().optional().describe(RAW_SECRETS.LIST.environment),
|
.string()
|
||||||
secretPath: z.string().trim().default("/").transform(removeTrailingSlash).describe(RAW_SECRETS.LIST.secretPath),
|
.trim()
|
||||||
|
.optional()
|
||||||
|
.describe(
|
||||||
|
"The slug of the workspace. This is only supported when authenticating Machine Identity's. Either the project ID or project slug has to be present in the request."
|
||||||
|
),
|
||||||
|
environment: z.string().trim().optional(),
|
||||||
|
secretPath: z.string().trim().default("/").transform(removeTrailingSlash),
|
||||||
include_imports: z
|
include_imports: z
|
||||||
.enum(["true", "false"])
|
.enum(["true", "false"])
|
||||||
.default("false")
|
.default("false")
|
||||||
.transform((value) => value === "true")
|
.transform((value) => value === "true")
|
||||||
.describe(RAW_SECRETS.LIST.includeImports)
|
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
@ -144,19 +148,18 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
params: z.object({
|
params: z.object({
|
||||||
secretName: z.string().trim().describe(RAW_SECRETS.GET.secretName)
|
secretName: z.string().trim()
|
||||||
}),
|
}),
|
||||||
querystring: z.object({
|
querystring: z.object({
|
||||||
workspaceId: z.string().trim().optional().describe(RAW_SECRETS.GET.workspaceId),
|
workspaceId: z.string().trim().optional(),
|
||||||
environment: z.string().trim().optional().describe(RAW_SECRETS.GET.environment),
|
environment: z.string().trim().optional(),
|
||||||
secretPath: z.string().trim().default("/").transform(removeTrailingSlash).describe(RAW_SECRETS.GET.secretPath),
|
secretPath: z.string().trim().default("/").transform(removeTrailingSlash),
|
||||||
version: z.coerce.number().optional().describe(RAW_SECRETS.GET.version),
|
version: z.coerce.number().optional(),
|
||||||
type: z.nativeEnum(SecretType).default(SecretType.Shared).describe(RAW_SECRETS.GET.type),
|
type: z.nativeEnum(SecretType).default(SecretType.Shared),
|
||||||
include_imports: z
|
include_imports: z
|
||||||
.enum(["true", "false"])
|
.enum(["true", "false"])
|
||||||
.default("false")
|
.default("false")
|
||||||
.transform((value) => value === "true")
|
.transform((value) => value === "true")
|
||||||
.describe(RAW_SECRETS.GET.includeImports)
|
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
@ -236,24 +239,16 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
params: z.object({
|
params: z.object({
|
||||||
secretName: z.string().trim().describe(RAW_SECRETS.CREATE.secretName)
|
secretName: z.string().trim()
|
||||||
}),
|
}),
|
||||||
body: z.object({
|
body: z.object({
|
||||||
workspaceId: z.string().trim().describe(RAW_SECRETS.CREATE.workspaceId),
|
workspaceId: z.string().trim(),
|
||||||
environment: z.string().trim().describe(RAW_SECRETS.CREATE.environment),
|
environment: z.string().trim(),
|
||||||
secretPath: z
|
secretPath: z.string().trim().default("/").transform(removeTrailingSlash),
|
||||||
.string()
|
secretValue: z.string().transform((val) => (val.at(-1) === "\n" ? `${val.trim()}\n` : val.trim())),
|
||||||
.trim()
|
secretComment: z.string().trim().optional().default(""),
|
||||||
.default("/")
|
skipMultilineEncoding: z.boolean().optional(),
|
||||||
.transform(removeTrailingSlash)
|
type: z.nativeEnum(SecretType).default(SecretType.Shared)
|
||||||
.describe(RAW_SECRETS.CREATE.secretPath),
|
|
||||||
secretValue: z
|
|
||||||
.string()
|
|
||||||
.transform((val) => (val.at(-1) === "\n" ? `${val.trim()}\n` : val.trim()))
|
|
||||||
.describe(RAW_SECRETS.CREATE.secretValue),
|
|
||||||
secretComment: z.string().trim().optional().default("").describe(RAW_SECRETS.CREATE.secretComment),
|
|
||||||
skipMultilineEncoding: z.boolean().optional().describe(RAW_SECRETS.CREATE.skipMultilineEncoding),
|
|
||||||
type: z.nativeEnum(SecretType).default(SecretType.Shared).describe(RAW_SECRETS.CREATE.type)
|
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
@ -322,23 +317,15 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
params: z.object({
|
params: z.object({
|
||||||
secretName: z.string().trim().describe(RAW_SECRETS.UPDATE.secretName)
|
secretName: z.string().trim()
|
||||||
}),
|
}),
|
||||||
body: z.object({
|
body: z.object({
|
||||||
workspaceId: z.string().trim().describe(RAW_SECRETS.UPDATE.workspaceId),
|
workspaceId: z.string().trim(),
|
||||||
environment: z.string().trim().describe(RAW_SECRETS.UPDATE.environment),
|
environment: z.string().trim(),
|
||||||
secretValue: z
|
secretValue: z.string().transform((val) => (val.at(-1) === "\n" ? `${val.trim()}\n` : val.trim())),
|
||||||
.string()
|
secretPath: z.string().trim().default("/").transform(removeTrailingSlash),
|
||||||
.transform((val) => (val.at(-1) === "\n" ? `${val.trim()}\n` : val.trim()))
|
skipMultilineEncoding: z.boolean().optional(),
|
||||||
.describe(RAW_SECRETS.UPDATE.secretValue),
|
type: z.nativeEnum(SecretType).default(SecretType.Shared)
|
||||||
secretPath: z
|
|
||||||
.string()
|
|
||||||
.trim()
|
|
||||||
.default("/")
|
|
||||||
.transform(removeTrailingSlash)
|
|
||||||
.describe(RAW_SECRETS.UPDATE.secretPath),
|
|
||||||
skipMultilineEncoding: z.boolean().optional().describe(RAW_SECRETS.UPDATE.skipMultilineEncoding),
|
|
||||||
type: z.nativeEnum(SecretType).default(SecretType.Shared).describe(RAW_SECRETS.UPDATE.type)
|
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
@ -405,18 +392,13 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
params: z.object({
|
params: z.object({
|
||||||
secretName: z.string().trim().describe(RAW_SECRETS.DELETE.secretName)
|
secretName: z.string().trim()
|
||||||
}),
|
}),
|
||||||
body: z.object({
|
body: z.object({
|
||||||
workspaceId: z.string().trim().describe(RAW_SECRETS.DELETE.workspaceId),
|
workspaceId: z.string().trim(),
|
||||||
environment: z.string().trim().describe(RAW_SECRETS.DELETE.environment),
|
environment: z.string().trim(),
|
||||||
secretPath: z
|
secretPath: z.string().trim().default("/").transform(removeTrailingSlash),
|
||||||
.string()
|
type: z.nativeEnum(SecretType).default(SecretType.Shared)
|
||||||
.trim()
|
|
||||||
.default("/")
|
|
||||||
.transform(removeTrailingSlash)
|
|
||||||
.describe(RAW_SECRETS.DELETE.secretPath),
|
|
||||||
type: z.nativeEnum(SecretType).default(SecretType.Shared).describe(RAW_SECRETS.DELETE.type)
|
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
|
@ -260,23 +260,8 @@ const getAppsGithub = async ({ accessToken }: { accessToken: string }) => {
|
|||||||
* Return list of services for Render integration
|
* Return list of services for Render integration
|
||||||
*/
|
*/
|
||||||
const getAppsRender = async ({ accessToken }: { accessToken: string }) => {
|
const getAppsRender = async ({ accessToken }: { accessToken: string }) => {
|
||||||
const apps: Array<{ name: string; appId: string }> = [];
|
const res = (
|
||||||
let hasMorePages = true;
|
await request.get<{ service: { name: string; id: string } }[]>(`${IntegrationUrls.RENDER_API_URL}/v1/services`, {
|
||||||
const perPage = 100;
|
|
||||||
let cursor;
|
|
||||||
|
|
||||||
interface RenderService {
|
|
||||||
cursor: string;
|
|
||||||
service: { name: string; id: string };
|
|
||||||
}
|
|
||||||
|
|
||||||
while (hasMorePages) {
|
|
||||||
const res: RenderService[] = (
|
|
||||||
await request.get<RenderService[]>(`${IntegrationUrls.RENDER_API_URL}/v1/services`, {
|
|
||||||
params: new URLSearchParams({
|
|
||||||
...(cursor ? { cursor: String(cursor) } : {}),
|
|
||||||
limit: String(perPage)
|
|
||||||
}),
|
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${accessToken}`,
|
Authorization: `Bearer ${accessToken}`,
|
||||||
Accept: "application/json",
|
Accept: "application/json",
|
||||||
@ -285,19 +270,10 @@ const getAppsRender = async ({ accessToken }: { accessToken: string }) => {
|
|||||||
})
|
})
|
||||||
).data;
|
).data;
|
||||||
|
|
||||||
res.forEach((a) => {
|
const apps = res.map((a) => ({
|
||||||
apps.push({
|
|
||||||
name: a.service.name,
|
name: a.service.name,
|
||||||
appId: a.service.id
|
appId: a.service.id
|
||||||
});
|
}));
|
||||||
});
|
|
||||||
|
|
||||||
if (res.length < perPage) {
|
|
||||||
hasMorePages = false;
|
|
||||||
} else {
|
|
||||||
cursor = res[res.length - 1].cursor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return apps;
|
return apps;
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
import { Octokit } from "@octokit/rest";
|
|
||||||
|
|
||||||
import { SecretEncryptionAlgo, SecretKeyEncoding, TIntegrationAuths, TIntegrationAuthsInsert } from "@app/db/schemas";
|
import { SecretEncryptionAlgo, SecretKeyEncoding, TIntegrationAuths, TIntegrationAuthsInsert } from "@app/db/schemas";
|
||||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||||
@ -25,8 +24,6 @@ import {
|
|||||||
TIntegrationAuthAppsDTO,
|
TIntegrationAuthAppsDTO,
|
||||||
TIntegrationAuthBitbucketWorkspaceDTO,
|
TIntegrationAuthBitbucketWorkspaceDTO,
|
||||||
TIntegrationAuthChecklyGroupsDTO,
|
TIntegrationAuthChecklyGroupsDTO,
|
||||||
TIntegrationAuthGithubEnvsDTO,
|
|
||||||
TIntegrationAuthGithubOrgsDTO,
|
|
||||||
TIntegrationAuthHerokuPipelinesDTO,
|
TIntegrationAuthHerokuPipelinesDTO,
|
||||||
TIntegrationAuthNorthflankSecretGroupDTO,
|
TIntegrationAuthNorthflankSecretGroupDTO,
|
||||||
TIntegrationAuthQoveryEnvironmentsDTO,
|
TIntegrationAuthQoveryEnvironmentsDTO,
|
||||||
@ -438,75 +435,6 @@ export const integrationAuthServiceFactory = ({
|
|||||||
return [];
|
return [];
|
||||||
};
|
};
|
||||||
|
|
||||||
const getGithubOrgs = async ({ actorId, actor, actorOrgId, actorAuthMethod, id }: TIntegrationAuthGithubOrgsDTO) => {
|
|
||||||
const integrationAuth = await integrationAuthDAL.findById(id);
|
|
||||||
if (!integrationAuth) throw new BadRequestError({ message: "Failed to find integration" });
|
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
|
||||||
actor,
|
|
||||||
actorId,
|
|
||||||
integrationAuth.projectId,
|
|
||||||
actorAuthMethod,
|
|
||||||
actorOrgId
|
|
||||||
);
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
|
||||||
const botKey = await projectBotService.getBotKey(integrationAuth.projectId);
|
|
||||||
const { accessToken } = await getIntegrationAccessToken(integrationAuth, botKey);
|
|
||||||
|
|
||||||
const octokit = new Octokit({
|
|
||||||
auth: accessToken
|
|
||||||
});
|
|
||||||
|
|
||||||
const { data } = await octokit.request("GET /user/orgs", {
|
|
||||||
headers: {
|
|
||||||
"X-GitHub-Api-Version": "2022-11-28"
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (!data) return [];
|
|
||||||
|
|
||||||
return data.map(({ login: name, id: orgId }) => ({ name, orgId: String(orgId) }));
|
|
||||||
};
|
|
||||||
|
|
||||||
const getGithubEnvs = async ({
|
|
||||||
actorId,
|
|
||||||
actor,
|
|
||||||
actorOrgId,
|
|
||||||
actorAuthMethod,
|
|
||||||
id,
|
|
||||||
repoOwner,
|
|
||||||
repoName
|
|
||||||
}: TIntegrationAuthGithubEnvsDTO) => {
|
|
||||||
const integrationAuth = await integrationAuthDAL.findById(id);
|
|
||||||
if (!integrationAuth) throw new BadRequestError({ message: "Failed to find integration" });
|
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission(
|
|
||||||
actor,
|
|
||||||
actorId,
|
|
||||||
integrationAuth.projectId,
|
|
||||||
actorAuthMethod,
|
|
||||||
actorOrgId
|
|
||||||
);
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
|
|
||||||
const botKey = await projectBotService.getBotKey(integrationAuth.projectId);
|
|
||||||
const { accessToken } = await getIntegrationAccessToken(integrationAuth, botKey);
|
|
||||||
|
|
||||||
const octokit = new Octokit({
|
|
||||||
auth: accessToken
|
|
||||||
});
|
|
||||||
|
|
||||||
const {
|
|
||||||
data: { environments }
|
|
||||||
} = await octokit.request("GET /repos/{owner}/{repo}/environments", {
|
|
||||||
headers: {
|
|
||||||
"X-GitHub-Api-Version": "2022-11-28"
|
|
||||||
},
|
|
||||||
owner: repoOwner,
|
|
||||||
repo: repoName
|
|
||||||
});
|
|
||||||
if (!environments) return [];
|
|
||||||
return environments.map(({ id: envId, name }) => ({ name, envId: String(envId) }));
|
|
||||||
};
|
|
||||||
|
|
||||||
const getQoveryOrgs = async ({ actorId, actor, actorOrgId, actorAuthMethod, id }: TIntegrationAuthQoveryOrgsDTO) => {
|
const getQoveryOrgs = async ({ actorId, actor, actorOrgId, actorAuthMethod, id }: TIntegrationAuthQoveryOrgsDTO) => {
|
||||||
const integrationAuth = await integrationAuthDAL.findById(id);
|
const integrationAuth = await integrationAuthDAL.findById(id);
|
||||||
if (!integrationAuth) throw new BadRequestError({ message: "Failed to find integration" });
|
if (!integrationAuth) throw new BadRequestError({ message: "Failed to find integration" });
|
||||||
@ -1133,8 +1061,6 @@ export const integrationAuthServiceFactory = ({
|
|||||||
getIntegrationApps,
|
getIntegrationApps,
|
||||||
getVercelBranches,
|
getVercelBranches,
|
||||||
getApps,
|
getApps,
|
||||||
getGithubOrgs,
|
|
||||||
getGithubEnvs,
|
|
||||||
getChecklyGroups,
|
getChecklyGroups,
|
||||||
getQoveryApps,
|
getQoveryApps,
|
||||||
getQoveryEnvs,
|
getQoveryEnvs,
|
||||||
|
@ -44,16 +44,6 @@ export type TIntegrationAuthChecklyGroupsDTO = {
|
|||||||
accountId: string;
|
accountId: string;
|
||||||
} & Omit<TProjectPermission, "projectId">;
|
} & Omit<TProjectPermission, "projectId">;
|
||||||
|
|
||||||
export type TIntegrationAuthGithubOrgsDTO = {
|
|
||||||
id: string;
|
|
||||||
} & Omit<TProjectPermission, "projectId">;
|
|
||||||
|
|
||||||
export type TIntegrationAuthGithubEnvsDTO = {
|
|
||||||
id: string;
|
|
||||||
repoName: string;
|
|
||||||
repoOwner: string;
|
|
||||||
} & Omit<TProjectPermission, "projectId">;
|
|
||||||
|
|
||||||
export type TIntegrationAuthQoveryOrgsDTO = {
|
export type TIntegrationAuthQoveryOrgsDTO = {
|
||||||
id: string;
|
id: string;
|
||||||
} & Omit<TProjectPermission, "projectId">;
|
} & Omit<TProjectPermission, "projectId">;
|
||||||
|
@ -459,7 +459,7 @@ const syncSecretsAWSParameterStore = async ({
|
|||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
Path: integration.path as string,
|
Path: integration.path as string,
|
||||||
Recursive: false,
|
Recursive: true,
|
||||||
WithDecryption: true
|
WithDecryption: true
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1110,132 +1110,78 @@ const syncSecretsGitHub = async ({
|
|||||||
interface GitHubRepoKey {
|
interface GitHubRepoKey {
|
||||||
key_id: string;
|
key_id: string;
|
||||||
key: string;
|
key: string;
|
||||||
id?: number | undefined;
|
|
||||||
url?: string | undefined;
|
|
||||||
title?: string | undefined;
|
|
||||||
created_at?: string | undefined;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface GitHubSecret {
|
interface GitHubSecret {
|
||||||
name: string;
|
name: string;
|
||||||
created_at: string;
|
created_at: string;
|
||||||
updated_at: string;
|
updated_at: string;
|
||||||
visibility?: "all" | "private" | "selected";
|
}
|
||||||
selected_repositories_url?: string | undefined;
|
|
||||||
|
interface GitHubSecretRes {
|
||||||
|
[index: string]: GitHubSecret;
|
||||||
}
|
}
|
||||||
|
|
||||||
const octokit = new Octokit({
|
const octokit = new Octokit({
|
||||||
auth: accessToken
|
auth: accessToken
|
||||||
});
|
});
|
||||||
|
|
||||||
enum GithubScope {
|
// const user = (await octokit.request('GET /user', {})).data;
|
||||||
Repo = "github-repo",
|
const repoPublicKey: GitHubRepoKey = (
|
||||||
Org = "github-org",
|
await octokit.request("GET /repos/{owner}/{repo}/actions/secrets/public-key", {
|
||||||
Env = "github-env"
|
|
||||||
}
|
|
||||||
|
|
||||||
let repoPublicKey: GitHubRepoKey;
|
|
||||||
|
|
||||||
switch (integration.scope) {
|
|
||||||
case GithubScope.Org: {
|
|
||||||
const { data } = await octokit.request("GET /orgs/{org}/actions/secrets/public-key", {
|
|
||||||
org: integration.owner as string
|
|
||||||
});
|
|
||||||
repoPublicKey = data;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case GithubScope.Env: {
|
|
||||||
const { data } = await octokit.request(
|
|
||||||
"GET /repositories/{repository_id}/environments/{environment_name}/secrets/public-key",
|
|
||||||
{
|
|
||||||
repository_id: Number(integration.appId),
|
|
||||||
environment_name: integration.targetEnvironmentId as string
|
|
||||||
}
|
|
||||||
);
|
|
||||||
repoPublicKey = data;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
const { data } = await octokit.request("GET /repos/{owner}/{repo}/actions/secrets/public-key", {
|
|
||||||
owner: integration.owner as string,
|
owner: integration.owner as string,
|
||||||
repo: integration.app as string
|
repo: integration.app as string
|
||||||
});
|
})
|
||||||
repoPublicKey = data;
|
).data;
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get local copy of decrypted secrets. We cannot decrypt them as we dont have access to GH private key
|
// Get local copy of decrypted secrets. We cannot decrypt them as we dont have access to GH private key
|
||||||
let encryptedSecrets: GitHubSecret[];
|
let encryptedSecrets: GitHubSecretRes = (
|
||||||
|
|
||||||
switch (integration.scope) {
|
|
||||||
case GithubScope.Org: {
|
|
||||||
encryptedSecrets = (
|
|
||||||
await octokit.request("GET /orgs/{org}/actions/secrets", {
|
|
||||||
org: integration.owner as string
|
|
||||||
})
|
|
||||||
).data.secrets;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case GithubScope.Env: {
|
|
||||||
encryptedSecrets = (
|
|
||||||
await octokit.request("GET /repositories/{repository_id}/environments/{environment_name}/secrets", {
|
|
||||||
repository_id: Number(integration.appId),
|
|
||||||
environment_name: integration.targetEnvironmentId as string
|
|
||||||
})
|
|
||||||
).data.secrets;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
encryptedSecrets = (
|
|
||||||
await octokit.request("GET /repos/{owner}/{repo}/actions/secrets", {
|
await octokit.request("GET /repos/{owner}/{repo}/actions/secrets", {
|
||||||
owner: integration.owner as string,
|
owner: integration.owner as string,
|
||||||
repo: integration.app as string
|
repo: integration.app as string
|
||||||
})
|
})
|
||||||
).data.secrets;
|
).data.secrets.reduce(
|
||||||
break;
|
(obj, secret) => ({
|
||||||
}
|
...obj,
|
||||||
}
|
[secret.name]: secret
|
||||||
|
}),
|
||||||
for await (const encryptedSecret of encryptedSecrets) {
|
{}
|
||||||
if (
|
|
||||||
!(encryptedSecret.name in secrets) &&
|
|
||||||
!(appendices?.prefix !== undefined && !encryptedSecret.name.startsWith(appendices?.prefix)) &&
|
|
||||||
!(appendices?.suffix !== undefined && !encryptedSecret.name.endsWith(appendices?.suffix))
|
|
||||||
) {
|
|
||||||
switch (integration.scope) {
|
|
||||||
case GithubScope.Org: {
|
|
||||||
await octokit.request("DELETE /orgs/{org}/actions/secrets/{secret_name}", {
|
|
||||||
org: integration.owner as string,
|
|
||||||
secret_name: encryptedSecret.name
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case GithubScope.Env: {
|
|
||||||
await octokit.request(
|
|
||||||
"DELETE /repositories/{repository_id}/environments/{environment_name}/secrets/{secret_name}",
|
|
||||||
{
|
|
||||||
repository_id: Number(integration.appId),
|
|
||||||
environment_name: integration.targetEnvironmentId as string,
|
|
||||||
secret_name: encryptedSecret.name
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
break;
|
|
||||||
|
encryptedSecrets = Object.keys(encryptedSecrets).reduce(
|
||||||
|
(
|
||||||
|
result: {
|
||||||
|
[key: string]: GitHubSecret;
|
||||||
|
},
|
||||||
|
key
|
||||||
|
) => {
|
||||||
|
if (
|
||||||
|
(appendices?.prefix !== undefined ? key.startsWith(appendices?.prefix) : true) &&
|
||||||
|
(appendices?.suffix !== undefined ? key.endsWith(appendices?.suffix) : true)
|
||||||
|
) {
|
||||||
|
result[key] = encryptedSecrets[key];
|
||||||
}
|
}
|
||||||
default: {
|
return result;
|
||||||
await octokit.request("DELETE /repos/{owner}/{repo}/actions/secrets/{secret_name}", {
|
},
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
|
||||||
|
await Promise.all(
|
||||||
|
Object.keys(encryptedSecrets).map(async (key) => {
|
||||||
|
if (!(key in secrets)) {
|
||||||
|
return octokit.request("DELETE /repos/{owner}/{repo}/actions/secrets/{secret_name}", {
|
||||||
owner: integration.owner as string,
|
owner: integration.owner as string,
|
||||||
repo: integration.app as string,
|
repo: integration.app as string,
|
||||||
secret_name: encryptedSecret.name
|
secret_name: key
|
||||||
});
|
});
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
await sodium.ready.then(async () => {
|
await Promise.all(
|
||||||
for await (const key of Object.keys(secrets)) {
|
Object.keys(secrets).map((key) => {
|
||||||
|
// let encryptedSecret;
|
||||||
|
return sodium.ready.then(async () => {
|
||||||
// convert secret & base64 key to Uint8Array.
|
// convert secret & base64 key to Uint8Array.
|
||||||
const binkey = sodium.from_base64(repoPublicKey.key, sodium.base64_variants.ORIGINAL);
|
const binkey = sodium.from_base64(repoPublicKey.key, sodium.base64_variants.ORIGINAL);
|
||||||
const binsec = sodium.from_string(secrets[key].value);
|
const binsec = sodium.from_string(secrets[key].value);
|
||||||
@ -1246,29 +1192,6 @@ const syncSecretsGitHub = async ({
|
|||||||
// convert encrypted Uint8Array to base64
|
// convert encrypted Uint8Array to base64
|
||||||
const encryptedSecret = sodium.to_base64(encBytes, sodium.base64_variants.ORIGINAL);
|
const encryptedSecret = sodium.to_base64(encBytes, sodium.base64_variants.ORIGINAL);
|
||||||
|
|
||||||
switch (integration.scope) {
|
|
||||||
case GithubScope.Org:
|
|
||||||
await octokit.request("PUT /orgs/{org}/actions/secrets/{secret_name}", {
|
|
||||||
org: integration.owner as string,
|
|
||||||
secret_name: key,
|
|
||||||
visibility: "all",
|
|
||||||
encrypted_value: encryptedSecret,
|
|
||||||
key_id: repoPublicKey.key_id
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case GithubScope.Env:
|
|
||||||
await octokit.request(
|
|
||||||
"PUT /repositories/{repository_id}/environments/{environment_name}/secrets/{secret_name}",
|
|
||||||
{
|
|
||||||
repository_id: Number(integration.appId),
|
|
||||||
environment_name: integration.targetEnvironmentId as string,
|
|
||||||
secret_name: key,
|
|
||||||
encrypted_value: encryptedSecret,
|
|
||||||
key_id: repoPublicKey.key_id
|
|
||||||
}
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
await octokit.request("PUT /repos/{owner}/{repo}/actions/secrets/{secret_name}", {
|
await octokit.request("PUT /repos/{owner}/{repo}/actions/secrets/{secret_name}", {
|
||||||
owner: integration.owner as string,
|
owner: integration.owner as string,
|
||||||
repo: integration.app as string,
|
repo: integration.app as string,
|
||||||
@ -1276,10 +1199,9 @@ const syncSecretsGitHub = async ({
|
|||||||
encrypted_value: encryptedSecret,
|
encrypted_value: encryptedSecret,
|
||||||
key_id: repoPublicKey.key_id
|
key_id: repoPublicKey.key_id
|
||||||
});
|
});
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1307,22 +1229,6 @@ const syncSecretsRender = async ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
if (integration.metadata) {
|
|
||||||
const metadata = z.record(z.any()).parse(integration.metadata);
|
|
||||||
if (metadata.shouldAutoRedeploy === true) {
|
|
||||||
await request.post(
|
|
||||||
`${IntegrationUrls.RENDER_API_URL}/v1/services/${integration.appId}/deploys`,
|
|
||||||
{},
|
|
||||||
{
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${accessToken}`,
|
|
||||||
"Accept-Encoding": "application/json"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -23,8 +23,7 @@ export default defineConfig({
|
|||||||
loader: {
|
loader: {
|
||||||
".handlebars": "copy",
|
".handlebars": "copy",
|
||||||
".md": "copy",
|
".md": "copy",
|
||||||
".txt": "copy",
|
".txt": "copy"
|
||||||
".pem": "copy"
|
|
||||||
},
|
},
|
||||||
external: ["../../../frontend/node_modules/next/dist/server/next-server.js"],
|
external: ["../../../frontend/node_modules/next/dist/server/next-server.js"],
|
||||||
outDir: "dist",
|
outDir: "dist",
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
---
|
---
|
||||||
title: "Delete"
|
title: "Delete"
|
||||||
openapi: "DELETE /api/v1/folders/{folderIdOrName}"
|
openapi: "DELETE /api/v1/folders/{folderId}"
|
||||||
---
|
---
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 691 KiB |
Binary file not shown.
Before Width: | Height: | Size: 709 KiB |
Binary file not shown.
Before Width: | Height: | Size: 715 KiB |
Binary file not shown.
Before Width: | Height: | Size: 649 KiB After Width: | Height: | Size: 398 KiB |
@ -3,14 +3,17 @@ title: "GitHub Actions"
|
|||||||
description: "How to sync secrets from Infisical to GitHub Actions"
|
description: "How to sync secrets from Infisical to GitHub Actions"
|
||||||
---
|
---
|
||||||
|
|
||||||
Infisical lets you sync secrets to GitHub at the organization-level, repository-level, and repository environment-level.
|
|
||||||
|
|
||||||
Prerequisites:
|
|
||||||
- Set up and add envars to [Infisical Cloud](https://app.infisical.com)
|
|
||||||
- Ensure that you have admin privileges to the repository you want to sync secrets to.
|
|
||||||
|
|
||||||
<Tabs>
|
<Tabs>
|
||||||
<Tab title="Usage">
|
<Tab title="Usage">
|
||||||
|
<Warning>
|
||||||
|
Infisical can sync secrets to GitHub repo secrets only. If your repo uses environment secrets, then stay tuned with this [issue](https://github.com/Infisical/infisical/issues/54).
|
||||||
|
</Warning>
|
||||||
|
|
||||||
|
Prerequisites:
|
||||||
|
|
||||||
|
- Set up and add envars to [Infisical Cloud](https://app.infisical.com)
|
||||||
|
- Ensure you have admin privileges to the repo you want to sync secrets to.
|
||||||
|
|
||||||
<Steps>
|
<Steps>
|
||||||
<Step title="Authorize Infisical for GitHub">
|
<Step title="Authorize Infisical for GitHub">
|
||||||
Navigate to your project's integrations tab in Infisical.
|
Navigate to your project's integrations tab in Infisical.
|
||||||
@ -26,27 +29,12 @@ Prerequisites:
|
|||||||
Although this step breaks E2EE, it's necessary for Infisical to sync the environment variables to the cloud platform.
|
Although this step breaks E2EE, it's necessary for Infisical to sync the environment variables to the cloud platform.
|
||||||
</Info>
|
</Info>
|
||||||
</Step>
|
</Step>
|
||||||
<Step title="Configure Infisical GitHub integration">
|
<Step title="Start integration">
|
||||||
Select which Infisical environment secrets you want to sync to which GitHub organization, repository, or repository environment.
|
Select which Infisical environment secrets you want to sync to which GitHub repo and press start integration to start syncing secrets to the repo.
|
||||||
|
|
||||||
<Tabs>
|
|
||||||
<Tab title="Repository">
|
|
||||||

|
|
||||||
</Tab>
|
|
||||||
<Tab title="Organization">
|
|
||||||

|
|
||||||
</Tab>
|
|
||||||
<Tab title="Repository Environment">
|
|
||||||

|
|
||||||
</Tab>
|
|
||||||
</Tabs>
|
|
||||||
|
|
||||||
Finally, press create integration to start syncing secrets to GitHub.
|
|
||||||
|
|
||||||

|

|
||||||
</Step>
|
</Step>
|
||||||
</Steps>
|
</Steps>
|
||||||
|
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab title="Self-Hosted Setup">
|
<Tab title="Self-Hosted Setup">
|
||||||
Using the GitHub integration on a self-hosted instance of Infisical requires configuring an OAuth application in GitHub
|
Using the GitHub integration on a self-hosted instance of Infisical requires configuring an OAuth application in GitHub
|
||||||
@ -82,6 +70,6 @@ Prerequisites:
|
|||||||
Once added, restart your Infisical instance and use the GitHub integration.
|
Once added, restart your Infisical instance and use the GitHub integration.
|
||||||
</Step>
|
</Step>
|
||||||
</Steps>
|
</Steps>
|
||||||
|
|
||||||
</Tab>
|
</Tab>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
|
||||||
|
@ -39,32 +39,32 @@
|
|||||||
"name": "Start for Free",
|
"name": "Start for Free",
|
||||||
"url": "https://app.infisical.com/signup"
|
"url": "https://app.infisical.com/signup"
|
||||||
},
|
},
|
||||||
"tabs": [
|
"anchors": [
|
||||||
{
|
{
|
||||||
"name": "Changelog",
|
"name": "Internals",
|
||||||
"url": "changelog"
|
"icon": "sitemap",
|
||||||
},
|
"url": "internals"
|
||||||
{
|
|
||||||
"name": "API Reference",
|
|
||||||
"url": "api-reference"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "SDKs",
|
"name": "SDKs",
|
||||||
|
"icon": "puzzle-piece",
|
||||||
"url": "sdks"
|
"url": "sdks"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Contributing",
|
"name": "API Reference",
|
||||||
"url": "contributing"
|
"icon": "cloud",
|
||||||
}
|
"url": "api-reference"
|
||||||
],
|
},
|
||||||
"anchors": [
|
{
|
||||||
|
"name": "Changelog",
|
||||||
|
"icon": "timer",
|
||||||
|
"url": "changelog"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "Contributing",
|
"name": "Contributing",
|
||||||
"icon": "code",
|
"icon": "code",
|
||||||
"url": "contributing"
|
"url": "contributing"
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
"name": "Blog",
|
"name": "Blog",
|
||||||
"icon": "newspaper",
|
"icon": "newspaper",
|
||||||
@ -79,11 +79,6 @@
|
|||||||
"name": "GitHub",
|
"name": "GitHub",
|
||||||
"icon": "github",
|
"icon": "github",
|
||||||
"url": "https://github.com/Infisical/infisical"
|
"url": "https://github.com/Infisical/infisical"
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Internals",
|
|
||||||
"icon": "sitemap",
|
|
||||||
"url": "internals"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"navigation": [
|
"navigation": [
|
||||||
@ -196,7 +191,6 @@
|
|||||||
"self-hosting/guides/mongo-to-postgres"
|
"self-hosting/guides/mongo-to-postgres"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"self-hosting/ee",
|
|
||||||
"self-hosting/faq"
|
"self-hosting/faq"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -362,18 +356,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"group": "Overview",
|
"group": "Overview",
|
||||||
"pages": [
|
"pages": ["sdks/overview"]
|
||||||
"sdks/overview"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group": "SDK's",
|
|
||||||
"pages": [
|
|
||||||
"sdks/languages/node",
|
|
||||||
"sdks/languages/python",
|
|
||||||
"sdks/languages/java",
|
|
||||||
"sdks/languages/csharp"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"group": "Overview",
|
"group": "Overview",
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: "Infisical .NET SDK"
|
title: "Infisical .NET SDK"
|
||||||
sidebarTitle: ".NET"
|
icon: "C#"
|
||||||
icon: "bars"
|
|
||||||
---
|
---
|
||||||
|
|
||||||
If you're working with C#, the official [Infisical C# SDK](https://github.com/Infisical/sdk/tree/main/languages/csharp) package is the easiest way to fetch and work with secrets for your application.
|
If you're working with C#, the official [Infisical C# SDK](https://github.com/Infisical/sdk/tree/main/languages/csharp) package is the easiest way to fetch and work with secrets for your application.
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
---
|
---
|
||||||
title: "Infisical Java SDK"
|
title: "Infisical Java SDK"
|
||||||
sidebarTitle: "Java"
|
|
||||||
icon: "java"
|
icon: "java"
|
||||||
---
|
---
|
||||||
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
---
|
---
|
||||||
title: "Infisical Node.js SDK"
|
title: "Infisical Node.js SDK"
|
||||||
sidebarTitle: "Node.js"
|
|
||||||
icon: "node"
|
icon: "node"
|
||||||
---
|
---
|
||||||
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
---
|
---
|
||||||
title: "Infisical Python SDK"
|
title: "Infisical Python SDK"
|
||||||
sidebarTitle: "Python"
|
|
||||||
icon: "python"
|
icon: "python"
|
||||||
---
|
---
|
||||||
|
|
||||||
|
@ -1,28 +0,0 @@
|
|||||||
---
|
|
||||||
title: "Using Infisical EE"
|
|
||||||
description: "How to activate Infisical Enterprise Edition (EE) features"
|
|
||||||
---
|
|
||||||
|
|
||||||
While most features in Infisical are free to use, others are paid and require purchasing an enterprise license to use them.
|
|
||||||
|
|
||||||
This guide walks through how you can use these paid features in Infisical.
|
|
||||||
|
|
||||||
<Steps>
|
|
||||||
<Step title="Purchase a license">
|
|
||||||
Start by either signing up for a free demo [here](https://infisical.com/schedule-demo) or contacting team@infisical.com to purchase a license.
|
|
||||||
|
|
||||||
Once purchased, you will be issued a license key.
|
|
||||||
</Step>
|
|
||||||
<Step title="Activate the license">
|
|
||||||
Depending on whether or not the environment where Infisical is deployed has internet access, you may be issued a regular license or an offline license.
|
|
||||||
|
|
||||||
- If using a regular license, you should set the value of the environment variable `LICENSE_KEY` in Infisical to the issued license key.
|
|
||||||
- If using an offline license, you should set the value of the environment variable `LICENSE_KEY_OFFLINE` in Infisical to the issued license key.
|
|
||||||
|
|
||||||
Once your instance starts up, the license key will be validated and you’ll be able to use the paid features.
|
|
||||||
|
|
||||||
<Note>
|
|
||||||
Once the license expires, Infisical will continue to run, but EE features will be disabled until the license is renewed or a new one is purchased.
|
|
||||||
</Note>
|
|
||||||
</Step>
|
|
||||||
</Steps>
|
|
@ -6,8 +6,6 @@ export {
|
|||||||
useGetIntegrationAuthBitBucketWorkspaces,
|
useGetIntegrationAuthBitBucketWorkspaces,
|
||||||
useGetIntegrationAuthById,
|
useGetIntegrationAuthById,
|
||||||
useGetIntegrationAuthChecklyGroups,
|
useGetIntegrationAuthChecklyGroups,
|
||||||
useGetIntegrationAuthGithubEnvs,
|
|
||||||
useGetIntegrationAuthGithubOrgs,
|
|
||||||
useGetIntegrationAuthNorthflankSecretGroups,
|
useGetIntegrationAuthNorthflankSecretGroups,
|
||||||
useGetIntegrationAuthRailwayEnvironments,
|
useGetIntegrationAuthRailwayEnvironments,
|
||||||
useGetIntegrationAuthRailwayServices,
|
useGetIntegrationAuthRailwayServices,
|
||||||
|
@ -39,10 +39,6 @@ const integrationAuthKeys = {
|
|||||||
integrationAuthId: string;
|
integrationAuthId: string;
|
||||||
accountId: string;
|
accountId: string;
|
||||||
}) => [{ integrationAuthId, accountId }, "integrationAuthChecklyGroups"] as const,
|
}) => [{ integrationAuthId, accountId }, "integrationAuthChecklyGroups"] as const,
|
||||||
getIntegrationAuthGithubOrgs: (integrationAuthId: string) =>
|
|
||||||
[{ integrationAuthId }, "integrationAuthGithubOrgs"] as const,
|
|
||||||
getIntegrationAuthGithubEnvs: (integrationAuthId: string, repoName: string, repoOwner: string) =>
|
|
||||||
[{ integrationAuthId, repoName, repoOwner }, "integrationAuthGithubOrgs"] as const,
|
|
||||||
getIntegrationAuthQoveryOrgs: (integrationAuthId: string) =>
|
getIntegrationAuthQoveryOrgs: (integrationAuthId: string) =>
|
||||||
[{ integrationAuthId }, "integrationAuthQoveryOrgs"] as const,
|
[{ integrationAuthId }, "integrationAuthQoveryOrgs"] as const,
|
||||||
getIntegrationAuthQoveryProjects: ({
|
getIntegrationAuthQoveryProjects: ({
|
||||||
@ -181,32 +177,6 @@ const fetchIntegrationAuthVercelBranches = async ({
|
|||||||
return branches;
|
return branches;
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchIntegrationAuthGithubOrgs = async (integrationAuthId: string) => {
|
|
||||||
const {
|
|
||||||
data: { orgs }
|
|
||||||
} = await apiRequest.get<{ orgs: Org[] }>(
|
|
||||||
`/api/v1/integration-auth/${integrationAuthId}/github/orgs`
|
|
||||||
);
|
|
||||||
|
|
||||||
return orgs;
|
|
||||||
};
|
|
||||||
|
|
||||||
const fetchIntegrationAuthGithubEnvs = async (
|
|
||||||
integrationAuthId: string,
|
|
||||||
repoName: string,
|
|
||||||
repoOwner: string
|
|
||||||
) => {
|
|
||||||
if (!repoName || !repoOwner) return [];
|
|
||||||
|
|
||||||
const {
|
|
||||||
data: { envs }
|
|
||||||
} = await apiRequest.get<{ envs: Array<{ name: string; envId: string }> }>(
|
|
||||||
`/api/v1/integration-auth/${integrationAuthId}/github/envs?repoName=${repoName}&repoOwner=${repoOwner}`
|
|
||||||
);
|
|
||||||
|
|
||||||
return envs;
|
|
||||||
};
|
|
||||||
|
|
||||||
const fetchIntegrationAuthQoveryOrgs = async (integrationAuthId: string) => {
|
const fetchIntegrationAuthQoveryOrgs = async (integrationAuthId: string) => {
|
||||||
const {
|
const {
|
||||||
data: { orgs }
|
data: { orgs }
|
||||||
@ -331,6 +301,8 @@ const fetchIntegrationAuthHerokuPipelines = async ({ integrationAuthId }: {
|
|||||||
`/api/v1/integration-auth/${integrationAuthId}/heroku/pipelines`
|
`/api/v1/integration-auth/${integrationAuthId}/heroku/pipelines`
|
||||||
);
|
);
|
||||||
|
|
||||||
|
console.log(99999, pipelines)
|
||||||
|
|
||||||
return pipelines;
|
return pipelines;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -510,30 +482,6 @@ export const useGetIntegrationAuthChecklyGroups = ({
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useGetIntegrationAuthGithubOrgs = (integrationAuthId: string) => {
|
|
||||||
return useQuery({
|
|
||||||
queryKey: integrationAuthKeys.getIntegrationAuthGithubOrgs(integrationAuthId),
|
|
||||||
queryFn: () => fetchIntegrationAuthGithubOrgs(integrationAuthId),
|
|
||||||
enabled: true
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useGetIntegrationAuthGithubEnvs = (
|
|
||||||
integrationAuthId: string,
|
|
||||||
repoName: string,
|
|
||||||
repoOwner: string
|
|
||||||
) => {
|
|
||||||
return useQuery({
|
|
||||||
queryKey: integrationAuthKeys.getIntegrationAuthGithubEnvs(
|
|
||||||
integrationAuthId,
|
|
||||||
repoName,
|
|
||||||
repoOwner
|
|
||||||
),
|
|
||||||
queryFn: () => fetchIntegrationAuthGithubEnvs(integrationAuthId, repoName, repoOwner),
|
|
||||||
enabled: true
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useGetIntegrationAuthQoveryOrgs = (integrationAuthId: string) => {
|
export const useGetIntegrationAuthQoveryOrgs = (integrationAuthId: string) => {
|
||||||
return useQuery({
|
return useQuery({
|
||||||
queryKey: integrationAuthKeys.getIntegrationAuthQoveryOrgs(integrationAuthId),
|
queryKey: integrationAuthKeys.getIntegrationAuthQoveryOrgs(integrationAuthId),
|
||||||
|
@ -62,7 +62,6 @@ export const useCreateIntegration = () => {
|
|||||||
secretPrefix?: string;
|
secretPrefix?: string;
|
||||||
secretSuffix?: string;
|
secretSuffix?: string;
|
||||||
initialSyncBehavior?: string;
|
initialSyncBehavior?: string;
|
||||||
shouldAutoRedeploy?: boolean;
|
|
||||||
}
|
}
|
||||||
}) => {
|
}) => {
|
||||||
const { data: { integration } } = await apiRequest.post("/api/v1/integration", {
|
const { data: { integration } } = await apiRequest.post("/api/v1/integration", {
|
||||||
|
@ -11,21 +11,22 @@ export type TCloudIntegration = {
|
|||||||
|
|
||||||
export type TIntegration = {
|
export type TIntegration = {
|
||||||
id: string;
|
id: string;
|
||||||
isActive: boolean;
|
projectId: string;
|
||||||
url?: string;
|
|
||||||
app?: string;
|
|
||||||
appId?: string;
|
|
||||||
targetEnvironment?: string;
|
|
||||||
targetEnvironmentId?: string;
|
|
||||||
targetService?: string;
|
|
||||||
targetServiceId?: string;
|
|
||||||
owner?: string;
|
|
||||||
path?: string;
|
|
||||||
region?: string;
|
|
||||||
scope?: string;
|
|
||||||
integration: string;
|
|
||||||
integrationAuthId: string;
|
|
||||||
envId: string;
|
envId: string;
|
||||||
|
environment: { slug: string; name: string; id: string };
|
||||||
|
isActive: boolean;
|
||||||
|
url: any;
|
||||||
|
app: string;
|
||||||
|
appId: string;
|
||||||
|
targetEnvironment: string;
|
||||||
|
targetEnvironmentId: string;
|
||||||
|
targetService: string;
|
||||||
|
targetServiceId: string;
|
||||||
|
owner: string;
|
||||||
|
path: string;
|
||||||
|
region: string;
|
||||||
|
integration: string;
|
||||||
|
integrationAuth: string;
|
||||||
secretPath: string;
|
secretPath: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { Controller, useForm } from "react-hook-form";
|
|
||||||
import Head from "next/head";
|
import Head from "next/head";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
@ -13,14 +12,11 @@ import {
|
|||||||
faCircleInfo
|
faCircleInfo
|
||||||
} from "@fortawesome/free-solid-svg-icons";
|
} from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import { yupResolver } from "@hookform/resolvers/yup";
|
|
||||||
import axios from "axios";
|
|
||||||
import { motion } from "framer-motion";
|
import { motion } from "framer-motion";
|
||||||
import queryString from "query-string";
|
import queryString from "query-string";
|
||||||
import { twMerge } from "tailwind-merge";
|
|
||||||
import * as yup from "yup";
|
|
||||||
|
|
||||||
import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
|
import { useCreateIntegration } from "@app/hooks/api";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
Card,
|
Card,
|
||||||
@ -37,131 +33,62 @@ import {
|
|||||||
TabList,
|
TabList,
|
||||||
TabPanel,
|
TabPanel,
|
||||||
Tabs
|
Tabs
|
||||||
} from "@app/components/v2";
|
} from "../../../components/v2";
|
||||||
import {
|
import {
|
||||||
useCreateIntegration,
|
|
||||||
useGetIntegrationAuthApps,
|
useGetIntegrationAuthApps,
|
||||||
useGetIntegrationAuthById,
|
useGetIntegrationAuthById
|
||||||
useGetIntegrationAuthGithubEnvs,
|
} from "../../../hooks/api/integrationAuth";
|
||||||
useGetIntegrationAuthGithubOrgs,
|
import { useGetWorkspaceById } from "../../../hooks/api/workspace";
|
||||||
useGetWorkspaceById
|
|
||||||
} from "@app/hooks/api";
|
|
||||||
|
|
||||||
enum TabSections {
|
enum TabSections {
|
||||||
Connection = "connection",
|
Connection = "connection",
|
||||||
Options = "options"
|
Options = "options"
|
||||||
}
|
}
|
||||||
|
|
||||||
const targetEnv = ["github-repo", "github-org", "github-env"] as const;
|
|
||||||
type TargetEnv = (typeof targetEnv)[number];
|
|
||||||
|
|
||||||
const schema = yup.object({
|
|
||||||
selectedSourceEnvironment: yup.string().trim().required("Project Environment is required"),
|
|
||||||
secretPath: yup.string().trim().required("Secrets Path is required"),
|
|
||||||
secretSuffix: yup.string().trim().optional(),
|
|
||||||
|
|
||||||
scope: yup.mixed<TargetEnv>().oneOf(targetEnv.slice()).required(),
|
|
||||||
|
|
||||||
repoIds: yup.mixed().when("scope", {
|
|
||||||
is: "github-repo",
|
|
||||||
then: yup.array(yup.string().required()).min(1, "Select at least one repositories")
|
|
||||||
}),
|
|
||||||
|
|
||||||
repoId: yup.mixed().when("scope", {
|
|
||||||
is: "github-env",
|
|
||||||
then: yup.string().required("Repository is required")
|
|
||||||
}),
|
|
||||||
|
|
||||||
repoName: yup.mixed().when("scope", {
|
|
||||||
is: "github-env",
|
|
||||||
then: yup.string().required("Repository is required")
|
|
||||||
}),
|
|
||||||
|
|
||||||
repoOwner: yup.mixed().when("scope", {
|
|
||||||
is: "github-env",
|
|
||||||
then: yup.string().required("Repository is required")
|
|
||||||
}),
|
|
||||||
|
|
||||||
envId: yup.mixed().when("scope", {
|
|
||||||
is: "github-env",
|
|
||||||
then: yup.string().required("Environment is required")
|
|
||||||
}),
|
|
||||||
|
|
||||||
orgId: yup.mixed().when("scope", {
|
|
||||||
is: "github-org",
|
|
||||||
then: yup.string().required("Organization is required")
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
type FormData = yup.InferType<typeof schema>;
|
|
||||||
|
|
||||||
export default function GitHubCreateIntegrationPage() {
|
export default function GitHubCreateIntegrationPage() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { mutateAsync } = useCreateIntegration();
|
const { mutateAsync } = useCreateIntegration();
|
||||||
const { createNotification } = useNotificationContext();
|
|
||||||
|
|
||||||
const integrationAuthId =
|
const { integrationAuthId } = queryString.parse(router.asPath.split("?")[1]);
|
||||||
(queryString.parse(router.asPath.split("?")[1]).integrationAuthId as string) ?? "";
|
|
||||||
|
|
||||||
const { data: workspace } = useGetWorkspaceById(localStorage.getItem("projectData.id") ?? "");
|
const { data: workspace } = useGetWorkspaceById(localStorage.getItem("projectData.id") ?? "");
|
||||||
const { data: integrationAuth } = useGetIntegrationAuthById(integrationAuthId);
|
const { data: integrationAuth } = useGetIntegrationAuthById((integrationAuthId as string) ?? "");
|
||||||
|
|
||||||
const { data: integrationAuthApps, isLoading: isIntegrationAuthAppsLoading } =
|
const { data: integrationAuthApps, isLoading: isIntegrationAuthAppsLoading } =
|
||||||
useGetIntegrationAuthApps({
|
useGetIntegrationAuthApps({
|
||||||
integrationAuthId
|
integrationAuthId: (integrationAuthId as string) ?? ""
|
||||||
});
|
});
|
||||||
|
|
||||||
const { data: integrationAuthOrgs } = useGetIntegrationAuthGithubOrgs(
|
const [selectedSourceEnvironment, setSelectedSourceEnvironment] = useState("");
|
||||||
integrationAuthId as string
|
const [secretPath, setSecretPath] = useState("/");
|
||||||
);
|
const [targetAppIds, setTargetAppIds] = useState<string[]>([]);
|
||||||
|
const [secretSuffix, setSecretSuffix] = useState("");
|
||||||
const { control, handleSubmit, watch, setValue } = useForm<FormData>({
|
|
||||||
resolver: yupResolver(schema),
|
|
||||||
defaultValues: {
|
|
||||||
secretPath: "/",
|
|
||||||
scope: "github-repo",
|
|
||||||
repoIds: []
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const scope = watch("scope");
|
|
||||||
const repoId = watch("repoId");
|
|
||||||
const repoIds = watch("repoIds");
|
|
||||||
const repoName = watch("repoName");
|
|
||||||
const repoOwner = watch("repoOwner");
|
|
||||||
|
|
||||||
const { data: integrationAuthGithubEnvs } = useGetIntegrationAuthGithubEnvs(
|
|
||||||
integrationAuthId as string,
|
|
||||||
repoName,
|
|
||||||
repoOwner
|
|
||||||
);
|
|
||||||
|
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (workspace) {
|
if (workspace) {
|
||||||
setValue("selectedSourceEnvironment", workspace.environments[0].slug);
|
setSelectedSourceEnvironment(workspace.environments[0].slug);
|
||||||
}
|
}
|
||||||
}, [workspace]);
|
}, [workspace]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (integrationAuthGithubEnvs && integrationAuthGithubEnvs?.length > 0) {
|
if (integrationAuthApps) {
|
||||||
setValue("envId", integrationAuthGithubEnvs[0].envId);
|
if (integrationAuthApps.length > 0) {
|
||||||
|
setTargetAppIds([String(integrationAuthApps[0].appId)]);
|
||||||
} else {
|
} else {
|
||||||
setValue("envId", undefined);
|
setTargetAppIds(["none"]);
|
||||||
}
|
}
|
||||||
}, [integrationAuthGithubEnvs]);
|
}
|
||||||
|
}, [integrationAuthApps]);
|
||||||
|
|
||||||
const onFormSubmit = async (data: FormData) => {
|
const handleButtonClick = async () => {
|
||||||
try {
|
try {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
|
|
||||||
if (!integrationAuth?.id) return;
|
if (!integrationAuth?.id) return;
|
||||||
|
|
||||||
switch (data.scope) {
|
|
||||||
case "github-repo": {
|
|
||||||
const targetApps = integrationAuthApps?.filter((integrationAuthApp) =>
|
const targetApps = integrationAuthApps?.filter((integrationAuthApp) =>
|
||||||
data.repoIds?.includes(String(integrationAuthApp.appId))
|
targetAppIds.includes(String(integrationAuthApp.appId))
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!targetApps) return;
|
if (!targetApps) return;
|
||||||
@ -171,87 +98,41 @@ export default function GitHubCreateIntegrationPage() {
|
|||||||
await mutateAsync({
|
await mutateAsync({
|
||||||
integrationAuthId: integrationAuth?.id,
|
integrationAuthId: integrationAuth?.id,
|
||||||
isActive: true,
|
isActive: true,
|
||||||
scope: data.scope,
|
app: targetApp.name,
|
||||||
secretPath: data.secretPath,
|
sourceEnvironment: selectedSourceEnvironment,
|
||||||
sourceEnvironment: data.selectedSourceEnvironment,
|
owner: targetApp.owner,
|
||||||
app: targetApp.name, // repo name
|
secretPath,
|
||||||
owner: targetApp.owner, // repo owner
|
|
||||||
metadata: {
|
metadata: {
|
||||||
secretSuffix: data.secretSuffix
|
secretSuffix
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "github-org":
|
|
||||||
await mutateAsync({
|
|
||||||
integrationAuthId: integrationAuth?.id,
|
|
||||||
isActive: true,
|
|
||||||
secretPath: data.secretPath,
|
|
||||||
sourceEnvironment: data.selectedSourceEnvironment,
|
|
||||||
scope: data.scope,
|
|
||||||
owner: integrationAuthOrgs?.find((e) => e.orgId === data.orgId)?.name,
|
|
||||||
metadata: {
|
|
||||||
secretSuffix: data.secretSuffix
|
|
||||||
}
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "github-env":
|
|
||||||
await mutateAsync({
|
|
||||||
integrationAuthId: integrationAuth?.id,
|
|
||||||
isActive: true,
|
|
||||||
secretPath: data.secretPath,
|
|
||||||
sourceEnvironment: data.selectedSourceEnvironment,
|
|
||||||
scope: data.scope,
|
|
||||||
app: repoName,
|
|
||||||
appId: data.repoId,
|
|
||||||
owner: repoOwner,
|
|
||||||
targetEnvironmentId: data.envId,
|
|
||||||
metadata: {
|
|
||||||
secretSuffix: data.secretSuffix
|
|
||||||
}
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Error("Invalid scope");
|
|
||||||
}
|
|
||||||
|
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
router.push(`/integrations/${localStorage.getItem("projectData.id")}`);
|
router.push(`/integrations/${localStorage.getItem("projectData.id")}`);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
|
|
||||||
let errorMessage: string = "Something went wrong!";
|
|
||||||
if (axios.isAxiosError(err)) {
|
|
||||||
const { message } = err?.response?.data as { message: string };
|
|
||||||
errorMessage = message;
|
|
||||||
}
|
|
||||||
|
|
||||||
createNotification({
|
|
||||||
text: errorMessage,
|
|
||||||
type: "error"
|
|
||||||
});
|
|
||||||
setIsLoading(false);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return integrationAuth && workspace && integrationAuthApps ? (
|
return integrationAuth &&
|
||||||
<div className="flex h-full w-full flex-col items-center justify-center py-4">
|
workspace &&
|
||||||
|
selectedSourceEnvironment &&
|
||||||
|
integrationAuthApps &&
|
||||||
|
targetAppIds ? (
|
||||||
|
<div className="flex h-full w-full flex-col items-center justify-center">
|
||||||
<Head>
|
<Head>
|
||||||
<title>Set Up GitHub Integration</title>
|
<title>Set Up GitHub Integration</title>
|
||||||
<link rel="icon" href="/infisical.ico" />
|
<link rel="icon" href="/infisical.ico" />
|
||||||
</Head>
|
</Head>
|
||||||
<Card className="max-w-lg rounded-md border border-mineshaft-600 p-0">
|
<Card className="max-w-lg rounded-md border border-mineshaft-600 p-0">
|
||||||
<form onSubmit={handleSubmit(onFormSubmit)} className="px-6">
|
|
||||||
<CardTitle
|
<CardTitle
|
||||||
className="px-0 text-left text-xl"
|
className="px-6 text-left text-xl"
|
||||||
subTitle="Choose which environment in Infisical you want to sync to environment variables in GitHub."
|
subTitle="Choose which environment in Infisical you want to sync to environment variables in GitHub."
|
||||||
>
|
>
|
||||||
<div className="flex flex-row items-center">
|
<div className="flex flex-row items-center">
|
||||||
<div className="flex items-center rounded-full bg-mineshaft-200">
|
<div className="inline flex items-center rounded-full bg-mineshaft-200">
|
||||||
<Image
|
<Image
|
||||||
src="/images/integrations/GitHub.png"
|
src="/images/integrations/GitHub.png"
|
||||||
height={30}
|
height={30}
|
||||||
@ -274,7 +155,7 @@ export default function GitHubCreateIntegrationPage() {
|
|||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
<Tabs defaultValue={TabSections.Connection}>
|
<Tabs defaultValue={TabSections.Connection} className="px-6">
|
||||||
<TabList>
|
<TabList>
|
||||||
<div className="flex w-full flex-row border-b border-mineshaft-600">
|
<div className="flex w-full flex-row border-b border-mineshaft-600">
|
||||||
<Tab value={TabSections.Connection}>Connection</Tab>
|
<Tab value={TabSections.Connection}>Connection</Tab>
|
||||||
@ -289,83 +170,40 @@ export default function GitHubCreateIntegrationPage() {
|
|||||||
animate={{ opacity: 1, translateX: 0 }}
|
animate={{ opacity: 1, translateX: 0 }}
|
||||||
exit={{ opacity: 0, translateX: 30 }}
|
exit={{ opacity: 0, translateX: 30 }}
|
||||||
>
|
>
|
||||||
<Controller
|
<FormControl label="Project Environment">
|
||||||
control={control}
|
|
||||||
name="selectedSourceEnvironment"
|
|
||||||
render={({ field: { onChange, ...field }, fieldState: { error } }) => (
|
|
||||||
<FormControl
|
|
||||||
label="Project Environment"
|
|
||||||
errorText={error?.message}
|
|
||||||
isError={Boolean(error)}
|
|
||||||
>
|
|
||||||
<Select
|
<Select
|
||||||
defaultValue={field.value}
|
value={selectedSourceEnvironment}
|
||||||
onValueChange={onChange}
|
onValueChange={(val) => setSelectedSourceEnvironment(val)}
|
||||||
className="w-full border border-mineshaft-500"
|
className="w-full border border-mineshaft-500"
|
||||||
>
|
>
|
||||||
{workspace?.environments.map((sourceEnvironment) => (
|
{workspace?.environments.map((sourceEnvironment) => (
|
||||||
<SelectItem
|
<SelectItem
|
||||||
value={sourceEnvironment.slug}
|
value={sourceEnvironment.slug}
|
||||||
key={`source-environment-${sourceEnvironment.slug}`}
|
key={`azure-key-vault-environment-${sourceEnvironment.slug}`}
|
||||||
>
|
>
|
||||||
{sourceEnvironment.name}
|
{sourceEnvironment.name}
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
))}
|
))}
|
||||||
</Select>
|
</Select>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
)}
|
<FormControl label="Secrets Path">
|
||||||
|
<Input
|
||||||
|
value={secretPath}
|
||||||
|
onChange={(evt) => setSecretPath(evt.target.value)}
|
||||||
|
placeholder="Provide a path, default is /"
|
||||||
/>
|
/>
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
name="secretPath"
|
|
||||||
render={({ field, fieldState: { error } }) => (
|
|
||||||
<FormControl
|
|
||||||
label="Secrets Path"
|
|
||||||
errorText={error?.message}
|
|
||||||
isError={Boolean(error)}
|
|
||||||
>
|
|
||||||
<Input {...field} placeholder="Provide a path, default is /" />
|
|
||||||
</FormControl>
|
</FormControl>
|
||||||
)}
|
<FormControl label="GitHub Repo">
|
||||||
/>
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
name="scope"
|
|
||||||
render={({ field: { onChange, ...field }, fieldState: { error } }) => (
|
|
||||||
<FormControl label="Scope" errorText={error?.message} isError={Boolean(error)}>
|
|
||||||
<Select
|
|
||||||
defaultValue={field.value}
|
|
||||||
onValueChange={onChange}
|
|
||||||
className="w-full border border-mineshaft-500"
|
|
||||||
>
|
|
||||||
<SelectItem value="github-org">Organization</SelectItem>
|
|
||||||
<SelectItem value="github-repo">Repository</SelectItem>
|
|
||||||
<SelectItem value="github-env">Repository Environment</SelectItem>
|
|
||||||
</Select>
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
{scope === "github-repo" && (
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
name="repoIds"
|
|
||||||
render={({ field: { onChange }, fieldState: { error } }) => (
|
|
||||||
<FormControl
|
|
||||||
label="Repositories"
|
|
||||||
isError={Boolean(error?.message)}
|
|
||||||
errorText={error?.message}
|
|
||||||
>
|
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
{integrationAuthApps.length > 0 ? (
|
{integrationAuthApps.length > 0 ? (
|
||||||
<div className="inline-flex w-full cursor-pointer items-center justify-between rounded-md border border-mineshaft-600 bg-mineshaft-900 px-3 py-2 font-inter text-sm font-normal text-bunker-200 outline-none data-[placeholder]:text-mineshaft-200">
|
<div className="inline-flex w-full cursor-pointer items-center justify-between rounded-md border border-mineshaft-600 bg-mineshaft-900 px-3 py-2 font-inter text-sm font-normal text-bunker-200 outline-none data-[placeholder]:text-mineshaft-200">
|
||||||
{repoIds.length === 1
|
{targetAppIds.length === 1
|
||||||
? integrationAuthApps?.reduce(
|
? integrationAuthApps?.find(
|
||||||
(acc, { appId, name, owner }) =>
|
(integrationAuthApp) =>
|
||||||
repoIds[0] === appId ? `${owner}/${name}` : acc,
|
targetAppIds[0] === String(integrationAuthApp.appId)
|
||||||
""
|
)?.name
|
||||||
)
|
: `${targetAppIds.length} repositories selected`}
|
||||||
: `${repoIds.length} repositories selected`}
|
|
||||||
<FontAwesomeIcon icon={faAngleDown} className="text-xs" />
|
<FontAwesomeIcon icon={faAngleDown} className="text-xs" />
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
@ -380,25 +218,25 @@ export default function GitHubCreateIntegrationPage() {
|
|||||||
>
|
>
|
||||||
{integrationAuthApps.length > 0 ? (
|
{integrationAuthApps.length > 0 ? (
|
||||||
integrationAuthApps.map((integrationAuthApp) => {
|
integrationAuthApps.map((integrationAuthApp) => {
|
||||||
const isSelected = repoIds.includes(
|
const isSelected = targetAppIds.includes(String(integrationAuthApp.appId));
|
||||||
String(integrationAuthApp.appId)
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (repoIds.includes(String(integrationAuthApp.appId))) {
|
if (targetAppIds.includes(String(integrationAuthApp.appId))) {
|
||||||
onChange(
|
setTargetAppIds(
|
||||||
repoIds.filter(
|
targetAppIds.filter(
|
||||||
(appId: string) =>
|
(appId) => appId !== String(integrationAuthApp.appId)
|
||||||
appId !== String(integrationAuthApp.appId)
|
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
onChange([...repoIds, String(integrationAuthApp.appId)]);
|
setTargetAppIds([
|
||||||
|
...targetAppIds,
|
||||||
|
String(integrationAuthApp.appId)
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
key={`repos-id-${integrationAuthApp.appId}`}
|
key={integrationAuthApp.appId}
|
||||||
icon={
|
icon={
|
||||||
isSelected ? (
|
isSelected ? (
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
@ -412,7 +250,7 @@ export default function GitHubCreateIntegrationPage() {
|
|||||||
iconPos="left"
|
iconPos="left"
|
||||||
className="w-[28.4rem] text-sm"
|
className="w-[28.4rem] text-sm"
|
||||||
>
|
>
|
||||||
{integrationAuthApp.owner}/{integrationAuthApp.name}
|
{integrationAuthApp.name}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
@ -422,120 +260,6 @@ export default function GitHubCreateIntegrationPage() {
|
|||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{scope === "github-org" && (
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
name="orgId"
|
|
||||||
render={({ field: { onChange, ...field }, fieldState: { error } }) => (
|
|
||||||
<FormControl
|
|
||||||
label="Organization"
|
|
||||||
errorText={
|
|
||||||
integrationAuthOrgs?.length ? error?.message : "No organizations found"
|
|
||||||
}
|
|
||||||
isError={Boolean(integrationAuthOrgs?.length && error?.message)}
|
|
||||||
>
|
|
||||||
<Select
|
|
||||||
value={field.value}
|
|
||||||
onValueChange={onChange}
|
|
||||||
className="w-full border border-mineshaft-500"
|
|
||||||
>
|
|
||||||
{integrationAuthOrgs &&
|
|
||||||
integrationAuthOrgs.map(({ name, orgId }) => (
|
|
||||||
<SelectItem key={`github-organization-${orgId}`} value={orgId}>
|
|
||||||
{name}
|
|
||||||
</SelectItem>
|
|
||||||
))}
|
|
||||||
</Select>
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{scope === "github-env" && (
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
name="repoId"
|
|
||||||
render={({ field: { onChange, ...field }, fieldState: { error } }) => (
|
|
||||||
<FormControl
|
|
||||||
label="Repository"
|
|
||||||
errorText={error?.message}
|
|
||||||
isError={Boolean(error)}
|
|
||||||
>
|
|
||||||
<Select
|
|
||||||
value={field.value}
|
|
||||||
onValueChange={(e) => {
|
|
||||||
const selectedRepo = integrationAuthApps.find((app) => app.appId === e);
|
|
||||||
setValue("repoName", selectedRepo?.name);
|
|
||||||
setValue("repoOwner", selectedRepo?.owner);
|
|
||||||
onChange(e);
|
|
||||||
}}
|
|
||||||
className="w-full border border-mineshaft-500"
|
|
||||||
>
|
|
||||||
{integrationAuthApps?.length ? (
|
|
||||||
integrationAuthApps.map((app) => {
|
|
||||||
return (
|
|
||||||
<SelectItem
|
|
||||||
value={app.appId as string}
|
|
||||||
key={`repo-id-${app.appId}`}
|
|
||||||
className="w-[28.4rem] text-sm"
|
|
||||||
>
|
|
||||||
{app.owner}/{app.name}
|
|
||||||
</SelectItem>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
) : (
|
|
||||||
<div />
|
|
||||||
)}
|
|
||||||
</Select>
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{scope === "github-env" && (
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
name="envId"
|
|
||||||
render={({ field: { onChange, ...field }, fieldState: { error } }) => (
|
|
||||||
<FormControl
|
|
||||||
label="Environment"
|
|
||||||
errorText={
|
|
||||||
integrationAuthGithubEnvs?.length
|
|
||||||
? error?.message
|
|
||||||
: "No Environment found"
|
|
||||||
}
|
|
||||||
isError={Boolean(integrationAuthGithubEnvs?.length || error?.message)}
|
|
||||||
>
|
|
||||||
<Select
|
|
||||||
value={field.value}
|
|
||||||
onValueChange={onChange}
|
|
||||||
isDisabled={!repoId}
|
|
||||||
className={twMerge(
|
|
||||||
"w-full border border-mineshaft-500",
|
|
||||||
!repoId && "h-10 cursor-not-allowed"
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{integrationAuthGithubEnvs?.length ? (
|
|
||||||
integrationAuthGithubEnvs.map((githubEnv) => {
|
|
||||||
return (
|
|
||||||
<SelectItem
|
|
||||||
value={githubEnv.name as string}
|
|
||||||
key={`env-id-${githubEnv.envId}`}
|
|
||||||
className="w-[28.4rem] text-sm"
|
|
||||||
>
|
|
||||||
{githubEnv.name}
|
|
||||||
</SelectItem>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
) : (
|
|
||||||
<div />
|
|
||||||
)}
|
|
||||||
</Select>
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
<TabPanel value={TabSections.Options}>
|
<TabPanel value={TabSections.Options}>
|
||||||
@ -546,38 +270,26 @@ export default function GitHubCreateIntegrationPage() {
|
|||||||
animate={{ opacity: 1, translateX: 0 }}
|
animate={{ opacity: 1, translateX: 0 }}
|
||||||
exit={{ opacity: 0, translateX: 30 }}
|
exit={{ opacity: 0, translateX: 30 }}
|
||||||
>
|
>
|
||||||
<Controller
|
<FormControl label="Append Secret Names with..." className="pb-[9.75rem]">
|
||||||
control={control}
|
|
||||||
name="secretSuffix"
|
|
||||||
render={({ field, fieldState: { error } }) => (
|
|
||||||
<FormControl
|
|
||||||
label="Append Secret Names with..."
|
|
||||||
className="pb-[9.75rem]"
|
|
||||||
errorText={error?.message}
|
|
||||||
isError={Boolean(error)}
|
|
||||||
>
|
|
||||||
<Input
|
<Input
|
||||||
{...field}
|
value={secretSuffix}
|
||||||
|
onChange={(evt) => setSecretSuffix(evt.target.value)}
|
||||||
placeholder="Provide a suffix for secret names, default is no suffix"
|
placeholder="Provide a suffix for secret names, default is no suffix"
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
<div className="flex w-full justify-end">
|
|
||||||
<Button
|
<Button
|
||||||
type="submit"
|
onClick={handleButtonClick}
|
||||||
color="mineshaft"
|
color="mineshaft"
|
||||||
variant="outline_bg"
|
variant="outline_bg"
|
||||||
className="mb-6"
|
className="mb-6 ml-auto mr-6"
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
|
isDisabled={integrationAuthApps.length === 0 || targetAppIds.length === 0}
|
||||||
>
|
>
|
||||||
Create Integration
|
Create Integration
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</Card>
|
</Card>
|
||||||
<div className="mt-6 w-full max-w-md border-t border-mineshaft-800" />
|
<div className="mt-6 w-full max-w-md border-t border-mineshaft-800" />
|
||||||
<div className="mt-6 flex w-full max-w-lg flex-col rounded-md border border-mineshaft-600 bg-mineshaft-800 p-4">
|
<div className="mt-6 flex w-full max-w-lg flex-col rounded-md border border-mineshaft-600 bg-mineshaft-800 p-4">
|
||||||
@ -606,7 +318,7 @@ export default function GitHubCreateIntegrationPage() {
|
|||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex h-max max-w-md flex-col rounded-md border border-mineshaft-600 bg-mineshaft-800 p-6 text-center text-mineshaft-200">
|
<div className="flex h-max max-w-md flex-col rounded-md border border-mineshaft-600 bg-mineshaft-800 p-6 text-center text-mineshaft-200">
|
||||||
<FontAwesomeIcon icon={faBugs} className="li my-2 inline text-6xl" />
|
<FontAwesomeIcon icon={faBugs} className="inlineli my-2 text-6xl" />
|
||||||
<p>
|
<p>
|
||||||
Something went wrong. Please contact{" "}
|
Something went wrong. Please contact{" "}
|
||||||
<a
|
<a
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { Controller, useForm } from "react-hook-form";
|
|
||||||
import Head from "next/head";
|
import Head from "next/head";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
@ -11,9 +10,7 @@ import {
|
|||||||
faCircleInfo
|
faCircleInfo
|
||||||
} from "@fortawesome/free-solid-svg-icons";
|
} from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import { yupResolver } from "@hookform/resolvers/yup";
|
|
||||||
import queryString from "query-string";
|
import queryString from "query-string";
|
||||||
import * as yup from "yup";
|
|
||||||
|
|
||||||
import { useCreateIntegration } from "@app/hooks/api";
|
import { useCreateIntegration } from "@app/hooks/api";
|
||||||
|
|
||||||
@ -24,8 +21,7 @@ import {
|
|||||||
FormControl,
|
FormControl,
|
||||||
Input,
|
Input,
|
||||||
Select,
|
Select,
|
||||||
SelectItem,
|
SelectItem
|
||||||
Switch
|
|
||||||
} from "../../../components/v2";
|
} from "../../../components/v2";
|
||||||
import {
|
import {
|
||||||
useGetIntegrationAuthApps,
|
useGetIntegrationAuthApps,
|
||||||
@ -33,29 +29,10 @@ import {
|
|||||||
} from "../../../hooks/api/integrationAuth";
|
} from "../../../hooks/api/integrationAuth";
|
||||||
import { useGetWorkspaceById } from "../../../hooks/api/workspace";
|
import { useGetWorkspaceById } from "../../../hooks/api/workspace";
|
||||||
|
|
||||||
const schema = yup.object({
|
|
||||||
selectedSourceEnvironment: yup.string().required("Source environment is required"),
|
|
||||||
secretPath: yup.string().required("Secret path is required"),
|
|
||||||
targetAppId: yup.string().required("Render service is required"),
|
|
||||||
shouldAutoRedeploy: yup.boolean()
|
|
||||||
});
|
|
||||||
|
|
||||||
type FormData = yup.InferType<typeof schema>;
|
|
||||||
|
|
||||||
export default function RenderCreateIntegrationPage() {
|
export default function RenderCreateIntegrationPage() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { mutateAsync } = useCreateIntegration();
|
const { mutateAsync } = useCreateIntegration();
|
||||||
|
|
||||||
const { control, handleSubmit, setValue, watch } = useForm<FormData>({
|
|
||||||
resolver: yupResolver(schema),
|
|
||||||
defaultValues: {
|
|
||||||
secretPath: "/",
|
|
||||||
shouldAutoRedeploy: false
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const selectedSourceEnvironment = watch("selectedSourceEnvironment");
|
|
||||||
|
|
||||||
const { integrationAuthId } = queryString.parse(router.asPath.split("?")[1]);
|
const { integrationAuthId } = queryString.parse(router.asPath.split("?")[1]);
|
||||||
|
|
||||||
const { data: workspace } = useGetWorkspaceById(localStorage.getItem("projectData.id") ?? "");
|
const { data: workspace } = useGetWorkspaceById(localStorage.getItem("projectData.id") ?? "");
|
||||||
@ -67,29 +44,28 @@ export default function RenderCreateIntegrationPage() {
|
|||||||
integrationAuthId: (integrationAuthId as string) ?? ""
|
integrationAuthId: (integrationAuthId as string) ?? ""
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const [selectedSourceEnvironment, setSelectedSourceEnvironment] = useState("");
|
||||||
|
const [targetApp, setTargetApp] = useState("");
|
||||||
|
const [secretPath, setSecretPath] = useState("/");
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (workspace) {
|
if (workspace) {
|
||||||
setValue("selectedSourceEnvironment", workspace.environments[0].slug);
|
setSelectedSourceEnvironment(workspace.environments[0].slug);
|
||||||
}
|
}
|
||||||
}, [workspace]);
|
}, [workspace]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (integrationAuthApps) {
|
if (integrationAuthApps) {
|
||||||
if (integrationAuthApps.length > 0) {
|
if (integrationAuthApps.length > 0) {
|
||||||
setValue("targetAppId", integrationAuthApps[0].appId as string);
|
setTargetApp(integrationAuthApps[0].name);
|
||||||
} else {
|
} else {
|
||||||
setValue("targetAppId", "none");
|
setTargetApp("none");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [integrationAuthApps]);
|
}, [integrationAuthApps]);
|
||||||
|
|
||||||
const onFormSubmit = async ({
|
const handleButtonClick = async () => {
|
||||||
secretPath,
|
|
||||||
targetAppId,
|
|
||||||
shouldAutoRedeploy
|
|
||||||
}: FormData) => {
|
|
||||||
try {
|
try {
|
||||||
if (!integrationAuth?.id) return;
|
if (!integrationAuth?.id) return;
|
||||||
|
|
||||||
@ -98,15 +74,12 @@ export default function RenderCreateIntegrationPage() {
|
|||||||
await mutateAsync({
|
await mutateAsync({
|
||||||
integrationAuthId: integrationAuth?.id,
|
integrationAuthId: integrationAuth?.id,
|
||||||
isActive: true,
|
isActive: true,
|
||||||
app: integrationAuthApps?.find(
|
app: targetApp,
|
||||||
(integrationAuthApp) => integrationAuthApp.appId === targetAppId
|
appId: integrationAuthApps?.find(
|
||||||
)?.name,
|
(integrationAuthApp) => integrationAuthApp.name === targetApp
|
||||||
appId: targetAppId,
|
)?.appId,
|
||||||
sourceEnvironment: selectedSourceEnvironment,
|
sourceEnvironment: selectedSourceEnvironment,
|
||||||
secretPath,
|
secretPath
|
||||||
metadata: {
|
|
||||||
shouldAutoRedeploy
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
@ -115,16 +88,14 @@ export default function RenderCreateIntegrationPage() {
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
return integrationAuth &&
|
return integrationAuth &&
|
||||||
workspace &&
|
workspace &&
|
||||||
selectedSourceEnvironment &&
|
selectedSourceEnvironment &&
|
||||||
integrationAuthApps ? (
|
integrationAuthApps &&
|
||||||
<form
|
targetApp ? (
|
||||||
onSubmit={handleSubmit(onFormSubmit)}
|
<div className="flex h-full w-full flex-col items-center justify-center">
|
||||||
className="flex h-full w-full flex-col items-center justify-center"
|
|
||||||
>
|
|
||||||
<Head>
|
<Head>
|
||||||
<title>Set Up Render Integration</title>
|
<title>Set Up Render Integration</title>
|
||||||
<link rel="icon" href="/infisical.ico" />
|
<link rel="icon" href="/infisical.ico" />
|
||||||
@ -158,21 +129,11 @@ export default function RenderCreateIntegrationPage() {
|
|||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
<div className="px-6">
|
<FormControl label="Project Environment" className="px-6">
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
name="selectedSourceEnvironment"
|
|
||||||
render={({ field: { onChange, ...field }, fieldState: { error } }) => (
|
|
||||||
<FormControl
|
|
||||||
label="Project Environment"
|
|
||||||
errorText={error?.message}
|
|
||||||
isError={Boolean(error)}
|
|
||||||
>
|
|
||||||
<Select
|
<Select
|
||||||
defaultValue={field.value}
|
value={selectedSourceEnvironment}
|
||||||
{...field}
|
onValueChange={(val) => setSelectedSourceEnvironment(val)}
|
||||||
onValueChange={(e) => onChange(e)}
|
className="w-full border border-mineshaft-500"
|
||||||
className="w-full"
|
|
||||||
>
|
>
|
||||||
{workspace?.environments.map((sourceEnvironment) => (
|
{workspace?.environments.map((sourceEnvironment) => (
|
||||||
<SelectItem
|
<SelectItem
|
||||||
@ -184,45 +145,25 @@ export default function RenderCreateIntegrationPage() {
|
|||||||
))}
|
))}
|
||||||
</Select>
|
</Select>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
)}
|
<FormControl label="Secrets Path" className="px-6">
|
||||||
|
<Input
|
||||||
|
value={secretPath}
|
||||||
|
onChange={(evt) => setSecretPath(evt.target.value)}
|
||||||
|
placeholder="Provide a path, default is /"
|
||||||
/>
|
/>
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
defaultValue=""
|
|
||||||
name="secretPath"
|
|
||||||
render={({ field, fieldState: { error } }) => (
|
|
||||||
<FormControl
|
|
||||||
label="Secrets Path"
|
|
||||||
isError={Boolean(error)}
|
|
||||||
errorText={error?.message}
|
|
||||||
>
|
|
||||||
<Input {...field} placeholder="/" />
|
|
||||||
</FormControl>
|
</FormControl>
|
||||||
)}
|
<FormControl label="Render Service" className="px-6">
|
||||||
/>
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
name="targetAppId"
|
|
||||||
render={({ field: { onChange, ...field }, fieldState: { error } }) => {
|
|
||||||
return (
|
|
||||||
<FormControl
|
|
||||||
label="Render Service"
|
|
||||||
errorText={error?.message}
|
|
||||||
isError={Boolean(error)}
|
|
||||||
>
|
|
||||||
<Select
|
<Select
|
||||||
{...field}
|
value={targetApp}
|
||||||
onValueChange={(e) => {
|
onValueChange={(val) => setTargetApp(val)}
|
||||||
if (e === "") return;
|
className="w-full border border-mineshaft-500"
|
||||||
onChange(e);
|
isDisabled={integrationAuthApps.length === 0}
|
||||||
}}
|
|
||||||
className="w-full"
|
|
||||||
>
|
>
|
||||||
{integrationAuthApps.length > 0 ? (
|
{integrationAuthApps.length > 0 ? (
|
||||||
integrationAuthApps.map((integrationAuthApp) => (
|
integrationAuthApps.map((integrationAuthApp) => (
|
||||||
<SelectItem
|
<SelectItem
|
||||||
value={String(integrationAuthApp.appId as string)}
|
value={integrationAuthApp.name}
|
||||||
key={`target-app-${String(integrationAuthApp.appId)}`}
|
key={`target-app-${integrationAuthApp.name}`}
|
||||||
>
|
>
|
||||||
{integrationAuthApp.name}
|
{integrationAuthApp.name}
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
@ -234,31 +175,11 @@ export default function RenderCreateIntegrationPage() {
|
|||||||
)}
|
)}
|
||||||
</Select>
|
</Select>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<div className="mt-8 mb-[2.36rem] ml-1">
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
name="shouldAutoRedeploy"
|
|
||||||
render={({ field: { onChange, value } }) => (
|
|
||||||
<Switch
|
|
||||||
id="redeploy-render"
|
|
||||||
onCheckedChange={(isChecked) => onChange(isChecked)}
|
|
||||||
isChecked={value}
|
|
||||||
>
|
|
||||||
Auto-redeploy service upon secret change
|
|
||||||
</Switch>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<Button
|
<Button
|
||||||
colorSchema="primary"
|
onClick={handleButtonClick}
|
||||||
|
color="mineshaft"
|
||||||
variant="outline_bg"
|
variant="outline_bg"
|
||||||
className="mb-8 ml-auto mr-6 w-min mt-4"
|
className="mb-6 mt-2 ml-auto mr-6"
|
||||||
size="sm"
|
|
||||||
type="submit"
|
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
isDisabled={integrationAuthApps.length === 0}
|
isDisabled={integrationAuthApps.length === 0}
|
||||||
>
|
>
|
||||||
@ -276,7 +197,7 @@ export default function RenderCreateIntegrationPage() {
|
|||||||
cause an unexpected override of current secrets in Render with secrets from Infisical.
|
cause an unexpected override of current secrets in Render with secrets from Infisical.
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex h-full w-full items-center justify-center">
|
<div className="flex h-full w-full items-center justify-center">
|
||||||
<Head>
|
<Head>
|
||||||
|
@ -60,7 +60,7 @@ export const redirectForProviderAuth = (integrationOption: TCloudIntegration) =>
|
|||||||
link = `https://app.netlify.com/authorize?client_id=${integrationOption.clientId}&response_type=code&state=${state}&redirect_uri=${window.location.origin}/integrations/netlify/oauth2/callback`;
|
link = `https://app.netlify.com/authorize?client_id=${integrationOption.clientId}&response_type=code&state=${state}&redirect_uri=${window.location.origin}/integrations/netlify/oauth2/callback`;
|
||||||
break;
|
break;
|
||||||
case "github":
|
case "github":
|
||||||
link = `https://github.com/login/oauth/authorize?client_id=${integrationOption.clientId}&response_type=code&scope=repo,admin:org&redirect_uri=${window.location.origin}/integrations/github/oauth2/callback&state=${state}`;
|
link = `https://github.com/login/oauth/authorize?client_id=${integrationOption.clientId}&response_type=code&scope=repo&redirect_uri=${window.location.origin}/integrations/github/oauth2/callback&state=${state}`;
|
||||||
break;
|
break;
|
||||||
case "gitlab":
|
case "gitlab":
|
||||||
link = `${window.location.origin}/integrations/gitlab/authorize`;
|
link = `${window.location.origin}/integrations/gitlab/authorize`;
|
||||||
|
@ -9,8 +9,11 @@ import {
|
|||||||
AlertDescription,
|
AlertDescription,
|
||||||
DeleteActionModal,
|
DeleteActionModal,
|
||||||
EmptyState,
|
EmptyState,
|
||||||
|
FormControl,
|
||||||
FormLabel,
|
FormLabel,
|
||||||
IconButton,
|
IconButton,
|
||||||
|
Select,
|
||||||
|
SelectItem,
|
||||||
Skeleton,
|
Skeleton,
|
||||||
Tooltip
|
Tooltip
|
||||||
} from "@app/components/v2";
|
} from "@app/components/v2";
|
||||||
@ -19,7 +22,7 @@ import { usePopUp } from "@app/hooks";
|
|||||||
import { TIntegration } from "@app/hooks/api/types";
|
import { TIntegration } from "@app/hooks/api/types";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
environments: Array<{ name: string; slug: string; id: string }>;
|
environments: Array<{ name: string; slug: string }>;
|
||||||
integrations?: TIntegration[];
|
integrations?: TIntegration[];
|
||||||
isLoading?: boolean;
|
isLoading?: boolean;
|
||||||
onIntegrationDelete: (integration: TIntegration, cb: () => void) => void;
|
onIntegrationDelete: (integration: TIntegration, cb: () => void) => void;
|
||||||
@ -77,15 +80,29 @@ export const IntegrationsSection = ({
|
|||||||
<div className="flex flex-col space-y-4 p-6 pt-0">
|
<div className="flex flex-col space-y-4 p-6 pt-0">
|
||||||
{integrations?.map((integration) => (
|
{integrations?.map((integration) => (
|
||||||
<div
|
<div
|
||||||
className="max-w-8xl flex justify-between rounded-md border border-mineshaft-600 bg-mineshaft-800 p-3"
|
className="max-w-8xl flex justify-between rounded-md border border-mineshaft-600 bg-mineshaft-800 p-3 pb-0"
|
||||||
key={`integration-${integration?.id.toString()}`}
|
key={`integration-${integration?.id.toString()}`}
|
||||||
>
|
>
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
<div className="ml-2 flex flex-col">
|
<div>
|
||||||
<FormLabel label="Environment" />
|
<FormControl label="Environment">
|
||||||
<div className="rounded-md border border-mineshaft-700 bg-mineshaft-900 px-3 py-2 font-inter text-sm text-bunker-200">
|
<Select
|
||||||
{environments.find((e) => e.id === integration.envId)?.name || "-"}
|
value={integration.environment.slug}
|
||||||
</div>
|
isDisabled={integration.isActive}
|
||||||
|
className="min-w-[8rem] border border-mineshaft-700"
|
||||||
|
>
|
||||||
|
{environments.map((environment) => {
|
||||||
|
return (
|
||||||
|
<SelectItem
|
||||||
|
value={environment.slug}
|
||||||
|
key={`environment-${environment.slug}`}
|
||||||
|
>
|
||||||
|
{environment.name}
|
||||||
|
</SelectItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
</div>
|
</div>
|
||||||
<div className="ml-2 flex flex-col">
|
<div className="ml-2 flex flex-col">
|
||||||
<FormLabel label="Secret Path" />
|
<FormLabel label="Secret Path" />
|
||||||
@ -125,22 +142,11 @@ export const IntegrationsSection = ({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="ml-2 flex flex-col">
|
<div className="ml-2 flex flex-col">
|
||||||
<FormLabel
|
<FormLabel label={integration?.metadata?.scope || "App"} />
|
||||||
label={
|
<div className="min-w-[8rem] rounded-md border border-mineshaft-700 bg-mineshaft-900 px-3 py-2 font-inter text-sm text-bunker-200">
|
||||||
(integration.integration === "qovery" && integration?.scope) ||
|
{integration.integration === "hashicorp-vault"
|
||||||
(integration?.scope === "github-org" && "Organization") ||
|
? `${integration.app} - path: ${integration.path}`
|
||||||
(["github-repo", "github-env"].includes(integration?.scope as string) &&
|
: integration.app}
|
||||||
"Repository") ||
|
|
||||||
"App"
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<div className="min-w-[8rem] max-w-[12rem] overflow-clip text-ellipsis whitespace-nowrap rounded-md border border-mineshaft-700 bg-mineshaft-900 px-3 py-2 font-inter text-sm text-bunker-200">
|
|
||||||
{(integration.integration === "hashicorp-vault" &&
|
|
||||||
`${integration.app} - path: ${integration.path}`) ||
|
|
||||||
(integration.scope === "github-org" && `${integration.owner}`) ||
|
|
||||||
(integration.scope?.startsWith("github-") &&
|
|
||||||
`${integration.owner}/${integration.app}`) ||
|
|
||||||
integration.app}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{(integration.integration === "vercel" ||
|
{(integration.integration === "vercel" ||
|
||||||
@ -148,16 +154,18 @@ export const IntegrationsSection = ({
|
|||||||
integration.integration === "railway" ||
|
integration.integration === "railway" ||
|
||||||
integration.integration === "gitlab" ||
|
integration.integration === "gitlab" ||
|
||||||
integration.integration === "teamcity" ||
|
integration.integration === "teamcity" ||
|
||||||
integration.integration === "bitbucket" ||
|
integration.integration === "bitbucket") && (
|
||||||
(integration.integration === "github" && integration.scope === "github-env")) && (
|
|
||||||
<div className="ml-4 flex flex-col">
|
<div className="ml-4 flex flex-col">
|
||||||
<FormLabel label="Target Environment" />
|
<FormLabel label="Target Environment" />
|
||||||
<div className="overflow-clip text-ellipsis whitespace-nowrap rounded-md border border-mineshaft-700 bg-mineshaft-900 px-3 py-2 font-inter text-sm text-bunker-200">
|
<div className="rounded-md border border-mineshaft-700 bg-mineshaft-900 px-3 py-2 font-inter text-sm text-bunker-200">
|
||||||
{integration.targetEnvironment || integration.targetEnvironmentId}
|
{integration.targetEnvironment}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{integration.integration === "checkly" && integration.targetService && (
|
{(integration.integration === "checkly" ||
|
||||||
|
integration.integration === "github") && (
|
||||||
|
<>
|
||||||
|
{integration.targetService && (
|
||||||
<div className="ml-2">
|
<div className="ml-2">
|
||||||
<FormLabel label="Group" />
|
<FormLabel label="Group" />
|
||||||
<div className="rounded-md border border-mineshaft-700 bg-mineshaft-900 px-3 py-2 font-inter text-sm text-bunker-200">
|
<div className="rounded-md border border-mineshaft-700 bg-mineshaft-900 px-3 py-2 font-inter text-sm text-bunker-200">
|
||||||
@ -165,14 +173,13 @@ export const IntegrationsSection = ({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{(integration.integration === "checkly" ||
|
|
||||||
integration.integration === "github") && (
|
|
||||||
<div className="ml-2">
|
<div className="ml-2">
|
||||||
<FormLabel label="Secret Suffix" />
|
<FormLabel label="Secret Suffix" />
|
||||||
<div className="rounded-md border border-mineshaft-700 bg-mineshaft-900 px-3 py-2 font-inter text-sm text-bunker-200">
|
<div className="rounded-md border border-mineshaft-700 bg-mineshaft-900 px-3 py-2 font-inter text-sm text-bunker-200">
|
||||||
{integration?.metadata?.secretSuffix || "-"}
|
{integration?.metadata?.secretSuffix || "-"}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex cursor-default items-center">
|
<div className="flex cursor-default items-center">
|
||||||
@ -207,7 +214,7 @@ export const IntegrationsSection = ({
|
|||||||
(popUp?.deleteConfirmation.data as TIntegration)?.integration || " "
|
(popUp?.deleteConfirmation.data as TIntegration)?.integration || " "
|
||||||
} integration for ${(popUp?.deleteConfirmation.data as TIntegration)?.app || " "}?`}
|
} integration for ${(popUp?.deleteConfirmation.data as TIntegration)?.app || " "}?`}
|
||||||
onChange={(isOpen) => handlePopUpToggle("deleteConfirmation", isOpen)}
|
onChange={(isOpen) => handlePopUpToggle("deleteConfirmation", isOpen)}
|
||||||
deleteKey={(popUp?.deleteConfirmation?.data as TIntegration)?.app || (popUp?.deleteConfirmation?.data as TIntegration)?.owner || ""}
|
deleteKey={(popUp?.deleteConfirmation?.data as TIntegration)?.app || ""}
|
||||||
onDeleteApproved={async () =>
|
onDeleteApproved={async () =>
|
||||||
onIntegrationDelete(popUp?.deleteConfirmation.data as TIntegration, () =>
|
onIntegrationDelete(popUp?.deleteConfirmation.data as TIntegration, () =>
|
||||||
handlePopUpClose("deleteConfirmation")
|
handlePopUpClose("deleteConfirmation")
|
||||||
|
@ -261,9 +261,7 @@ export const MemberListTab = () => {
|
|||||||
ariaLabel="update"
|
ariaLabel="update"
|
||||||
className="ml-4"
|
className="ml-4"
|
||||||
isDisabled={userId === u?.id || !isAllowed}
|
isDisabled={userId === u?.id || !isAllowed}
|
||||||
onClick={() =>
|
onClick={() => handlePopUpOpen("removeMember", { email: u.email })}
|
||||||
handlePopUpOpen("removeMember", { username: u.username })
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
<FontAwesomeIcon icon={faXmark} />
|
<FontAwesomeIcon icon={faXmark} />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
|
@ -188,7 +188,7 @@ function SecretRenameRow({ environments, getSecretByKey, secretKey, secretPath }
|
|||||||
animate={{ x: 0, opacity: 1 }}
|
animate={{ x: 0, opacity: 1 }}
|
||||||
exit={{ x: 10, opacity: 0 }}
|
exit={{ x: 10, opacity: 0 }}
|
||||||
>
|
>
|
||||||
<Tooltip content="Copy secret name">
|
<Tooltip content="Copy secret">
|
||||||
<IconButton
|
<IconButton
|
||||||
ariaLabel="copy-value"
|
ariaLabel="copy-value"
|
||||||
variant="plain"
|
variant="plain"
|
||||||
|
@ -2,7 +2,7 @@ import { useTranslation } from "react-i18next";
|
|||||||
import { faPlus } from "@fortawesome/free-solid-svg-icons";
|
import { faPlus } from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
|
||||||
import { Button, Tooltip } from "@app/components/v2";
|
import { Button } from "@app/components/v2";
|
||||||
import { usePopUp } from "@app/hooks/usePopUp";
|
import { usePopUp } from "@app/hooks/usePopUp";
|
||||||
|
|
||||||
import { AddAPIKeyModal } from "./AddAPIKeyModal";
|
import { AddAPIKeyModal } from "./AddAPIKeyModal";
|
||||||
@ -10,17 +10,17 @@ import { APIKeyTable } from "./APIKeyTable";
|
|||||||
|
|
||||||
export const APIKeySection = () => {
|
export const APIKeySection = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { popUp, handlePopUpOpen, handlePopUpToggle } = usePopUp(["addAPIKey"] as const);
|
const { popUp, handlePopUpOpen, handlePopUpToggle } = usePopUp([
|
||||||
|
"addAPIKey"
|
||||||
|
] as const);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
|
<div className="mb-6 p-4 bg-mineshaft-900 rounded-lg border border-mineshaft-600">
|
||||||
<div className="mb-8 flex justify-between">
|
<div className="flex justify-between mb-8">
|
||||||
<p className="text-xl font-semibold text-mineshaft-100">
|
<p className="text-xl font-semibold text-mineshaft-100">
|
||||||
{t("settings.personal.api-keys.title")}
|
{t("settings.personal.api-keys.title")}
|
||||||
</p>
|
</p>
|
||||||
<Tooltip content="API Keys are deprecated and will be removed in the future.">
|
|
||||||
<Button
|
<Button
|
||||||
isDisabled
|
|
||||||
colorSchema="secondary"
|
colorSchema="secondary"
|
||||||
type="submit"
|
type="submit"
|
||||||
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
||||||
@ -28,10 +28,12 @@ export const APIKeySection = () => {
|
|||||||
>
|
>
|
||||||
Add API Key
|
Add API Key
|
||||||
</Button>
|
</Button>
|
||||||
</Tooltip>
|
|
||||||
</div>
|
</div>
|
||||||
<APIKeyTable />
|
<APIKeyTable />
|
||||||
<AddAPIKeyModal popUp={popUp} handlePopUpToggle={handlePopUpToggle} />
|
<AddAPIKeyModal
|
||||||
|
popUp={popUp}
|
||||||
|
handlePopUpToggle={handlePopUpToggle}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
}
|
@ -0,0 +1,199 @@
|
|||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { Controller, useForm } from "react-hook-form";
|
||||||
|
import { faCheck, faCopy } from "@fortawesome/free-solid-svg-icons";
|
||||||
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
import { yupResolver } from "@hookform/resolvers/yup";
|
||||||
|
import * as yup from "yup";
|
||||||
|
|
||||||
|
import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
FormControl,
|
||||||
|
IconButton,
|
||||||
|
Input,
|
||||||
|
Modal,
|
||||||
|
ModalContent} from "@app/components/v2";
|
||||||
|
import { useToggle } from "@app/hooks";
|
||||||
|
import {
|
||||||
|
useCreateAPIKeyV2,
|
||||||
|
useUpdateAPIKeyV2
|
||||||
|
} from "@app/hooks/api";
|
||||||
|
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||||
|
|
||||||
|
const schema = yup.object({
|
||||||
|
name: yup.string().required("API Key V2 name is required")
|
||||||
|
}).required();
|
||||||
|
|
||||||
|
export type FormData = yup.InferType<typeof schema>;
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
popUp: UsePopUpState<["apiKeyV2"]>;
|
||||||
|
handlePopUpToggle: (popUpName: keyof UsePopUpState<["apiKeyV2"]>, state?: boolean) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const APIKeyV2Modal = ({
|
||||||
|
popUp,
|
||||||
|
handlePopUpToggle
|
||||||
|
}: Props) => {
|
||||||
|
const [newAPIKey, setNewAPIKey] = useState("");
|
||||||
|
const [isAPIKeyCopied, setIsAPIKeyCopied] = useToggle(false);
|
||||||
|
|
||||||
|
const { createNotification } = useNotificationContext();
|
||||||
|
|
||||||
|
const { mutateAsync: createMutateAsync } = useCreateAPIKeyV2();
|
||||||
|
const { mutateAsync: updateMutateAsync } = useUpdateAPIKeyV2();
|
||||||
|
|
||||||
|
const {
|
||||||
|
control,
|
||||||
|
handleSubmit,
|
||||||
|
reset,
|
||||||
|
formState: { isSubmitting }
|
||||||
|
} = useForm<FormData>({
|
||||||
|
resolver: yupResolver(schema),
|
||||||
|
defaultValues: {
|
||||||
|
name: ""
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let timer: NodeJS.Timeout;
|
||||||
|
|
||||||
|
if (isAPIKeyCopied) {
|
||||||
|
timer = setTimeout(() => setIsAPIKeyCopied.off(), 2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => clearTimeout(timer);
|
||||||
|
}, [setIsAPIKeyCopied]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const apiKeyData = popUp?.apiKeyV2?.data as {
|
||||||
|
apiKeyDataId: string;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (apiKeyData) {
|
||||||
|
reset({
|
||||||
|
name: apiKeyData.name
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
reset({
|
||||||
|
name: ""
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [popUp?.apiKeyV2?.data]);
|
||||||
|
|
||||||
|
const copyTokenToClipboard = () => {
|
||||||
|
navigator.clipboard.writeText(newAPIKey);
|
||||||
|
setIsAPIKeyCopied.on();
|
||||||
|
};
|
||||||
|
|
||||||
|
const onFormSubmit = async ({
|
||||||
|
name
|
||||||
|
}: FormData) => {
|
||||||
|
try {
|
||||||
|
const apiKeyData = popUp?.apiKeyV2?.data as {
|
||||||
|
apiKeyDataId: string;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (apiKeyData) {
|
||||||
|
// update
|
||||||
|
|
||||||
|
await updateMutateAsync({
|
||||||
|
apiKeyDataId: apiKeyData.apiKeyDataId,
|
||||||
|
name
|
||||||
|
});
|
||||||
|
|
||||||
|
handlePopUpToggle("apiKeyV2", false);
|
||||||
|
} else {
|
||||||
|
// create
|
||||||
|
|
||||||
|
const { apiKey } = await createMutateAsync({
|
||||||
|
name
|
||||||
|
});
|
||||||
|
|
||||||
|
setNewAPIKey(apiKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
createNotification({
|
||||||
|
text: `Successfully ${popUp?.apiKeyV2?.data ? "updated" : "created"} API Key`,
|
||||||
|
type: "success"
|
||||||
|
});
|
||||||
|
|
||||||
|
reset();
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
createNotification({
|
||||||
|
text: `Failed to ${popUp?.apiKeyV2?.data ? "updated" : "created"} API Key`,
|
||||||
|
type: "error"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const hasAPIKey = Boolean(newAPIKey);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
isOpen={popUp?.apiKeyV2?.isOpen}
|
||||||
|
onOpenChange={(isOpen) => {
|
||||||
|
handlePopUpToggle("apiKeyV2", isOpen);
|
||||||
|
reset();
|
||||||
|
setNewAPIKey("");
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ModalContent title={`${popUp?.apiKeyV2?.data ? "Update" : "Create"} API Key V2`}>
|
||||||
|
{!hasAPIKey ? (
|
||||||
|
<form onSubmit={handleSubmit(onFormSubmit)}>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
defaultValue=""
|
||||||
|
name="name"
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label="Name"
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
{...field}
|
||||||
|
placeholder="My API Key"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<div className="mt-8 flex items-center">
|
||||||
|
<Button
|
||||||
|
className="mr-4"
|
||||||
|
size="sm"
|
||||||
|
type="submit"
|
||||||
|
isLoading={isSubmitting}
|
||||||
|
isDisabled={isSubmitting}
|
||||||
|
>
|
||||||
|
{popUp?.apiKeyV2?.data ? "Update" : "Create"}
|
||||||
|
</Button>
|
||||||
|
<Button colorSchema="secondary" variant="plain">
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
) : (
|
||||||
|
<div className="mt-2 mb-3 mr-2 flex items-center justify-end rounded-md bg-white/[0.07] p-2 text-base text-gray-400">
|
||||||
|
<p className="mr-4 break-all">{newAPIKey}</p>
|
||||||
|
<IconButton
|
||||||
|
ariaLabel="copy icon"
|
||||||
|
colorSchema="secondary"
|
||||||
|
className="group relative"
|
||||||
|
onClick={copyTokenToClipboard}
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={isAPIKeyCopied ? faCheck : faCopy} />
|
||||||
|
<span className="absolute -left-8 -top-20 hidden w-28 translate-y-full rounded-md bg-bunker-800 py-2 pl-3 text-center text-sm text-gray-400 group-hover:flex group-hover:animate-fadeIn">
|
||||||
|
Click to copy
|
||||||
|
</span>
|
||||||
|
</IconButton>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</ModalContent>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,81 @@
|
|||||||
|
import { faPlus } from "@fortawesome/free-solid-svg-icons";
|
||||||
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
|
||||||
|
import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
DeleteActionModal
|
||||||
|
} from "@app/components/v2";
|
||||||
|
import { useDeleteAPIKeyV2 } from "@app/hooks/api";
|
||||||
|
import { usePopUp } from "@app/hooks/usePopUp";
|
||||||
|
|
||||||
|
import { APIKeyV2Modal } from "./APIKeyV2Modal";
|
||||||
|
import { APIKeyV2Table } from "./APIKeyV2Table";
|
||||||
|
|
||||||
|
export const APIKeyV2Section = () => {
|
||||||
|
const { createNotification } = useNotificationContext();
|
||||||
|
const { mutateAsync: deleteMutateAsync } = useDeleteAPIKeyV2();
|
||||||
|
const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([
|
||||||
|
"apiKeyV2",
|
||||||
|
"deleteAPIKeyV2"
|
||||||
|
] as const);
|
||||||
|
|
||||||
|
const onDeleteAPIKeyDataSubmit = async (apiKeyDataId: string) => {
|
||||||
|
try {
|
||||||
|
await deleteMutateAsync({
|
||||||
|
apiKeyDataId
|
||||||
|
});
|
||||||
|
|
||||||
|
createNotification({
|
||||||
|
text: "Successfully deleted API Key V2",
|
||||||
|
type: "success"
|
||||||
|
});
|
||||||
|
|
||||||
|
handlePopUpClose("deleteAPIKeyV2");
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
createNotification({
|
||||||
|
text: "Failed to delete API Key V2",
|
||||||
|
type: "error"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="mb-6 p-4 bg-mineshaft-900 rounded-lg border border-mineshaft-600">
|
||||||
|
<div className="flex justify-between mb-8">
|
||||||
|
<p className="text-xl font-semibold text-mineshaft-100">
|
||||||
|
API Keys V2 (Beta)
|
||||||
|
</p>
|
||||||
|
<Button
|
||||||
|
colorSchema="secondary"
|
||||||
|
type="submit"
|
||||||
|
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
||||||
|
onClick={() => handlePopUpOpen("apiKeyV2")}
|
||||||
|
>
|
||||||
|
Add API Key
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<APIKeyV2Table
|
||||||
|
handlePopUpOpen={handlePopUpOpen}
|
||||||
|
/>
|
||||||
|
<APIKeyV2Modal
|
||||||
|
popUp={popUp}
|
||||||
|
handlePopUpToggle={handlePopUpToggle}
|
||||||
|
/>
|
||||||
|
<DeleteActionModal
|
||||||
|
isOpen={popUp.deleteAPIKeyV2.isOpen}
|
||||||
|
title={`Are you sure want to delete ${
|
||||||
|
(popUp?.deleteAPIKeyV2?.data as { name: string })?.name || ""
|
||||||
|
}?`}
|
||||||
|
onChange={(isOpen) => handlePopUpToggle("deleteAPIKeyV2", isOpen)}
|
||||||
|
deleteKey="confirm"
|
||||||
|
onDeleteApproved={() =>
|
||||||
|
onDeleteAPIKeyDataSubmit(
|
||||||
|
(popUp?.deleteAPIKeyV2?.data as { apiKeyDataId: string })?.apiKeyDataId
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,98 @@
|
|||||||
|
import { faKey, faPencil, faXmark } from "@fortawesome/free-solid-svg-icons";
|
||||||
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
import { format } from "date-fns";
|
||||||
|
|
||||||
|
import {
|
||||||
|
EmptyState,
|
||||||
|
IconButton,
|
||||||
|
Table,
|
||||||
|
TableContainer,
|
||||||
|
TableSkeleton,
|
||||||
|
TBody,
|
||||||
|
Td,
|
||||||
|
Th,
|
||||||
|
THead,
|
||||||
|
Tr
|
||||||
|
} from "@app/components/v2";
|
||||||
|
import { useGetMyAPIKeysV2 } from "@app/hooks/api";
|
||||||
|
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
handlePopUpOpen: (
|
||||||
|
popUpName: keyof UsePopUpState<["deleteAPIKeyV2", "apiKeyV2"]>,
|
||||||
|
data?: {
|
||||||
|
apiKeyDataId?: string;
|
||||||
|
name?: string;
|
||||||
|
}
|
||||||
|
) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const APIKeyV2Table = ({ handlePopUpOpen }: Props) => {
|
||||||
|
const { data, isLoading } = useGetMyAPIKeysV2();
|
||||||
|
return (
|
||||||
|
<TableContainer>
|
||||||
|
<Table>
|
||||||
|
<THead>
|
||||||
|
<Tr>
|
||||||
|
<Th className="">Name</Th>
|
||||||
|
<Th className="">Last Used</Th>
|
||||||
|
<Th className="">Created At</Th>
|
||||||
|
<Th className="w-5" />
|
||||||
|
</Tr>
|
||||||
|
</THead>
|
||||||
|
<TBody>
|
||||||
|
{isLoading && <TableSkeleton columns={4} innerKey="api-keys-v2" />}
|
||||||
|
{!isLoading &&
|
||||||
|
data &&
|
||||||
|
data.length > 0 &&
|
||||||
|
data.map(({ id, name, lastUsed, createdAt }) => {
|
||||||
|
return (
|
||||||
|
<Tr className="h-10" key={`api-key-v2-${id}`}>
|
||||||
|
<Td>{name}</Td>
|
||||||
|
<Td>{lastUsed ? format(new Date(lastUsed), "yyyy-MM-dd") : "-"}</Td>
|
||||||
|
<Td>{format(new Date(createdAt), "yyyy-MM-dd")}</Td>
|
||||||
|
<Td className="flex justify-end">
|
||||||
|
<IconButton
|
||||||
|
onClick={async () => {
|
||||||
|
handlePopUpOpen("apiKeyV2", {
|
||||||
|
apiKeyDataId: id,
|
||||||
|
name
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
size="lg"
|
||||||
|
colorSchema="primary"
|
||||||
|
variant="plain"
|
||||||
|
ariaLabel="update"
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faPencil} />
|
||||||
|
</IconButton>
|
||||||
|
<IconButton
|
||||||
|
onClick={() => {
|
||||||
|
handlePopUpOpen("deleteAPIKeyV2", {
|
||||||
|
apiKeyDataId: id
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
size="lg"
|
||||||
|
colorSchema="danger"
|
||||||
|
variant="plain"
|
||||||
|
ariaLabel="update"
|
||||||
|
className="ml-4"
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faXmark} />
|
||||||
|
</IconButton>
|
||||||
|
</Td>
|
||||||
|
</Tr>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
{!isLoading && data && data?.length === 0 && (
|
||||||
|
<Tr>
|
||||||
|
<Td colSpan={4}>
|
||||||
|
<EmptyState title="No API key v2 on file" icon={faKey} />
|
||||||
|
</Td>
|
||||||
|
</Tr>
|
||||||
|
)}
|
||||||
|
</TBody>
|
||||||
|
</Table>
|
||||||
|
</TableContainer>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1 @@
|
|||||||
|
export { APIKeyV2Section } from "./APIKeyV2Section";
|
@ -1,5 +1,11 @@
|
|||||||
import { APIKeySection } from "../APIKeySection";
|
import { APIKeySection } from "../APIKeySection";
|
||||||
|
// import { APIKeyV2Section } from "../APIKeyV2Section";
|
||||||
|
|
||||||
export const PersonalAPIKeyTab = () => {
|
export const PersonalAPIKeyTab = () => {
|
||||||
return <APIKeySection />;
|
return (
|
||||||
};
|
<>
|
||||||
|
{/* <APIKeyV2Section /> */}
|
||||||
|
<APIKeySection />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
@ -1,8 +1,5 @@
|
|||||||
import { Fragment } from "react";
|
import { Fragment } from "react"
|
||||||
import Link from "next/link";
|
import { Tab } from "@headlessui/react"
|
||||||
import { faWarning } from "@fortawesome/free-solid-svg-icons";
|
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|
||||||
import { Tab } from "@headlessui/react";
|
|
||||||
|
|
||||||
import { PersonalAPIKeyTab } from "../PersonalAPIKeyTab";
|
import { PersonalAPIKeyTab } from "../PersonalAPIKeyTab";
|
||||||
import { PersonalAuthTab } from "../PersonalAuthTab";
|
import { PersonalAuthTab } from "../PersonalAuthTab";
|
||||||
@ -17,15 +14,13 @@ const tabs = [
|
|||||||
export const PersonalTabGroup = () => {
|
export const PersonalTabGroup = () => {
|
||||||
return (
|
return (
|
||||||
<Tab.Group>
|
<Tab.Group>
|
||||||
<Tab.List className="mb-4 w-full border-b-2 border-mineshaft-800">
|
<Tab.List className="mb-4 border-b-2 border-mineshaft-800 w-full">
|
||||||
{tabs.map((tab) => (
|
{tabs.map((tab) => (
|
||||||
<Tab as={Fragment} key={tab.key}>
|
<Tab as={Fragment} key={tab.key}>
|
||||||
{({ selected }) => (
|
{({ selected }) => (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className={`w-30 mx-2 mr-4 py-2 text-sm font-medium outline-none ${
|
className={`w-30 py-2 mx-2 mr-4 font-medium text-sm outline-none ${selected ? "border-b border-white text-white" : "text-mineshaft-400"}`}
|
||||||
selected ? "border-b border-white text-white" : "text-mineshaft-400"
|
|
||||||
}`}
|
|
||||||
>
|
>
|
||||||
{tab.name}
|
{tab.name}
|
||||||
</button>
|
</button>
|
||||||
@ -41,28 +36,9 @@ export const PersonalTabGroup = () => {
|
|||||||
<PersonalAuthTab />
|
<PersonalAuthTab />
|
||||||
</Tab.Panel>
|
</Tab.Panel>
|
||||||
<Tab.Panel>
|
<Tab.Panel>
|
||||||
<div className="space-y-3">
|
|
||||||
<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">
|
|
||||||
<FontAwesomeIcon icon={faWarning} className="pr-6 text-4xl text-white/80" />
|
|
||||||
<div className="flex w-full flex-col text-sm">
|
|
||||||
<span className="mb-4 text-lg font-semibold">Deprecation Notice</span>
|
|
||||||
<p>
|
|
||||||
API Keys are deprecated and will be removed in the future.
|
|
||||||
<br /> Please use Machine Identity authentication for your applications and
|
|
||||||
services.
|
|
||||||
</p>
|
|
||||||
<Link href="https://infisical.com/docs/documentation/platform/identities/overview">
|
|
||||||
<a target="_blank" className="font-semibold text-primary-400">
|
|
||||||
Learn more about Machine Identities
|
|
||||||
</a>
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<PersonalAPIKeyTab />
|
<PersonalAPIKeyTab />
|
||||||
</div>
|
|
||||||
</Tab.Panel>
|
</Tab.Panel>
|
||||||
</Tab.Panels>
|
</Tab.Panels>
|
||||||
</Tab.Group>
|
</Tab.Group>
|
||||||
);
|
);
|
||||||
};
|
}
|
Reference in New Issue
Block a user