mirror of
https://github.com/Infisical/infisical.git
synced 2025-07-05 04:29:09 +00:00
Compare commits
36 Commits
misc/add-e
...
daniel/air
Author | SHA1 | Date | |
---|---|---|---|
962dd5d919 | |||
f50c72c033 | |||
e1046e2d56 | |||
ed3fa8add1 | |||
d123283849 | |||
d7fd44b845 | |||
3ffee049ee | |||
524462d7bc | |||
351e573fea | |||
f1bc26e2e5 | |||
8aeb607f6e | |||
e530b7a788 | |||
bf61090b5a | |||
106b068a51 | |||
6f0a97a2fa | |||
5d604be091 | |||
905cf47d90 | |||
2c40d316f4 | |||
32521523c1 | |||
3a2e8939b1 | |||
a6d9c74054 | |||
82520a7f0a | |||
af236ba892 | |||
c4b7d4618d | |||
003f2b003d | |||
747b5ec68d | |||
ed0dc324a3 | |||
1c13ed54af | |||
8abfea0409 | |||
ce4adccc80 | |||
dcd3b5df56 | |||
f6425480ca | |||
a3e9392a2f | |||
633a2ae985 | |||
e67a8f9c05 | |||
ad110f490c |
1
backend/src/@types/fastify.d.ts
vendored
1
backend/src/@types/fastify.d.ts
vendored
@ -106,6 +106,7 @@ declare module "@fastify/request-context" {
|
|||||||
claims: Record<string, string>;
|
claims: Record<string, string>;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
identityPermissionMetadata?: Record<string, unknown>; // filled by permission service
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,23 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { TableName } from "../schemas";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
const doesParentColumExist = await knex.schema.hasColumn(TableName.SecretFolder, "parentId");
|
||||||
|
const doesNameColumnExist = await knex.schema.hasColumn(TableName.SecretFolder, "name");
|
||||||
|
if (doesParentColumExist && doesNameColumnExist) {
|
||||||
|
await knex.schema.alterTable(TableName.SecretFolder, (t) => {
|
||||||
|
t.index(["parentId", "name"]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
const doesParentColumExist = await knex.schema.hasColumn(TableName.SecretFolder, "parentId");
|
||||||
|
const doesNameColumnExist = await knex.schema.hasColumn(TableName.SecretFolder, "name");
|
||||||
|
if (doesParentColumExist && doesNameColumnExist) {
|
||||||
|
await knex.schema.alterTable(TableName.SecretFolder, (t) => {
|
||||||
|
t.dropIndex(["parentId", "name"]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -1,8 +1,10 @@
|
|||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
|
import { requestContext } from "@fastify/request-context";
|
||||||
|
|
||||||
import { ActionProjectType } from "@app/db/schemas";
|
import { ActionProjectType } from "@app/db/schemas";
|
||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
import { BadRequestError } from "@app/lib/errors";
|
import { BadRequestError } from "@app/lib/errors";
|
||||||
|
import { ActorType } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
import { OrgPermissionActions, OrgPermissionSubjects } from "../permission/org-permission";
|
import { OrgPermissionActions, OrgPermissionSubjects } from "../permission/org-permission";
|
||||||
import { TPermissionServiceFactory } from "../permission/permission-service";
|
import { TPermissionServiceFactory } from "../permission/permission-service";
|
||||||
@ -81,8 +83,12 @@ export const auditLogServiceFactory = ({
|
|||||||
if (!data.projectId && !data.orgId)
|
if (!data.projectId && !data.orgId)
|
||||||
throw new BadRequestError({ message: "Must specify either project id or org id" });
|
throw new BadRequestError({ message: "Must specify either project id or org id" });
|
||||||
}
|
}
|
||||||
|
const el = { ...data };
|
||||||
return auditLogQueue.pushToLog(data);
|
if (el.actor.type === ActorType.USER || el.actor.type === ActorType.IDENTITY) {
|
||||||
|
const permissionMetadata = requestContext.get("identityPermissionMetadata");
|
||||||
|
el.actor.metadata.permission = permissionMetadata;
|
||||||
|
}
|
||||||
|
return auditLogQueue.pushToLog(el);
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -290,6 +290,7 @@ interface UserActorMetadata {
|
|||||||
userId: string;
|
userId: string;
|
||||||
email?: string | null;
|
email?: string | null;
|
||||||
username: string;
|
username: string;
|
||||||
|
permission?: Record<string, unknown>;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ServiceActorMetadata {
|
interface ServiceActorMetadata {
|
||||||
@ -300,6 +301,7 @@ interface ServiceActorMetadata {
|
|||||||
interface IdentityActorMetadata {
|
interface IdentityActorMetadata {
|
||||||
identityId: string;
|
identityId: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
permission?: Record<string, unknown>;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ScimClientActorMetadata {}
|
interface ScimClientActorMetadata {}
|
||||||
|
@ -244,22 +244,20 @@ export const permissionServiceFactory = ({
|
|||||||
|
|
||||||
const rules = buildProjectPermissionRules(rolePermissions.concat(additionalPrivileges));
|
const rules = buildProjectPermissionRules(rolePermissions.concat(additionalPrivileges));
|
||||||
const templatedRules = handlebars.compile(JSON.stringify(rules), { data: false });
|
const templatedRules = handlebars.compile(JSON.stringify(rules), { data: false });
|
||||||
const metadataKeyValuePair = escapeHandlebarsMissingDict(
|
const unescapedMetadata = objectify(
|
||||||
objectify(
|
userProjectPermission.metadata,
|
||||||
userProjectPermission.metadata,
|
(i) => i.key,
|
||||||
(i) => i.key,
|
(i) => i.value
|
||||||
(i) => i.value
|
|
||||||
),
|
|
||||||
"identity.metadata"
|
|
||||||
);
|
);
|
||||||
const templateValue = {
|
const metadataKeyValuePair = escapeHandlebarsMissingDict(unescapedMetadata, "identity.metadata");
|
||||||
id: userProjectPermission.userId,
|
requestContext.set("identityPermissionMetadata", { metadata: unescapedMetadata });
|
||||||
username: userProjectPermission.username,
|
|
||||||
metadata: metadataKeyValuePair
|
|
||||||
};
|
|
||||||
const interpolateRules = templatedRules(
|
const interpolateRules = templatedRules(
|
||||||
{
|
{
|
||||||
identity: templateValue
|
identity: {
|
||||||
|
id: userProjectPermission.userId,
|
||||||
|
username: userProjectPermission.username,
|
||||||
|
metadata: metadataKeyValuePair
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{ data: false }
|
{ data: false }
|
||||||
);
|
);
|
||||||
@ -331,15 +329,16 @@ export const permissionServiceFactory = ({
|
|||||||
? escapeHandlebarsMissingDict(unescapedIdentityAuthInfo as never, "identity.auth")
|
? escapeHandlebarsMissingDict(unescapedIdentityAuthInfo as never, "identity.auth")
|
||||||
: {};
|
: {};
|
||||||
const metadataKeyValuePair = escapeHandlebarsMissingDict(unescapedMetadata, "identity.metadata");
|
const metadataKeyValuePair = escapeHandlebarsMissingDict(unescapedMetadata, "identity.metadata");
|
||||||
const templateValue = {
|
|
||||||
id: identityProjectPermission.identityId,
|
requestContext.set("identityPermissionMetadata", { metadata: unescapedMetadata, auth: unescapedIdentityAuthInfo });
|
||||||
username: identityProjectPermission.username,
|
|
||||||
metadata: metadataKeyValuePair,
|
|
||||||
auth: identityAuthInfo
|
|
||||||
};
|
|
||||||
const interpolateRules = templatedRules(
|
const interpolateRules = templatedRules(
|
||||||
{
|
{
|
||||||
identity: templateValue
|
identity: {
|
||||||
|
id: identityProjectPermission.identityId,
|
||||||
|
username: identityProjectPermission.username,
|
||||||
|
metadata: metadataKeyValuePair,
|
||||||
|
auth: identityAuthInfo
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{ data: false }
|
{ data: false }
|
||||||
);
|
);
|
||||||
@ -440,14 +439,13 @@ export const permissionServiceFactory = ({
|
|||||||
),
|
),
|
||||||
"identity.metadata"
|
"identity.metadata"
|
||||||
);
|
);
|
||||||
const templateValue = {
|
|
||||||
id: userProjectPermission.userId,
|
|
||||||
username: userProjectPermission.username,
|
|
||||||
metadata: metadataKeyValuePair
|
|
||||||
};
|
|
||||||
const interpolateRules = templatedRules(
|
const interpolateRules = templatedRules(
|
||||||
{
|
{
|
||||||
identity: templateValue
|
identity: {
|
||||||
|
id: userProjectPermission.userId,
|
||||||
|
username: userProjectPermission.username,
|
||||||
|
metadata: metadataKeyValuePair
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{ data: false }
|
{ data: false }
|
||||||
);
|
);
|
||||||
@ -487,14 +485,13 @@ export const permissionServiceFactory = ({
|
|||||||
),
|
),
|
||||||
"identity.metadata"
|
"identity.metadata"
|
||||||
);
|
);
|
||||||
const templateValue = {
|
|
||||||
id: identityProjectPermission.identityId,
|
|
||||||
username: identityProjectPermission.username,
|
|
||||||
metadata: metadataKeyValuePair
|
|
||||||
};
|
|
||||||
const interpolateRules = templatedRules(
|
const interpolateRules = templatedRules(
|
||||||
{
|
{
|
||||||
identity: templateValue
|
identity: {
|
||||||
|
id: identityProjectPermission.identityId,
|
||||||
|
username: identityProjectPermission.username,
|
||||||
|
metadata: metadataKeyValuePair
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{ data: false }
|
{ data: false }
|
||||||
);
|
);
|
||||||
|
@ -815,7 +815,8 @@ export const DASHBOARD = {
|
|||||||
search: "The text string to filter secret keys and folder names by.",
|
search: "The text string to filter secret keys and folder names by.",
|
||||||
includeSecrets: "Whether to include project secrets in the response.",
|
includeSecrets: "Whether to include project secrets in the response.",
|
||||||
includeFolders: "Whether to include project folders in the response.",
|
includeFolders: "Whether to include project folders in the response.",
|
||||||
includeDynamicSecrets: "Whether to include dynamic project secrets in the response."
|
includeDynamicSecrets: "Whether to include dynamic project secrets in the response.",
|
||||||
|
includeImports: "Whether to include project secret imports in the response."
|
||||||
},
|
},
|
||||||
SECRET_DETAILS_LIST: {
|
SECRET_DETAILS_LIST: {
|
||||||
projectId: "The ID of the project to list secrets/folders from.",
|
projectId: "The ID of the project to list secrets/folders from.",
|
||||||
|
@ -56,6 +56,7 @@ const envSchema = z
|
|||||||
// TODO(akhilmhdh): will be changed to one
|
// TODO(akhilmhdh): will be changed to one
|
||||||
ENCRYPTION_KEY: zpStr(z.string().optional()),
|
ENCRYPTION_KEY: zpStr(z.string().optional()),
|
||||||
ROOT_ENCRYPTION_KEY: zpStr(z.string().optional()),
|
ROOT_ENCRYPTION_KEY: zpStr(z.string().optional()),
|
||||||
|
QUEUE_WORKERS_ENABLED: zodStrBool.default("true"),
|
||||||
HTTPS_ENABLED: zodStrBool,
|
HTTPS_ENABLED: zodStrBool,
|
||||||
// smtp options
|
// smtp options
|
||||||
SMTP_HOST: zpStr(z.string().optional()),
|
SMTP_HOST: zpStr(z.string().optional()),
|
||||||
|
@ -272,10 +272,13 @@ export const queueServiceFactory = (
|
|||||||
connection
|
connection
|
||||||
});
|
});
|
||||||
|
|
||||||
workerContainer[name] = new Worker<TQueueJobTypes[T]["payload"], void, TQueueJobTypes[T]["name"]>(name, jobFn, {
|
const appCfg = getConfig();
|
||||||
...queueSettings,
|
if (appCfg.QUEUE_WORKERS_ENABLED) {
|
||||||
connection
|
workerContainer[name] = new Worker<TQueueJobTypes[T]["payload"], void, TQueueJobTypes[T]["name"]>(name, jobFn, {
|
||||||
});
|
...queueSettings,
|
||||||
|
connection
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const startPg = async <T extends QueueName>(
|
const startPg = async <T extends QueueName>(
|
||||||
@ -307,6 +310,11 @@ export const queueServiceFactory = (
|
|||||||
event: U,
|
event: U,
|
||||||
listener: WorkerListener<TQueueJobTypes[T]["payload"], void, TQueueJobTypes[T]["name"]>[U]
|
listener: WorkerListener<TQueueJobTypes[T]["payload"], void, TQueueJobTypes[T]["name"]>[U]
|
||||||
) => {
|
) => {
|
||||||
|
const appCfg = getConfig();
|
||||||
|
if (!appCfg.QUEUE_WORKERS_ENABLED) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const worker = workerContainer[name];
|
const worker = workerContainer[name];
|
||||||
worker.on(event, listener);
|
worker.on(event, listener);
|
||||||
};
|
};
|
||||||
|
@ -65,7 +65,7 @@ export const registerSecretScannerGhApp = async (server: FastifyZodProvider) =>
|
|||||||
payload: JSON.stringify(req.body),
|
payload: JSON.stringify(req.body),
|
||||||
signature: signatureSHA256
|
signature: signatureSHA256
|
||||||
});
|
});
|
||||||
void res.send("ok");
|
return res.send("ok");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ export const registerServeUI = async (
|
|||||||
TELEMETRY_CAPTURING_ENABLED: appCfg.TELEMETRY_ENABLED
|
TELEMETRY_CAPTURING_ENABLED: appCfg.TELEMETRY_ENABLED
|
||||||
};
|
};
|
||||||
const js = `window.__INFISICAL_RUNTIME_ENV__ = Object.freeze(${JSON.stringify(config)});`;
|
const js = `window.__INFISICAL_RUNTIME_ENV__ = Object.freeze(${JSON.stringify(config)});`;
|
||||||
void res.send(js);
|
return res.send(js);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -57,7 +57,7 @@ export const registerServeUI = async (
|
|||||||
reply.callNotFound();
|
reply.callNotFound();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
void reply.sendFile("index.html");
|
return reply.sendFile("index.html");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -109,6 +109,7 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
|||||||
search: z.string().trim().describe(DASHBOARD.SECRET_OVERVIEW_LIST.search).optional(),
|
search: z.string().trim().describe(DASHBOARD.SECRET_OVERVIEW_LIST.search).optional(),
|
||||||
includeSecrets: booleanSchema.describe(DASHBOARD.SECRET_OVERVIEW_LIST.includeSecrets),
|
includeSecrets: booleanSchema.describe(DASHBOARD.SECRET_OVERVIEW_LIST.includeSecrets),
|
||||||
includeFolders: booleanSchema.describe(DASHBOARD.SECRET_OVERVIEW_LIST.includeFolders),
|
includeFolders: booleanSchema.describe(DASHBOARD.SECRET_OVERVIEW_LIST.includeFolders),
|
||||||
|
includeImports: booleanSchema.describe(DASHBOARD.SECRET_OVERVIEW_LIST.includeImports),
|
||||||
includeDynamicSecrets: booleanSchema.describe(DASHBOARD.SECRET_OVERVIEW_LIST.includeDynamicSecrets)
|
includeDynamicSecrets: booleanSchema.describe(DASHBOARD.SECRET_OVERVIEW_LIST.includeDynamicSecrets)
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
@ -124,9 +125,17 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
|||||||
})
|
})
|
||||||
.array()
|
.array()
|
||||||
.optional(),
|
.optional(),
|
||||||
|
imports: SecretImportsSchema.omit({ importEnv: true })
|
||||||
|
.extend({
|
||||||
|
importEnv: z.object({ name: z.string(), slug: z.string(), id: z.string() }),
|
||||||
|
environment: z.string()
|
||||||
|
})
|
||||||
|
.array()
|
||||||
|
.optional(),
|
||||||
totalFolderCount: z.number().optional(),
|
totalFolderCount: z.number().optional(),
|
||||||
totalDynamicSecretCount: z.number().optional(),
|
totalDynamicSecretCount: z.number().optional(),
|
||||||
totalSecretCount: z.number().optional(),
|
totalSecretCount: z.number().optional(),
|
||||||
|
totalImportCount: z.number().optional(),
|
||||||
totalCount: z.number()
|
totalCount: z.number()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -143,6 +152,7 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
|||||||
orderDirection,
|
orderDirection,
|
||||||
includeFolders,
|
includeFolders,
|
||||||
includeSecrets,
|
includeSecrets,
|
||||||
|
includeImports,
|
||||||
includeDynamicSecrets
|
includeDynamicSecrets
|
||||||
} = req.query;
|
} = req.query;
|
||||||
|
|
||||||
@ -159,6 +169,7 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
|||||||
let remainingLimit = limit;
|
let remainingLimit = limit;
|
||||||
let adjustedOffset = offset;
|
let adjustedOffset = offset;
|
||||||
|
|
||||||
|
let imports: Awaited<ReturnType<typeof server.services.secretImport.getImportsMultiEnv>> | undefined;
|
||||||
let folders: Awaited<ReturnType<typeof server.services.folder.getFoldersMultiEnv>> | undefined;
|
let folders: Awaited<ReturnType<typeof server.services.folder.getFoldersMultiEnv>> | undefined;
|
||||||
let secrets: Awaited<ReturnType<typeof server.services.secret.getSecretsRawMultiEnv>> | undefined;
|
let secrets: Awaited<ReturnType<typeof server.services.secret.getSecretsRawMultiEnv>> | undefined;
|
||||||
let dynamicSecrets:
|
let dynamicSecrets:
|
||||||
@ -168,6 +179,53 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
|||||||
let totalFolderCount: number | undefined;
|
let totalFolderCount: number | undefined;
|
||||||
let totalDynamicSecretCount: number | undefined;
|
let totalDynamicSecretCount: number | undefined;
|
||||||
let totalSecretCount: number | undefined;
|
let totalSecretCount: number | undefined;
|
||||||
|
let totalImportCount: number | undefined;
|
||||||
|
|
||||||
|
if (includeImports) {
|
||||||
|
totalImportCount = await server.services.secretImport.getProjectImportMultiEnvCount({
|
||||||
|
actorId: req.permission.id,
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorAuthMethod: req.permission.authMethod,
|
||||||
|
actorOrgId: req.permission.orgId,
|
||||||
|
projectId,
|
||||||
|
environments,
|
||||||
|
path: secretPath,
|
||||||
|
search
|
||||||
|
});
|
||||||
|
|
||||||
|
if (remainingLimit > 0 && totalImportCount > adjustedOffset) {
|
||||||
|
imports = await server.services.secretImport.getImportsMultiEnv({
|
||||||
|
actorId: req.permission.id,
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorAuthMethod: req.permission.authMethod,
|
||||||
|
actorOrgId: req.permission.orgId,
|
||||||
|
projectId,
|
||||||
|
environments,
|
||||||
|
path: secretPath,
|
||||||
|
search,
|
||||||
|
limit: remainingLimit,
|
||||||
|
offset: adjustedOffset
|
||||||
|
});
|
||||||
|
|
||||||
|
await server.services.auditLog.createAuditLog({
|
||||||
|
...req.auditLogInfo,
|
||||||
|
projectId: req.query.projectId,
|
||||||
|
event: {
|
||||||
|
type: EventType.GET_SECRET_IMPORTS,
|
||||||
|
metadata: {
|
||||||
|
environment: environments.join(","),
|
||||||
|
folderId: imports?.[0]?.folderId,
|
||||||
|
numberOfImports: imports.length
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
remainingLimit -= imports.length;
|
||||||
|
adjustedOffset = 0;
|
||||||
|
} else {
|
||||||
|
adjustedOffset = Math.max(0, adjustedOffset - totalImportCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (includeFolders) {
|
if (includeFolders) {
|
||||||
// this is the unique count, ie duplicate folders across envs only count as 1
|
// this is the unique count, ie duplicate folders across envs only count as 1
|
||||||
@ -345,10 +403,13 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
|||||||
folders,
|
folders,
|
||||||
dynamicSecrets,
|
dynamicSecrets,
|
||||||
secrets,
|
secrets,
|
||||||
|
imports,
|
||||||
totalFolderCount,
|
totalFolderCount,
|
||||||
totalDynamicSecretCount,
|
totalDynamicSecretCount,
|
||||||
|
totalImportCount,
|
||||||
totalSecretCount,
|
totalSecretCount,
|
||||||
totalCount: (totalFolderCount ?? 0) + (totalDynamicSecretCount ?? 0) + (totalSecretCount ?? 0)
|
totalCount:
|
||||||
|
(totalFolderCount ?? 0) + (totalDynamicSecretCount ?? 0) + (totalSecretCount ?? 0) + (totalImportCount ?? 0)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -69,9 +69,15 @@ export const identityUaServiceFactory = ({
|
|||||||
isClientSecretRevoked: false
|
isClientSecretRevoked: false
|
||||||
});
|
});
|
||||||
|
|
||||||
const validClientSecretInfo = clientSecrtInfo.find(({ clientSecretHash }) =>
|
let validClientSecretInfo: (typeof clientSecrtInfo)[0] | null = null;
|
||||||
bcrypt.compareSync(clientSecret, clientSecretHash)
|
for await (const info of clientSecrtInfo) {
|
||||||
);
|
const isMatch = await bcrypt.compare(clientSecret, info.clientSecretHash);
|
||||||
|
if (isMatch) {
|
||||||
|
validClientSecretInfo = info;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!validClientSecretInfo) throw new UnauthorizedError({ message: "Invalid credentials" });
|
if (!validClientSecretInfo) throw new UnauthorizedError({ message: "Invalid credentials" });
|
||||||
|
|
||||||
const { clientSecretTTL, clientSecretNumUses, clientSecretNumUsesLimit } = validClientSecretInfo;
|
const { clientSecretTTL, clientSecretNumUses, clientSecretNumUsesLimit } = validClientSecretInfo;
|
||||||
@ -104,7 +110,7 @@ export const identityUaServiceFactory = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const identityAccessToken = await identityUaDAL.transaction(async (tx) => {
|
const identityAccessToken = await identityUaDAL.transaction(async (tx) => {
|
||||||
const uaClientSecretDoc = await identityUaClientSecretDAL.incrementUsage(validClientSecretInfo.id, tx);
|
const uaClientSecretDoc = await identityUaClientSecretDAL.incrementUsage(validClientSecretInfo!.id, tx);
|
||||||
const newToken = await identityAccessTokenDAL.create(
|
const newToken = await identityAccessTokenDAL.create(
|
||||||
{
|
{
|
||||||
identityId: identityUa.identityId,
|
identityId: identityUa.identityId,
|
||||||
|
@ -923,16 +923,14 @@ const getAppsCodefresh = async ({ accessToken }: { accessToken: string }) => {
|
|||||||
/**
|
/**
|
||||||
* Return list of projects for Windmill integration
|
* Return list of projects for Windmill integration
|
||||||
*/
|
*/
|
||||||
const getAppsWindmill = async ({ accessToken }: { accessToken: string }) => {
|
const getAppsWindmill = async ({ accessToken, url }: { accessToken: string; url?: string | null }) => {
|
||||||
const { data } = await request.get<{ id: string; name: string }[]>(
|
const apiUrl = url ? `${url}/api` : IntegrationUrls.WINDMILL_API_URL;
|
||||||
`${IntegrationUrls.WINDMILL_API_URL}/workspaces/list`,
|
const { data } = await request.get<{ id: string; name: string }[]>(`${apiUrl}/workspaces/list`, {
|
||||||
{
|
headers: {
|
||||||
headers: {
|
Authorization: `Bearer ${accessToken}`,
|
||||||
Authorization: `Bearer ${accessToken}`,
|
"Accept-Encoding": "application/json"
|
||||||
"Accept-Encoding": "application/json"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
);
|
});
|
||||||
|
|
||||||
// check for write access of secrets in windmill workspaces
|
// check for write access of secrets in windmill workspaces
|
||||||
const writeAccessCheck = data.map(async (app) => {
|
const writeAccessCheck = data.map(async (app) => {
|
||||||
@ -941,7 +939,7 @@ const getAppsWindmill = async ({ accessToken }: { accessToken: string }) => {
|
|||||||
const folderPath = "f/folder/variable";
|
const folderPath = "f/folder/variable";
|
||||||
|
|
||||||
const { data: writeUser } = await request.post<object>(
|
const { data: writeUser } = await request.post<object>(
|
||||||
`${IntegrationUrls.WINDMILL_API_URL}/w/${app.id}/variables/create`,
|
`${apiUrl}/w/${app.id}/variables/create`,
|
||||||
{
|
{
|
||||||
path: userPath,
|
path: userPath,
|
||||||
value: "variable",
|
value: "variable",
|
||||||
@ -957,7 +955,7 @@ const getAppsWindmill = async ({ accessToken }: { accessToken: string }) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const { data: writeFolder } = await request.post<object>(
|
const { data: writeFolder } = await request.post<object>(
|
||||||
`${IntegrationUrls.WINDMILL_API_URL}/w/${app.id}/variables/create`,
|
`${apiUrl}/w/${app.id}/variables/create`,
|
||||||
{
|
{
|
||||||
path: folderPath,
|
path: folderPath,
|
||||||
value: "variable",
|
value: "variable",
|
||||||
@ -974,14 +972,14 @@ const getAppsWindmill = async ({ accessToken }: { accessToken: string }) => {
|
|||||||
|
|
||||||
// is write access is allowed then delete the created secrets from workspace
|
// is write access is allowed then delete the created secrets from workspace
|
||||||
if (writeUser && writeFolder) {
|
if (writeUser && writeFolder) {
|
||||||
await request.delete(`${IntegrationUrls.WINDMILL_API_URL}/w/${app.id}/variables/delete/${userPath}`, {
|
await request.delete(`${apiUrl}/w/${app.id}/variables/delete/${userPath}`, {
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${accessToken}`,
|
Authorization: `Bearer ${accessToken}`,
|
||||||
"Accept-Encoding": "application/json"
|
"Accept-Encoding": "application/json"
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
await request.delete(`${IntegrationUrls.WINDMILL_API_URL}/w/${app.id}/variables/delete/${folderPath}`, {
|
await request.delete(`${apiUrl}/w/${app.id}/variables/delete/${folderPath}`, {
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${accessToken}`,
|
Authorization: `Bearer ${accessToken}`,
|
||||||
"Accept-Encoding": "application/json"
|
"Accept-Encoding": "application/json"
|
||||||
@ -1316,7 +1314,8 @@ export const getApps = async ({
|
|||||||
|
|
||||||
case Integrations.WINDMILL:
|
case Integrations.WINDMILL:
|
||||||
return getAppsWindmill({
|
return getAppsWindmill({
|
||||||
accessToken
|
accessToken,
|
||||||
|
url
|
||||||
});
|
});
|
||||||
|
|
||||||
case Integrations.DIGITAL_OCEAN_APP_PLATFORM:
|
case Integrations.DIGITAL_OCEAN_APP_PLATFORM:
|
||||||
|
@ -4127,10 +4127,10 @@ const syncSecretsWindmill = async ({
|
|||||||
is_secret: boolean;
|
is_secret: boolean;
|
||||||
description?: string;
|
description?: string;
|
||||||
}
|
}
|
||||||
|
const apiUrl = integration.url ? `${integration.url}/api` : IntegrationUrls.WINDMILL_API_URL;
|
||||||
// get secrets stored in windmill workspace
|
// get secrets stored in windmill workspace
|
||||||
const res = (
|
const res = (
|
||||||
await request.get<WindmillSecret[]>(`${IntegrationUrls.WINDMILL_API_URL}/w/${integration.appId}/variables/list`, {
|
await request.get<WindmillSecret[]>(`${apiUrl}/w/${integration.appId}/variables/list`, {
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${accessToken}`,
|
Authorization: `Bearer ${accessToken}`,
|
||||||
"Accept-Encoding": "application/json"
|
"Accept-Encoding": "application/json"
|
||||||
@ -4146,7 +4146,6 @@ const syncSecretsWindmill = async ({
|
|||||||
|
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
const pattern = new RegExp("^(u/|f/)[a-zA-Z0-9_-]+/([a-zA-Z0-9_-]+/)*[a-zA-Z0-9_-]*[^/]$");
|
const pattern = new RegExp("^(u/|f/)[a-zA-Z0-9_-]+/([a-zA-Z0-9_-]+/)*[a-zA-Z0-9_-]*[^/]$");
|
||||||
|
|
||||||
for await (const key of Object.keys(secrets)) {
|
for await (const key of Object.keys(secrets)) {
|
||||||
if ((key.startsWith("u/") || key.startsWith("f/")) && pattern.test(key)) {
|
if ((key.startsWith("u/") || key.startsWith("f/")) && pattern.test(key)) {
|
||||||
if (!(key in res)) {
|
if (!(key in res)) {
|
||||||
@ -4154,7 +4153,7 @@ const syncSecretsWindmill = async ({
|
|||||||
// -> create secret
|
// -> create secret
|
||||||
|
|
||||||
await request.post(
|
await request.post(
|
||||||
`${IntegrationUrls.WINDMILL_API_URL}/w/${integration.appId}/variables/create`,
|
`${apiUrl}/w/${integration.appId}/variables/create`,
|
||||||
{
|
{
|
||||||
path: key,
|
path: key,
|
||||||
value: secrets[key].value,
|
value: secrets[key].value,
|
||||||
@ -4171,7 +4170,7 @@ const syncSecretsWindmill = async ({
|
|||||||
} else {
|
} else {
|
||||||
// -> update secret
|
// -> update secret
|
||||||
await request.post(
|
await request.post(
|
||||||
`${IntegrationUrls.WINDMILL_API_URL}/w/${integration.appId}/variables/update/${res[key].path}`,
|
`${apiUrl}/w/${integration.appId}/variables/update/${res[key].path}`,
|
||||||
{
|
{
|
||||||
path: key,
|
path: key,
|
||||||
value: secrets[key].value,
|
value: secrets[key].value,
|
||||||
@ -4192,16 +4191,13 @@ const syncSecretsWindmill = async ({
|
|||||||
for await (const key of Object.keys(res)) {
|
for await (const key of Object.keys(res)) {
|
||||||
if (!(key in secrets)) {
|
if (!(key in secrets)) {
|
||||||
// -> delete secret
|
// -> delete secret
|
||||||
await request.delete(
|
await request.delete(`${apiUrl}/w/${integration.appId}/variables/delete/${res[key].path}`, {
|
||||||
`${IntegrationUrls.WINDMILL_API_URL}/w/${integration.appId}/variables/delete/${res[key].path}`,
|
headers: {
|
||||||
{
|
Authorization: `Bearer ${accessToken}`,
|
||||||
headers: {
|
"Content-Type": "application/json",
|
||||||
Authorization: `Bearer ${accessToken}`,
|
"Accept-Encoding": "application/json"
|
||||||
"Content-Type": "application/json",
|
|
||||||
"Accept-Encoding": "application/json"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
);
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { Knex } from "knex";
|
import { Knex } from "knex";
|
||||||
|
|
||||||
import { TDbClient } from "@app/db";
|
import { TDbClient } from "@app/db";
|
||||||
import { TableName, TProjectEnvironments, TSecretFolders, TSecretFoldersUpdate } from "@app/db/schemas";
|
import { TableName, TSecretFolders, TSecretFoldersUpdate } from "@app/db/schemas";
|
||||||
import { BadRequestError, DatabaseError } from "@app/lib/errors";
|
import { BadRequestError, DatabaseError } from "@app/lib/errors";
|
||||||
import { groupBy, removeTrailingSlash } from "@app/lib/fn";
|
import { groupBy, removeTrailingSlash } from "@app/lib/fn";
|
||||||
import { ormify, selectAllTableCols } from "@app/lib/knex";
|
import { ormify, selectAllTableCols } from "@app/lib/knex";
|
||||||
@ -41,12 +41,12 @@ const sqlFindMultipleFolderByEnvPathQuery = (db: Knex, query: Array<{ envId: str
|
|||||||
void baseQb
|
void baseQb
|
||||||
.select({
|
.select({
|
||||||
depth: 1,
|
depth: 1,
|
||||||
// latestFolderVerId: db.raw("NULL::uuid"),
|
|
||||||
path: db.raw("'/'")
|
path: db.raw("'/'")
|
||||||
})
|
})
|
||||||
.from(TableName.SecretFolder)
|
.from(TableName.SecretFolder)
|
||||||
.where({
|
.where({
|
||||||
parentId: null
|
parentId: null,
|
||||||
|
name: "root"
|
||||||
})
|
})
|
||||||
.whereIn(
|
.whereIn(
|
||||||
"envId",
|
"envId",
|
||||||
@ -69,9 +69,7 @@ const sqlFindMultipleFolderByEnvPathQuery = (db: Knex, query: Array<{ envId: str
|
|||||||
.where((wb) =>
|
.where((wb) =>
|
||||||
formatedQuery.map(({ secretPath }) =>
|
formatedQuery.map(({ secretPath }) =>
|
||||||
wb.orWhereRaw(
|
wb.orWhereRaw(
|
||||||
`depth = array_position(ARRAY[${secretPath.map(() => "?").join(",")}]::varchar[], ${
|
`secret_folders.name = (ARRAY[${secretPath.map(() => "?").join(",")}]::varchar[])[depth]`,
|
||||||
TableName.SecretFolder
|
|
||||||
}.name,depth)`,
|
|
||||||
[...secretPath]
|
[...secretPath]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -107,7 +105,6 @@ const sqlFindFolderByPathQuery = (db: Knex, projectId: string, environments: str
|
|||||||
void baseQb
|
void baseQb
|
||||||
.select({
|
.select({
|
||||||
depth: 1,
|
depth: 1,
|
||||||
// latestFolderVerId: db.raw("NULL::uuid"),
|
|
||||||
path: db.raw("'/'")
|
path: db.raw("'/'")
|
||||||
})
|
})
|
||||||
.from(TableName.SecretFolder)
|
.from(TableName.SecretFolder)
|
||||||
@ -117,6 +114,11 @@ const sqlFindFolderByPathQuery = (db: Knex, projectId: string, environments: str
|
|||||||
parentId: null
|
parentId: null
|
||||||
})
|
})
|
||||||
.whereIn(`${TableName.Environment}.slug`, environments)
|
.whereIn(`${TableName.Environment}.slug`, environments)
|
||||||
|
.select(
|
||||||
|
db.ref("slug").withSchema(TableName.Environment).as("envSlug"),
|
||||||
|
db.ref("name").withSchema(TableName.Environment).as("envName"),
|
||||||
|
db.ref("projectId").withSchema(TableName.Environment)
|
||||||
|
)
|
||||||
.select(selectAllTableCols(TableName.SecretFolder))
|
.select(selectAllTableCols(TableName.SecretFolder))
|
||||||
.union(
|
.union(
|
||||||
(qb) =>
|
(qb) =>
|
||||||
@ -128,21 +130,20 @@ const sqlFindFolderByPathQuery = (db: Knex, projectId: string, environments: str
|
|||||||
depth: db.raw("parent.depth + 1"),
|
depth: db.raw("parent.depth + 1"),
|
||||||
path: db.raw(
|
path: db.raw(
|
||||||
"CONCAT((CASE WHEN parent.path = '/' THEN '' ELSE parent.path END),'/', secret_folders.name)"
|
"CONCAT((CASE WHEN parent.path = '/' THEN '' ELSE parent.path END),'/', secret_folders.name)"
|
||||||
)
|
),
|
||||||
|
envSlug: db.ref("envSlug").withSchema("parent"),
|
||||||
|
envName: db.ref("envName").withSchema("parent"),
|
||||||
|
projectId: db.ref("projectId").withSchema("parent")
|
||||||
})
|
})
|
||||||
.select(selectAllTableCols(TableName.SecretFolder))
|
.select(selectAllTableCols(TableName.SecretFolder))
|
||||||
.whereRaw(
|
.whereRaw(`secret_folders.name = (ARRAY[${pathSegments.map(() => "?").join(",")}]::varchar[])[depth]`, [
|
||||||
`depth = array_position(ARRAY[${pathSegments
|
...pathSegments
|
||||||
.map(() => "?")
|
])
|
||||||
.join(",")}]::varchar[], secret_folders.name,depth)`,
|
|
||||||
[...pathSegments]
|
|
||||||
)
|
|
||||||
.from(TableName.SecretFolder)
|
.from(TableName.SecretFolder)
|
||||||
.join("parent", "parent.id", `${TableName.SecretFolder}.parentId`)
|
.join("parent", "parent.id", `${TableName.SecretFolder}.parentId`)
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
.from<TSecretFolders & { depth: number; path: string }>("parent")
|
.from<TSecretFolders & { depth: number; path: string }>("parent")
|
||||||
.leftJoin<TProjectEnvironments>(TableName.Environment, `${TableName.Environment}.id`, "parent.envId")
|
|
||||||
.select<
|
.select<
|
||||||
(TSecretFolders & {
|
(TSecretFolders & {
|
||||||
depth: number;
|
depth: number;
|
||||||
@ -152,13 +153,7 @@ const sqlFindFolderByPathQuery = (db: Knex, projectId: string, environments: str
|
|||||||
envName: string;
|
envName: string;
|
||||||
projectId: string;
|
projectId: string;
|
||||||
})[]
|
})[]
|
||||||
>(
|
>(selectAllTableCols("parent" as TableName.SecretFolder));
|
||||||
selectAllTableCols("parent" as TableName.SecretFolder),
|
|
||||||
db.ref("id").withSchema(TableName.Environment).as("envId"),
|
|
||||||
db.ref("slug").withSchema(TableName.Environment).as("envSlug"),
|
|
||||||
db.ref("name").withSchema(TableName.Environment).as("envName"),
|
|
||||||
db.ref("projectId").withSchema(TableName.Environment)
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const sqlFindSecretPathByFolderId = (db: Knex, projectId: string, folderIds: string[]) =>
|
const sqlFindSecretPathByFolderId = (db: Knex, projectId: string, folderIds: string[]) =>
|
||||||
@ -220,19 +215,12 @@ export const secretFolderDALFactory = (db: TDbClient) => {
|
|||||||
throw new BadRequestError({
|
throw new BadRequestError({
|
||||||
message: "Invalid secret path. Only alphanumeric characters, dashes, and underscores are allowed."
|
message: "Invalid secret path. Only alphanumeric characters, dashes, and underscores are allowed."
|
||||||
});
|
});
|
||||||
|
const formatedPath = removeTrailingSlash(path);
|
||||||
try {
|
try {
|
||||||
const folder = await sqlFindFolderByPathQuery(
|
const query = sqlFindFolderByPathQuery(tx || db.replicaNode(), projectId, [environment], formatedPath)
|
||||||
tx || db.replicaNode(),
|
.where("path", formatedPath)
|
||||||
projectId,
|
|
||||||
[environment],
|
|
||||||
removeTrailingSlash(path)
|
|
||||||
)
|
|
||||||
.orderBy("depth", "desc")
|
|
||||||
.first();
|
.first();
|
||||||
if (folder && folder.path !== removeTrailingSlash(path)) {
|
const folder = await query;
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!folder) return;
|
if (!folder) return;
|
||||||
const { envId: id, envName: name, envSlug: slug, ...el } = folder;
|
const { envId: id, envName: name, envSlug: slug, ...el } = folder;
|
||||||
return { ...el, envId: id, environment: { id, name, slug } };
|
return { ...el, envId: id, environment: { id, name, slug } };
|
||||||
@ -250,22 +238,13 @@ export const secretFolderDALFactory = (db: TDbClient) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const pathDepth = removeTrailingSlash(path).split("/").filter(Boolean).length + 1;
|
const formatedPath = removeTrailingSlash(path);
|
||||||
|
|
||||||
const folders = await sqlFindFolderByPathQuery(
|
const folders = await sqlFindFolderByPathQuery(
|
||||||
tx || db.replicaNode(),
|
tx || db.replicaNode(),
|
||||||
projectId,
|
projectId,
|
||||||
environments,
|
environments,
|
||||||
removeTrailingSlash(path)
|
formatedPath
|
||||||
)
|
).where("path", removeTrailingSlash(path));
|
||||||
.orderBy("depth", "desc")
|
|
||||||
.where("depth", pathDepth);
|
|
||||||
|
|
||||||
const firstFolder = folders[0];
|
|
||||||
|
|
||||||
if (firstFolder && firstFolder.path !== removeTrailingSlash(path)) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
return folders.map((folder) => {
|
return folders.map((folder) => {
|
||||||
const { envId: id, envName: name, envSlug: slug, ...el } = folder;
|
const { envId: id, envName: name, envSlug: slug, ...el } = folder;
|
||||||
@ -323,7 +302,6 @@ export const secretFolderDALFactory = (db: TDbClient) => {
|
|||||||
const findSecretPathByFolderIds = async (projectId: string, folderIds: string[], tx?: Knex) => {
|
const findSecretPathByFolderIds = async (projectId: string, folderIds: string[], tx?: Knex) => {
|
||||||
try {
|
try {
|
||||||
const folders = await sqlFindSecretPathByFolderId(tx || db.replicaNode(), projectId, folderIds);
|
const folders = await sqlFindSecretPathByFolderId(tx || db.replicaNode(), projectId, folderIds);
|
||||||
|
|
||||||
// travelling all the way from leaf node to root contains real path
|
// travelling all the way from leaf node to root contains real path
|
||||||
const rootFolders = groupBy(
|
const rootFolders = groupBy(
|
||||||
folders.filter(({ parentId }) => parentId === null),
|
folders.filter(({ parentId }) => parentId === null),
|
||||||
|
@ -469,6 +469,58 @@ export const secretImportServiceFactory = ({
|
|||||||
return count;
|
return count;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getProjectImportMultiEnvCount = async ({
|
||||||
|
path: secretPath,
|
||||||
|
environments,
|
||||||
|
projectId,
|
||||||
|
actor,
|
||||||
|
actorId,
|
||||||
|
actorAuthMethod,
|
||||||
|
actorOrgId,
|
||||||
|
search
|
||||||
|
}: Omit<TGetSecretImportsDTO, "environment"> & { environments: string[] }) => {
|
||||||
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
|
actor,
|
||||||
|
actorId,
|
||||||
|
projectId,
|
||||||
|
actorAuthMethod,
|
||||||
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
|
});
|
||||||
|
const filteredEnvironments = [];
|
||||||
|
for (const environment of environments) {
|
||||||
|
if (
|
||||||
|
permission.can(
|
||||||
|
ProjectPermissionActions.Read,
|
||||||
|
subject(ProjectPermissionSub.SecretImports, { environment, secretPath })
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
filteredEnvironments.push(environment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (filteredEnvironments.length === 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const environment of filteredEnvironments) {
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Read,
|
||||||
|
subject(ProjectPermissionSub.SecretImports, { environment, secretPath })
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const folders = await folderDAL.findBySecretPathMultiEnv(projectId, environments, secretPath);
|
||||||
|
if (!folders?.length)
|
||||||
|
throw new NotFoundError({
|
||||||
|
message: `Folder with path '${secretPath}' not found on environments with slugs '${environments.join(", ")}'`
|
||||||
|
});
|
||||||
|
const counts = await Promise.all(
|
||||||
|
folders.map((folder) => secretImportDAL.getProjectImportCount({ folderId: folder.id, search }))
|
||||||
|
);
|
||||||
|
|
||||||
|
return counts.reduce((sum, count) => sum + count, 0);
|
||||||
|
};
|
||||||
|
|
||||||
const getImports = async ({
|
const getImports = async ({
|
||||||
path: secretPath,
|
path: secretPath,
|
||||||
environment,
|
environment,
|
||||||
@ -688,6 +740,59 @@ export const secretImportServiceFactory = ({
|
|||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getImportsMultiEnv = async ({
|
||||||
|
path: secretPath,
|
||||||
|
environments,
|
||||||
|
projectId,
|
||||||
|
actor,
|
||||||
|
actorId,
|
||||||
|
actorAuthMethod,
|
||||||
|
actorOrgId,
|
||||||
|
search,
|
||||||
|
limit,
|
||||||
|
offset
|
||||||
|
}: Omit<TGetSecretImportsDTO, "environment"> & { environments: string[] }) => {
|
||||||
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
|
actor,
|
||||||
|
actorId,
|
||||||
|
projectId,
|
||||||
|
actorAuthMethod,
|
||||||
|
actorOrgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretManager
|
||||||
|
});
|
||||||
|
const filteredEnvironments = [];
|
||||||
|
for (const environment of environments) {
|
||||||
|
if (
|
||||||
|
permission.can(
|
||||||
|
ProjectPermissionActions.Read,
|
||||||
|
subject(ProjectPermissionSub.SecretImports, { environment, secretPath })
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
filteredEnvironments.push(environment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (filteredEnvironments.length === 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const folders = await folderDAL.findBySecretPathMultiEnv(projectId, filteredEnvironments, secretPath);
|
||||||
|
if (!folders?.length)
|
||||||
|
throw new NotFoundError({
|
||||||
|
message: `Folder with path '${secretPath}' not found on environments with slugs '${environments.join(", ")}'`
|
||||||
|
});
|
||||||
|
|
||||||
|
const secImportsArrays = await Promise.all(
|
||||||
|
folders.map(async (folder) => {
|
||||||
|
const imports = await secretImportDAL.find({ folderId: folder.id, search, limit, offset });
|
||||||
|
return imports.map((importItem) => ({
|
||||||
|
...importItem,
|
||||||
|
environment: folder.environment.slug
|
||||||
|
}));
|
||||||
|
})
|
||||||
|
);
|
||||||
|
return secImportsArrays.flat();
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
createImport,
|
createImport,
|
||||||
updateImport,
|
updateImport,
|
||||||
@ -698,6 +803,8 @@ export const secretImportServiceFactory = ({
|
|||||||
getRawSecretsFromImports,
|
getRawSecretsFromImports,
|
||||||
resyncSecretImportReplication,
|
resyncSecretImportReplication,
|
||||||
getProjectImportCount,
|
getProjectImportCount,
|
||||||
fnSecretsFromImports
|
fnSecretsFromImports,
|
||||||
|
getProjectImportMultiEnvCount,
|
||||||
|
getImportsMultiEnv
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -9,20 +9,76 @@ description: "Track evert event action performed within Infisical projects."
|
|||||||
If you're using Infisical Cloud, then it is available under the **Pro**,
|
If you're using Infisical Cloud, then it is available under the **Pro**,
|
||||||
and **Enterprise Tier** with varying retention periods. If you're self-hosting Infisical,
|
and **Enterprise Tier** with varying retention periods. If you're self-hosting Infisical,
|
||||||
then you should contact sales@infisical.com to purchase an enterprise license to use it.
|
then you should contact sales@infisical.com to purchase an enterprise license to use it.
|
||||||
|
|
||||||
</Info>
|
</Info>
|
||||||
|
|
||||||
Infisical provides audit logs for security and compliance teams to monitor information access.
|
Infisical provides audit logs for security and compliance teams to monitor information access.
|
||||||
With the Audit Log functionality, teams can:
|
With the Audit Log functionality, teams can:
|
||||||
|
|
||||||
- **Track** 40+ different events;
|
- **Track** 40+ different events;
|
||||||
- **Filter** audit logs by event, actor, source, date or any combination of these filters;
|
- **Filter** audit logs by event, actor, source, date or any combination of these filters;
|
||||||
- **Inspect** extensive metadata in the event of any suspicious activity or incident review.
|
- **Inspect** extensive metadata in the event of any suspicious activity or incident review.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
## Audit Log Structure
|
||||||
|
|
||||||
Each log contains the following data:
|
Each log contains the following data:
|
||||||
|
|
||||||
- **Event**: The underlying action such as create, list, read, update, or delete secret(s).
|
| Field | Type | Description | Purpose |
|
||||||
- **Actor**: The entity responsible for performing or causing the event; this can be a user or service.
|
| ------------------------- | -------- | --------------------------------------------------------- | ------------------------------------------------------------- |
|
||||||
- **Timestamp**: The date and time at which point the event occurred.
|
| **event** | Object | Contains details about the action performed | Captures what happened |
|
||||||
- **Source** (User agent + IP): The software (user agent) and network address (IP) from which the event was initiated.
|
| event.type | String | The specific action that occurred (e.g., "create-secret") | Identifies the exact operation |
|
||||||
- **Metadata**: Additional data to provide context for each event. For example, this could be the path at which a secret was fetched from etc.
|
| event.metadata | Object | Context-specific details about the event | Provides detailed information relevant to the specific action |
|
||||||
|
| **actor** | Object | Information about who performed the action | Identifies the responsible entity |
|
||||||
|
| actor.type | String | Category of actor (user, service, identity, etc.) | Distinguishes between human and non-human actors |
|
||||||
|
| actor.metadata | Object | Details about the specific actor | Provides identity information |
|
||||||
|
| actor.metadata.userId | String | Unique identifier for user actors | Links to specific user account |
|
||||||
|
| actor.metadata.email | String | Email address for user actors | Email of the executing user |
|
||||||
|
| actor.metadata.username | String | Username for user actors | Username of the executing user |
|
||||||
|
| actor.metadata.serviceId | String | Identifier for service actors | ID of specific service token |
|
||||||
|
| actor.metadata.identityId | String | Identifier for identity actors | ID to specific identity |
|
||||||
|
| actor.metadata.permission | Object | Permission context for the action | Shows permission template data when action was performed |
|
||||||
|
| **orgId** | String | Organization identifier | Indicates which organization the action occurred in |
|
||||||
|
| **projectId** | String | Project identifier | Indicates which project the action affected |
|
||||||
|
| **ipAddress** | String | Source IP address | Shows where the request originated from |
|
||||||
|
| **userAgent** | String | Client application information | Identifies browser or application used |
|
||||||
|
| **userAgentType** | String | Category of client (web, CLI, SDK, etc.) | Classifies the access method |
|
||||||
|
| **timestamp** | DateTime | When the action occurred | Records the exact time of the event |
|
||||||
|
|
||||||
|
<Accordion title="Example Payload">
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": "[UUID]",
|
||||||
|
"ipAddress": "[IP_ADDRESS]",
|
||||||
|
"userAgent": "[USER_AGENT_STRING]",
|
||||||
|
"userAgentType": "web",
|
||||||
|
"expiresAt": "[TIMESTAMP]",
|
||||||
|
"createdAt": "[TIMESTAMP]",
|
||||||
|
"updatedAt": "[TIMESTAMP]",
|
||||||
|
"orgId": "[ORGANIZATION_UUID]",
|
||||||
|
"projectId": "[PROJECT_UUID]",
|
||||||
|
"projectName": "[PROJECT_NAME]",
|
||||||
|
"event": {
|
||||||
|
"type": "get-secrets",
|
||||||
|
"metadata": {
|
||||||
|
"secretPath": "[PATH]",
|
||||||
|
"environment": "[ENVIRONMENT_NAME]",
|
||||||
|
"numberOfSecrets": [NUMBER]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"actor": {
|
||||||
|
"type": "user",
|
||||||
|
"metadata": {
|
||||||
|
"email": "[EMAIL]",
|
||||||
|
"userId": "[USER_UUID]",
|
||||||
|
"username": "[USERNAME]",
|
||||||
|
"permission": {
|
||||||
|
"metadata": {},
|
||||||
|
"auth": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
</Accordion>
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 1.6 MiB After Width: | Height: | Size: 430 KiB |
@ -6,7 +6,7 @@ description: "Learn how to fetch secrets from Infisical with Terraform using bot
|
|||||||
This guide demonstrates how to use Infisical to manage secrets in your Terraform infrastructure code, supporting both traditional data sources and ephemeral resources for enhanced security. It uses:
|
This guide demonstrates how to use Infisical to manage secrets in your Terraform infrastructure code, supporting both traditional data sources and ephemeral resources for enhanced security. It uses:
|
||||||
|
|
||||||
- Infisical (you can use [Infisical Cloud](https://app.infisical.com) or a [self-hosted instance of Infisical](https://infisical.com/docs/self-hosting/overview)) to store your secrets
|
- Infisical (you can use [Infisical Cloud](https://app.infisical.com) or a [self-hosted instance of Infisical](https://infisical.com/docs/self-hosting/overview)) to store your secrets
|
||||||
- The [Terraform Provider](https://registry.terraform.io/providers/Infisical/infisical/latest) to fetch secrets for your infrastructure
|
- The [Terraform Provider](https://registry.terraform.io/providers/Infisical/infisical/latest/docs) to fetch secrets for your infrastructure
|
||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
|
5
docs/integrations/platforms/apache-airflow.mdx
Normal file
5
docs/integrations/platforms/apache-airflow.mdx
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
title: "Apache Airflow"
|
||||||
|
description: "Learn how to use Infisical as your custom secrets backend in Apache Airflow."
|
||||||
|
url: "https://github.com/Infisical/airflow-provider-infisical?tab=readme-ov-file#airflow-infisical-provider"
|
||||||
|
---
|
@ -402,7 +402,8 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"integrations/frameworks/terraform",
|
"integrations/frameworks/terraform",
|
||||||
"integrations/platforms/ansible"
|
"integrations/platforms/ansible",
|
||||||
|
"integrations/platforms/apache-airflow"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
351
frontend/package-lock.json
generated
351
frontend/package-lock.json
generated
@ -362,26 +362,26 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/helpers": {
|
"node_modules/@babel/helpers": {
|
||||||
"version": "7.26.0",
|
"version": "7.26.10",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.10.tgz",
|
||||||
"integrity": "sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==",
|
"integrity": "sha512-UPYc3SauzZ3JGgj87GgZ89JVdC5dj0AoetR5Bw6wj4niittNyFh6+eOGonYvJ1ao6B8lEa3Q3klS7ADZ53bc5g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/template": "^7.25.9",
|
"@babel/template": "^7.26.9",
|
||||||
"@babel/types": "^7.26.0"
|
"@babel/types": "^7.26.10"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/parser": {
|
"node_modules/@babel/parser": {
|
||||||
"version": "7.26.3",
|
"version": "7.26.10",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.3.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.10.tgz",
|
||||||
"integrity": "sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA==",
|
"integrity": "sha512-6aQR2zGE/QFi8JpDLjUZEPYOs7+mhKXm86VaKFiLP35JQwQb6bwUE+XbvkH0EptsYhbNBSUGaUBLKqxH1xSgsA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/types": "^7.26.3"
|
"@babel/types": "^7.26.10"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"parser": "bin/babel-parser.js"
|
"parser": "bin/babel-parser.js"
|
||||||
@ -423,9 +423,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/runtime": {
|
"node_modules/@babel/runtime": {
|
||||||
"version": "7.26.0",
|
"version": "7.26.10",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.10.tgz",
|
||||||
"integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==",
|
"integrity": "sha512-2WJMeRQPHKSPemqk/awGrAiuFfzBmOIPXKizAsVhWH9YJqLZ0H+HS4c8loHGgW6utJ3E/ejXQUsiGaQy2NZ9Fw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"regenerator-runtime": "^0.14.0"
|
"regenerator-runtime": "^0.14.0"
|
||||||
@ -435,14 +435,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/template": {
|
"node_modules/@babel/template": {
|
||||||
"version": "7.25.9",
|
"version": "7.26.9",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.26.9.tgz",
|
||||||
"integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==",
|
"integrity": "sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/code-frame": "^7.25.9",
|
"@babel/code-frame": "^7.26.2",
|
||||||
"@babel/parser": "^7.25.9",
|
"@babel/parser": "^7.26.9",
|
||||||
"@babel/types": "^7.25.9"
|
"@babel/types": "^7.26.9"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
@ -476,9 +476,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/types": {
|
"node_modules/@babel/types": {
|
||||||
"version": "7.26.3",
|
"version": "7.26.10",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.10.tgz",
|
||||||
"integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==",
|
"integrity": "sha512-emqcG3vHrpxUKTrxcblR36dcrcoRDvKmnL/dCL6ZsHaShW80qxCAcNhzQZrpeM765VzEos+xOi4s+r4IXzTwdQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/helper-string-parser": "^7.25.9",
|
"@babel/helper-string-parser": "^7.25.9",
|
||||||
@ -1010,6 +1010,23 @@
|
|||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@esbuild/netbsd-arm64": {
|
||||||
|
"version": "0.25.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.1.tgz",
|
||||||
|
"integrity": "sha512-O96poM2XGhLtpTh+s4+nP7YCCAfb4tJNRVZHfIE7dgmax+yMP2WgMd2OecBuaATHKTHsLWHQeuaxMRnCsH8+5g==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"netbsd"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@esbuild/netbsd-x64": {
|
"node_modules/@esbuild/netbsd-x64": {
|
||||||
"version": "0.21.5",
|
"version": "0.21.5",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz",
|
||||||
@ -1028,9 +1045,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/openbsd-arm64": {
|
"node_modules/@esbuild/openbsd-arm64": {
|
||||||
"version": "0.23.1",
|
"version": "0.25.1",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.1.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.1.tgz",
|
||||||
"integrity": "sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==",
|
"integrity": "sha512-Na9T3szbXezdzM/Kfs3GcRQNjHzM6GzFBeU1/6IV/npKP5ORtp9zbQjvkDJ47s6BCgaAZnnnu/cY1x342+MvZg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@ -1647,12 +1664,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@octokit/endpoint": {
|
"node_modules/@octokit/endpoint": {
|
||||||
"version": "10.1.1",
|
"version": "10.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-10.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-10.1.3.tgz",
|
||||||
"integrity": "sha512-JYjh5rMOwXMJyUpj028cu0Gbp7qe/ihxfJMLc8VZBMMqSwLgOxDI1911gV4Enl1QSavAQNJcwmwBF9M0VvLh6Q==",
|
"integrity": "sha512-nBRBMpKPhQUxCsQQeW+rCJ/OPSMcj3g0nfHn01zGYZXuNDvvXudF/TYY6APj5THlurerpFN4a/dQAIAaM6BYhA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@octokit/types": "^13.0.0",
|
"@octokit/types": "^13.6.2",
|
||||||
"universal-user-agent": "^7.0.2"
|
"universal-user-agent": "^7.0.2"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@ -1674,18 +1691,18 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@octokit/openapi-types": {
|
"node_modules/@octokit/openapi-types": {
|
||||||
"version": "22.2.0",
|
"version": "24.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-22.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz",
|
||||||
"integrity": "sha512-QBhVjcUa9W7Wwhm6DBFu6ZZ+1/t/oYxqc2tp81Pi41YNuJinbFRx8B133qVOrAaBbF7D/m0Et6f9/pZt9Rc+tg==",
|
"integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@octokit/plugin-paginate-rest": {
|
"node_modules/@octokit/plugin-paginate-rest": {
|
||||||
"version": "11.3.6",
|
"version": "11.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-11.3.6.tgz",
|
"resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-11.6.0.tgz",
|
||||||
"integrity": "sha512-zcvqqf/+TicbTCa/Z+3w4eBJcAxCFymtc0UAIsR3dEVoNilWld4oXdscQ3laXamTszUZdusw97K8+DrbFiOwjw==",
|
"integrity": "sha512-n5KPteiF7pWKgBIBJSk8qzoZWcUkza2O6A0za97pMGVrGfPdltxrfmfF5GucHYvHGZD8BdaZmmHGz5cX/3gdpw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@octokit/types": "^13.6.2"
|
"@octokit/types": "^13.10.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 18"
|
"node": ">= 18"
|
||||||
@ -1722,14 +1739,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@octokit/request": {
|
"node_modules/@octokit/request": {
|
||||||
"version": "9.1.3",
|
"version": "9.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-9.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-9.2.2.tgz",
|
||||||
"integrity": "sha512-V+TFhu5fdF3K58rs1pGUJIDH5RZLbZm5BI+MNF+6o/ssFNT4vWlCh/tVpF3NxGtP15HUxTTMUbsG5llAuU2CZA==",
|
"integrity": "sha512-dZl0ZHx6gOQGcffgm1/Sf6JfEpmh34v3Af2Uci02vzUYz6qEN6zepoRtmybWXIGXFIK8K9ylE3b+duCWqhArtg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@octokit/endpoint": "^10.0.0",
|
"@octokit/endpoint": "^10.1.3",
|
||||||
"@octokit/request-error": "^6.0.1",
|
"@octokit/request-error": "^6.1.7",
|
||||||
"@octokit/types": "^13.1.0",
|
"@octokit/types": "^13.6.2",
|
||||||
|
"fast-content-type-parse": "^2.0.0",
|
||||||
"universal-user-agent": "^7.0.2"
|
"universal-user-agent": "^7.0.2"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@ -1737,12 +1755,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@octokit/request-error": {
|
"node_modules/@octokit/request-error": {
|
||||||
"version": "6.1.5",
|
"version": "6.1.7",
|
||||||
"resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-6.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-6.1.7.tgz",
|
||||||
"integrity": "sha512-IlBTfGX8Yn/oFPMwSfvugfncK2EwRLjzbrpifNaMY8o/HTEAFqCA1FZxjD9cWvSKBHgrIhc4CSBIzMxiLsbzFQ==",
|
"integrity": "sha512-69NIppAwaauwZv6aOzb+VVLwt+0havz9GT5YplkeJv7fG7a40qpLt/yZKyiDxAhgz0EtgNdNcb96Z0u+Zyuy2g==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@octokit/types": "^13.0.0"
|
"@octokit/types": "^13.6.2"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 18"
|
"node": ">= 18"
|
||||||
@ -1764,12 +1782,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@octokit/types": {
|
"node_modules/@octokit/types": {
|
||||||
"version": "13.6.2",
|
"version": "13.10.0",
|
||||||
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.6.2.tgz",
|
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz",
|
||||||
"integrity": "sha512-WpbZfZUcZU77DrSW4wbsSgTPfKcp286q3ItaIgvSbBpZJlu6mnYXAkjZz6LVZPXkEvLIM8McanyZejKTYUHipA==",
|
"integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@octokit/openapi-types": "^22.2.0"
|
"@octokit/openapi-types": "^24.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@peculiar/asn1-cms": {
|
"node_modules/@peculiar/asn1-cms": {
|
||||||
@ -4956,9 +4974,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/axios": {
|
"node_modules/axios": {
|
||||||
"version": "1.7.9",
|
"version": "1.8.3",
|
||||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz",
|
"resolved": "https://registry.npmjs.org/axios/-/axios-1.8.3.tgz",
|
||||||
"integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==",
|
"integrity": "sha512-iP4DebzoNlP/YN2dpwCgb8zoCmhtkajzS48JvwmkSkXvPI3DHc7m+XYL5tGnSlJtR6nImXZmdCuN5aP8dh1d8A==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"follow-redirects": "^1.15.6",
|
"follow-redirects": "^1.15.6",
|
||||||
@ -5451,9 +5469,9 @@
|
|||||||
"license": "CC-BY-4.0"
|
"license": "CC-BY-4.0"
|
||||||
},
|
},
|
||||||
"node_modules/canvg": {
|
"node_modules/canvg": {
|
||||||
"version": "3.0.10",
|
"version": "3.0.11",
|
||||||
"resolved": "https://registry.npmjs.org/canvg/-/canvg-3.0.10.tgz",
|
"resolved": "https://registry.npmjs.org/canvg/-/canvg-3.0.11.tgz",
|
||||||
"integrity": "sha512-qwR2FRNO9NlzTeKIPIKpnTY6fqwuYSequ8Ru8c0YkYU7U0oW+hLUvWadLvAu1Rl72OMNiFhoLu4f8eUjQ7l/+Q==",
|
"integrity": "sha512-5ON+q7jCTgMp9cjpu4Jo6XbvfYwSB2Ow3kzHKfIyJfaCAOHLbdKPQqGKgfED/R5B+3TFFfe8pegYA+b423SRyA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -7314,6 +7332,22 @@
|
|||||||
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
|
||||||
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
|
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
|
||||||
},
|
},
|
||||||
|
"node_modules/fast-content-type-parse": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-nGqtvLrj5w0naR6tDPfB4cUmYCqouzyQiz6C5y/LtcDllJdrcc6WaWW6iXyIIOErTa/XRybj28aasdn4LkVk6Q==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/fastify"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/fastify"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/fast-deep-equal": {
|
"node_modules/fast-deep-equal": {
|
||||||
"version": "3.1.3",
|
"version": "3.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||||
@ -12588,13 +12622,13 @@
|
|||||||
"license": "0BSD"
|
"license": "0BSD"
|
||||||
},
|
},
|
||||||
"node_modules/tsx": {
|
"node_modules/tsx": {
|
||||||
"version": "4.19.2",
|
"version": "4.19.3",
|
||||||
"resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.2.tgz",
|
"resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.3.tgz",
|
||||||
"integrity": "sha512-pOUl6Vo2LUq/bSa8S5q7b91cgNSjctn9ugq/+Mvow99qW6x/UZYwzxy/3NmqoT66eHYfCVvFvACC58UBPFf28g==",
|
"integrity": "sha512-4H8vUNGNjQ4V2EOoGw005+c+dGuPSnhpPBPHBtsZdGZBk/iJb4kguGlPWaZTZ3q5nMtFOEsY0nRDlh9PJyd6SQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"esbuild": "~0.23.0",
|
"esbuild": "~0.25.0",
|
||||||
"get-tsconfig": "^4.7.5"
|
"get-tsconfig": "^4.7.5"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
@ -12608,9 +12642,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/tsx/node_modules/@esbuild/aix-ppc64": {
|
"node_modules/tsx/node_modules/@esbuild/aix-ppc64": {
|
||||||
"version": "0.23.1",
|
"version": "0.25.1",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.1.tgz",
|
||||||
"integrity": "sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==",
|
"integrity": "sha512-kfYGy8IdzTGy+z0vFGvExZtxkFlA4zAxgKEahG9KE1ScBjpQnFsNOX8KTU5ojNru5ed5CVoJYXFtoxaq5nFbjQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ppc64"
|
"ppc64"
|
||||||
],
|
],
|
||||||
@ -12625,9 +12659,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/tsx/node_modules/@esbuild/android-arm": {
|
"node_modules/tsx/node_modules/@esbuild/android-arm": {
|
||||||
"version": "0.23.1",
|
"version": "0.25.1",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.1.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.1.tgz",
|
||||||
"integrity": "sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==",
|
"integrity": "sha512-dp+MshLYux6j/JjdqVLnMglQlFu+MuVeNrmT5nk6q07wNhCdSnB7QZj+7G8VMUGh1q+vj2Bq8kRsuyA00I/k+Q==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
@ -12642,9 +12676,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/tsx/node_modules/@esbuild/android-arm64": {
|
"node_modules/tsx/node_modules/@esbuild/android-arm64": {
|
||||||
"version": "0.23.1",
|
"version": "0.25.1",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.1.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.1.tgz",
|
||||||
"integrity": "sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==",
|
"integrity": "sha512-50tM0zCJW5kGqgG7fQ7IHvQOcAn9TKiVRuQ/lN0xR+T2lzEFvAi1ZcS8DiksFcEpf1t/GYOeOfCAgDHFpkiSmA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@ -12659,9 +12693,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/tsx/node_modules/@esbuild/android-x64": {
|
"node_modules/tsx/node_modules/@esbuild/android-x64": {
|
||||||
"version": "0.23.1",
|
"version": "0.25.1",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.1.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.1.tgz",
|
||||||
"integrity": "sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==",
|
"integrity": "sha512-GCj6WfUtNldqUzYkN/ITtlhwQqGWu9S45vUXs7EIYf+7rCiiqH9bCloatO9VhxsL0Pji+PF4Lz2XXCES+Q8hDw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@ -12676,9 +12710,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/tsx/node_modules/@esbuild/darwin-arm64": {
|
"node_modules/tsx/node_modules/@esbuild/darwin-arm64": {
|
||||||
"version": "0.23.1",
|
"version": "0.25.1",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.1.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.1.tgz",
|
||||||
"integrity": "sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==",
|
"integrity": "sha512-5hEZKPf+nQjYoSr/elb62U19/l1mZDdqidGfmFutVUjjUZrOazAtwK+Kr+3y0C/oeJfLlxo9fXb1w7L+P7E4FQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@ -12693,9 +12727,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/tsx/node_modules/@esbuild/darwin-x64": {
|
"node_modules/tsx/node_modules/@esbuild/darwin-x64": {
|
||||||
"version": "0.23.1",
|
"version": "0.25.1",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.1.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.1.tgz",
|
||||||
"integrity": "sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==",
|
"integrity": "sha512-hxVnwL2Dqs3fM1IWq8Iezh0cX7ZGdVhbTfnOy5uURtao5OIVCEyj9xIzemDi7sRvKsuSdtCAhMKarxqtlyVyfA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@ -12710,9 +12744,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/tsx/node_modules/@esbuild/freebsd-arm64": {
|
"node_modules/tsx/node_modules/@esbuild/freebsd-arm64": {
|
||||||
"version": "0.23.1",
|
"version": "0.25.1",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.1.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.1.tgz",
|
||||||
"integrity": "sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==",
|
"integrity": "sha512-1MrCZs0fZa2g8E+FUo2ipw6jw5qqQiH+tERoS5fAfKnRx6NXH31tXBKI3VpmLijLH6yriMZsxJtaXUyFt/8Y4A==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@ -12727,9 +12761,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/tsx/node_modules/@esbuild/freebsd-x64": {
|
"node_modules/tsx/node_modules/@esbuild/freebsd-x64": {
|
||||||
"version": "0.23.1",
|
"version": "0.25.1",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.1.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.1.tgz",
|
||||||
"integrity": "sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==",
|
"integrity": "sha512-0IZWLiTyz7nm0xuIs0q1Y3QWJC52R8aSXxe40VUxm6BB1RNmkODtW6LHvWRrGiICulcX7ZvyH6h5fqdLu4gkww==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@ -12744,9 +12778,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/tsx/node_modules/@esbuild/linux-arm": {
|
"node_modules/tsx/node_modules/@esbuild/linux-arm": {
|
||||||
"version": "0.23.1",
|
"version": "0.25.1",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.1.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.1.tgz",
|
||||||
"integrity": "sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==",
|
"integrity": "sha512-NdKOhS4u7JhDKw9G3cY6sWqFcnLITn6SqivVArbzIaf3cemShqfLGHYMx8Xlm/lBit3/5d7kXvriTUGa5YViuQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
@ -12761,9 +12795,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/tsx/node_modules/@esbuild/linux-arm64": {
|
"node_modules/tsx/node_modules/@esbuild/linux-arm64": {
|
||||||
"version": "0.23.1",
|
"version": "0.25.1",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.1.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.1.tgz",
|
||||||
"integrity": "sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==",
|
"integrity": "sha512-jaN3dHi0/DDPelk0nLcXRm1q7DNJpjXy7yWaWvbfkPvI+7XNSc/lDOnCLN7gzsyzgu6qSAmgSvP9oXAhP973uQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@ -12778,9 +12812,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/tsx/node_modules/@esbuild/linux-ia32": {
|
"node_modules/tsx/node_modules/@esbuild/linux-ia32": {
|
||||||
"version": "0.23.1",
|
"version": "0.25.1",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.1.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.1.tgz",
|
||||||
"integrity": "sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==",
|
"integrity": "sha512-OJykPaF4v8JidKNGz8c/q1lBO44sQNUQtq1KktJXdBLn1hPod5rE/Hko5ugKKZd+D2+o1a9MFGUEIUwO2YfgkQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ia32"
|
"ia32"
|
||||||
],
|
],
|
||||||
@ -12795,9 +12829,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/tsx/node_modules/@esbuild/linux-loong64": {
|
"node_modules/tsx/node_modules/@esbuild/linux-loong64": {
|
||||||
"version": "0.23.1",
|
"version": "0.25.1",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.1.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.1.tgz",
|
||||||
"integrity": "sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==",
|
"integrity": "sha512-nGfornQj4dzcq5Vp835oM/o21UMlXzn79KobKlcs3Wz9smwiifknLy4xDCLUU0BWp7b/houtdrgUz7nOGnfIYg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"loong64"
|
"loong64"
|
||||||
],
|
],
|
||||||
@ -12812,9 +12846,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/tsx/node_modules/@esbuild/linux-mips64el": {
|
"node_modules/tsx/node_modules/@esbuild/linux-mips64el": {
|
||||||
"version": "0.23.1",
|
"version": "0.25.1",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.1.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.1.tgz",
|
||||||
"integrity": "sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==",
|
"integrity": "sha512-1osBbPEFYwIE5IVB/0g2X6i1qInZa1aIoj1TdL4AaAb55xIIgbg8Doq6a5BzYWgr+tEcDzYH67XVnTmUzL+nXg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"mips64el"
|
"mips64el"
|
||||||
],
|
],
|
||||||
@ -12829,9 +12863,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/tsx/node_modules/@esbuild/linux-ppc64": {
|
"node_modules/tsx/node_modules/@esbuild/linux-ppc64": {
|
||||||
"version": "0.23.1",
|
"version": "0.25.1",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.1.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.1.tgz",
|
||||||
"integrity": "sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==",
|
"integrity": "sha512-/6VBJOwUf3TdTvJZ82qF3tbLuWsscd7/1w+D9LH0W/SqUgM5/JJD0lrJ1fVIfZsqB6RFmLCe0Xz3fmZc3WtyVg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ppc64"
|
"ppc64"
|
||||||
],
|
],
|
||||||
@ -12846,9 +12880,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/tsx/node_modules/@esbuild/linux-riscv64": {
|
"node_modules/tsx/node_modules/@esbuild/linux-riscv64": {
|
||||||
"version": "0.23.1",
|
"version": "0.25.1",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.1.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.1.tgz",
|
||||||
"integrity": "sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==",
|
"integrity": "sha512-nSut/Mx5gnilhcq2yIMLMe3Wl4FK5wx/o0QuuCLMtmJn+WeWYoEGDN1ipcN72g1WHsnIbxGXd4i/MF0gTcuAjQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"riscv64"
|
"riscv64"
|
||||||
],
|
],
|
||||||
@ -12863,9 +12897,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/tsx/node_modules/@esbuild/linux-s390x": {
|
"node_modules/tsx/node_modules/@esbuild/linux-s390x": {
|
||||||
"version": "0.23.1",
|
"version": "0.25.1",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.1.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.1.tgz",
|
||||||
"integrity": "sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==",
|
"integrity": "sha512-cEECeLlJNfT8kZHqLarDBQso9a27o2Zd2AQ8USAEoGtejOrCYHNtKP8XQhMDJMtthdF4GBmjR2au3x1udADQQQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"s390x"
|
"s390x"
|
||||||
],
|
],
|
||||||
@ -12880,9 +12914,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/tsx/node_modules/@esbuild/linux-x64": {
|
"node_modules/tsx/node_modules/@esbuild/linux-x64": {
|
||||||
"version": "0.23.1",
|
"version": "0.25.1",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.1.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.1.tgz",
|
||||||
"integrity": "sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==",
|
"integrity": "sha512-xbfUhu/gnvSEg+EGovRc+kjBAkrvtk38RlerAzQxvMzlB4fXpCFCeUAYzJvrnhFtdeyVCDANSjJvOvGYoeKzFA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@ -12897,9 +12931,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/tsx/node_modules/@esbuild/netbsd-x64": {
|
"node_modules/tsx/node_modules/@esbuild/netbsd-x64": {
|
||||||
"version": "0.23.1",
|
"version": "0.25.1",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.1.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.1.tgz",
|
||||||
"integrity": "sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==",
|
"integrity": "sha512-X53z6uXip6KFXBQ+Krbx25XHV/NCbzryM6ehOAeAil7X7oa4XIq+394PWGnwaSQ2WRA0KI6PUO6hTO5zeF5ijA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@ -12914,9 +12948,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/tsx/node_modules/@esbuild/openbsd-x64": {
|
"node_modules/tsx/node_modules/@esbuild/openbsd-x64": {
|
||||||
"version": "0.23.1",
|
"version": "0.25.1",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.1.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.1.tgz",
|
||||||
"integrity": "sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==",
|
"integrity": "sha512-T3H78X2h1tszfRSf+txbt5aOp/e7TAz3ptVKu9Oyir3IAOFPGV6O9c2naym5TOriy1l0nNf6a4X5UXRZSGX/dw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@ -12931,9 +12965,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/tsx/node_modules/@esbuild/sunos-x64": {
|
"node_modules/tsx/node_modules/@esbuild/sunos-x64": {
|
||||||
"version": "0.23.1",
|
"version": "0.25.1",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.1.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.1.tgz",
|
||||||
"integrity": "sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==",
|
"integrity": "sha512-2H3RUvcmULO7dIE5EWJH8eubZAI4xw54H1ilJnRNZdeo8dTADEZ21w6J22XBkXqGJbe0+wnNJtw3UXRoLJnFEg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@ -12948,9 +12982,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/tsx/node_modules/@esbuild/win32-arm64": {
|
"node_modules/tsx/node_modules/@esbuild/win32-arm64": {
|
||||||
"version": "0.23.1",
|
"version": "0.25.1",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.1.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.1.tgz",
|
||||||
"integrity": "sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==",
|
"integrity": "sha512-GE7XvrdOzrb+yVKB9KsRMq+7a2U/K5Cf/8grVFRAGJmfADr/e/ODQ134RK2/eeHqYV5eQRFxb1hY7Nr15fv1NQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@ -12965,9 +12999,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/tsx/node_modules/@esbuild/win32-ia32": {
|
"node_modules/tsx/node_modules/@esbuild/win32-ia32": {
|
||||||
"version": "0.23.1",
|
"version": "0.25.1",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.1.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.1.tgz",
|
||||||
"integrity": "sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==",
|
"integrity": "sha512-uOxSJCIcavSiT6UnBhBzE8wy3n0hOkJsBOzy7HDAuTDE++1DJMRRVCPGisULScHL+a/ZwdXPpXD3IyFKjA7K8A==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ia32"
|
"ia32"
|
||||||
],
|
],
|
||||||
@ -12982,9 +13016,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/tsx/node_modules/@esbuild/win32-x64": {
|
"node_modules/tsx/node_modules/@esbuild/win32-x64": {
|
||||||
"version": "0.23.1",
|
"version": "0.25.1",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.1.tgz",
|
||||||
"integrity": "sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==",
|
"integrity": "sha512-Y1EQdcfwMSeQN/ujR5VayLOJ1BHaK+ssyk0AEzPjC+t1lITgsnccPqFjb6V+LsTp/9Iov4ysfjxLaGJ9RPtkVg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@ -12999,9 +13033,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/tsx/node_modules/esbuild": {
|
"node_modules/tsx/node_modules/esbuild": {
|
||||||
"version": "0.23.1",
|
"version": "0.25.1",
|
||||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz",
|
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.1.tgz",
|
||||||
"integrity": "sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==",
|
"integrity": "sha512-BGO5LtrGC7vxnqucAe/rmvKdJllfGaYWdyABvyMoXQlfYMb2bbRuReWR5tEGE//4LcNJj9XrkovTqNYRFZHAMQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@ -13012,30 +13046,31 @@
|
|||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@esbuild/aix-ppc64": "0.23.1",
|
"@esbuild/aix-ppc64": "0.25.1",
|
||||||
"@esbuild/android-arm": "0.23.1",
|
"@esbuild/android-arm": "0.25.1",
|
||||||
"@esbuild/android-arm64": "0.23.1",
|
"@esbuild/android-arm64": "0.25.1",
|
||||||
"@esbuild/android-x64": "0.23.1",
|
"@esbuild/android-x64": "0.25.1",
|
||||||
"@esbuild/darwin-arm64": "0.23.1",
|
"@esbuild/darwin-arm64": "0.25.1",
|
||||||
"@esbuild/darwin-x64": "0.23.1",
|
"@esbuild/darwin-x64": "0.25.1",
|
||||||
"@esbuild/freebsd-arm64": "0.23.1",
|
"@esbuild/freebsd-arm64": "0.25.1",
|
||||||
"@esbuild/freebsd-x64": "0.23.1",
|
"@esbuild/freebsd-x64": "0.25.1",
|
||||||
"@esbuild/linux-arm": "0.23.1",
|
"@esbuild/linux-arm": "0.25.1",
|
||||||
"@esbuild/linux-arm64": "0.23.1",
|
"@esbuild/linux-arm64": "0.25.1",
|
||||||
"@esbuild/linux-ia32": "0.23.1",
|
"@esbuild/linux-ia32": "0.25.1",
|
||||||
"@esbuild/linux-loong64": "0.23.1",
|
"@esbuild/linux-loong64": "0.25.1",
|
||||||
"@esbuild/linux-mips64el": "0.23.1",
|
"@esbuild/linux-mips64el": "0.25.1",
|
||||||
"@esbuild/linux-ppc64": "0.23.1",
|
"@esbuild/linux-ppc64": "0.25.1",
|
||||||
"@esbuild/linux-riscv64": "0.23.1",
|
"@esbuild/linux-riscv64": "0.25.1",
|
||||||
"@esbuild/linux-s390x": "0.23.1",
|
"@esbuild/linux-s390x": "0.25.1",
|
||||||
"@esbuild/linux-x64": "0.23.1",
|
"@esbuild/linux-x64": "0.25.1",
|
||||||
"@esbuild/netbsd-x64": "0.23.1",
|
"@esbuild/netbsd-arm64": "0.25.1",
|
||||||
"@esbuild/openbsd-arm64": "0.23.1",
|
"@esbuild/netbsd-x64": "0.25.1",
|
||||||
"@esbuild/openbsd-x64": "0.23.1",
|
"@esbuild/openbsd-arm64": "0.25.1",
|
||||||
"@esbuild/sunos-x64": "0.23.1",
|
"@esbuild/openbsd-x64": "0.25.1",
|
||||||
"@esbuild/win32-arm64": "0.23.1",
|
"@esbuild/sunos-x64": "0.25.1",
|
||||||
"@esbuild/win32-ia32": "0.23.1",
|
"@esbuild/win32-arm64": "0.25.1",
|
||||||
"@esbuild/win32-x64": "0.23.1"
|
"@esbuild/win32-ia32": "0.25.1",
|
||||||
|
"@esbuild/win32-x64": "0.25.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/tsyringe": {
|
"node_modules/tsyringe": {
|
||||||
@ -13552,9 +13587,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/vite": {
|
"node_modules/vite": {
|
||||||
"version": "5.4.11",
|
"version": "5.4.14",
|
||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.11.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.14.tgz",
|
||||||
"integrity": "sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q==",
|
"integrity": "sha512-EK5cY7Q1D8JNhSaPKVK4pwBFvaTmZxEnoKXLG/U9gmdDcihQGNzFlgIvaxezFR4glP1LsuiedwMBqCXH3wZccA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
@ -143,6 +143,7 @@ export const useGetProjectSecretsOverview = (
|
|||||||
search = "",
|
search = "",
|
||||||
includeSecrets,
|
includeSecrets,
|
||||||
includeFolders,
|
includeFolders,
|
||||||
|
includeImports,
|
||||||
includeDynamicSecrets,
|
includeDynamicSecrets,
|
||||||
environments
|
environments
|
||||||
}: TGetDashboardProjectSecretsOverviewDTO,
|
}: TGetDashboardProjectSecretsOverviewDTO,
|
||||||
@ -170,6 +171,7 @@ export const useGetProjectSecretsOverview = (
|
|||||||
projectId,
|
projectId,
|
||||||
includeSecrets,
|
includeSecrets,
|
||||||
includeFolders,
|
includeFolders,
|
||||||
|
includeImports,
|
||||||
includeDynamicSecrets,
|
includeDynamicSecrets,
|
||||||
environments
|
environments
|
||||||
}),
|
}),
|
||||||
@ -184,6 +186,7 @@ export const useGetProjectSecretsOverview = (
|
|||||||
projectId,
|
projectId,
|
||||||
includeSecrets,
|
includeSecrets,
|
||||||
includeFolders,
|
includeFolders,
|
||||||
|
includeImports,
|
||||||
includeDynamicSecrets,
|
includeDynamicSecrets,
|
||||||
environments
|
environments
|
||||||
}),
|
}),
|
||||||
@ -197,12 +200,15 @@ export const useGetProjectSecretsOverview = (
|
|||||||
? unique(select.dynamicSecrets, (i) => i.name)
|
? unique(select.dynamicSecrets, (i) => i.name)
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
|
const uniqueSecretImports = select.imports ? unique(select.imports, (i) => i.id) : [];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...select,
|
...select,
|
||||||
secrets: secrets ? mergePersonalSecrets(secrets) : undefined,
|
secrets: secrets ? mergePersonalSecrets(secrets) : undefined,
|
||||||
totalUniqueSecretsInPage: uniqueSecrets.length,
|
totalUniqueSecretsInPage: uniqueSecrets.length,
|
||||||
totalUniqueDynamicSecretsInPage: uniqueDynamicSecrets.length,
|
totalUniqueDynamicSecretsInPage: uniqueDynamicSecrets.length,
|
||||||
totalUniqueFoldersInPage: uniqueFolders.length
|
totalUniqueFoldersInPage: uniqueFolders.length,
|
||||||
|
totalUniqueSecretImportsInPage: uniqueSecretImports.length
|
||||||
};
|
};
|
||||||
}, []),
|
}, []),
|
||||||
placeholderData: (previousData) => previousData
|
placeholderData: (previousData) => previousData
|
||||||
|
@ -9,13 +9,16 @@ export type DashboardProjectSecretsOverviewResponse = {
|
|||||||
folders?: (TSecretFolder & { environment: string })[];
|
folders?: (TSecretFolder & { environment: string })[];
|
||||||
dynamicSecrets?: (TDynamicSecret & { environment: string })[];
|
dynamicSecrets?: (TDynamicSecret & { environment: string })[];
|
||||||
secrets?: SecretV3Raw[];
|
secrets?: SecretV3Raw[];
|
||||||
|
imports?: TSecretImport[];
|
||||||
totalSecretCount?: number;
|
totalSecretCount?: number;
|
||||||
totalFolderCount?: number;
|
totalFolderCount?: number;
|
||||||
totalDynamicSecretCount?: number;
|
totalDynamicSecretCount?: number;
|
||||||
|
totalImportCount?: number;
|
||||||
totalCount: number;
|
totalCount: number;
|
||||||
totalUniqueSecretsInPage: number;
|
totalUniqueSecretsInPage: number;
|
||||||
totalUniqueDynamicSecretsInPage: number;
|
totalUniqueDynamicSecretsInPage: number;
|
||||||
totalUniqueFoldersInPage: number;
|
totalUniqueFoldersInPage: number;
|
||||||
|
totalUniqueSecretImportsInPage: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type DashboardProjectSecretsDetailsResponse = {
|
export type DashboardProjectSecretsDetailsResponse = {
|
||||||
@ -63,6 +66,7 @@ export type TGetDashboardProjectSecretsOverviewDTO = {
|
|||||||
includeSecrets?: boolean;
|
includeSecrets?: boolean;
|
||||||
includeFolders?: boolean;
|
includeFolders?: boolean;
|
||||||
includeDynamicSecrets?: boolean;
|
includeDynamicSecrets?: boolean;
|
||||||
|
includeImports?: boolean;
|
||||||
environments: string[];
|
environments: string[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@ export type TSecretImport = {
|
|||||||
isReplicationSuccess?: boolean;
|
isReplicationSuccess?: boolean;
|
||||||
replicationStatus?: string;
|
replicationStatus?: string;
|
||||||
lastReplicated?: string;
|
lastReplicated?: string;
|
||||||
|
environment?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TGetImportedFoldersByEnvDTO = {
|
export type TGetImportedFoldersByEnvDTO = {
|
||||||
|
@ -22,14 +22,37 @@ import "./translation";
|
|||||||
// have a look at the Quick start guide
|
// have a look at the Quick start guide
|
||||||
// for passing in lng and translations on init/
|
// for passing in lng and translations on init/
|
||||||
|
|
||||||
// https://vite.dev/guide/build#load-error-handling
|
|
||||||
window.addEventListener("vite:preloadError", () => {
|
|
||||||
window.location.reload(); // for example, refresh the page
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create a new router instance
|
// Create a new router instance
|
||||||
NProgress.configure({ showSpinner: false });
|
NProgress.configure({ showSpinner: false });
|
||||||
|
|
||||||
|
window.addEventListener("vite:preloadError", async (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
// Get current count from session storage or initialize to 0
|
||||||
|
const reloadCount = parseInt(sessionStorage.getItem("vitePreloadErrorCount") || "0", 10);
|
||||||
|
|
||||||
|
// Check if we've already tried 3 times
|
||||||
|
if (reloadCount >= 2) {
|
||||||
|
console.warn("Vite preload has failed multiple times. Stopping automatic reload.");
|
||||||
|
// Optionally show a user-facing message here
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if ("caches" in window) {
|
||||||
|
const keys = await caches.keys();
|
||||||
|
await Promise.all(keys.map((key) => caches.delete(key)));
|
||||||
|
}
|
||||||
|
} catch (cleanupError) {
|
||||||
|
console.error(cleanupError);
|
||||||
|
}
|
||||||
|
//
|
||||||
|
// Increment and save the counter
|
||||||
|
sessionStorage.setItem("vitePreloadErrorCount", (reloadCount + 1).toString());
|
||||||
|
|
||||||
|
console.log(`Reloading page (attempt ${reloadCount + 1} of 2)...`);
|
||||||
|
window.location.reload(); // for example, refresh the page
|
||||||
|
});
|
||||||
|
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
routeTree,
|
routeTree,
|
||||||
context: { serverConfig: null, queryClient },
|
context: { serverConfig: null, queryClient },
|
||||||
|
@ -18,14 +18,6 @@ export const RequestNewInvitePage = () => {
|
|||||||
<span className="rounded-md bg-primary-500/40 px-1 text-black">Note:</span> If it still
|
<span className="rounded-md bg-primary-500/40 px-1 text-black">Note:</span> If it still
|
||||||
doesn't work, please reach out to us at support@infisical.com
|
doesn't work, please reach out to us at support@infisical.com
|
||||||
</p>
|
</p>
|
||||||
<div className="">
|
|
||||||
<img
|
|
||||||
src="/images/invitation-expired.svg"
|
|
||||||
height={500}
|
|
||||||
width={800}
|
|
||||||
alt="invitation expired illustration"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -223,15 +223,10 @@ export const SignupInvitePage = () => {
|
|||||||
// Step 4 of the sign up process (download the emergency kit pdf)
|
// Step 4 of the sign up process (download the emergency kit pdf)
|
||||||
const stepConfirmEmail = (
|
const stepConfirmEmail = (
|
||||||
<div className="h-7/12 mx-1 mb-36 flex w-full max-w-xs flex-col items-center rounded-xl border border-mineshaft-600 bg-mineshaft-800 px-4 py-8 drop-shadow-xl md:mb-16 md:max-w-lg md:px-6">
|
<div className="h-7/12 mx-1 mb-36 flex w-full max-w-xs flex-col items-center rounded-xl border border-mineshaft-600 bg-mineshaft-800 px-4 py-8 drop-shadow-xl md:mb-16 md:max-w-lg md:px-6">
|
||||||
<p className="mb-6 flex justify-center text-center text-4xl font-semibold text-primary-100">
|
<p className="mb-2 flex justify-center text-center text-4xl font-semibold text-primary-100">
|
||||||
Confirm your email
|
Confirm your email
|
||||||
</p>
|
</p>
|
||||||
<img
|
<div className="mx-auto mb-2 mt-4 flex max-h-24 max-w-md flex-col items-center justify-center px-4 text-lg md:p-2">
|
||||||
src="/images/dragon-signupinvite.svg"
|
|
||||||
style={{ height: "262px", width: "410px" }}
|
|
||||||
alt="verify email"
|
|
||||||
/>
|
|
||||||
<div className="mx-auto mb-2 mt-10 flex max-h-24 max-w-md flex-col items-center justify-center px-4 text-lg md:p-2">
|
|
||||||
<Button
|
<Button
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
try {
|
try {
|
||||||
|
@ -18,7 +18,7 @@ export const AuditLogsPage = () => {
|
|||||||
title="Audit logs"
|
title="Audit logs"
|
||||||
description="Audit logs for security and compliance teams to monitor information access."
|
description="Audit logs for security and compliance teams to monitor information access."
|
||||||
/>
|
/>
|
||||||
<LogsSection filterClassName="static py-2" showFilters isOrgAuditLogs showActorColumn />
|
<LogsSection filterClassName="static py-2" showFilters />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -10,7 +10,7 @@ import { ActorType, EventType, UserAgentType } from "@app/hooks/api/auditLogs/en
|
|||||||
import { usePopUp } from "@app/hooks/usePopUp";
|
import { usePopUp } from "@app/hooks/usePopUp";
|
||||||
|
|
||||||
import { LogsFilter } from "./LogsFilter";
|
import { LogsFilter } from "./LogsFilter";
|
||||||
import { LogsTable, TAuditLogTableHeader } from "./LogsTable";
|
import { LogsTable } from "./LogsTable";
|
||||||
import { AuditLogFilterFormData, auditLogFilterFormSchema } from "./types";
|
import { AuditLogFilterFormData, auditLogFilterFormSchema } from "./types";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@ -25,22 +25,11 @@ type Props = {
|
|||||||
|
|
||||||
showFilters?: boolean;
|
showFilters?: boolean;
|
||||||
filterClassName?: string;
|
filterClassName?: string;
|
||||||
isOrgAuditLogs?: boolean;
|
|
||||||
showActorColumn?: boolean;
|
|
||||||
remappedHeaders?: Partial<Record<TAuditLogTableHeader, string>>;
|
|
||||||
refetchInterval?: number;
|
refetchInterval?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const LogsSection = withPermission(
|
export const LogsSection = withPermission(
|
||||||
({
|
({ presets, filterClassName, refetchInterval, showFilters }: Props) => {
|
||||||
presets,
|
|
||||||
filterClassName,
|
|
||||||
remappedHeaders,
|
|
||||||
isOrgAuditLogs,
|
|
||||||
showActorColumn,
|
|
||||||
refetchInterval,
|
|
||||||
showFilters
|
|
||||||
}: Props) => {
|
|
||||||
const { subscription } = useSubscription();
|
const { subscription } = useSubscription();
|
||||||
|
|
||||||
const { popUp, handlePopUpOpen, handlePopUpToggle } = usePopUp(["upgradePlan"] as const);
|
const { popUp, handlePopUpOpen, handlePopUpToggle } = usePopUp(["upgradePlan"] as const);
|
||||||
@ -90,9 +79,6 @@ export const LogsSection = withPermission(
|
|||||||
)}
|
)}
|
||||||
<LogsTable
|
<LogsTable
|
||||||
refetchInterval={refetchInterval}
|
refetchInterval={refetchInterval}
|
||||||
remappedHeaders={remappedHeaders}
|
|
||||||
isOrgAuditLogs={isOrgAuditLogs}
|
|
||||||
showActorColumn={!!showActorColumn}
|
|
||||||
filter={{
|
filter={{
|
||||||
secretPath: debouncedSecretPath || undefined,
|
secretPath: debouncedSecretPath || undefined,
|
||||||
eventMetadata: presets?.eventMetadata,
|
eventMetadata: presets?.eventMetadata,
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { Fragment } from "react";
|
import { Fragment } from "react";
|
||||||
import { faFile } from "@fortawesome/free-solid-svg-icons";
|
import { faFile, faInfoCircle } from "@fortawesome/free-solid-svg-icons";
|
||||||
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
@ -11,6 +12,7 @@ import {
|
|||||||
Td,
|
Td,
|
||||||
Th,
|
Th,
|
||||||
THead,
|
THead,
|
||||||
|
Tooltip,
|
||||||
Tr
|
Tr
|
||||||
} from "@app/components/v2";
|
} from "@app/components/v2";
|
||||||
import { useGetAuditLogs } from "@app/hooks/api";
|
import { useGetAuditLogs } from "@app/hooks/api";
|
||||||
@ -19,32 +21,13 @@ import { TGetAuditLogsFilter } from "@app/hooks/api/auditLogs/types";
|
|||||||
import { LogsTableRow } from "./LogsTableRow";
|
import { LogsTableRow } from "./LogsTableRow";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
isOrgAuditLogs?: boolean;
|
|
||||||
showActorColumn: boolean;
|
|
||||||
filter?: TGetAuditLogsFilter;
|
filter?: TGetAuditLogsFilter;
|
||||||
remappedHeaders?: Partial<Record<TAuditLogTableHeader, string>>;
|
|
||||||
refetchInterval?: number;
|
refetchInterval?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
const AUDIT_LOG_LIMIT = 15;
|
const AUDIT_LOG_LIMIT = 15;
|
||||||
|
|
||||||
const TABLE_HEADERS = [
|
export const LogsTable = ({ filter, refetchInterval }: Props) => {
|
||||||
"Timestamp (MM/DD/YYYY)",
|
|
||||||
"Event",
|
|
||||||
"Project",
|
|
||||||
"Actor",
|
|
||||||
"Source",
|
|
||||||
"Metadata"
|
|
||||||
] as const;
|
|
||||||
export type TAuditLogTableHeader = (typeof TABLE_HEADERS)[number];
|
|
||||||
|
|
||||||
export const LogsTable = ({
|
|
||||||
showActorColumn,
|
|
||||||
isOrgAuditLogs,
|
|
||||||
filter,
|
|
||||||
remappedHeaders,
|
|
||||||
refetchInterval
|
|
||||||
}: Props) => {
|
|
||||||
// Determine the project ID for filtering
|
// Determine the project ID for filtering
|
||||||
const filterProjectId =
|
const filterProjectId =
|
||||||
// Use the projectId from the filter if it exists
|
// Use the projectId from the filter if it exists
|
||||||
@ -69,38 +52,37 @@ export const LogsTable = ({
|
|||||||
<Table>
|
<Table>
|
||||||
<THead>
|
<THead>
|
||||||
<Tr>
|
<Tr>
|
||||||
{TABLE_HEADERS.map((header, idx) => {
|
<Th className="w-24" />
|
||||||
if (
|
<Th className="w-64">
|
||||||
(header === "Project" && !isOrgAuditLogs) ||
|
Timestamp
|
||||||
(header === "Actor" && !showActorColumn)
|
<Tooltip
|
||||||
) {
|
className="normal-case"
|
||||||
return null;
|
content="Time displayed in your system's time zone."
|
||||||
}
|
sideOffset={10}
|
||||||
|
>
|
||||||
return (
|
<FontAwesomeIcon icon={faInfoCircle} className="ml-1" />
|
||||||
<Th key={`table-header-${idx + 1}`}>{remappedHeaders?.[header] || header}</Th>
|
</Tooltip>
|
||||||
);
|
</Th>
|
||||||
})}
|
<Th>Event</Th>
|
||||||
</Tr>
|
</Tr>
|
||||||
</THead>
|
</THead>
|
||||||
<TBody>
|
<TBody>
|
||||||
{!isPending &&
|
{!isPending &&
|
||||||
data?.pages?.map((group, i) => (
|
data?.pages?.map((group, i) => (
|
||||||
<Fragment key={`audit-log-fragment-${i + 1}`}>
|
<Fragment key={`audit-log-fragment-${i + 1}`}>
|
||||||
{group.map((auditLog) => (
|
{group.map((auditLog, index) => (
|
||||||
<LogsTableRow
|
<LogsTableRow
|
||||||
showActorColumn={showActorColumn}
|
rowNumber={index + i * AUDIT_LOG_LIMIT + 1}
|
||||||
isOrgAuditLogs={isOrgAuditLogs}
|
|
||||||
auditLog={auditLog}
|
auditLog={auditLog}
|
||||||
key={`audit-log-${auditLog.id}`}
|
key={`audit-log-${auditLog.id}`}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</Fragment>
|
</Fragment>
|
||||||
))}
|
))}
|
||||||
{isPending && <TableSkeleton innerKey="logs-table" columns={5} key="logs" />}
|
{isPending && <TableSkeleton innerKey="logs-table" columns={3} key="logs-loading" />}
|
||||||
{isEmpty && (
|
{isEmpty && (
|
||||||
<Tr>
|
<Tr>
|
||||||
<Td colSpan={5}>
|
<Td colSpan={3}>
|
||||||
<EmptyState title="No audit logs on file" icon={faFile} />
|
<EmptyState title="No audit logs on file" icon={faFile} />
|
||||||
</Td>
|
</Td>
|
||||||
</Tr>
|
</Tr>
|
||||||
|
@ -1,128 +1,76 @@
|
|||||||
import { faQuestionCircle } from "@fortawesome/free-solid-svg-icons";
|
import { faCaretDown, faCaretRight } from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
import { format } from "date-fns";
|
||||||
|
|
||||||
import { Td, Tooltip, Tr } from "@app/components/v2";
|
import { Td, Tr } from "@app/components/v2";
|
||||||
import { eventToNameMap, userAgentTTypeoNameMap } from "@app/hooks/api/auditLogs/constants";
|
import { useToggle } from "@app/hooks";
|
||||||
import { ActorType, EventType } from "@app/hooks/api/auditLogs/enums";
|
import { ActorType } from "@app/hooks/api/auditLogs/enums";
|
||||||
import { Actor, AuditLog } from "@app/hooks/api/auditLogs/types";
|
import { AuditLog } from "@app/hooks/api/auditLogs/types";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
auditLog: AuditLog;
|
auditLog: AuditLog;
|
||||||
isOrgAuditLogs?: boolean;
|
rowNumber: number;
|
||||||
showActorColumn: boolean;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const LogsTableRow = ({ auditLog, isOrgAuditLogs, showActorColumn }: Props) => {
|
type TagProps = {
|
||||||
const renderActor = (actor: Actor) => {
|
label: string;
|
||||||
if (!actor) {
|
value?: string;
|
||||||
return <Td />;
|
};
|
||||||
}
|
const Tag = ({ label, value }: TagProps) => {
|
||||||
|
if (!value) return null;
|
||||||
switch (actor.type) {
|
|
||||||
case ActorType.USER:
|
|
||||||
return (
|
|
||||||
<Td>
|
|
||||||
<p>{actor.metadata.email}</p>
|
|
||||||
<p>User</p>
|
|
||||||
</Td>
|
|
||||||
);
|
|
||||||
case ActorType.SERVICE:
|
|
||||||
return (
|
|
||||||
<Td>
|
|
||||||
<p>{`${actor.metadata.name}`}</p>
|
|
||||||
<p>Service token</p>
|
|
||||||
</Td>
|
|
||||||
);
|
|
||||||
case ActorType.IDENTITY:
|
|
||||||
return (
|
|
||||||
<Td>
|
|
||||||
<p>{`${actor.metadata.name}`}</p>
|
|
||||||
<p>Machine Identity</p>
|
|
||||||
</Td>
|
|
||||||
);
|
|
||||||
case ActorType.PLATFORM:
|
|
||||||
return (
|
|
||||||
<Td>
|
|
||||||
<p>Platform</p>
|
|
||||||
</Td>
|
|
||||||
);
|
|
||||||
case ActorType.KMIP_CLIENT:
|
|
||||||
return (
|
|
||||||
<Td>
|
|
||||||
<p>{actor.metadata.name}</p>
|
|
||||||
<p>KMIP Client</p>
|
|
||||||
</Td>
|
|
||||||
);
|
|
||||||
case ActorType.UNKNOWN_USER:
|
|
||||||
return (
|
|
||||||
<Td>
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<p>Unknown User</p>
|
|
||||||
<Tooltip content="This action was performed by a user who was not authenticated at the time.">
|
|
||||||
<FontAwesomeIcon className="text-mineshaft-400" icon={faQuestionCircle} />
|
|
||||||
</Tooltip>
|
|
||||||
</div>
|
|
||||||
</Td>
|
|
||||||
);
|
|
||||||
default:
|
|
||||||
return <Td />;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const formatDate = (dateToFormat: string) => {
|
|
||||||
const date = new Date(dateToFormat);
|
|
||||||
const year = date.getFullYear();
|
|
||||||
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
||||||
const day = String(date.getDate()).padStart(2, "0");
|
|
||||||
|
|
||||||
let hours = date.getHours();
|
|
||||||
const minutes = String(date.getMinutes()).padStart(2, "0");
|
|
||||||
|
|
||||||
// convert from 24h to 12h format
|
|
||||||
const period = hours >= 12 ? "PM" : "AM";
|
|
||||||
hours %= 12;
|
|
||||||
hours = hours || 12; // the hour '0' should be '12'
|
|
||||||
|
|
||||||
const formattedDate = `${month}-${day}-${year} at ${hours}:${minutes} ${period}`;
|
|
||||||
return formattedDate;
|
|
||||||
};
|
|
||||||
|
|
||||||
const renderSource = () => {
|
|
||||||
const { event, actor } = auditLog;
|
|
||||||
|
|
||||||
if (event.type === EventType.INTEGRATION_SYNCED) {
|
|
||||||
if (actor.type === ActorType.USER) {
|
|
||||||
return (
|
|
||||||
<Td>
|
|
||||||
<p>Manually triggered by {actor.metadata.email}</p>
|
|
||||||
</Td>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Platform / automatic syncs
|
|
||||||
return (
|
|
||||||
<Td>
|
|
||||||
<p>Automatically synced by Infisical</p>
|
|
||||||
</Td>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Td>
|
|
||||||
<p>{userAgentTTypeoNameMap[auditLog.userAgentType]}</p>
|
|
||||||
<p>{auditLog.ipAddress}</p>
|
|
||||||
</Td>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tr className={`log-${auditLog.id} h-10 border-x-0 border-b border-t-0`}>
|
<div className="flex items-center space-x-1.5">
|
||||||
<Td>{formatDate(auditLog.createdAt)}</Td>
|
<div className="rounded bg-mineshaft-600 p-0.5 pl-1 font-mono">{label}:</div>
|
||||||
<Td>{`${eventToNameMap[auditLog.event.type]}`}</Td>
|
<div>{value}</div>
|
||||||
{isOrgAuditLogs && <Td>{auditLog?.projectName ?? auditLog?.projectId ?? "N/A"}</Td>}
|
</div>
|
||||||
{showActorColumn && renderActor(auditLog.actor)}
|
);
|
||||||
{renderSource()}
|
};
|
||||||
<Td className="max-w-xs break-all">{JSON.stringify(auditLog.event.metadata || {})}</Td>
|
|
||||||
</Tr>
|
export const LogsTableRow = ({ auditLog, rowNumber }: Props) => {
|
||||||
|
const [isOpen, setIsOpen] = useToggle();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Tr
|
||||||
|
className="h-10 cursor-pointer border-x-0 border-b border-t-0 hover:bg-mineshaft-700"
|
||||||
|
role="button"
|
||||||
|
tabIndex={0}
|
||||||
|
onClick={() => setIsOpen.toggle()}
|
||||||
|
onKeyDown={(evt) => {
|
||||||
|
if (evt.key === "Enter") setIsOpen.toggle();
|
||||||
|
}}
|
||||||
|
isHoverable
|
||||||
|
>
|
||||||
|
<Td className="flex items-center gap-2 pr-0 align-top">
|
||||||
|
<FontAwesomeIcon icon={isOpen ? faCaretDown : faCaretRight} />
|
||||||
|
{rowNumber}
|
||||||
|
</Td>
|
||||||
|
<Td className="align-top">
|
||||||
|
{format(new Date(auditLog.createdAt), "MMM do yyyy, hh:mm a")}
|
||||||
|
</Td>
|
||||||
|
<Td>
|
||||||
|
<div className="flex flex-wrap gap-4 text-sm">
|
||||||
|
<Tag label="event" value={auditLog.event.type} />
|
||||||
|
<Tag label="actor" value={auditLog.actor.type} />
|
||||||
|
{auditLog.actor.type === ActorType.USER && (
|
||||||
|
<Tag label="user_email" value={auditLog.actor.metadata.email} />
|
||||||
|
)}
|
||||||
|
{auditLog.actor.type === ActorType.IDENTITY && (
|
||||||
|
<Tag label="identity_name" value={auditLog.actor.metadata.name} />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</Td>
|
||||||
|
</Tr>
|
||||||
|
{isOpen && (
|
||||||
|
<Tr className={`log-${auditLog.id} h-10 border-x-0 border-b border-t-0`}>
|
||||||
|
<Td colSpan={3} className="px-3 py-2">
|
||||||
|
<div className="thin-scrollbar my-1 max-h-96 overflow-auto whitespace-pre-wrap rounded border border-mineshaft-600 bg-bunker-800 p-2 font-mono leading-6">
|
||||||
|
{JSON.stringify(auditLog, null, 4)}
|
||||||
|
</div>
|
||||||
|
</Td>
|
||||||
|
</Tr>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -45,7 +45,6 @@ export const UserAuditLogsSection = withPermission(
|
|||||||
presets={{
|
presets={{
|
||||||
actorId: orgMembership.user.id
|
actorId: orgMembership.user.id
|
||||||
}}
|
}}
|
||||||
isOrgAuditLogs
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
@ -29,9 +29,6 @@ export const IntegrationAuditLogsSection = ({ integration }: Props) => {
|
|||||||
</div>
|
</div>
|
||||||
<LogsSection
|
<LogsSection
|
||||||
refetchInterval={4000}
|
refetchInterval={4000}
|
||||||
remappedHeaders={{
|
|
||||||
Metadata: "Sync Status"
|
|
||||||
}}
|
|
||||||
showFilters={false}
|
showFilters={false}
|
||||||
presets={{
|
presets={{
|
||||||
eventMetadata: { integrationId: integration.id },
|
eventMetadata: { integrationId: integration.id },
|
||||||
|
@ -192,6 +192,15 @@ export const IntegrationConnectionSection = ({ integration }: Props) => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (integration.integration === "windmill" && integration.url) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<FormLabel className="text-sm font-semibold text-mineshaft-300" label="Instance URL" />
|
||||||
|
<div className="text-sm text-mineshaft-300">{integration.url}</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ import {
|
|||||||
faAngleDown,
|
faAngleDown,
|
||||||
faArrowDown,
|
faArrowDown,
|
||||||
faArrowUp,
|
faArrowUp,
|
||||||
|
faFileImport,
|
||||||
faFingerprint,
|
faFingerprint,
|
||||||
faFolder,
|
faFolder,
|
||||||
faFolderBlank,
|
faFolderBlank,
|
||||||
@ -80,6 +81,7 @@ import { CreateSecretForm } from "./components/CreateSecretForm";
|
|||||||
import { FolderBreadCrumbs } from "./components/FolderBreadCrumbs";
|
import { FolderBreadCrumbs } from "./components/FolderBreadCrumbs";
|
||||||
import { SecretOverviewDynamicSecretRow } from "./components/SecretOverviewDynamicSecretRow";
|
import { SecretOverviewDynamicSecretRow } from "./components/SecretOverviewDynamicSecretRow";
|
||||||
import { SecretOverviewFolderRow } from "./components/SecretOverviewFolderRow";
|
import { SecretOverviewFolderRow } from "./components/SecretOverviewFolderRow";
|
||||||
|
import { SecretOverviewImportListView } from "./components/SecretOverviewImportListView";
|
||||||
import {
|
import {
|
||||||
SecretNoAccessOverviewTableRow,
|
SecretNoAccessOverviewTableRow,
|
||||||
SecretOverviewTableRow
|
SecretOverviewTableRow
|
||||||
@ -97,7 +99,8 @@ export enum EntryType {
|
|||||||
enum RowType {
|
enum RowType {
|
||||||
Folder = "folder",
|
Folder = "folder",
|
||||||
DynamicSecret = "dynamic",
|
DynamicSecret = "dynamic",
|
||||||
Secret = "secret"
|
Secret = "secret",
|
||||||
|
Import = "import"
|
||||||
}
|
}
|
||||||
|
|
||||||
type Filter = {
|
type Filter = {
|
||||||
@ -107,7 +110,8 @@ type Filter = {
|
|||||||
const DEFAULT_FILTER_STATE = {
|
const DEFAULT_FILTER_STATE = {
|
||||||
[RowType.Folder]: true,
|
[RowType.Folder]: true,
|
||||||
[RowType.DynamicSecret]: true,
|
[RowType.DynamicSecret]: true,
|
||||||
[RowType.Secret]: true
|
[RowType.Secret]: true,
|
||||||
|
[RowType.Import]: true
|
||||||
};
|
};
|
||||||
|
|
||||||
export const OverviewPage = () => {
|
export const OverviewPage = () => {
|
||||||
@ -216,6 +220,7 @@ export const OverviewPage = () => {
|
|||||||
includeFolders: filter.folder,
|
includeFolders: filter.folder,
|
||||||
includeDynamicSecrets: filter.dynamic,
|
includeDynamicSecrets: filter.dynamic,
|
||||||
includeSecrets: filter.secret,
|
includeSecrets: filter.secret,
|
||||||
|
includeImports: filter.import,
|
||||||
search: debouncedSearchFilter,
|
search: debouncedSearchFilter,
|
||||||
limit,
|
limit,
|
||||||
offset
|
offset
|
||||||
@ -227,15 +232,29 @@ export const OverviewPage = () => {
|
|||||||
secrets,
|
secrets,
|
||||||
folders,
|
folders,
|
||||||
dynamicSecrets,
|
dynamicSecrets,
|
||||||
|
imports,
|
||||||
totalFolderCount,
|
totalFolderCount,
|
||||||
totalSecretCount,
|
totalSecretCount,
|
||||||
totalDynamicSecretCount,
|
totalDynamicSecretCount,
|
||||||
|
totalImportCount,
|
||||||
totalCount = 0,
|
totalCount = 0,
|
||||||
totalUniqueFoldersInPage,
|
totalUniqueFoldersInPage,
|
||||||
totalUniqueSecretsInPage,
|
totalUniqueSecretsInPage,
|
||||||
|
totalUniqueSecretImportsInPage,
|
||||||
totalUniqueDynamicSecretsInPage
|
totalUniqueDynamicSecretsInPage
|
||||||
} = overview ?? {};
|
} = overview ?? {};
|
||||||
|
|
||||||
|
const importsShaped = imports
|
||||||
|
?.filter((el) => !el.isReserved)
|
||||||
|
?.map(({ importPath, importEnv }) => ({ importPath, importEnv }))
|
||||||
|
.filter(
|
||||||
|
(el, index, self) =>
|
||||||
|
index ===
|
||||||
|
self.findIndex(
|
||||||
|
(item) => item.importPath === el.importPath && item.importEnv.slug === el.importEnv.slug
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
useResetPageHelper({
|
useResetPageHelper({
|
||||||
totalCount,
|
totalCount,
|
||||||
offset,
|
offset,
|
||||||
@ -678,7 +697,6 @@ export const OverviewPage = () => {
|
|||||||
<SecretV2MigrationSection />
|
<SecretV2MigrationSection />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="">
|
<div className="">
|
||||||
<Helmet>
|
<Helmet>
|
||||||
@ -767,6 +785,19 @@ export const OverviewPage = () => {
|
|||||||
</Button>
|
</Button>
|
||||||
</DropdownMenuItem> */}
|
</DropdownMenuItem> */}
|
||||||
<DropdownMenuLabel>Filter project resources</DropdownMenuLabel>
|
<DropdownMenuLabel>Filter project resources</DropdownMenuLabel>
|
||||||
|
<DropdownMenuItem
|
||||||
|
onClick={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
handleToggleRowType(RowType.Import);
|
||||||
|
}}
|
||||||
|
icon={filter[RowType.Import] && <FontAwesomeIcon icon={faCheckCircle} />}
|
||||||
|
iconPos="right"
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<FontAwesomeIcon icon={faFileImport} className="text-green-700" />
|
||||||
|
<span>Imports</span>
|
||||||
|
</div>
|
||||||
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@ -1093,6 +1124,17 @@ export const OverviewPage = () => {
|
|||||||
key={`overview-${dynamicSecretName}-${index + 1}`}
|
key={`overview-${dynamicSecretName}-${index + 1}`}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
{filter.import &&
|
||||||
|
importsShaped &&
|
||||||
|
importsShaped?.length > 0 &&
|
||||||
|
importsShaped?.map((item, index) => (
|
||||||
|
<SecretOverviewImportListView
|
||||||
|
secretImport={item}
|
||||||
|
environments={visibleEnvs}
|
||||||
|
key={`overview-secret-input-${index + 1}`}
|
||||||
|
allSecretImports={imports}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
{secKeys.map((key, index) => (
|
{secKeys.map((key, index) => (
|
||||||
<SecretOverviewTableRow
|
<SecretOverviewTableRow
|
||||||
isSelected={Boolean(selectedEntries.secret[key])}
|
isSelected={Boolean(selectedEntries.secret[key])}
|
||||||
@ -1116,7 +1158,8 @@ export const OverviewPage = () => {
|
|||||||
(page * perPage > totalCount ? totalCount % perPage : perPage) -
|
(page * perPage > totalCount ? totalCount % perPage : perPage) -
|
||||||
(totalUniqueFoldersInPage || 0) -
|
(totalUniqueFoldersInPage || 0) -
|
||||||
(totalUniqueDynamicSecretsInPage || 0) -
|
(totalUniqueDynamicSecretsInPage || 0) -
|
||||||
(totalUniqueSecretsInPage || 0),
|
(totalUniqueSecretsInPage || 0) -
|
||||||
|
(totalUniqueSecretImportsInPage || 0),
|
||||||
0
|
0
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
@ -1156,6 +1199,7 @@ export const OverviewPage = () => {
|
|||||||
dynamicSecretCount={totalDynamicSecretCount}
|
dynamicSecretCount={totalDynamicSecretCount}
|
||||||
secretCount={totalSecretCount}
|
secretCount={totalSecretCount}
|
||||||
folderCount={totalFolderCount}
|
folderCount={totalFolderCount}
|
||||||
|
importCount={totalImportCount}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
className="rounded-b-md border-t border-solid border-t-mineshaft-600"
|
className="rounded-b-md border-t border-solid border-t-mineshaft-600"
|
||||||
|
@ -0,0 +1,85 @@
|
|||||||
|
import { faCheck, faFileImport, faXmark } from "@fortawesome/free-solid-svg-icons";
|
||||||
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
import { twMerge } from "tailwind-merge";
|
||||||
|
|
||||||
|
import { Td, Tr } from "@app/components/v2";
|
||||||
|
import { TSecretImport, WorkspaceEnv } from "@app/hooks/api/types";
|
||||||
|
import { EnvFolderIcon } from "@app/pages/secret-manager/SecretDashboardPage/components/SecretImportListView/SecretImportItem";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
secretImport: { importPath: string; importEnv: WorkspaceEnv };
|
||||||
|
environments: { name: string; slug: string }[];
|
||||||
|
allSecretImports?: TSecretImport[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const SecretOverviewImportListView = ({
|
||||||
|
secretImport,
|
||||||
|
environments = [],
|
||||||
|
allSecretImports = []
|
||||||
|
}: Props) => {
|
||||||
|
const isSecretPresentInEnv = (envSlug: string) => {
|
||||||
|
return allSecretImports.some((item) => {
|
||||||
|
if (item.isReplication) {
|
||||||
|
if (
|
||||||
|
item.importPath === secretImport.importPath &&
|
||||||
|
item.importEnv.slug === secretImport.importEnv.slug
|
||||||
|
) {
|
||||||
|
const reservedItem = allSecretImports.find((element) =>
|
||||||
|
element.importPath.includes(`__reserve_replication_${item.id}`)
|
||||||
|
);
|
||||||
|
// If the reserved item exists, check if the envSlug matches
|
||||||
|
if (reservedItem) {
|
||||||
|
return reservedItem.environment === envSlug;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// If the item is not replication, check if the envSlug matches directly
|
||||||
|
return (
|
||||||
|
item.environment === envSlug &&
|
||||||
|
item.importPath === secretImport.importPath &&
|
||||||
|
item.importEnv.slug === secretImport.importEnv.slug
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tr className="group">
|
||||||
|
<Td className="sticky left-0 z-10 border-r border-mineshaft-600 bg-mineshaft-800 bg-clip-padding px-0 py-0 group-hover:bg-mineshaft-700">
|
||||||
|
<div className="group flex cursor-pointer">
|
||||||
|
<div className="flex w-11 items-center py-2 pl-5 text-green-700">
|
||||||
|
<FontAwesomeIcon icon={faFileImport} />
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-grow items-center py-2 pl-4 pr-2">
|
||||||
|
<EnvFolderIcon
|
||||||
|
env={secretImport.importEnv.slug || ""}
|
||||||
|
secretPath={secretImport.importPath || ""}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Td>
|
||||||
|
{environments.map(({ slug }, i) => {
|
||||||
|
const isPresent = isSecretPresentInEnv(slug);
|
||||||
|
return (
|
||||||
|
<Td
|
||||||
|
key={`sec-overview-${slug}-${i + 1}-value`}
|
||||||
|
className={twMerge(
|
||||||
|
"px-0 py-0 group-hover:bg-mineshaft-700",
|
||||||
|
isPresent ? "text-green-600" : "text-red-600"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div className="h-full w-full border-r border-mineshaft-600 px-5 py-[0.85rem]">
|
||||||
|
<div className="flex justify-center">
|
||||||
|
<FontAwesomeIcon
|
||||||
|
// eslint-disable-next-line no-nested-ternary
|
||||||
|
icon={isSecretPresentInEnv(slug) ? faCheck : faXmark}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Td>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Tr>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1 @@
|
|||||||
|
export { SecretOverviewImportListView } from "./SecretOverviewImportListView";
|
@ -35,9 +35,6 @@ export const SecretSyncAuditLogsSection = ({ secretSync }: Props) => {
|
|||||||
{subscription.auditLogs ? (
|
{subscription.auditLogs ? (
|
||||||
<LogsSection
|
<LogsSection
|
||||||
refetchInterval={4000}
|
refetchInterval={4000}
|
||||||
remappedHeaders={{
|
|
||||||
Metadata: "Sync Status"
|
|
||||||
}}
|
|
||||||
showFilters={false}
|
showFilters={false}
|
||||||
presets={{
|
presets={{
|
||||||
eventMetadata: { syncId: secretSync.id },
|
eventMetadata: { syncId: secretSync.id },
|
||||||
|
@ -3,31 +3,75 @@ import { useNavigate } from "@tanstack/react-router";
|
|||||||
|
|
||||||
import { Button, Card, CardTitle, FormControl, Input } from "@app/components/v2";
|
import { Button, Card, CardTitle, FormControl, Input } from "@app/components/v2";
|
||||||
import { useWorkspace } from "@app/context";
|
import { useWorkspace } from "@app/context";
|
||||||
|
import { isInfisicalCloud } from "@app/helpers/platform";
|
||||||
import { useSaveIntegrationAccessToken } from "@app/hooks/api";
|
import { useSaveIntegrationAccessToken } from "@app/hooks/api";
|
||||||
|
|
||||||
export const WindmillAuthorizePage = () => {
|
export const WindmillAuthorizePage = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { mutateAsync } = useSaveIntegrationAccessToken();
|
const { mutateAsync } = useSaveIntegrationAccessToken();
|
||||||
|
|
||||||
const { currentWorkspace } = useWorkspace();
|
const { currentWorkspace } = useWorkspace();
|
||||||
const [apiKey, setApiKey] = useState("");
|
const [apiKey, setApiKey] = useState("");
|
||||||
const [apiKeyErrorText, setApiKeyErrorText] = useState("");
|
const [apiKeyErrorText, setApiKeyErrorText] = useState("");
|
||||||
|
const [apiUrl, setApiUrl] = useState<string | null>(null);
|
||||||
|
const [apiUrlErrorText, setApiUrlErrorText] = useState("");
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
|
const isLocalOrPrivateIpAddress = (url: string): boolean => {
|
||||||
|
try {
|
||||||
|
const validUrl = new URL(url);
|
||||||
|
// Check for localhost
|
||||||
|
if (validUrl.hostname === "localhost" || validUrl.hostname === "127.0.0.1") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for 10.x.x.x
|
||||||
|
if (validUrl.hostname.match(/^10\.\d+\.\d+\.\d+/)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for host.docker.internal
|
||||||
|
if (validUrl.hostname === "host.docker.internal") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for 192.168.x.x
|
||||||
|
if (validUrl.hostname.match(/^192\.168\.\d+\.\d+/)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleButtonClick = async () => {
|
const handleButtonClick = async () => {
|
||||||
try {
|
try {
|
||||||
setApiKeyErrorText("");
|
setApiKeyErrorText("");
|
||||||
|
setApiUrlErrorText("");
|
||||||
if (apiKey.length === 0) {
|
if (apiKey.length === 0) {
|
||||||
setApiKeyErrorText("API Key cannot be blank");
|
setApiKeyErrorText("API Key cannot be blank");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (apiUrl) {
|
||||||
|
if (!apiUrl.startsWith("http://") && !apiUrl.startsWith("https://")) {
|
||||||
|
setApiUrlErrorText("API URL must start with http:// or https://");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (isInfisicalCloud() && isLocalOrPrivateIpAddress(apiUrl)) {
|
||||||
|
setApiUrlErrorText("Local IPs not allowed as URL");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
|
|
||||||
const integrationAuth = await mutateAsync({
|
const integrationAuth = await mutateAsync({
|
||||||
workspaceId: currentWorkspace.id,
|
workspaceId: currentWorkspace.id,
|
||||||
integration: "windmill",
|
integration: "windmill",
|
||||||
accessToken: apiKey
|
accessToken: apiKey,
|
||||||
|
url: apiUrl ?? undefined
|
||||||
});
|
});
|
||||||
|
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
@ -57,6 +101,18 @@ export const WindmillAuthorizePage = () => {
|
|||||||
>
|
>
|
||||||
<Input placeholder="" value={apiKey} onChange={(e) => setApiKey(e.target.value)} />
|
<Input placeholder="" value={apiKey} onChange={(e) => setApiKey(e.target.value)} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
<FormControl
|
||||||
|
label="Windmill Instance URL"
|
||||||
|
errorText={apiUrlErrorText}
|
||||||
|
isError={apiUrlErrorText !== ""}
|
||||||
|
tooltipText="If you are using a custom domain, enter it here. Otherwise, leave it blank."
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
value={apiUrl ?? ""}
|
||||||
|
onChange={(e) => setApiUrl(e.target.value.trim() === "" ? null : e.target.value.trim())}
|
||||||
|
placeholder="https://xxxx.windmill.dev"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
<Button
|
<Button
|
||||||
onClick={handleButtonClick}
|
onClick={handleButtonClick}
|
||||||
color="mineshaft"
|
color="mineshaft"
|
||||||
|
@ -67,7 +67,8 @@ export const WindmillConfigurePage = () => {
|
|||||||
(integrationAuthApp) => integrationAuthApp.name === targetApp
|
(integrationAuthApp) => integrationAuthApp.name === targetApp
|
||||||
)?.appId,
|
)?.appId,
|
||||||
sourceEnvironment: selectedSourceEnvironment,
|
sourceEnvironment: selectedSourceEnvironment,
|
||||||
secretPath
|
secretPath,
|
||||||
|
url: integrationAuth.url ?? undefined
|
||||||
});
|
});
|
||||||
|
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
|
Reference in New Issue
Block a user