Compare commits
1 Commits
commit-ui-
...
misc/updat
Author | SHA1 | Date | |
---|---|---|---|
d89418803e |
@ -1,66 +0,0 @@
|
||||
import { Knex } from "knex";
|
||||
|
||||
import { inMemoryKeyStore } from "@app/keystore/memory";
|
||||
import { selectAllTableCols } from "@app/lib/knex";
|
||||
|
||||
import { TableName } from "../schemas";
|
||||
import { getMigrationEnvConfig } from "./utils/env-config";
|
||||
import { getMigrationEncryptionServices } from "./utils/services";
|
||||
|
||||
export async function up(knex: Knex) {
|
||||
const existingSuperAdminsWithGithubConnection = await knex(TableName.SuperAdmin)
|
||||
.select(selectAllTableCols(TableName.SuperAdmin))
|
||||
.whereNotNull(`${TableName.SuperAdmin}.encryptedGitHubAppConnectionClientId`);
|
||||
|
||||
const envConfig = getMigrationEnvConfig();
|
||||
const keyStore = inMemoryKeyStore();
|
||||
const { kmsService } = await getMigrationEncryptionServices({ envConfig, keyStore, db: knex });
|
||||
|
||||
const decryptor = kmsService.decryptWithRootKey();
|
||||
const encryptor = kmsService.encryptWithRootKey();
|
||||
|
||||
const tasks = existingSuperAdminsWithGithubConnection.map(async (admin) => {
|
||||
const overrides = (
|
||||
admin.encryptedEnvOverrides ? JSON.parse(decryptor(Buffer.from(admin.encryptedEnvOverrides)).toString()) : {}
|
||||
) as Record<string, string>;
|
||||
|
||||
if (admin.encryptedGitHubAppConnectionClientId) {
|
||||
overrides.INF_APP_CONNECTION_GITHUB_APP_CLIENT_ID = decryptor(
|
||||
admin.encryptedGitHubAppConnectionClientId
|
||||
).toString();
|
||||
}
|
||||
|
||||
if (admin.encryptedGitHubAppConnectionClientSecret) {
|
||||
overrides.INF_APP_CONNECTION_GITHUB_APP_CLIENT_SECRET = decryptor(
|
||||
admin.encryptedGitHubAppConnectionClientSecret
|
||||
).toString();
|
||||
}
|
||||
|
||||
if (admin.encryptedGitHubAppConnectionPrivateKey) {
|
||||
overrides.INF_APP_CONNECTION_GITHUB_APP_PRIVATE_KEY = decryptor(
|
||||
admin.encryptedGitHubAppConnectionPrivateKey
|
||||
).toString();
|
||||
}
|
||||
|
||||
if (admin.encryptedGitHubAppConnectionSlug) {
|
||||
overrides.INF_APP_CONNECTION_GITHUB_APP_SLUG = decryptor(admin.encryptedGitHubAppConnectionSlug).toString();
|
||||
}
|
||||
|
||||
if (admin.encryptedGitHubAppConnectionId) {
|
||||
overrides.INF_APP_CONNECTION_GITHUB_APP_ID = decryptor(admin.encryptedGitHubAppConnectionId).toString();
|
||||
}
|
||||
|
||||
const encryptedEnvOverrides = encryptor(Buffer.from(JSON.stringify(overrides)));
|
||||
|
||||
await knex(TableName.SuperAdmin).where({ id: admin.id }).update({
|
||||
encryptedEnvOverrides
|
||||
});
|
||||
});
|
||||
|
||||
await Promise.all(tasks);
|
||||
}
|
||||
|
||||
export async function down() {
|
||||
// No down migration needed as this migration is only for data transformation
|
||||
// and does not change the schema.
|
||||
}
|
@ -2472,9 +2472,6 @@ export const SecretSyncs = {
|
||||
projectName: "The name of the Cloudflare Pages project to sync secrets to.",
|
||||
environment: "The environment of the Cloudflare Pages project to sync secrets to."
|
||||
},
|
||||
CLOUDFLARE_WORKERS: {
|
||||
scriptId: "The ID of the Cloudflare Workers script to sync secrets to."
|
||||
},
|
||||
ZABBIX: {
|
||||
scope: "The Zabbix scope that secrets should be synced to.",
|
||||
hostId: "The ID of the Zabbix host to sync secrets to.",
|
||||
|
@ -50,32 +50,4 @@ export const registerCloudflareConnectionRouter = async (server: FastifyZodProvi
|
||||
return projects;
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: `/:connectionId/cloudflare-workers-scripts`,
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
connectionId: z.string().uuid()
|
||||
}),
|
||||
response: {
|
||||
200: z
|
||||
.object({
|
||||
id: z.string()
|
||||
})
|
||||
.array()
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
handler: async (req) => {
|
||||
const { connectionId } = req.params;
|
||||
|
||||
const projects = await server.services.appConnection.cloudflare.listWorkersScripts(connectionId, req.permission);
|
||||
|
||||
return projects;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
@ -1,17 +0,0 @@
|
||||
import {
|
||||
CloudflareWorkersSyncSchema,
|
||||
CreateCloudflareWorkersSyncSchema,
|
||||
UpdateCloudflareWorkersSyncSchema
|
||||
} from "@app/services/secret-sync/cloudflare-workers/cloudflare-workers-schemas";
|
||||
import { SecretSync } from "@app/services/secret-sync/secret-sync-enums";
|
||||
|
||||
import { registerSyncSecretsEndpoints } from "./secret-sync-endpoints";
|
||||
|
||||
export const registerCloudflareWorkersSyncRouter = async (server: FastifyZodProvider) =>
|
||||
registerSyncSecretsEndpoints({
|
||||
destination: SecretSync.CloudflareWorkers,
|
||||
server,
|
||||
responseSchema: CloudflareWorkersSyncSchema,
|
||||
createSchema: CreateCloudflareWorkersSyncSchema,
|
||||
updateSchema: UpdateCloudflareWorkersSyncSchema
|
||||
});
|
@ -9,7 +9,6 @@ import { registerAzureDevOpsSyncRouter } from "./azure-devops-sync-router";
|
||||
import { registerAzureKeyVaultSyncRouter } from "./azure-key-vault-sync-router";
|
||||
import { registerCamundaSyncRouter } from "./camunda-sync-router";
|
||||
import { registerCloudflarePagesSyncRouter } from "./cloudflare-pages-sync-router";
|
||||
import { registerCloudflareWorkersSyncRouter } from "./cloudflare-workers-sync-router";
|
||||
import { registerDatabricksSyncRouter } from "./databricks-sync-router";
|
||||
import { registerFlyioSyncRouter } from "./flyio-sync-router";
|
||||
import { registerGcpSyncRouter } from "./gcp-sync-router";
|
||||
@ -51,8 +50,6 @@ export const SECRET_SYNC_REGISTER_ROUTER_MAP: Record<SecretSync, (server: Fastif
|
||||
[SecretSync.Flyio]: registerFlyioSyncRouter,
|
||||
[SecretSync.GitLab]: registerGitLabSyncRouter,
|
||||
[SecretSync.CloudflarePages]: registerCloudflarePagesSyncRouter,
|
||||
[SecretSync.CloudflareWorkers]: registerCloudflareWorkersSyncRouter,
|
||||
|
||||
[SecretSync.Zabbix]: registerZabbixSyncRouter,
|
||||
[SecretSync.Railway]: registerRailwaySyncRouter
|
||||
};
|
||||
|
@ -26,10 +26,6 @@ import {
|
||||
CloudflarePagesSyncListItemSchema,
|
||||
CloudflarePagesSyncSchema
|
||||
} from "@app/services/secret-sync/cloudflare-pages/cloudflare-pages-schema";
|
||||
import {
|
||||
CloudflareWorkersSyncListItemSchema,
|
||||
CloudflareWorkersSyncSchema
|
||||
} from "@app/services/secret-sync/cloudflare-workers/cloudflare-workers-schemas";
|
||||
import { DatabricksSyncListItemSchema, DatabricksSyncSchema } from "@app/services/secret-sync/databricks";
|
||||
import { FlyioSyncListItemSchema, FlyioSyncSchema } from "@app/services/secret-sync/flyio";
|
||||
import { GcpSyncListItemSchema, GcpSyncSchema } from "@app/services/secret-sync/gcp";
|
||||
@ -69,8 +65,6 @@ const SecretSyncSchema = z.discriminatedUnion("destination", [
|
||||
FlyioSyncSchema,
|
||||
GitLabSyncSchema,
|
||||
CloudflarePagesSyncSchema,
|
||||
CloudflareWorkersSyncSchema,
|
||||
|
||||
ZabbixSyncSchema,
|
||||
RailwaySyncSchema
|
||||
]);
|
||||
@ -98,8 +92,6 @@ const SecretSyncOptionsSchema = z.discriminatedUnion("destination", [
|
||||
FlyioSyncListItemSchema,
|
||||
GitLabSyncListItemSchema,
|
||||
CloudflarePagesSyncListItemSchema,
|
||||
CloudflareWorkersSyncListItemSchema,
|
||||
|
||||
ZabbixSyncListItemSchema,
|
||||
RailwaySyncListItemSchema
|
||||
]);
|
||||
|
@ -9,8 +9,7 @@ import { CloudflareConnectionMethod } from "./cloudflare-connection-enum";
|
||||
import {
|
||||
TCloudflareConnection,
|
||||
TCloudflareConnectionConfig,
|
||||
TCloudflarePagesProject,
|
||||
TCloudflareWorkersScript
|
||||
TCloudflarePagesProject
|
||||
} from "./cloudflare-connection-types";
|
||||
|
||||
export const getCloudflareConnectionListItem = () => {
|
||||
@ -44,28 +43,6 @@ export const listCloudflarePagesProjects = async (
|
||||
}));
|
||||
};
|
||||
|
||||
export const listCloudflareWorkersScripts = async (
|
||||
appConnection: TCloudflareConnection
|
||||
): Promise<TCloudflareWorkersScript[]> => {
|
||||
const {
|
||||
credentials: { apiToken, accountId }
|
||||
} = appConnection;
|
||||
|
||||
const { data } = await request.get<{ result: { id: string }[] }>(
|
||||
`${IntegrationUrls.CLOUDFLARE_API_URL}/client/v4/accounts/${accountId}/workers/scripts`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${apiToken}`,
|
||||
Accept: "application/json"
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return data.result.map((a) => ({
|
||||
id: a.id
|
||||
}));
|
||||
};
|
||||
|
||||
export const validateCloudflareConnectionCredentials = async (config: TCloudflareConnectionConfig) => {
|
||||
const { apiToken, accountId } = config.credentials;
|
||||
|
||||
|
@ -2,7 +2,7 @@ import { logger } from "@app/lib/logger";
|
||||
import { OrgServiceActor } from "@app/lib/types";
|
||||
|
||||
import { AppConnection } from "../app-connection-enums";
|
||||
import { listCloudflarePagesProjects, listCloudflareWorkersScripts } from "./cloudflare-connection-fns";
|
||||
import { listCloudflarePagesProjects } from "./cloudflare-connection-fns";
|
||||
import { TCloudflareConnection } from "./cloudflare-connection-types";
|
||||
|
||||
type TGetAppConnectionFunc = (
|
||||
@ -19,31 +19,12 @@ export const cloudflareConnectionService = (getAppConnection: TGetAppConnectionF
|
||||
|
||||
return projects;
|
||||
} catch (error) {
|
||||
logger.error(
|
||||
error,
|
||||
`Failed to list Cloudflare Pages projects for Cloudflare connection [connectionId=${connectionId}]`
|
||||
);
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
const listWorkersScripts = async (connectionId: string, actor: OrgServiceActor) => {
|
||||
const appConnection = await getAppConnection(AppConnection.Cloudflare, connectionId, actor);
|
||||
try {
|
||||
const projects = await listCloudflareWorkersScripts(appConnection);
|
||||
|
||||
return projects;
|
||||
} catch (error) {
|
||||
logger.error(
|
||||
error,
|
||||
`Failed to list Cloudflare Workers scripts for Cloudflare connection [connectionId=${connectionId}]`
|
||||
);
|
||||
logger.error(error, "Failed to list Cloudflare Pages projects for Cloudflare connection");
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
listPagesProjects,
|
||||
listWorkersScripts
|
||||
listPagesProjects
|
||||
};
|
||||
};
|
||||
|
@ -28,7 +28,3 @@ export type TCloudflarePagesProject = {
|
||||
id: string;
|
||||
name: string;
|
||||
};
|
||||
|
||||
export type TCloudflareWorkersScript = {
|
||||
id: string;
|
||||
};
|
||||
|
@ -7,6 +7,7 @@ import { request } from "@app/lib/config/request";
|
||||
import { BadRequestError, ForbiddenRequestError, InternalServerError } from "@app/lib/errors";
|
||||
import { getAppConnectionMethodName } from "@app/services/app-connection/app-connection-fns";
|
||||
import { IntegrationUrls } from "@app/services/integration-auth/integration-list";
|
||||
import { getInstanceIntegrationsConfig } from "@app/services/super-admin/super-admin-service";
|
||||
|
||||
import { AppConnection } from "../app-connection-enums";
|
||||
import { GitHubConnectionMethod } from "./github-connection-enums";
|
||||
@ -14,13 +15,14 @@ import { TGitHubConnection, TGitHubConnectionConfig } from "./github-connection-
|
||||
|
||||
export const getGitHubConnectionListItem = () => {
|
||||
const { INF_APP_CONNECTION_GITHUB_OAUTH_CLIENT_ID, INF_APP_CONNECTION_GITHUB_APP_SLUG } = getConfig();
|
||||
const { gitHubAppConnection } = getInstanceIntegrationsConfig();
|
||||
|
||||
return {
|
||||
name: "GitHub" as const,
|
||||
app: AppConnection.GitHub as const,
|
||||
methods: Object.values(GitHubConnectionMethod) as [GitHubConnectionMethod.App, GitHubConnectionMethod.OAuth],
|
||||
oauthClientId: INF_APP_CONNECTION_GITHUB_OAUTH_CLIENT_ID,
|
||||
appClientSlug: INF_APP_CONNECTION_GITHUB_APP_SLUG
|
||||
appClientSlug: gitHubAppConnection.appSlug || INF_APP_CONNECTION_GITHUB_APP_SLUG
|
||||
};
|
||||
};
|
||||
|
||||
@ -30,9 +32,10 @@ export const getGitHubClient = (appConnection: TGitHubConnection) => {
|
||||
const { method, credentials } = appConnection;
|
||||
|
||||
let client: Octokit;
|
||||
const { gitHubAppConnection } = getInstanceIntegrationsConfig();
|
||||
|
||||
const appId = appCfg.INF_APP_CONNECTION_GITHUB_APP_ID;
|
||||
const appPrivateKey = appCfg.INF_APP_CONNECTION_GITHUB_APP_PRIVATE_KEY;
|
||||
const appId = gitHubAppConnection.appId || appCfg.INF_APP_CONNECTION_GITHUB_APP_ID;
|
||||
const appPrivateKey = gitHubAppConnection.privateKey || appCfg.INF_APP_CONNECTION_GITHUB_APP_PRIVATE_KEY;
|
||||
|
||||
switch (method) {
|
||||
case GitHubConnectionMethod.App:
|
||||
@ -154,6 +157,8 @@ type TokenRespData = {
|
||||
export const validateGitHubConnectionCredentials = async (config: TGitHubConnectionConfig) => {
|
||||
const { credentials, method } = config;
|
||||
|
||||
const { gitHubAppConnection } = getInstanceIntegrationsConfig();
|
||||
|
||||
const {
|
||||
INF_APP_CONNECTION_GITHUB_OAUTH_CLIENT_ID,
|
||||
INF_APP_CONNECTION_GITHUB_OAUTH_CLIENT_SECRET,
|
||||
@ -165,8 +170,8 @@ export const validateGitHubConnectionCredentials = async (config: TGitHubConnect
|
||||
const { clientId, clientSecret } =
|
||||
method === GitHubConnectionMethod.App
|
||||
? {
|
||||
clientId: INF_APP_CONNECTION_GITHUB_APP_CLIENT_ID,
|
||||
clientSecret: INF_APP_CONNECTION_GITHUB_APP_CLIENT_SECRET
|
||||
clientId: gitHubAppConnection.clientId || INF_APP_CONNECTION_GITHUB_APP_CLIENT_ID,
|
||||
clientSecret: gitHubAppConnection.clientSecret || INF_APP_CONNECTION_GITHUB_APP_CLIENT_SECRET
|
||||
}
|
||||
: // oauth
|
||||
{
|
||||
|
@ -1,10 +0,0 @@
|
||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||
import { SecretSync } from "@app/services/secret-sync/secret-sync-enums";
|
||||
import { TSecretSyncListItem } from "@app/services/secret-sync/secret-sync-types";
|
||||
|
||||
export const CLOUDFLARE_WORKERS_SYNC_LIST_OPTION: TSecretSyncListItem = {
|
||||
name: "Cloudflare Workers",
|
||||
destination: SecretSync.CloudflareWorkers,
|
||||
connection: AppConnection.Cloudflare,
|
||||
canImportSecrets: false
|
||||
};
|
@ -1,121 +0,0 @@
|
||||
import { request } from "@app/lib/config/request";
|
||||
import { applyJitter } from "@app/lib/dates";
|
||||
import { delay as delayMs } from "@app/lib/delay";
|
||||
import { IntegrationUrls } from "@app/services/integration-auth/integration-list";
|
||||
import { matchesSchema } from "@app/services/secret-sync/secret-sync-fns";
|
||||
import { TSecretMap } from "@app/services/secret-sync/secret-sync-types";
|
||||
|
||||
import { SECRET_SYNC_NAME_MAP } from "../secret-sync-maps";
|
||||
import { TCloudflareWorkersSyncWithCredentials } from "./cloudflare-workers-types";
|
||||
|
||||
const getSecretKeys = async (secretSync: TCloudflareWorkersSyncWithCredentials): Promise<string[]> => {
|
||||
const {
|
||||
destinationConfig,
|
||||
connection: {
|
||||
credentials: { apiToken, accountId }
|
||||
}
|
||||
} = secretSync;
|
||||
|
||||
const { data } = await request.get<{
|
||||
result: Array<{ name: string }>;
|
||||
}>(
|
||||
`${IntegrationUrls.CLOUDFLARE_WORKERS_API_URL}/client/v4/accounts/${accountId}/workers/scripts/${destinationConfig.scriptId}/secrets`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${apiToken}`,
|
||||
Accept: "application/json"
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return data.result.map((s) => s.name);
|
||||
};
|
||||
|
||||
export const CloudflareWorkersSyncFns = {
|
||||
syncSecrets: async (secretSync: TCloudflareWorkersSyncWithCredentials, secretMap: TSecretMap) => {
|
||||
const {
|
||||
connection: {
|
||||
credentials: { apiToken, accountId }
|
||||
},
|
||||
destinationConfig: { scriptId }
|
||||
} = secretSync;
|
||||
|
||||
const existingSecretNames = await getSecretKeys(secretSync);
|
||||
const secretMapKeys = new Set(Object.keys(secretMap));
|
||||
|
||||
for await (const [key, val] of Object.entries(secretMap)) {
|
||||
await delayMs(Math.max(0, applyJitter(100, 200)));
|
||||
await request.put(
|
||||
`${IntegrationUrls.CLOUDFLARE_WORKERS_API_URL}/client/v4/accounts/${accountId}/workers/scripts/${scriptId}/secrets`,
|
||||
{ name: key, text: val.value, type: "secret_text" },
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${apiToken}`,
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if (!secretSync.syncOptions.disableSecretDeletion) {
|
||||
const secretsToDelete = existingSecretNames.filter((existingKey) => {
|
||||
const isManagedBySchema = matchesSchema(
|
||||
existingKey,
|
||||
secretSync.environment?.slug || "",
|
||||
secretSync.syncOptions.keySchema
|
||||
);
|
||||
const isInNewSecretMap = secretMapKeys.has(existingKey);
|
||||
return !isInNewSecretMap && isManagedBySchema;
|
||||
});
|
||||
|
||||
for await (const key of secretsToDelete) {
|
||||
await delayMs(Math.max(0, applyJitter(100, 200)));
|
||||
await request.delete(
|
||||
`${IntegrationUrls.CLOUDFLARE_WORKERS_API_URL}/client/v4/accounts/${accountId}/workers/scripts/${scriptId}/secrets/${key}`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${apiToken}`
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
getSecrets: async (secretSync: TCloudflareWorkersSyncWithCredentials): Promise<TSecretMap> => {
|
||||
throw new Error(`${SECRET_SYNC_NAME_MAP[secretSync.destination]} does not support importing secrets.`);
|
||||
},
|
||||
|
||||
removeSecrets: async (secretSync: TCloudflareWorkersSyncWithCredentials, secretMap: TSecretMap) => {
|
||||
const {
|
||||
connection: {
|
||||
credentials: { apiToken, accountId }
|
||||
},
|
||||
destinationConfig: { scriptId }
|
||||
} = secretSync;
|
||||
|
||||
const existingSecretNames = await getSecretKeys(secretSync);
|
||||
const secretMapToRemoveKeys = new Set(Object.keys(secretMap));
|
||||
|
||||
for await (const existingKey of existingSecretNames) {
|
||||
const isManagedBySchema = matchesSchema(
|
||||
existingKey,
|
||||
secretSync.environment?.slug || "",
|
||||
secretSync.syncOptions.keySchema
|
||||
);
|
||||
const isInSecretMapToRemove = secretMapToRemoveKeys.has(existingKey);
|
||||
|
||||
if (isInSecretMapToRemove && isManagedBySchema) {
|
||||
await delayMs(Math.max(0, applyJitter(100, 200)));
|
||||
await request.delete(
|
||||
`${IntegrationUrls.CLOUDFLARE_WORKERS_API_URL}/client/v4/accounts/${accountId}/workers/scripts/${scriptId}/secrets/${existingKey}`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${apiToken}`
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
@ -1,55 +0,0 @@
|
||||
import RE2 from "re2";
|
||||
import { z } from "zod";
|
||||
|
||||
import { SecretSyncs } from "@app/lib/api-docs";
|
||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||
import { SecretSync } from "@app/services/secret-sync/secret-sync-enums";
|
||||
import {
|
||||
BaseSecretSyncSchema,
|
||||
GenericCreateSecretSyncFieldsSchema,
|
||||
GenericUpdateSecretSyncFieldsSchema
|
||||
} from "@app/services/secret-sync/secret-sync-schemas";
|
||||
import { TSyncOptionsConfig } from "@app/services/secret-sync/secret-sync-types";
|
||||
|
||||
const CloudflareWorkersSyncDestinationConfigSchema = z.object({
|
||||
scriptId: z
|
||||
.string()
|
||||
.min(1, "Script ID is required")
|
||||
.max(64)
|
||||
.refine((val) => {
|
||||
const re2 = new RE2(/^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/);
|
||||
return re2.test(val);
|
||||
}, "Invalid script ID format")
|
||||
.describe(SecretSyncs.DESTINATION_CONFIG.CLOUDFLARE_WORKERS.scriptId)
|
||||
});
|
||||
|
||||
const CloudflareWorkersSyncOptionsConfig: TSyncOptionsConfig = { canImportSecrets: false };
|
||||
|
||||
export const CloudflareWorkersSyncSchema = BaseSecretSyncSchema(
|
||||
SecretSync.CloudflareWorkers,
|
||||
CloudflareWorkersSyncOptionsConfig
|
||||
).extend({
|
||||
destination: z.literal(SecretSync.CloudflareWorkers),
|
||||
destinationConfig: CloudflareWorkersSyncDestinationConfigSchema
|
||||
});
|
||||
|
||||
export const CreateCloudflareWorkersSyncSchema = GenericCreateSecretSyncFieldsSchema(
|
||||
SecretSync.CloudflareWorkers,
|
||||
CloudflareWorkersSyncOptionsConfig
|
||||
).extend({
|
||||
destinationConfig: CloudflareWorkersSyncDestinationConfigSchema
|
||||
});
|
||||
|
||||
export const UpdateCloudflareWorkersSyncSchema = GenericUpdateSecretSyncFieldsSchema(
|
||||
SecretSync.CloudflareWorkers,
|
||||
CloudflareWorkersSyncOptionsConfig
|
||||
).extend({
|
||||
destinationConfig: CloudflareWorkersSyncDestinationConfigSchema.optional()
|
||||
});
|
||||
|
||||
export const CloudflareWorkersSyncListItemSchema = z.object({
|
||||
name: z.literal("Cloudflare Workers"),
|
||||
connection: z.literal(AppConnection.Cloudflare),
|
||||
destination: z.literal(SecretSync.CloudflareWorkers),
|
||||
canImportSecrets: z.literal(false)
|
||||
});
|
@ -1,19 +0,0 @@
|
||||
import z from "zod";
|
||||
|
||||
import { TCloudflareConnection } from "@app/services/app-connection/cloudflare/cloudflare-connection-types";
|
||||
|
||||
import {
|
||||
CloudflareWorkersSyncListItemSchema,
|
||||
CloudflareWorkersSyncSchema,
|
||||
CreateCloudflareWorkersSyncSchema
|
||||
} from "./cloudflare-workers-schemas";
|
||||
|
||||
export type TCloudflareWorkersSyncListItem = z.infer<typeof CloudflareWorkersSyncListItemSchema>;
|
||||
|
||||
export type TCloudflareWorkersSync = z.infer<typeof CloudflareWorkersSyncSchema>;
|
||||
|
||||
export type TCloudflareWorkersSyncInput = z.infer<typeof CreateCloudflareWorkersSyncSchema>;
|
||||
|
||||
export type TCloudflareWorkersSyncWithCredentials = TCloudflareWorkersSync & {
|
||||
connection: TCloudflareConnection;
|
||||
};
|
@ -1,4 +0,0 @@
|
||||
export * from "./cloudflare-workers-constants";
|
||||
export * from "./cloudflare-workers-fns";
|
||||
export * from "./cloudflare-workers-schemas";
|
||||
export * from "./cloudflare-workers-types";
|
@ -21,8 +21,6 @@ export enum SecretSync {
|
||||
Flyio = "flyio",
|
||||
GitLab = "gitlab",
|
||||
CloudflarePages = "cloudflare-pages",
|
||||
CloudflareWorkers = "cloudflare-workers",
|
||||
|
||||
Zabbix = "zabbix",
|
||||
Railway = "railway"
|
||||
}
|
||||
|
@ -31,7 +31,6 @@ import { AZURE_KEY_VAULT_SYNC_LIST_OPTION, azureKeyVaultSyncFactory } from "./az
|
||||
import { CAMUNDA_SYNC_LIST_OPTION, camundaSyncFactory } from "./camunda";
|
||||
import { CLOUDFLARE_PAGES_SYNC_LIST_OPTION } from "./cloudflare-pages/cloudflare-pages-constants";
|
||||
import { CloudflarePagesSyncFns } from "./cloudflare-pages/cloudflare-pages-fns";
|
||||
import { CLOUDFLARE_WORKERS_SYNC_LIST_OPTION, CloudflareWorkersSyncFns } from "./cloudflare-workers";
|
||||
import { FLYIO_SYNC_LIST_OPTION, FlyioSyncFns } from "./flyio";
|
||||
import { GCP_SYNC_LIST_OPTION } from "./gcp";
|
||||
import { GcpSyncFns } from "./gcp/gcp-sync-fns";
|
||||
@ -73,8 +72,6 @@ const SECRET_SYNC_LIST_OPTIONS: Record<SecretSync, TSecretSyncListItem> = {
|
||||
[SecretSync.Flyio]: FLYIO_SYNC_LIST_OPTION,
|
||||
[SecretSync.GitLab]: GITLAB_SYNC_LIST_OPTION,
|
||||
[SecretSync.CloudflarePages]: CLOUDFLARE_PAGES_SYNC_LIST_OPTION,
|
||||
[SecretSync.CloudflareWorkers]: CLOUDFLARE_WORKERS_SYNC_LIST_OPTION,
|
||||
|
||||
[SecretSync.Zabbix]: ZABBIX_SYNC_LIST_OPTION,
|
||||
[SecretSync.Railway]: RAILWAY_SYNC_LIST_OPTION
|
||||
};
|
||||
@ -244,8 +241,6 @@ export const SecretSyncFns = {
|
||||
return GitLabSyncFns.syncSecrets(secretSync, schemaSecretMap, { appConnectionDAL, kmsService });
|
||||
case SecretSync.CloudflarePages:
|
||||
return CloudflarePagesSyncFns.syncSecrets(secretSync, schemaSecretMap);
|
||||
case SecretSync.CloudflareWorkers:
|
||||
return CloudflareWorkersSyncFns.syncSecrets(secretSync, schemaSecretMap);
|
||||
case SecretSync.Zabbix:
|
||||
return ZabbixSyncFns.syncSecrets(secretSync, schemaSecretMap);
|
||||
case SecretSync.Railway:
|
||||
@ -342,9 +337,6 @@ export const SecretSyncFns = {
|
||||
case SecretSync.CloudflarePages:
|
||||
secretMap = await CloudflarePagesSyncFns.getSecrets(secretSync);
|
||||
break;
|
||||
case SecretSync.CloudflareWorkers:
|
||||
secretMap = await CloudflareWorkersSyncFns.getSecrets(secretSync);
|
||||
break;
|
||||
case SecretSync.Zabbix:
|
||||
secretMap = await ZabbixSyncFns.getSecrets(secretSync);
|
||||
break;
|
||||
@ -428,8 +420,6 @@ export const SecretSyncFns = {
|
||||
return GitLabSyncFns.removeSecrets(secretSync, schemaSecretMap, { appConnectionDAL, kmsService });
|
||||
case SecretSync.CloudflarePages:
|
||||
return CloudflarePagesSyncFns.removeSecrets(secretSync, schemaSecretMap);
|
||||
case SecretSync.CloudflareWorkers:
|
||||
return CloudflareWorkersSyncFns.removeSecrets(secretSync, schemaSecretMap);
|
||||
case SecretSync.Zabbix:
|
||||
return ZabbixSyncFns.removeSecrets(secretSync, schemaSecretMap);
|
||||
case SecretSync.Railway:
|
||||
|
@ -24,8 +24,6 @@ export const SECRET_SYNC_NAME_MAP: Record<SecretSync, string> = {
|
||||
[SecretSync.Flyio]: "Fly.io",
|
||||
[SecretSync.GitLab]: "GitLab",
|
||||
[SecretSync.CloudflarePages]: "Cloudflare Pages",
|
||||
[SecretSync.CloudflareWorkers]: "Cloudflare Workers",
|
||||
|
||||
[SecretSync.Zabbix]: "Zabbix",
|
||||
[SecretSync.Railway]: "Railway"
|
||||
};
|
||||
@ -53,8 +51,6 @@ export const SECRET_SYNC_CONNECTION_MAP: Record<SecretSync, AppConnection> = {
|
||||
[SecretSync.Flyio]: AppConnection.Flyio,
|
||||
[SecretSync.GitLab]: AppConnection.GitLab,
|
||||
[SecretSync.CloudflarePages]: AppConnection.Cloudflare,
|
||||
[SecretSync.CloudflareWorkers]: AppConnection.Cloudflare,
|
||||
|
||||
[SecretSync.Zabbix]: AppConnection.Zabbix,
|
||||
[SecretSync.Railway]: AppConnection.Railway
|
||||
};
|
||||
@ -82,8 +78,6 @@ export const SECRET_SYNC_PLAN_MAP: Record<SecretSync, SecretSyncPlanType> = {
|
||||
[SecretSync.Flyio]: SecretSyncPlanType.Regular,
|
||||
[SecretSync.GitLab]: SecretSyncPlanType.Regular,
|
||||
[SecretSync.CloudflarePages]: SecretSyncPlanType.Regular,
|
||||
[SecretSync.CloudflareWorkers]: SecretSyncPlanType.Regular,
|
||||
|
||||
[SecretSync.Zabbix]: SecretSyncPlanType.Regular,
|
||||
[SecretSync.Railway]: SecretSyncPlanType.Regular
|
||||
};
|
||||
|
@ -78,12 +78,6 @@ import {
|
||||
TCloudflarePagesSyncListItem,
|
||||
TCloudflarePagesSyncWithCredentials
|
||||
} from "./cloudflare-pages/cloudflare-pages-types";
|
||||
import {
|
||||
TCloudflareWorkersSync,
|
||||
TCloudflareWorkersSyncInput,
|
||||
TCloudflareWorkersSyncListItem,
|
||||
TCloudflareWorkersSyncWithCredentials
|
||||
} from "./cloudflare-workers";
|
||||
import { TFlyioSync, TFlyioSyncInput, TFlyioSyncListItem, TFlyioSyncWithCredentials } from "./flyio/flyio-sync-types";
|
||||
import { TGcpSync, TGcpSyncInput, TGcpSyncListItem, TGcpSyncWithCredentials } from "./gcp";
|
||||
import { TGitLabSync, TGitLabSyncInput, TGitLabSyncListItem, TGitLabSyncWithCredentials } from "./gitlab";
|
||||
@ -150,7 +144,6 @@ export type TSecretSync =
|
||||
| TFlyioSync
|
||||
| TGitLabSync
|
||||
| TCloudflarePagesSync
|
||||
| TCloudflareWorkersSync
|
||||
| TZabbixSync
|
||||
| TRailwaySync;
|
||||
|
||||
@ -177,7 +170,6 @@ export type TSecretSyncWithCredentials =
|
||||
| TFlyioSyncWithCredentials
|
||||
| TGitLabSyncWithCredentials
|
||||
| TCloudflarePagesSyncWithCredentials
|
||||
| TCloudflareWorkersSyncWithCredentials
|
||||
| TZabbixSyncWithCredentials
|
||||
| TRailwaySyncWithCredentials;
|
||||
|
||||
@ -204,7 +196,6 @@ export type TSecretSyncInput =
|
||||
| TFlyioSyncInput
|
||||
| TGitLabSyncInput
|
||||
| TCloudflarePagesSyncInput
|
||||
| TCloudflareWorkersSyncInput
|
||||
| TZabbixSyncInput
|
||||
| TRailwaySyncInput;
|
||||
|
||||
@ -231,7 +222,6 @@ export type TSecretSyncListItem =
|
||||
| TFlyioSyncListItem
|
||||
| TGitLabSyncListItem
|
||||
| TCloudflarePagesSyncListItem
|
||||
| TCloudflareWorkersSyncListItem
|
||||
| TZabbixSyncListItem
|
||||
| TRailwaySyncListItem;
|
||||
|
||||
|
@ -20,7 +20,7 @@ func CheckForUpdate() {
|
||||
if checkEnv := os.Getenv("INFISICAL_DISABLE_UPDATE_CHECK"); checkEnv != "" {
|
||||
return
|
||||
}
|
||||
latestVersion, _, err := getLatestTag("Infisical", "infisical")
|
||||
latestVersion, _, err := getLatestTag("Infisical", "cli")
|
||||
if err != nil {
|
||||
log.Debug().Err(err)
|
||||
// do nothing and continue
|
||||
@ -98,7 +98,7 @@ func getLatestTag(repoOwner string, repoName string) (string, string, error) {
|
||||
return "", "", fmt.Errorf("failed to unmarshal github response: %w", err)
|
||||
}
|
||||
|
||||
tag_prefix := "infisical-cli/v"
|
||||
tag_prefix := "v"
|
||||
|
||||
// Extract the version from the first valid tag
|
||||
version := strings.TrimPrefix(releaseDetails.TagName, tag_prefix)
|
||||
|
@ -19,17 +19,13 @@ FROM node:20-alpine
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
RUN addgroup -g 1001 -S mintuser && \
|
||||
adduser -S -D -H -u 1001 -s /sbin/nologin -G mintuser mintuser && \
|
||||
npm install -g mint@4.2.13
|
||||
RUN npm install -g mint@4.2.13
|
||||
|
||||
COPY --chown=mintuser:mintuser . .
|
||||
COPY . .
|
||||
|
||||
COPY --from=builder --chown=mintuser:mintuser /root/.mintlify /home/mintuser/.mintlify
|
||||
COPY --from=builder --chown=mintuser:mintuser /app/docs.json /app/docs.json
|
||||
COPY --from=builder --chown=mintuser:mintuser /app/spec.json /app/spec.json
|
||||
|
||||
USER mintuser
|
||||
COPY --from=builder /root/.mintlify /root/.mintlify
|
||||
COPY --from=builder /app/docs.json /app/docs.json
|
||||
COPY --from=builder /app/spec.json /app/spec.json
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
|
@ -1,4 +0,0 @@
|
||||
---
|
||||
title: "Create"
|
||||
openapi: "POST /api/v1/secret-syncs/cloudflare-workers"
|
||||
---
|
@ -1,4 +0,0 @@
|
||||
---
|
||||
title: "Delete"
|
||||
openapi: "DELETE /api/v1/secret-syncs/cloudflare-workers/{syncId}"
|
||||
---
|
@ -1,4 +0,0 @@
|
||||
---
|
||||
title: "Get by ID"
|
||||
openapi: "GET /api/v1/secret-syncs/cloudflare-workers/{syncId}"
|
||||
---
|
@ -1,4 +0,0 @@
|
||||
---
|
||||
title: "Get by Name"
|
||||
openapi: "GET /api/v1/secret-syncs/cloudflare-workers/sync-name/{syncName}"
|
||||
---
|
@ -1,4 +0,0 @@
|
||||
---
|
||||
title: "List"
|
||||
openapi: "GET /api/v1/secret-syncs/cloudflare-workers"
|
||||
---
|
@ -1,4 +0,0 @@
|
||||
---
|
||||
title: "Remove Secrets"
|
||||
openapi: "POST /api/v1/secret-syncs/cloudflare-workers/{syncId}/remove-secrets"
|
||||
---
|
@ -1,4 +0,0 @@
|
||||
---
|
||||
title: "Sync Secrets"
|
||||
openapi: "POST /api/v1/secret-syncs/cloudflare-workers/{syncId}/sync-secrets"
|
||||
---
|
@ -1,4 +0,0 @@
|
||||
---
|
||||
title: "Update"
|
||||
openapi: "PATCH /api/v1/secret-syncs/cloudflare-workers/{syncId}"
|
||||
---
|
@ -514,7 +514,6 @@
|
||||
"integrations/secret-syncs/azure-key-vault",
|
||||
"integrations/secret-syncs/camunda",
|
||||
"integrations/secret-syncs/cloudflare-pages",
|
||||
"integrations/secret-syncs/cloudflare-workers",
|
||||
"integrations/secret-syncs/databricks",
|
||||
"integrations/secret-syncs/flyio",
|
||||
"integrations/secret-syncs/gcp-secret-manager",
|
||||
@ -1721,19 +1720,6 @@
|
||||
"api-reference/endpoints/secret-syncs/cloudflare-pages/remove-secrets"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Cloudflare Workers",
|
||||
"pages": [
|
||||
"api-reference/endpoints/secret-syncs/cloudflare-workers/list",
|
||||
"api-reference/endpoints/secret-syncs/cloudflare-workers/get-by-id",
|
||||
"api-reference/endpoints/secret-syncs/cloudflare-workers/get-by-name",
|
||||
"api-reference/endpoints/secret-syncs/cloudflare-workers/create",
|
||||
"api-reference/endpoints/secret-syncs/cloudflare-workers/update",
|
||||
"api-reference/endpoints/secret-syncs/cloudflare-workers/delete",
|
||||
"api-reference/endpoints/secret-syncs/cloudflare-workers/sync-secrets",
|
||||
"api-reference/endpoints/secret-syncs/cloudflare-workers/remove-secrets"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Databricks",
|
||||
"pages": [
|
||||
|
Before Width: | Height: | Size: 325 KiB |
Before Width: | Height: | Size: 966 KiB |
Before Width: | Height: | Size: 578 KiB |
Before Width: | Height: | Size: 575 KiB |
Before Width: | Height: | Size: 624 KiB |
Before Width: | Height: | Size: 598 KiB |
Before Width: | Height: | Size: 558 KiB |
Before Width: | Height: | Size: 637 KiB |
@ -35,17 +35,6 @@ Infisical supports connecting to Cloudflare using API tokens and Account ID for
|
||||
- **Account** - **Cloudflare Pages** - **Edit**
|
||||
- **Account** - **Account Settings** - **Read**
|
||||
|
||||
Add these permissions to your API token and click **Continue to summary**, then **Create Token** to generate your API token.
|
||||
</Accordion>
|
||||
<Accordion title="Cloudflare Workers">
|
||||
Use the following permissions to grant Infisical access to sync secrets to Cloudflare Workers:
|
||||
|
||||

|
||||
|
||||
**Required Permissions:**
|
||||
- **Account** - **Workers Scripts** - **Edit**
|
||||
- **Account** - **Account Settings** - **Read**
|
||||
|
||||
Add these permissions to your API token and click **Continue to summary**, then **Create Token** to generate your API token.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
@ -55,7 +44,7 @@ Infisical supports connecting to Cloudflare using API tokens and Account ID for
|
||||
</Step>
|
||||
<Step title="Save Your API Token">
|
||||
After creation, copy and securely store your API token as it will not be shown again.
|
||||
|
||||
|
||||

|
||||
|
||||
<Warning>
|
||||
|
@ -1,128 +0,0 @@
|
||||
---
|
||||
title: "Cloudflare Workers Sync"
|
||||
description: "Learn how to configure a Cloudflare Workers Sync for Infisical."
|
||||
---
|
||||
|
||||
**Prerequisites:**
|
||||
|
||||
- Set up and add secrets to [Infisical Cloud](https://app.infisical.com)
|
||||
- Create a [Cloudflare Connection](/integrations/app-connections/cloudflare)
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Infisical UI">
|
||||
1. Navigate to **Project** > **Integrations** and select the **Secret Syncs** tab. Click on the **Add Sync** button.
|
||||

|
||||
|
||||
2. Select the **Cloudflare Workers** option.
|
||||

|
||||
|
||||
3. Configure the **Source** from where secrets should be retrieved, then click **Next**.
|
||||

|
||||
|
||||
- **Environment**: The project environment to retrieve secrets from.
|
||||
- **Secret Path**: The folder path to retrieve secrets from.
|
||||
|
||||
<Tip>
|
||||
If you need to sync secrets from multiple folder locations, check out [secret imports](/documentation/platform/secret-reference#secret-imports).
|
||||
</Tip>
|
||||
|
||||
4. Configure the **Destination** to where secrets should be deployed, then click **Next**.
|
||||

|
||||
|
||||
- **Cloudflare Connection**: The Cloudflare Connection to authenticate with.
|
||||
- **Cloudflare Workers Script**: Choose the Cloudflare Workers script you want to sync secrets to.
|
||||
|
||||
5. Configure the **Sync Options** to specify how secrets should be synced, then click **Next**.
|
||||

|
||||
|
||||
- **Initial Sync Behavior**: Determines how Infisical should resolve the initial sync.
|
||||
- **Overwrite Destination Secrets**: Removes any secrets at the destination endpoint not present in Infisical.
|
||||
- **Key Schema**: Template that determines how secret names are transformed when syncing, using `{{secretKey}}` as a placeholder for the original secret name and `{{environment}}` for the environment.
|
||||
- **Auto-Sync Enabled**: If enabled, secrets will automatically be synced from the source location when changes occur. Disable to enforce manual syncing only.
|
||||
- **Disable Secret Deletion**: If enabled, Infisical will not remove secrets from the sync destination. Enable this option if you intend to manage some secrets manually outside of Infisical.
|
||||
|
||||
6. Configure the **Details** of your Cloudflare Workers Sync, then click **Next**.
|
||||

|
||||
|
||||
- **Name**: The name of your sync. Must be slug-friendly.
|
||||
- **Description**: An optional description for your sync.
|
||||
|
||||
7. Review your Cloudflare Workers Sync configuration, then click **Create Sync**.
|
||||

|
||||
|
||||
8. If enabled, your Cloudflare Workers Sync will begin syncing your secrets to the destination endpoint.
|
||||

|
||||
|
||||
</Tab>
|
||||
<Tab title="API">
|
||||
To create a **Cloudflare Workers Sync**, make an API request to the [Create Cloudflare Workers Sync](/api-reference/endpoints/secret-syncs/cloudflare-workers/create) API endpoint.
|
||||
|
||||
### Sample request
|
||||
|
||||
```bash Request
|
||||
curl --request POST \
|
||||
--url https://app.infisical.com/api/v1/secret-syncs/cloudflare-workers \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"name": "my-cloudflare-workers-sync",
|
||||
"projectId": "your-project-id",
|
||||
"description": "an example sync",
|
||||
"connectionId": "your-cloudflare-connection-id",
|
||||
"environment": "production",
|
||||
"secretPath": "/my-secrets",
|
||||
"isEnabled": true,
|
||||
"syncOptions": {
|
||||
"initialSyncBehavior": "overwrite-destination"
|
||||
},
|
||||
"destinationConfig": {
|
||||
"scriptId": "my-workers-script"
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
### Sample response
|
||||
|
||||
```bash Response
|
||||
{
|
||||
"secretSync": {
|
||||
"id": "your-sync-id",
|
||||
"name": "my-cloudflare-workers-sync",
|
||||
"description": "an example sync",
|
||||
"isEnabled": true,
|
||||
"version": 1,
|
||||
"folderId": "your-folder-id",
|
||||
"connectionId": "your-cloudflare-connection-id",
|
||||
"createdAt": "2024-05-01T12:00:00Z",
|
||||
"updatedAt": "2024-05-01T12:00:00Z",
|
||||
"syncStatus": "succeeded",
|
||||
"lastSyncJobId": "123",
|
||||
"lastSyncMessage": null,
|
||||
"lastSyncedAt": "2024-05-01T12:00:00Z",
|
||||
"syncOptions": {
|
||||
"initialSyncBehavior": "overwrite-destination"
|
||||
},
|
||||
"projectId": "your-project-id",
|
||||
"connection": {
|
||||
"app": "cloudflare",
|
||||
"name": "my-cloudflare-connection",
|
||||
"id": "your-cloudflare-connection-id"
|
||||
},
|
||||
"environment": {
|
||||
"slug": "production",
|
||||
"name": "Production",
|
||||
"id": "your-env-id"
|
||||
},
|
||||
"folder": {
|
||||
"id": "your-folder-id",
|
||||
"path": "/my-secrets"
|
||||
},
|
||||
"destination": "cloudflare-workers",
|
||||
"destinationConfig": {
|
||||
"scriptId": "my-workers-script"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
@ -4,7 +4,7 @@ import { SingleValue } from "react-select";
|
||||
import { SecretSyncConnectionField } from "@app/components/secret-syncs/forms/SecretSyncConnectionField";
|
||||
import { FilterableSelect, FormControl, Select, SelectItem } from "@app/components/v2";
|
||||
import {
|
||||
TCloudflarePagesProject,
|
||||
TCloudflareProject,
|
||||
useCloudflareConnectionListPagesProjects
|
||||
} from "@app/hooks/api/appConnections/cloudflare";
|
||||
import { SecretSync } from "@app/hooks/api/secretSyncs";
|
||||
@ -52,7 +52,7 @@ export const CloudflarePagesSyncFields = () => {
|
||||
isDisabled={!connectionId}
|
||||
value={projects ? (projects.find((project) => project.name === value) ?? []) : []}
|
||||
onChange={(option) => {
|
||||
onChange((option as SingleValue<TCloudflarePagesProject>)?.name ?? null);
|
||||
onChange((option as SingleValue<TCloudflareProject>)?.name ?? null);
|
||||
}}
|
||||
options={projects}
|
||||
placeholder="Select a project..."
|
||||
|
@ -1,59 +0,0 @@
|
||||
import { Controller, useFormContext, useWatch } from "react-hook-form";
|
||||
import { SingleValue } from "react-select";
|
||||
|
||||
import { SecretSyncConnectionField } from "@app/components/secret-syncs/forms/SecretSyncConnectionField";
|
||||
import { FilterableSelect, FormControl } from "@app/components/v2";
|
||||
import {
|
||||
TCloudflareWorkersScript,
|
||||
useCloudflareConnectionListWorkersScripts
|
||||
} from "@app/hooks/api/appConnections/cloudflare";
|
||||
import { SecretSync } from "@app/hooks/api/secretSyncs";
|
||||
|
||||
import { TSecretSyncForm } from "../schemas";
|
||||
|
||||
export const CloudflareWorkersSyncFields = () => {
|
||||
const { control, setValue } = useFormContext<
|
||||
TSecretSyncForm & { destination: SecretSync.CloudflareWorkers }
|
||||
>();
|
||||
|
||||
const connectionId = useWatch({ name: "connection.id", control });
|
||||
|
||||
const { data: scripts = [], isPending: isScriptsPending } =
|
||||
useCloudflareConnectionListWorkersScripts(connectionId, {
|
||||
enabled: Boolean(connectionId)
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<SecretSyncConnectionField
|
||||
onChange={() => {
|
||||
setValue("destinationConfig.scriptId", "");
|
||||
}}
|
||||
/>
|
||||
<Controller
|
||||
name="destinationConfig.scriptId"
|
||||
control={control}
|
||||
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
errorText={error?.message}
|
||||
isError={Boolean(error?.message)}
|
||||
label="Worker Script"
|
||||
>
|
||||
<FilterableSelect
|
||||
isLoading={isScriptsPending && Boolean(connectionId)}
|
||||
isDisabled={!connectionId}
|
||||
value={scripts?.find((script) => script.id === value) || []}
|
||||
onChange={(option) => {
|
||||
onChange((option as SingleValue<TCloudflareWorkersScript>)?.id ?? null);
|
||||
}}
|
||||
options={scripts}
|
||||
placeholder="Select a worker script..."
|
||||
getOptionLabel={(option) => option.id}
|
||||
getOptionValue={(option) => option.id}
|
||||
/>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
@ -11,7 +11,6 @@ import { AzureDevOpsSyncFields } from "./AzureDevOpsSyncFields";
|
||||
import { AzureKeyVaultSyncFields } from "./AzureKeyVaultSyncFields";
|
||||
import { CamundaSyncFields } from "./CamundaSyncFields";
|
||||
import { CloudflarePagesSyncFields } from "./CloudflarePagesSyncFields";
|
||||
import { CloudflareWorkersSyncFields } from "./CloudflareWorkersSyncFields";
|
||||
import { DatabricksSyncFields } from "./DatabricksSyncFields";
|
||||
import { FlyioSyncFields } from "./FlyioSyncFields";
|
||||
import { GcpSyncFields } from "./GcpSyncFields";
|
||||
@ -79,8 +78,6 @@ export const SecretSyncDestinationFields = () => {
|
||||
return <GitLabSyncFields />;
|
||||
case SecretSync.CloudflarePages:
|
||||
return <CloudflarePagesSyncFields />;
|
||||
case SecretSync.CloudflareWorkers:
|
||||
return <CloudflareWorkersSyncFields />;
|
||||
case SecretSync.Zabbix:
|
||||
return <ZabbixSyncFields />;
|
||||
case SecretSync.Railway:
|
||||
|
@ -58,7 +58,6 @@ export const SecretSyncOptionsFields = ({ hideInitialSync }: Props) => {
|
||||
case SecretSync.Flyio:
|
||||
case SecretSync.GitLab:
|
||||
case SecretSync.CloudflarePages:
|
||||
case SecretSync.CloudflareWorkers:
|
||||
case SecretSync.Zabbix:
|
||||
case SecretSync.Railway:
|
||||
AdditionalSyncOptionsFieldsComponent = null;
|
||||
|
@ -1,14 +0,0 @@
|
||||
import { useFormContext } from "react-hook-form";
|
||||
|
||||
import { TSecretSyncForm } from "@app/components/secret-syncs/forms/schemas";
|
||||
import { GenericFieldLabel } from "@app/components/v2";
|
||||
import { SecretSync } from "@app/hooks/api/secretSyncs";
|
||||
|
||||
export const CloudflareWorkersSyncReviewFields = () => {
|
||||
const { watch } = useFormContext<
|
||||
TSecretSyncForm & { destination: SecretSync.CloudflareWorkers }
|
||||
>();
|
||||
const scriptId = watch("destinationConfig.scriptId");
|
||||
|
||||
return <GenericFieldLabel label="Script">{scriptId}</GenericFieldLabel>;
|
||||
};
|
@ -20,7 +20,6 @@ import { AzureDevOpsSyncReviewFields } from "./AzureDevOpsSyncReviewFields";
|
||||
import { AzureKeyVaultSyncReviewFields } from "./AzureKeyVaultSyncReviewFields";
|
||||
import { CamundaSyncReviewFields } from "./CamundaSyncReviewFields";
|
||||
import { CloudflarePagesSyncReviewFields } from "./CloudflarePagesReviewFields";
|
||||
import { CloudflareWorkersSyncReviewFields } from "./CloudflareWorkersReviewFields";
|
||||
import { DatabricksSyncReviewFields } from "./DatabricksSyncReviewFields";
|
||||
import { FlyioSyncReviewFields } from "./FlyioSyncReviewFields";
|
||||
import { GcpSyncReviewFields } from "./GcpSyncReviewFields";
|
||||
@ -127,9 +126,6 @@ export const SecretSyncReviewFields = () => {
|
||||
case SecretSync.CloudflarePages:
|
||||
DestinationFieldsComponent = <CloudflarePagesSyncReviewFields />;
|
||||
break;
|
||||
case SecretSync.CloudflareWorkers:
|
||||
DestinationFieldsComponent = <CloudflareWorkersSyncReviewFields />;
|
||||
break;
|
||||
case SecretSync.Zabbix:
|
||||
DestinationFieldsComponent = <ZabbixSyncReviewFields />;
|
||||
break;
|
||||
|
@ -1,18 +0,0 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { BaseSecretSyncSchema } from "@app/components/secret-syncs/forms/schemas/base-secret-sync-schema";
|
||||
import { SecretSync } from "@app/hooks/api/secretSyncs";
|
||||
|
||||
export const CloudflareWorkersSyncDestinationSchema = BaseSecretSyncSchema().merge(
|
||||
z.object({
|
||||
destination: z.literal(SecretSync.CloudflareWorkers),
|
||||
destinationConfig: z.object({
|
||||
scriptId: z
|
||||
.string()
|
||||
.trim()
|
||||
.min(1, "Script ID is required")
|
||||
.max(64)
|
||||
.regex(/^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/, "Invalid script ID format")
|
||||
})
|
||||
})
|
||||
);
|
@ -8,7 +8,6 @@ import { AzureDevOpsSyncDestinationSchema } from "./azure-devops-sync-destinatio
|
||||
import { AzureKeyVaultSyncDestinationSchema } from "./azure-key-vault-sync-destination-schema";
|
||||
import { CamundaSyncDestinationSchema } from "./camunda-sync-destination-schema";
|
||||
import { CloudflarePagesSyncDestinationSchema } from "./cloudflare-pages-sync-destination-schema";
|
||||
import { CloudflareWorkersSyncDestinationSchema } from "./cloudflare-workers-sync-destination-schema";
|
||||
import { DatabricksSyncDestinationSchema } from "./databricks-sync-destination-schema";
|
||||
import { FlyioSyncDestinationSchema } from "./flyio-sync-destination-schema";
|
||||
import { GcpSyncDestinationSchema } from "./gcp-sync-destination-schema";
|
||||
@ -49,8 +48,6 @@ const SecretSyncUnionSchema = z.discriminatedUnion("destination", [
|
||||
FlyioSyncDestinationSchema,
|
||||
GitlabSyncDestinationSchema,
|
||||
CloudflarePagesSyncDestinationSchema,
|
||||
CloudflareWorkersSyncDestinationSchema,
|
||||
|
||||
ZabbixSyncDestinationSchema,
|
||||
RailwaySyncDestinationSchema
|
||||
]);
|
||||
|
@ -29,6 +29,10 @@ export const ROUTE_PATHS = Object.freeze({
|
||||
"/_authenticate/_inject-org-details/_org-layout/organization/settings/oauth/callback"
|
||||
)
|
||||
},
|
||||
SsoPage: setRoute(
|
||||
"/organization/sso",
|
||||
"/_authenticate/_inject-org-details/_org-layout/organization/sso"
|
||||
),
|
||||
SecretSharing: setRoute(
|
||||
"/organization/secret-sharing",
|
||||
"/_authenticate/_inject-org-details/_org-layout/organization/secret-sharing/"
|
||||
|
@ -82,10 +82,6 @@ export const SECRET_SYNC_MAP: Record<SecretSync, { name: string; image: string }
|
||||
name: "Cloudflare Pages",
|
||||
image: "Cloudflare.png"
|
||||
},
|
||||
[SecretSync.CloudflareWorkers]: {
|
||||
name: "Cloudflare Workers",
|
||||
image: "Cloudflare.png"
|
||||
},
|
||||
[SecretSync.Zabbix]: {
|
||||
name: "Zabbix",
|
||||
image: "Zabbix.png"
|
||||
@ -119,8 +115,6 @@ export const SECRET_SYNC_CONNECTION_MAP: Record<SecretSync, AppConnection> = {
|
||||
[SecretSync.Flyio]: AppConnection.Flyio,
|
||||
[SecretSync.GitLab]: AppConnection.Gitlab,
|
||||
[SecretSync.CloudflarePages]: AppConnection.Cloudflare,
|
||||
[SecretSync.CloudflareWorkers]: AppConnection.Cloudflare,
|
||||
|
||||
[SecretSync.Zabbix]: AppConnection.Zabbix,
|
||||
[SecretSync.Railway]: AppConnection.Railway
|
||||
};
|
||||
|
@ -3,23 +3,21 @@ import { useQuery, UseQueryOptions } from "@tanstack/react-query";
|
||||
import { apiRequest } from "@app/config/request";
|
||||
|
||||
import { appConnectionKeys } from "../queries";
|
||||
import { TCloudflarePagesProject, TCloudflareWorkersScript } from "./types";
|
||||
import { TCloudflareProject } from "./types";
|
||||
|
||||
const cloudflareConnectionKeys = {
|
||||
all: [...appConnectionKeys.all, "cloudflare"] as const,
|
||||
listPagesProjects: (connectionId: string) =>
|
||||
[...cloudflareConnectionKeys.all, "pages-projects", connectionId] as const,
|
||||
listWorkersScripts: (connectionId: string) =>
|
||||
[...cloudflareConnectionKeys.all, "workers-scripts", connectionId] as const
|
||||
[...cloudflareConnectionKeys.all, "pages-projects", connectionId] as const
|
||||
};
|
||||
|
||||
export const useCloudflareConnectionListPagesProjects = (
|
||||
connectionId: string,
|
||||
options?: Omit<
|
||||
UseQueryOptions<
|
||||
TCloudflarePagesProject[],
|
||||
TCloudflareProject[],
|
||||
unknown,
|
||||
TCloudflarePagesProject[],
|
||||
TCloudflareProject[],
|
||||
ReturnType<typeof cloudflareConnectionKeys.listPagesProjects>
|
||||
>,
|
||||
"queryKey" | "queryFn"
|
||||
@ -28,7 +26,7 @@ export const useCloudflareConnectionListPagesProjects = (
|
||||
return useQuery({
|
||||
queryKey: cloudflareConnectionKeys.listPagesProjects(connectionId),
|
||||
queryFn: async () => {
|
||||
const { data } = await apiRequest.get<TCloudflarePagesProject[]>(
|
||||
const { data } = await apiRequest.get<TCloudflareProject[]>(
|
||||
`/api/v1/app-connections/cloudflare/${connectionId}/cloudflare-pages-projects`
|
||||
);
|
||||
|
||||
@ -37,28 +35,3 @@ export const useCloudflareConnectionListPagesProjects = (
|
||||
...options
|
||||
});
|
||||
};
|
||||
|
||||
export const useCloudflareConnectionListWorkersScripts = (
|
||||
connectionId: string,
|
||||
options?: Omit<
|
||||
UseQueryOptions<
|
||||
TCloudflareWorkersScript[],
|
||||
unknown,
|
||||
TCloudflareWorkersScript[],
|
||||
ReturnType<typeof cloudflareConnectionKeys.listWorkersScripts>
|
||||
>,
|
||||
"queryKey" | "queryFn"
|
||||
>
|
||||
) => {
|
||||
return useQuery({
|
||||
queryKey: cloudflareConnectionKeys.listWorkersScripts(connectionId),
|
||||
queryFn: async () => {
|
||||
const { data } = await apiRequest.get<TCloudflareWorkersScript[]>(
|
||||
`/api/v1/app-connections/cloudflare/${connectionId}/cloudflare-workers-scripts`
|
||||
);
|
||||
|
||||
return data;
|
||||
},
|
||||
...options
|
||||
});
|
||||
};
|
||||
|
@ -1,8 +1,4 @@
|
||||
export type TCloudflarePagesProject = {
|
||||
export type TCloudflareProject = {
|
||||
id: string;
|
||||
name: string;
|
||||
};
|
||||
|
||||
export type TCloudflareWorkersScript = {
|
||||
id: string;
|
||||
};
|
||||
|
@ -21,8 +21,6 @@ export enum SecretSync {
|
||||
Flyio = "flyio",
|
||||
GitLab = "gitlab",
|
||||
CloudflarePages = "cloudflare-pages",
|
||||
CloudflareWorkers = "cloudflare-workers",
|
||||
|
||||
Zabbix = "zabbix",
|
||||
Railway = "railway"
|
||||
}
|
||||
|
@ -1,15 +0,0 @@
|
||||
import { AppConnection } from "@app/hooks/api/appConnections/enums";
|
||||
import { SecretSync } from "@app/hooks/api/secretSyncs";
|
||||
import { TRootSecretSync } from "@app/hooks/api/secretSyncs/types/root-sync";
|
||||
|
||||
export type TCloudflareWorkersSync = TRootSecretSync & {
|
||||
destination: SecretSync.CloudflareWorkers;
|
||||
destinationConfig: {
|
||||
scriptId: string;
|
||||
};
|
||||
connection: {
|
||||
app: AppConnection.Cloudflare;
|
||||
name: string;
|
||||
id: string;
|
||||
};
|
||||
};
|
@ -10,7 +10,6 @@ import { TAzureDevOpsSync } from "./azure-devops-sync";
|
||||
import { TAzureKeyVaultSync } from "./azure-key-vault-sync";
|
||||
import { TCamundaSync } from "./camunda-sync";
|
||||
import { TCloudflarePagesSync } from "./cloudflare-pages-sync";
|
||||
import { TCloudflareWorkersSync } from "./cloudflare-workers-sync";
|
||||
import { TDatabricksSync } from "./databricks-sync";
|
||||
import { TFlyioSync } from "./flyio-sync";
|
||||
import { TGcpSync } from "./gcp-sync";
|
||||
@ -57,7 +56,6 @@ export type TSecretSync =
|
||||
| TFlyioSync
|
||||
| TGitLabSync
|
||||
| TCloudflarePagesSync
|
||||
| TCloudflareWorkersSync
|
||||
| TZabbixSync
|
||||
| TRailwaySync;
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
import {
|
||||
faBook,
|
||||
faCheckCircle,
|
||||
faCog,
|
||||
faCubes,
|
||||
faDoorClosed,
|
||||
@ -99,6 +100,18 @@ export const OrgSidebar = ({ isHidden }: Props) => {
|
||||
</MenuItem>
|
||||
)}
|
||||
</Link>
|
||||
<Link to="/organization/sso">
|
||||
{({ isActive }) => (
|
||||
<MenuItem isSelected={isActive}>
|
||||
<div className="mx-1 flex gap-2">
|
||||
<div className="w-6">
|
||||
<FontAwesomeIcon icon={faCheckCircle} className="mr-4" />
|
||||
</div>
|
||||
SSO Settings
|
||||
</div>
|
||||
</MenuItem>
|
||||
)}
|
||||
</Link>
|
||||
<Link to="/organization/settings">
|
||||
{({ isActive }) => (
|
||||
<MenuItem isSelected={isActive}>
|
||||
|
@ -0,0 +1,222 @@
|
||||
import { useEffect } from "react";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
import { FaGithub } from "react-icons/fa";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { z } from "zod";
|
||||
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
import {
|
||||
Accordion,
|
||||
AccordionContent,
|
||||
AccordionItem,
|
||||
AccordionTrigger,
|
||||
Button,
|
||||
FormControl,
|
||||
Input,
|
||||
TextArea
|
||||
} from "@app/components/v2";
|
||||
import { useToggle } from "@app/hooks";
|
||||
import { useUpdateServerConfig } from "@app/hooks/api";
|
||||
import { AdminIntegrationsConfig } from "@app/hooks/api/admin/types";
|
||||
|
||||
const gitHubAppFormSchema = z.object({
|
||||
clientId: z.string(),
|
||||
clientSecret: z.string(),
|
||||
appSlug: z.string(),
|
||||
appId: z.string(),
|
||||
privateKey: z.string()
|
||||
});
|
||||
|
||||
type TGitHubAppConnectionForm = z.infer<typeof gitHubAppFormSchema>;
|
||||
|
||||
type Props = {
|
||||
adminIntegrationsConfig?: AdminIntegrationsConfig;
|
||||
};
|
||||
|
||||
export const GitHubAppConnectionForm = ({ adminIntegrationsConfig }: Props) => {
|
||||
const { mutateAsync: updateAdminServerConfig } = useUpdateServerConfig();
|
||||
const [isGitHubAppClientSecretFocused, setIsGitHubAppClientSecretFocused] = useToggle();
|
||||
const {
|
||||
control,
|
||||
handleSubmit,
|
||||
setValue,
|
||||
formState: { isSubmitting, isDirty }
|
||||
} = useForm<TGitHubAppConnectionForm>({
|
||||
resolver: zodResolver(gitHubAppFormSchema)
|
||||
});
|
||||
|
||||
const onSubmit = async (data: TGitHubAppConnectionForm) => {
|
||||
await updateAdminServerConfig({
|
||||
gitHubAppConnectionClientId: data.clientId,
|
||||
gitHubAppConnectionClientSecret: data.clientSecret,
|
||||
gitHubAppConnectionSlug: data.appSlug,
|
||||
gitHubAppConnectionId: data.appId,
|
||||
gitHubAppConnectionPrivateKey: data.privateKey
|
||||
});
|
||||
|
||||
createNotification({
|
||||
text: "Updated GitHub app connection configuration. It can take up to 5 minutes to take effect.",
|
||||
type: "success"
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (adminIntegrationsConfig) {
|
||||
setValue("clientId", adminIntegrationsConfig.gitHubAppConnection.clientId);
|
||||
setValue("clientSecret", adminIntegrationsConfig.gitHubAppConnection.clientSecret);
|
||||
setValue("appSlug", adminIntegrationsConfig.gitHubAppConnection.appSlug);
|
||||
setValue("appId", adminIntegrationsConfig.gitHubAppConnection.appId);
|
||||
setValue("privateKey", adminIntegrationsConfig.gitHubAppConnection.privateKey);
|
||||
}
|
||||
}, [adminIntegrationsConfig]);
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<Accordion type="single" collapsible className="w-full">
|
||||
<AccordionItem value="github-app-integration" className="data-[state=open]:border-none">
|
||||
<AccordionTrigger className="flex h-fit w-full justify-start rounded-md border border-mineshaft-500 bg-mineshaft-700 px-4 py-6 text-sm transition-colors data-[state=open]:rounded-b-none">
|
||||
<div className="text-md group order-1 ml-3 flex items-center gap-2">
|
||||
<FaGithub className="text-lg group-hover:text-primary-400" />
|
||||
<div className="text-[15px] font-semibold">GitHub App</div>
|
||||
</div>
|
||||
</AccordionTrigger>
|
||||
<AccordionContent childrenClassName="px-0 py-0">
|
||||
<div className="flex w-full flex-col justify-start rounded-md rounded-t-none border border-t-0 border-mineshaft-500 bg-mineshaft-700 px-4 py-4">
|
||||
<div className="mb-2 max-w-lg text-sm text-mineshaft-300">
|
||||
Step 1: Create and configure GitHub App. Please refer to the documentation below for
|
||||
more information.
|
||||
</div>
|
||||
<div className="mb-6">
|
||||
<a
|
||||
href="https://infisical.com/docs/integrations/app-connections/github#self-hosted-instance"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Button colorSchema="secondary">Documentation</Button>
|
||||
</a>
|
||||
</div>
|
||||
<div className="mb-4 max-w-lg text-sm text-mineshaft-300">
|
||||
Step 2: Configure your instance-wide settings to enable GitHub App connections. Copy
|
||||
the credentials from your GitHub App's settings page.
|
||||
</div>
|
||||
<Controller
|
||||
control={control}
|
||||
name="clientId"
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
label="Client ID"
|
||||
className="w-96"
|
||||
isError={Boolean(error)}
|
||||
errorText={error?.message}
|
||||
>
|
||||
<Input
|
||||
{...field}
|
||||
value={field.value || ""}
|
||||
type="text"
|
||||
onChange={(e) => field.onChange(e.target.value)}
|
||||
/>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
control={control}
|
||||
name="clientSecret"
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
label="Client Secret"
|
||||
tooltipText="You can find your Client Secret in the GitHub App's settings under 'Client secrets'."
|
||||
className="w-96"
|
||||
isError={Boolean(error)}
|
||||
errorText={error?.message}
|
||||
>
|
||||
<Input
|
||||
{...field}
|
||||
value={field.value || ""}
|
||||
type={isGitHubAppClientSecretFocused ? "text" : "password"}
|
||||
onFocus={() => setIsGitHubAppClientSecretFocused.on()}
|
||||
onBlur={() => setIsGitHubAppClientSecretFocused.off()}
|
||||
onChange={(e) => field.onChange(e.target.value)}
|
||||
/>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
|
||||
<Controller
|
||||
control={control}
|
||||
name="appSlug"
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
label="App Slug"
|
||||
tooltipText="The GitHub App slug from the app's URL (e.g., 'my-app' from github.com/apps/my-app)."
|
||||
className="w-96"
|
||||
isError={Boolean(error)}
|
||||
errorText={error?.message}
|
||||
>
|
||||
<Input
|
||||
{...field}
|
||||
value={field.value || ""}
|
||||
type="text"
|
||||
onChange={(e) => field.onChange(e.target.value)}
|
||||
/>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
|
||||
<Controller
|
||||
control={control}
|
||||
name="appId"
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
label="App ID"
|
||||
tooltipText="The numeric App ID found in your GitHub App's settings."
|
||||
className="w-96"
|
||||
isError={Boolean(error)}
|
||||
errorText={error?.message}
|
||||
>
|
||||
<Input
|
||||
{...field}
|
||||
value={field.value || ""}
|
||||
type="text"
|
||||
onChange={(e) => field.onChange(e.target.value)}
|
||||
/>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
|
||||
<Controller
|
||||
control={control}
|
||||
name="privateKey"
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
label="Private Key"
|
||||
tooltipText="The private key generated for your GitHub App (PEM format)."
|
||||
className="w-96"
|
||||
isError={Boolean(error)}
|
||||
errorText={error?.message}
|
||||
>
|
||||
<TextArea
|
||||
{...field}
|
||||
value={field.value || ""}
|
||||
className="min-h-32"
|
||||
onChange={(e) => field.onChange(e.target.value)}
|
||||
/>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
<div>
|
||||
<Button
|
||||
className="mt-2"
|
||||
type="submit"
|
||||
isLoading={isSubmitting}
|
||||
isDisabled={isSubmitting || !isDirty}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
</form>
|
||||
);
|
||||
};
|
@ -5,17 +5,23 @@ import { ROUTE_PATHS } from "@app/const/routes";
|
||||
import { useGetAdminIntegrationsConfig } from "@app/hooks/api";
|
||||
import { AdminIntegrationsConfig } from "@app/hooks/api/admin/types";
|
||||
|
||||
import { GitHubAppConnectionForm } from "./GitHubAppConnectionForm";
|
||||
import { MicrosoftTeamsIntegrationForm } from "./MicrosoftTeamsIntegrationForm";
|
||||
import { SlackIntegrationForm } from "./SlackIntegrationForm";
|
||||
|
||||
enum IntegrationTabSections {
|
||||
Workflow = "workflow"
|
||||
Workflow = "workflow",
|
||||
AppConnections = "app-connections"
|
||||
}
|
||||
|
||||
interface WorkflowTabProps {
|
||||
adminIntegrationsConfig: AdminIntegrationsConfig;
|
||||
}
|
||||
|
||||
interface AppConnectionsTabProps {
|
||||
adminIntegrationsConfig: AdminIntegrationsConfig;
|
||||
}
|
||||
|
||||
const WorkflowTab = ({ adminIntegrationsConfig }: WorkflowTabProps) => (
|
||||
<div className="flex flex-col gap-2">
|
||||
<SlackIntegrationForm adminIntegrationsConfig={adminIntegrationsConfig} />
|
||||
@ -23,6 +29,12 @@ const WorkflowTab = ({ adminIntegrationsConfig }: WorkflowTabProps) => (
|
||||
</div>
|
||||
);
|
||||
|
||||
const AppConnectionsTab = ({ adminIntegrationsConfig }: AppConnectionsTabProps) => (
|
||||
<div className="flex flex-col gap-2">
|
||||
<GitHubAppConnectionForm adminIntegrationsConfig={adminIntegrationsConfig} />
|
||||
</div>
|
||||
);
|
||||
|
||||
export const IntegrationsPageForm = () => {
|
||||
const { data: adminIntegrationsConfig } = useGetAdminIntegrationsConfig();
|
||||
|
||||
@ -47,6 +59,11 @@ export const IntegrationsPageForm = () => {
|
||||
key: IntegrationTabSections.Workflow,
|
||||
label: "Workflows",
|
||||
component: WorkflowTab
|
||||
},
|
||||
{
|
||||
key: IntegrationTabSections.AppConnections,
|
||||
label: "App Connections",
|
||||
component: AppConnectionsTab
|
||||
}
|
||||
];
|
||||
|
||||
|
@ -1,3 +1,6 @@
|
||||
import { Link } from "@tanstack/react-router";
|
||||
|
||||
import { NoticeBannerV2 } from "@app/components/v2/NoticeBannerV2/NoticeBannerV2";
|
||||
import { OrgPermissionActions, OrgPermissionSubjects } from "@app/context";
|
||||
import { withPermission } from "@app/hoc";
|
||||
|
||||
@ -8,6 +11,21 @@ export const OrgSecurityTab = withPermission(
|
||||
() => {
|
||||
return (
|
||||
<>
|
||||
<NoticeBannerV2
|
||||
className="mx-auto mb-4"
|
||||
titleClassName="text-base"
|
||||
title="Single Sign-On (SSO) Settings"
|
||||
>
|
||||
<p className="mt-1 text-mineshaft-300">
|
||||
SSO Settings have been relocated:{" "}
|
||||
<Link
|
||||
className="text-mineshaft-200 underline underline-offset-2"
|
||||
to="/organization/sso"
|
||||
>
|
||||
Click here to view SSO Settings
|
||||
</Link>
|
||||
</p>
|
||||
</NoticeBannerV2>
|
||||
<OrgGenericAuthSection />
|
||||
<OrgUserAccessTokenLimitSection />
|
||||
</>
|
||||
|
@ -9,10 +9,8 @@ import { ImportTab } from "../ImportTab";
|
||||
import { KmipTab } from "../KmipTab/OrgKmipTab";
|
||||
import { OrgEncryptionTab } from "../OrgEncryptionTab";
|
||||
import { OrgGeneralTab } from "../OrgGeneralTab";
|
||||
import { OrgProvisioningTab } from "../OrgProvisioningTab";
|
||||
import { OrgSecurityTab } from "../OrgSecurityTab";
|
||||
import { OrgSsoTab } from "../OrgSsoTab";
|
||||
import { OrgWorkflowIntegrationTab } from "../OrgWorkflowIntegrationTab";
|
||||
import { OrgWorkflowIntegrationTab } from "../OrgWorkflowIntegrationTab/OrgWorkflowIntegrationTab";
|
||||
import { ProjectTemplatesTab } from "../ProjectTemplatesTab";
|
||||
|
||||
export const OrgTabGroup = () => {
|
||||
@ -21,16 +19,6 @@ export const OrgTabGroup = () => {
|
||||
});
|
||||
const tabs = [
|
||||
{ name: "General", key: "tab-org-general", component: OrgGeneralTab },
|
||||
{
|
||||
name: "SSO",
|
||||
key: "sso-settings",
|
||||
component: OrgSsoTab
|
||||
},
|
||||
{
|
||||
name: "Provisioning",
|
||||
key: "provisioning-settings",
|
||||
component: OrgProvisioningTab
|
||||
},
|
||||
{ name: "Security", key: "tab-org-security", component: OrgSecurityTab },
|
||||
{ name: "Encryption", key: "tab-org-encryption", component: OrgEncryptionTab },
|
||||
{
|
||||
|
@ -1 +0,0 @@
|
||||
export * from "./OrgWorkflowIntegrationTab";
|
21
frontend/src/pages/organization/SsoPage/SsoPage.tsx
Normal file
@ -0,0 +1,21 @@
|
||||
import { Helmet } from "react-helmet";
|
||||
|
||||
import { PageHeader } from "@app/components/v2";
|
||||
|
||||
import { SsoTabGroup } from "./components/SsoTabGroup";
|
||||
|
||||
export const SsoPage = () => {
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>Single Sign-On (SSO)</title>
|
||||
</Helmet>
|
||||
<div className="flex w-full justify-center bg-bunker-800 text-white">
|
||||
<div className="w-full max-w-7xl">
|
||||
<PageHeader title="Single Sign-On (SSO)" />
|
||||
<SsoTabGroup />
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
@ -0,0 +1,37 @@
|
||||
import { useState } from "react";
|
||||
import { useSearch } from "@tanstack/react-router";
|
||||
|
||||
import { Tab, TabList, TabPanel, Tabs } from "@app/components/v2";
|
||||
import { ROUTE_PATHS } from "@app/const/routes";
|
||||
|
||||
import { OrgProvisioningTab } from "../OrgProvisioningTab";
|
||||
import { OrgSsoTab } from "../OrgSsoTab";
|
||||
|
||||
export const SsoTabGroup = () => {
|
||||
const search = useSearch({
|
||||
from: ROUTE_PATHS.Organization.SsoPage.id
|
||||
});
|
||||
const tabs = [
|
||||
{ name: "General", key: "tab-sso-auth", component: OrgSsoTab },
|
||||
{ name: "Provisioning", key: "tab-sso-identity", component: OrgProvisioningTab }
|
||||
];
|
||||
|
||||
const [selectedTab, setSelectedTab] = useState(search.selectedTab || tabs[0].key);
|
||||
|
||||
return (
|
||||
<Tabs value={selectedTab} onValueChange={setSelectedTab}>
|
||||
<TabList>
|
||||
{tabs.map((tab) => (
|
||||
<Tab value={tab.key} key={tab.key}>
|
||||
{tab.name}
|
||||
</Tab>
|
||||
))}
|
||||
</TabList>
|
||||
{tabs.map(({ key, component: Component }) => (
|
||||
<TabPanel value={key} key={`tab-panel-${key}`}>
|
||||
<Component />
|
||||
</TabPanel>
|
||||
))}
|
||||
</Tabs>
|
||||
);
|
||||
};
|
@ -0,0 +1 @@
|
||||
export { SsoTabGroup } from "./SsoTabGroup";
|
26
frontend/src/pages/organization/SsoPage/route.tsx
Normal file
@ -0,0 +1,26 @@
|
||||
import { createFileRoute, stripSearchParams } from "@tanstack/react-router";
|
||||
import { zodValidator } from "@tanstack/zod-adapter";
|
||||
import { z } from "zod";
|
||||
|
||||
import { SsoPage } from "./SsoPage";
|
||||
|
||||
const SettingsPageQueryParams = z.object({
|
||||
selectedTab: z.string().catch("")
|
||||
});
|
||||
|
||||
export const Route = createFileRoute(
|
||||
"/_authenticate/_inject-org-details/_org-layout/organization/sso"
|
||||
)({
|
||||
component: SsoPage,
|
||||
validateSearch: zodValidator(SettingsPageQueryParams),
|
||||
search: {
|
||||
middlewares: [stripSearchParams({ selectedTab: "" })]
|
||||
},
|
||||
context: () => ({
|
||||
breadcrumbs: [
|
||||
{
|
||||
label: "Single Sign-On (SSO)"
|
||||
}
|
||||
]
|
||||
})
|
||||
});
|
@ -1,5 +1,5 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { faAngleDown, faCodeCommit, faWarning } from "@fortawesome/free-solid-svg-icons";
|
||||
import { faAngleDown } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { DropdownMenuItem } from "@radix-ui/react-dropdown-menu";
|
||||
import { useSearch } from "@tanstack/react-router";
|
||||
@ -7,14 +7,12 @@ import { useSearch } from "@tanstack/react-router";
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
import { ProjectPermissionCan } from "@app/components/permissions";
|
||||
import {
|
||||
Button,
|
||||
ContentLoader,
|
||||
DeleteActionModal,
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuTrigger,
|
||||
EmptyState,
|
||||
PageHeader
|
||||
IconButton,
|
||||
Spinner
|
||||
} from "@app/components/v2";
|
||||
import { ROUTE_PATHS } from "@app/const/routes";
|
||||
import {
|
||||
@ -110,25 +108,25 @@ export const CommitDetailsTab = ({
|
||||
// If no commit is selected or data is loading, show appropriate message
|
||||
if (!selectedCommitId) {
|
||||
return (
|
||||
<EmptyState className="mt-40" title="Select a commit to view details." icon={faCodeCommit}>
|
||||
<Button className="mt-4" colorSchema="secondary" onClick={() => goBackToHistory()}>
|
||||
Back to Commits
|
||||
</Button>
|
||||
</EmptyState>
|
||||
<div className="flex h-64 items-center justify-center">
|
||||
<p className="text-gray-400">Select a commit to view details</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (isLoading) {
|
||||
return <ContentLoader />;
|
||||
return (
|
||||
<div className="flex h-64 items-center justify-center">
|
||||
<Spinner size="lg" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!commitDetails) {
|
||||
return (
|
||||
<EmptyState className="mt-40" title="No details found for this commit." icon={faCodeCommit}>
|
||||
<Button className="mt-4" colorSchema="secondary" onClick={() => goBackToHistory()}>
|
||||
Back to Commits
|
||||
</Button>
|
||||
</EmptyState>
|
||||
<div className="flex h-64 items-center justify-center">
|
||||
<p className="text-gray-400">No details found for this commit</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -140,11 +138,9 @@ export const CommitDetailsTab = ({
|
||||
} catch (error) {
|
||||
console.error("Failed to parse commit details:", error);
|
||||
return (
|
||||
<EmptyState className="mt-40" title="Error parsing commit details." icon={faWarning}>
|
||||
<Button className="mt-4" colorSchema="secondary" onClick={() => goBackToHistory()}>
|
||||
Back to Commits
|
||||
</Button>
|
||||
</EmptyState>
|
||||
<div className="flex h-64 items-center justify-center">
|
||||
<p className="text-gray-400">Error parsing commit details</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -227,12 +223,13 @@ export const CommitDetailsTab = ({
|
||||
// Render an item from the merged list
|
||||
const renderMergedItem = (item: MergedItem): JSX.Element => {
|
||||
return (
|
||||
<SecretVersionDiffView
|
||||
key={item.id}
|
||||
item={item}
|
||||
isCollapsed={collapsedItems[item.id]}
|
||||
onToggleCollapse={(id) => toggleItemCollapsed(id)}
|
||||
/>
|
||||
<div key={item.id} className="mb-2">
|
||||
<SecretVersionDiffView
|
||||
item={item}
|
||||
isCollapsed={collapsedItems[item.id]}
|
||||
onToggleCollapse={(id) => toggleItemCollapsed(id)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@ -243,94 +240,114 @@ export const CommitDetailsTab = ({
|
||||
"Unknown";
|
||||
|
||||
return (
|
||||
<>
|
||||
<PageHeader
|
||||
title={`${parsedCommitDetails.changes?.message}` || "No message"}
|
||||
description={
|
||||
<>
|
||||
Commited by {actorDisplay} on{" "}
|
||||
{formatDisplayDate(parsedCommitDetails.changes?.createdAt || new Date().toISOString())}
|
||||
{parsedCommitDetails.changes?.isLatest && (
|
||||
<span className="ml-1 text-mineshaft-400">(Latest)</span>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
>
|
||||
<ProjectPermissionCan
|
||||
I={ProjectPermissionCommitsActions.PerformRollback}
|
||||
a={ProjectPermissionSub.Commits}
|
||||
>
|
||||
{(isAllowed) => (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger
|
||||
asChild
|
||||
disabled={!isAllowed}
|
||||
className={`${!isAllowed ? "cursor-not-allowed" : ""}`}
|
||||
>
|
||||
<Button
|
||||
rightIcon={<FontAwesomeIcon className="ml-2" icon={faAngleDown} />}
|
||||
variant="solid"
|
||||
className="h-min"
|
||||
colorSchema="secondary"
|
||||
>
|
||||
Restore Options
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end" sideOffset={2}>
|
||||
{!parsedCommitDetails.changes.isLatest && (
|
||||
<DropdownMenuItem
|
||||
className="group cursor-pointer rounded-md px-3 py-3 transition-colors hover:bg-mineshaft-700"
|
||||
onClick={() => goToRollbackPreview()}
|
||||
>
|
||||
<div className="flex items-center space-x-3">
|
||||
<div className="flex flex-col">
|
||||
<span className="text-sm font-medium text-white">
|
||||
Roll back to this commit
|
||||
</span>
|
||||
<span className="whitespace-normal break-words text-xs leading-snug text-gray-400">
|
||||
Return this folder to its exact state at the time of this commit,
|
||||
discarding all other changes made after it
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</DropdownMenuItem>
|
||||
<div className="w-full">
|
||||
<div>
|
||||
<div className="flex justify-between pb-2">
|
||||
<div className="w-5/6">
|
||||
<div>
|
||||
<div className="flex items-center">
|
||||
<h1 className="mr-4 truncate text-3xl font-semibold text-white">
|
||||
{parsedCommitDetails.changes?.message || "No message"}
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
<div className="font-small mb-4 mt-2 flex items-center text-sm">
|
||||
<p>
|
||||
<span> Commited by </span>
|
||||
<b>{actorDisplay}</b>
|
||||
<span> on </span>
|
||||
<b>
|
||||
{formatDisplayDate(
|
||||
parsedCommitDetails.changes?.createdAt || new Date().toISOString()
|
||||
)}
|
||||
</b>
|
||||
{parsedCommitDetails.changes?.isLatest && (
|
||||
<span className="ml-1 italic text-gray-400">(Latest)</span>
|
||||
)}
|
||||
<DropdownMenuItem
|
||||
className="group cursor-pointer rounded-md px-3 py-3 transition-colors hover:bg-mineshaft-700"
|
||||
onClick={() => handlePopUpOpen("revertChanges")}
|
||||
>
|
||||
<div className="flex items-center space-x-3">
|
||||
<div className="flex flex-col">
|
||||
<span className="text-sm font-medium text-white">Revert changes</span>
|
||||
<span className="whitespace-normal break-words text-xs leading-snug text-gray-400">
|
||||
Will restore to the previous version of affected resources
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
)}
|
||||
</ProjectPermissionCan>
|
||||
</PageHeader>
|
||||
<div className="flex w-full flex-col rounded-lg border border-mineshaft-600 bg-mineshaft-900 pt-4">
|
||||
<div className="mx-4 flex items-center justify-between border-b border-mineshaft-400 pb-4">
|
||||
<h3 className="text-lg font-semibold text-mineshaft-100">Commit Changes</h3>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center justify-start">
|
||||
<ProjectPermissionCan
|
||||
I={ProjectPermissionCommitsActions.PerformRollback}
|
||||
a={ProjectPermissionSub.Commits}
|
||||
>
|
||||
{(isAllowed) => (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger
|
||||
asChild
|
||||
disabled={!isAllowed}
|
||||
className={`${!isAllowed ? "cursor-not-allowed" : ""}`}
|
||||
>
|
||||
<IconButton
|
||||
ariaLabel="commit-options"
|
||||
variant="outline_bg"
|
||||
className="h-10 rounded border border-mineshaft-600 bg-mineshaft-800 px-4 py-2 text-sm font-medium"
|
||||
>
|
||||
<p className="mr-2">Restore Options</p>
|
||||
<FontAwesomeIcon icon={faAngleDown} />
|
||||
</IconButton>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent
|
||||
align="end"
|
||||
sideOffset={2}
|
||||
className="animate-in fade-in-50 zoom-in-95 min-w-[240px] rounded-md bg-mineshaft-800 p-1 shadow-lg"
|
||||
style={{ marginTop: "0" }}
|
||||
>
|
||||
{!parsedCommitDetails.changes.isLatest && (
|
||||
<DropdownMenuItem
|
||||
className="group cursor-pointer rounded-md px-3 py-3 transition-colors hover:bg-mineshaft-700"
|
||||
onClick={() => goToRollbackPreview()}
|
||||
>
|
||||
<div className="flex items-center space-x-3">
|
||||
<div className="flex flex-col">
|
||||
<span className="text-sm font-medium text-white">
|
||||
Roll back to this commit
|
||||
</span>
|
||||
<span className="max-w-[180px] whitespace-normal break-words text-xs leading-snug text-gray-400">
|
||||
Return this folder to its exact state at the time of this commit,
|
||||
discarding all other changes made after it
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
|
||||
<DropdownMenuItem
|
||||
className="group cursor-pointer rounded-md px-3 py-3 transition-colors hover:bg-mineshaft-700"
|
||||
onClick={() => handlePopUpOpen("revertChanges")}
|
||||
>
|
||||
<div className="flex items-center space-x-3">
|
||||
<div className="flex flex-col">
|
||||
<span className="text-sm font-medium text-white">Revert changes</span>
|
||||
<span className="max-w-[180px] whitespace-normal break-words text-xs leading-snug text-gray-400">
|
||||
Will restore to the previous version of affected resources
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
)}
|
||||
</ProjectPermissionCan>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col overflow-hidden pl-4 pr-1">
|
||||
<div className="thin-scrollbar overflow-y-scroll py-4">
|
||||
{sortedChangedItems.length > 0 ? (
|
||||
sortedChangedItems.map((item) => renderMergedItem(item))
|
||||
) : (
|
||||
<EmptyState
|
||||
title="No changes found."
|
||||
className="h-full pb-0 pt-28"
|
||||
icon={faCodeCommit}
|
||||
/>
|
||||
)}
|
||||
|
||||
<div className="py-2">
|
||||
<div className="overflow-hidden">
|
||||
<div className="space-y-2">
|
||||
{sortedChangedItems.length > 0 ? (
|
||||
sortedChangedItems.map((item) => renderMergedItem(item))
|
||||
) : (
|
||||
<div className="flex h-32 items-center justify-center rounded-lg border border-mineshaft-600 bg-mineshaft-800">
|
||||
<p className="text-gray-400">No changed items found</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<DeleteActionModal
|
||||
isOpen={popUp.revertChanges.isOpen}
|
||||
deleteKey="revert"
|
||||
@ -340,6 +357,6 @@ export const CommitDetailsTab = ({
|
||||
onDeleteApproved={handleRevertChanges}
|
||||
buttonText="Yes, revert changes"
|
||||
/>
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -225,17 +225,15 @@ const renderJsonWithDiffs = (
|
||||
|
||||
const getLineClass = (different: boolean) => {
|
||||
if (!different) return "flex";
|
||||
return isOldVersion
|
||||
? "flex bg-red-500/50 rounded-sm text-red-300"
|
||||
: "flex bg-green-500/50 rounded-sm text-green-300";
|
||||
return isOldVersion ? "flex bg-red-950 text-red-300" : "flex bg-green-950 text-green-300";
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const getHighlightClass = (_different: boolean) => {
|
||||
return "";
|
||||
const getHighlightClass = (different: boolean) => {
|
||||
if (!different) return "";
|
||||
return isOldVersion ? "bg-red-900 rounded px-1" : "bg-green-900 rounded px-1";
|
||||
};
|
||||
|
||||
const prefix = isDifferent ? (isOldVersion ? " -" : " +") : " ";
|
||||
const prefix = isDifferent ? (isOldVersion ? "-" : "+") : " ";
|
||||
const keyDisplay = keyName ? `"${keyName}": ` : "";
|
||||
const comma = !isLastItem ? "," : "";
|
||||
|
||||
@ -322,7 +320,7 @@ const renderJsonWithDiffs = (
|
||||
<div key={reactKey}>
|
||||
<div className={getLineClass(isContainerAddedOrRemoved)}>
|
||||
<div className="w-4 flex-shrink-0">
|
||||
{isContainerAddedOrRemoved ? (isOldVersion ? " -" : " +") : " "}
|
||||
{isContainerAddedOrRemoved ? (isOldVersion ? "-" : "+") : " "}
|
||||
</div>
|
||||
<div>
|
||||
{indent}
|
||||
@ -627,11 +625,12 @@ export const SecretVersionDiffView = ({
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="overflow-hidden border border-b-0 border-mineshaft-600 bg-mineshaft-800 first:rounded-t last:rounded-b last:border-b">
|
||||
<div className="overflow-hidden rounded-lg border border-mineshaft-600 bg-mineshaft-800">
|
||||
{showHeader && renderHeader()}
|
||||
|
||||
{!collapsed && (
|
||||
<div className="border-t border-mineshaft-700 bg-bunker-900 p-3 text-mineshaft-100">
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<div className="border-t border-mineshaft-700 bg-mineshaft-900 px-6 py-4">
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div
|
||||
ref={oldContainerRef}
|
||||
className="thin-scrollbar max-h-96 overflow-auto whitespace-pre rounded border border-mineshaft-600 bg-mineshaft-900 p-4"
|
||||
|
@ -52,7 +52,7 @@ export const CommitsPage = () => {
|
||||
title="Commits"
|
||||
description="Track, inspect, and restore your secrets and folders with confidence. View the complete history of changes made to your environment, examine specific modifications at each commit point, and preview the exact impact before rolling back to previous states."
|
||||
/>
|
||||
<NoticeBannerV2 title="Secret Snapshots Update" className="mb-2">
|
||||
<NoticeBannerV2 title="" className="mb-2">
|
||||
<p className="my-1 text-sm text-mineshaft-300">
|
||||
Secret Snapshots have been officially renamed to Commits. Going forward, all secret
|
||||
changes will be tracked as Commits. If you made changes before this update, you can
|
||||
|
@ -2,14 +2,13 @@ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import {
|
||||
faArrowDownWideShort,
|
||||
faArrowUpWideShort,
|
||||
faCodeCommit,
|
||||
faCopy,
|
||||
faSearch
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { format, formatDistanceToNow } from "date-fns";
|
||||
|
||||
import { Button, ContentLoader, EmptyState, IconButton, Input } from "@app/components/v2";
|
||||
import { Button, Input, Spinner } from "@app/components/v2";
|
||||
import { CopyButton } from "@app/components/v2/CopyButton";
|
||||
import { useGetFolderCommitHistory } from "@app/hooks/api/folderCommits";
|
||||
|
||||
@ -41,40 +40,58 @@ const CommitItem = ({
|
||||
onSelectCommit: (commitId: string, tab: string) => void;
|
||||
}) => {
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onSelectCommit(commit.id, "tab-commit-details");
|
||||
}}
|
||||
className="w-full border border-b-0 border-mineshaft-600 bg-mineshaft-800 first:rounded-t-md last:rounded-b-md last:border-b"
|
||||
>
|
||||
<div className="flex gap-2 px-4 py-3 transition-colors duration-200 hover:bg-zinc-800">
|
||||
<div className="flex flex-1 flex-col items-start">
|
||||
<span className="w-min whitespace-nowrap text-sm text-mineshaft-100">
|
||||
{commit.message}
|
||||
</span>
|
||||
<p className="text-left text-xs text-mineshaft-300">
|
||||
{commit.actorMetadata?.email || commit.actorMetadata?.name || commit.actorType}{" "}
|
||||
committed <time dateTime={commit.createdAt}>{formatTimeAgo(commit.createdAt)}</time>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center space-x-2">
|
||||
<code className="mt-0.5 font-mono text-xs text-mineshaft-400">
|
||||
{commit.id?.substring(0, 11)}
|
||||
</code>
|
||||
<CopyButton
|
||||
value={commit.id}
|
||||
name={commit.id}
|
||||
size="xs"
|
||||
variant="plain"
|
||||
color="text-mineshaft-400"
|
||||
icon={faCopy}
|
||||
/>
|
||||
<div className="border-b border-zinc-800 last:border-b-0">
|
||||
<div className="px-4 py-4 transition-colors duration-200 hover:bg-zinc-800">
|
||||
<div className="flex flex-col sm:flex-row sm:justify-between">
|
||||
<div className="w-5/6 flex-1">
|
||||
<div className="flex items-center">
|
||||
<Button
|
||||
variant="link"
|
||||
className="truncate text-left text-white hover:underline"
|
||||
isFullWidth
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onSelectCommit(commit.id, "tab-commit-details");
|
||||
}}
|
||||
>
|
||||
{commit.message}
|
||||
</Button>
|
||||
</div>
|
||||
<p className="text-white-400 mt-2 flex flex-wrap items-center gap-4 text-sm">
|
||||
<span className="flex items-center text-mineshaft-300">
|
||||
{commit.actorMetadata?.email || commit.actorMetadata?.name || commit.actorType}
|
||||
<p className="ml-1 mr-1">committed</p>
|
||||
<time dateTime={commit.createdAt}>{formatTimeAgo(commit.createdAt)}</time>
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
<div className="mt-2 flex w-1/6 items-center justify-end sm:mt-0">
|
||||
<div className="flex items-center space-x-1">
|
||||
<Button
|
||||
variant="link"
|
||||
className="text-white hover:underline"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onSelectCommit(commit.id, "tab-commit-details");
|
||||
}}
|
||||
>
|
||||
<code className="text-white-400 px-3 py-1 font-mono text-sm">
|
||||
{commit.id?.substring(0, 11)}
|
||||
</code>
|
||||
</Button>
|
||||
<CopyButton
|
||||
value={commit.id}
|
||||
name={commit.id}
|
||||
size="sm"
|
||||
variant="plain"
|
||||
color="text-mineshaft-400"
|
||||
icon={faCopy}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@ -91,16 +108,24 @@ const DateGroup = ({
|
||||
onSelectCommit: (commitId: string, tab: string) => void;
|
||||
}) => {
|
||||
return (
|
||||
<div className="mt-4 first:mt-0">
|
||||
<div className="mb-4 ml-[0.15rem] flex items-center">
|
||||
<FontAwesomeIcon icon={faCodeCommit} className="text-mineshaft-400" />
|
||||
<h2 className="ml-4 text-sm text-mineshaft-400">Commits on {date}</h2>
|
||||
<div className="mb-8 last:mb-0 last:pb-2">
|
||||
<div className="mb-4 flex items-center">
|
||||
<div className="relative mr-3 flex h-6 w-6 items-center justify-center">
|
||||
<div className="z-10 h-3 w-3 rounded-full border-2 border-mineshaft-600 bg-bunker-800" />
|
||||
<div className="absolute left-0 right-0 top-1/2 h-0.5 -translate-y-1/2 bg-mineshaft-600" />
|
||||
</div>
|
||||
<h2 className="text-sm text-white">Commits on {date}</h2>
|
||||
</div>
|
||||
|
||||
<div className="relative">
|
||||
<div className="absolute bottom-0 left-3 top-0 w-[0.1rem] bg-mineshaft-500" />
|
||||
<div className="absolute bottom-0 left-3 top-0 w-0.5 bg-mineshaft-600" />
|
||||
<div className="ml-10">
|
||||
{commits.map((commit) => (
|
||||
<CommitItem key={commit.id} commit={commit} onSelectCommit={onSelectCommit} />
|
||||
<div key={commit.id} className="relative mb-3 pb-1">
|
||||
<div className="overflow-hidden rounded-md border border-solid border-mineshaft-600">
|
||||
<CommitItem commit={commit} onSelectCommit={onSelectCommit} />
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
@ -125,7 +150,7 @@ export const CommitHistoryTab = ({
|
||||
const [offset, setOffset] = useState(0);
|
||||
const [allCommits, setAllCommits] = useState<Commit[]>([]);
|
||||
const debounceTimeoutRef = useRef<NodeJS.Timeout>();
|
||||
const limit = 10;
|
||||
const limit = 5;
|
||||
|
||||
// Debounce search term
|
||||
useEffect(() => {
|
||||
@ -209,37 +234,42 @@ export const CommitHistoryTab = ({
|
||||
}, [hasMore, isFetching, limit]);
|
||||
|
||||
return (
|
||||
<div className="mt-4 w-full rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
|
||||
<p className="mb-4 text-xl font-semibold text-mineshaft-100">Commit History</p>
|
||||
<div className="w-full">
|
||||
<div className="mb-4 flex flex-col sm:flex-row sm:justify-end">
|
||||
<div className="flex w-full flex-wrap items-center gap-2">
|
||||
<div className="relative flex-grow">
|
||||
<Input
|
||||
leftIcon={<FontAwesomeIcon icon={faSearch} aria-hidden="true" />}
|
||||
placeholder="Search commits..."
|
||||
className="h-10 w-full rounded-md border-transparent bg-zinc-800 pl-9 pr-3 text-sm text-white placeholder-gray-400 focus:border-gray-600 focus:ring-primary-500/20"
|
||||
onChange={(e) => handleSearch(e.target.value)}
|
||||
value={searchTerm}
|
||||
aria-label="Search commits"
|
||||
/>
|
||||
<div className="absolute left-3 top-1/2 -translate-y-1/2 transform text-gray-400">
|
||||
<FontAwesomeIcon icon={faSearch} aria-hidden="true" />
|
||||
</div>
|
||||
</div>
|
||||
<IconButton
|
||||
<Button
|
||||
variant="outline_bg"
|
||||
size="sm"
|
||||
className="flex h-[2.4rem] items-center justify-center gap-2 rounded-md"
|
||||
size="md"
|
||||
className="flex h-10 items-center justify-center gap-2 rounded-md bg-zinc-800 px-4 py-2 text-sm text-white transition-colors duration-200 hover:bg-zinc-700 focus:outline-none focus:ring-2 focus:ring-primary-500"
|
||||
onClick={handleSort}
|
||||
ariaLabel={`Sort by date ${sortDirection === "desc" ? "ascending" : "descending"}`}
|
||||
aria-label={`Sort by date ${sortDirection === "desc" ? "ascending" : "descending"}`}
|
||||
>
|
||||
<FontAwesomeIcon
|
||||
icon={sortDirection === "desc" ? faArrowDownWideShort : faArrowUpWideShort}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</IconButton>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{isLoading && offset === 0 ? (
|
||||
<ContentLoader className="h-80" />
|
||||
<div className="flex h-64 items-center justify-center">
|
||||
<Spinner size="lg" aria-label="Loading commits" />
|
||||
</div>
|
||||
) : (
|
||||
<div className="">
|
||||
<div className="space-y-8">
|
||||
{Object.keys(groupedCommits).length > 0 ? (
|
||||
<>
|
||||
{Object.entries(groupedCommits).map(([date, dateCommits]) => (
|
||||
@ -252,21 +282,34 @@ export const CommitHistoryTab = ({
|
||||
))}
|
||||
</>
|
||||
) : (
|
||||
<EmptyState title="No commits found." icon={faCodeCommit} />
|
||||
<div className="text-white-400 flex min-h-40 flex-col items-center justify-center rounded-lg bg-zinc-900 py-8 text-center">
|
||||
<FontAwesomeIcon
|
||||
icon={faSearch}
|
||||
className="text-white-500 mb-3 text-3xl"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<p>No matching commits found. Try a different search term.</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{hasMore && (
|
||||
<div className="flex justify-center pb-2">
|
||||
<Button
|
||||
variant="outline_bg"
|
||||
size="sm"
|
||||
className="ml-10 mt-4 w-full"
|
||||
size="md"
|
||||
className="rounded-md bg-zinc-900 px-6 py-2 text-sm font-medium text-white transition-colors duration-200 hover:bg-zinc-800 focus:outline-none focus:ring-2 focus:ring-primary-500"
|
||||
onClick={loadMoreCommits}
|
||||
disabled={isFetching}
|
||||
isLoading={isFetching}
|
||||
aria-label="Load more commits"
|
||||
>
|
||||
Load More Commits
|
||||
{isFetching ? (
|
||||
<>
|
||||
<Spinner size="sm" className="mr-2" />
|
||||
Loading...
|
||||
</>
|
||||
) : (
|
||||
"Load more commits"
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
|
@ -1,14 +0,0 @@
|
||||
import { TCloudflareWorkersSync } from "@app/hooks/api/secretSyncs/types/cloudflare-workers-sync";
|
||||
|
||||
import { getSecretSyncDestinationColValues } from "../helpers";
|
||||
import { SecretSyncTableCell } from "../SecretSyncTableCell";
|
||||
|
||||
type Props = {
|
||||
secretSync: TCloudflareWorkersSync;
|
||||
};
|
||||
|
||||
export const CloudflareWorkersSyncDestinationCol = ({ secretSync }: Props) => {
|
||||
const { primaryText, secondaryText } = getSecretSyncDestinationColValues(secretSync);
|
||||
|
||||
return <SecretSyncTableCell primaryText={primaryText} secondaryText={secondaryText} />;
|
||||
};
|
@ -8,7 +8,6 @@ import { AzureDevOpsSyncDestinationCol } from "./AzureDevOpsSyncDestinationCol";
|
||||
import { AzureKeyVaultDestinationSyncCol } from "./AzureKeyVaultDestinationSyncCol";
|
||||
import { CamundaSyncDestinationCol } from "./CamundaSyncDestinationCol";
|
||||
import { CloudflarePagesSyncDestinationCol } from "./CloudflarePagesSyncDestinationCol";
|
||||
import { CloudflareWorkersSyncDestinationCol } from "./CloudflareWorkersSyncDestinationCol";
|
||||
import { DatabricksSyncDestinationCol } from "./DatabricksSyncDestinationCol";
|
||||
import { FlyioSyncDestinationCol } from "./FlyioSyncDestinationCol";
|
||||
import { GcpSyncDestinationCol } from "./GcpSyncDestinationCol";
|
||||
@ -76,8 +75,6 @@ export const SecretSyncDestinationCol = ({ secretSync }: Props) => {
|
||||
return <GitLabSyncDestinationCol secretSync={secretSync} />;
|
||||
case SecretSync.CloudflarePages:
|
||||
return <CloudflarePagesSyncDestinationCol secretSync={secretSync} />;
|
||||
case SecretSync.CloudflareWorkers:
|
||||
return <CloudflareWorkersSyncDestinationCol secretSync={secretSync} />;
|
||||
case SecretSync.Zabbix:
|
||||
return <ZabbixSyncDestinationCol secretSync={secretSync} />;
|
||||
case SecretSync.Railway:
|
||||
|
@ -145,10 +145,6 @@ export const getSecretSyncDestinationColValues = (secretSync: TSecretSync) => {
|
||||
primaryText = destinationConfig.projectName;
|
||||
secondaryText = destinationConfig.environment;
|
||||
break;
|
||||
case SecretSync.CloudflareWorkers:
|
||||
primaryText = destinationConfig.scriptId;
|
||||
secondaryText = "Script ID";
|
||||
break;
|
||||
case SecretSync.Zabbix:
|
||||
if (destinationConfig.scope === ZabbixSyncScope.Host) {
|
||||
primaryText = destinationConfig.hostName;
|
||||
|
@ -21,7 +21,6 @@ import {
|
||||
Tag,
|
||||
Tooltip
|
||||
} from "@app/components/v2";
|
||||
import { SecretPathInput } from "@app/components/v2/SecretPathInput";
|
||||
import { useWorkspace } from "@app/context";
|
||||
import { getMemberLabel } from "@app/helpers/members";
|
||||
import { policyDetails } from "@app/helpers/policies";
|
||||
@ -204,7 +203,6 @@ const Form = ({
|
||||
|
||||
const formUserBypassers = watch("userBypassers");
|
||||
const formGroupBypassers = watch("groupBypassers");
|
||||
const formEnvironment = watch("environment")?.slug;
|
||||
const bypasserCount = (formUserBypassers || []).length + (formGroupBypassers || []).length;
|
||||
|
||||
const handleCreatePolicy = async ({
|
||||
@ -476,11 +474,7 @@ const Form = ({
|
||||
errorText={error?.message}
|
||||
className="flex-1"
|
||||
>
|
||||
<SecretPathInput
|
||||
{...field}
|
||||
value={field.value || ""}
|
||||
environment={formEnvironment}
|
||||
/>
|
||||
<Input {...field} value={field.value || ""} />
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
|
@ -1,14 +0,0 @@
|
||||
import { GenericFieldLabel } from "@app/components/secret-syncs";
|
||||
import { TCloudflareWorkersSync } from "@app/hooks/api/secretSyncs/types/cloudflare-workers-sync";
|
||||
|
||||
type Props = {
|
||||
secretSync: TCloudflareWorkersSync;
|
||||
};
|
||||
|
||||
export const CloudflareWorkersSyncDestinationSection = ({ secretSync }: Props) => {
|
||||
const {
|
||||
destinationConfig: { scriptId }
|
||||
} = secretSync;
|
||||
|
||||
return <GenericFieldLabel label="Script ID">{scriptId}</GenericFieldLabel>;
|
||||
};
|
@ -19,7 +19,6 @@ import { AzureDevOpsSyncDestinationSection } from "./AzureDevOpsSyncDestinationS
|
||||
import { AzureKeyVaultSyncDestinationSection } from "./AzureKeyVaultSyncDestinationSection";
|
||||
import { CamundaSyncDestinationSection } from "./CamundaSyncDestinationSection";
|
||||
import { CloudflarePagesSyncDestinationSection } from "./CloudflarePagesSyncDestinationSection";
|
||||
import { CloudflareWorkersSyncDestinationSection } from "./CloudflareWorkersSyncDestinationSection";
|
||||
import { DatabricksSyncDestinationSection } from "./DatabricksSyncDestinationSection";
|
||||
import { FlyioSyncDestinationSection } from "./FlyioSyncDestinationSection";
|
||||
import { GcpSyncDestinationSection } from "./GcpSyncDestinationSection";
|
||||
@ -117,9 +116,6 @@ export const SecretSyncDestinationSection = ({ secretSync, onEditDestination }:
|
||||
case SecretSync.CloudflarePages:
|
||||
DestinationComponents = <CloudflarePagesSyncDestinationSection secretSync={secretSync} />;
|
||||
break;
|
||||
case SecretSync.CloudflareWorkers:
|
||||
DestinationComponents = <CloudflareWorkersSyncDestinationSection secretSync={secretSync} />;
|
||||
break;
|
||||
case SecretSync.Zabbix:
|
||||
DestinationComponents = <ZabbixSyncDestinationSection secretSync={secretSync} />;
|
||||
break;
|
||||
|
@ -60,7 +60,6 @@ export const SecretSyncOptionsSection = ({ secretSync, onEditOptions }: Props) =
|
||||
case SecretSync.Flyio:
|
||||
case SecretSync.GitLab:
|
||||
case SecretSync.CloudflarePages:
|
||||
case SecretSync.CloudflareWorkers:
|
||||
case SecretSync.Zabbix:
|
||||
case SecretSync.Railway:
|
||||
AdditionalSyncOptionsComponent = null;
|
||||
|
@ -47,6 +47,7 @@ import { Route as adminEnvironmentPageRouteImport } from './pages/admin/Environm
|
||||
import { Route as adminEncryptionPageRouteImport } from './pages/admin/EncryptionPage/route'
|
||||
import { Route as adminCachingPageRouteImport } from './pages/admin/CachingPage/route'
|
||||
import { Route as adminAuthenticationPageRouteImport } from './pages/admin/AuthenticationPage/route'
|
||||
import { Route as organizationSsoPageRouteImport } from './pages/organization/SsoPage/route'
|
||||
import { Route as organizationProjectsPageRouteImport } from './pages/organization/ProjectsPage/route'
|
||||
import { Route as organizationBillingPageRouteImport } from './pages/organization/BillingPage/route'
|
||||
import { Route as organizationAuditLogsPageRouteImport } from './pages/organization/AuditLogsPage/route'
|
||||
@ -590,6 +591,12 @@ const adminAuthenticationPageRouteRoute =
|
||||
getParentRoute: () => adminLayoutRoute,
|
||||
} as any)
|
||||
|
||||
const organizationSsoPageRouteRoute = organizationSsoPageRouteImport.update({
|
||||
id: '/sso',
|
||||
path: '/sso',
|
||||
getParentRoute: () => AuthenticateInjectOrgDetailsOrgLayoutOrganizationRoute,
|
||||
} as any)
|
||||
|
||||
const organizationProjectsPageRouteRoute =
|
||||
organizationProjectsPageRouteImport.update({
|
||||
id: '/projects',
|
||||
@ -2152,6 +2159,13 @@ declare module '@tanstack/react-router' {
|
||||
preLoaderRoute: typeof organizationProjectsPageRouteImport
|
||||
parentRoute: typeof AuthenticateInjectOrgDetailsOrgLayoutOrganizationImport
|
||||
}
|
||||
'/_authenticate/_inject-org-details/_org-layout/organization/sso': {
|
||||
id: '/_authenticate/_inject-org-details/_org-layout/organization/sso'
|
||||
path: '/sso'
|
||||
fullPath: '/organization/sso'
|
||||
preLoaderRoute: typeof organizationSsoPageRouteImport
|
||||
parentRoute: typeof AuthenticateInjectOrgDetailsOrgLayoutOrganizationImport
|
||||
}
|
||||
'/_authenticate/_inject-org-details/admin/_admin-layout/authentication': {
|
||||
id: '/_authenticate/_inject-org-details/admin/_admin-layout/authentication'
|
||||
path: '/authentication'
|
||||
@ -3414,6 +3428,7 @@ interface AuthenticateInjectOrgDetailsOrgLayoutOrganizationRouteChildren {
|
||||
organizationAuditLogsPageRouteRoute: typeof organizationAuditLogsPageRouteRoute
|
||||
organizationBillingPageRouteRoute: typeof organizationBillingPageRouteRoute
|
||||
organizationProjectsPageRouteRoute: typeof organizationProjectsPageRouteRoute
|
||||
organizationSsoPageRouteRoute: typeof organizationSsoPageRouteRoute
|
||||
AuthenticateInjectOrgDetailsOrgLayoutOrganizationAppConnectionsRoute: typeof AuthenticateInjectOrgDetailsOrgLayoutOrganizationAppConnectionsRouteWithChildren
|
||||
AuthenticateInjectOrgDetailsOrgLayoutOrganizationGatewaysRoute: typeof AuthenticateInjectOrgDetailsOrgLayoutOrganizationGatewaysRouteWithChildren
|
||||
AuthenticateInjectOrgDetailsOrgLayoutOrganizationSecretSharingRoute: typeof AuthenticateInjectOrgDetailsOrgLayoutOrganizationSecretSharingRouteWithChildren
|
||||
@ -3432,6 +3447,7 @@ const AuthenticateInjectOrgDetailsOrgLayoutOrganizationRouteChildren: Authentica
|
||||
organizationAuditLogsPageRouteRoute: organizationAuditLogsPageRouteRoute,
|
||||
organizationBillingPageRouteRoute: organizationBillingPageRouteRoute,
|
||||
organizationProjectsPageRouteRoute: organizationProjectsPageRouteRoute,
|
||||
organizationSsoPageRouteRoute: organizationSsoPageRouteRoute,
|
||||
AuthenticateInjectOrgDetailsOrgLayoutOrganizationAppConnectionsRoute:
|
||||
AuthenticateInjectOrgDetailsOrgLayoutOrganizationAppConnectionsRouteWithChildren,
|
||||
AuthenticateInjectOrgDetailsOrgLayoutOrganizationGatewaysRoute:
|
||||
@ -4329,6 +4345,7 @@ export interface FileRoutesByFullPath {
|
||||
'/organization/audit-logs': typeof organizationAuditLogsPageRouteRoute
|
||||
'/organization/billing': typeof organizationBillingPageRouteRoute
|
||||
'/organization/projects': typeof organizationProjectsPageRouteRoute
|
||||
'/organization/sso': typeof organizationSsoPageRouteRoute
|
||||
'/admin/authentication': typeof adminAuthenticationPageRouteRoute
|
||||
'/admin/caching': typeof adminCachingPageRouteRoute
|
||||
'/admin/encryption': typeof adminEncryptionPageRouteRoute
|
||||
@ -4525,6 +4542,7 @@ export interface FileRoutesByTo {
|
||||
'/organization/audit-logs': typeof organizationAuditLogsPageRouteRoute
|
||||
'/organization/billing': typeof organizationBillingPageRouteRoute
|
||||
'/organization/projects': typeof organizationProjectsPageRouteRoute
|
||||
'/organization/sso': typeof organizationSsoPageRouteRoute
|
||||
'/admin/authentication': typeof adminAuthenticationPageRouteRoute
|
||||
'/admin/caching': typeof adminCachingPageRouteRoute
|
||||
'/admin/encryption': typeof adminEncryptionPageRouteRoute
|
||||
@ -4721,6 +4739,7 @@ export interface FileRoutesById {
|
||||
'/_authenticate/_inject-org-details/_org-layout/organization/audit-logs': typeof organizationAuditLogsPageRouteRoute
|
||||
'/_authenticate/_inject-org-details/_org-layout/organization/billing': typeof organizationBillingPageRouteRoute
|
||||
'/_authenticate/_inject-org-details/_org-layout/organization/projects': typeof organizationProjectsPageRouteRoute
|
||||
'/_authenticate/_inject-org-details/_org-layout/organization/sso': typeof organizationSsoPageRouteRoute
|
||||
'/_authenticate/_inject-org-details/admin/_admin-layout/authentication': typeof adminAuthenticationPageRouteRoute
|
||||
'/_authenticate/_inject-org-details/admin/_admin-layout/caching': typeof adminCachingPageRouteRoute
|
||||
'/_authenticate/_inject-org-details/admin/_admin-layout/encryption': typeof adminEncryptionPageRouteRoute
|
||||
@ -4930,6 +4949,7 @@ export interface FileRouteTypes {
|
||||
| '/organization/audit-logs'
|
||||
| '/organization/billing'
|
||||
| '/organization/projects'
|
||||
| '/organization/sso'
|
||||
| '/admin/authentication'
|
||||
| '/admin/caching'
|
||||
| '/admin/encryption'
|
||||
@ -5125,6 +5145,7 @@ export interface FileRouteTypes {
|
||||
| '/organization/audit-logs'
|
||||
| '/organization/billing'
|
||||
| '/organization/projects'
|
||||
| '/organization/sso'
|
||||
| '/admin/authentication'
|
||||
| '/admin/caching'
|
||||
| '/admin/encryption'
|
||||
@ -5319,6 +5340,7 @@ export interface FileRouteTypes {
|
||||
| '/_authenticate/_inject-org-details/_org-layout/organization/audit-logs'
|
||||
| '/_authenticate/_inject-org-details/_org-layout/organization/billing'
|
||||
| '/_authenticate/_inject-org-details/_org-layout/organization/projects'
|
||||
| '/_authenticate/_inject-org-details/_org-layout/organization/sso'
|
||||
| '/_authenticate/_inject-org-details/admin/_admin-layout/authentication'
|
||||
| '/_authenticate/_inject-org-details/admin/_admin-layout/caching'
|
||||
| '/_authenticate/_inject-org-details/admin/_admin-layout/encryption'
|
||||
@ -5710,6 +5732,7 @@ export const routeTree = rootRoute
|
||||
"/_authenticate/_inject-org-details/_org-layout/organization/audit-logs",
|
||||
"/_authenticate/_inject-org-details/_org-layout/organization/billing",
|
||||
"/_authenticate/_inject-org-details/_org-layout/organization/projects",
|
||||
"/_authenticate/_inject-org-details/_org-layout/organization/sso",
|
||||
"/_authenticate/_inject-org-details/_org-layout/organization/app-connections",
|
||||
"/_authenticate/_inject-org-details/_org-layout/organization/gateways",
|
||||
"/_authenticate/_inject-org-details/_org-layout/organization/secret-sharing",
|
||||
@ -5759,6 +5782,10 @@ export const routeTree = rootRoute
|
||||
"filePath": "organization/ProjectsPage/route.tsx",
|
||||
"parent": "/_authenticate/_inject-org-details/_org-layout/organization"
|
||||
},
|
||||
"/_authenticate/_inject-org-details/_org-layout/organization/sso": {
|
||||
"filePath": "organization/SsoPage/route.tsx",
|
||||
"parent": "/_authenticate/_inject-org-details/_org-layout/organization"
|
||||
},
|
||||
"/_authenticate/_inject-org-details/admin/_admin-layout/authentication": {
|
||||
"filePath": "admin/AuthenticationPage/route.tsx",
|
||||
"parent": "/_authenticate/_inject-org-details/admin/_admin-layout"
|
||||
|
@ -31,6 +31,7 @@ const organizationRoutes = route("/organization", [
|
||||
index("organization/SettingsPage/route.tsx"),
|
||||
route("/oauth/callback", "organization/SettingsPage/OauthCallbackPage/route.tsx")
|
||||
]),
|
||||
route("/sso", "organization/SsoPage/route.tsx"),
|
||||
route("/groups/$groupId", "organization/GroupDetailsByIDPage/route.tsx"),
|
||||
route("/members/$membershipId", "organization/UserDetailsByIDPage/route.tsx"),
|
||||
route("/roles/$roleId", "organization/RoleByIDPage/route.tsx"),
|
||||
|