mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-06 09:26:47 +00:00
Add add/remove/list hosts in ssh host groups functionality
This commit is contained in:
@@ -1,7 +1,9 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { sanitizedSshHost, loginMappingSchema } from "@app/ee/services/ssh-host/ssh-host-schema";
|
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||||
|
import { loginMappingSchema, sanitizedSshHost } from "@app/ee/services/ssh-host/ssh-host-schema";
|
||||||
import { sanitizedSshHostGroup } from "@app/ee/services/ssh-host-group/ssh-host-group-schema";
|
import { sanitizedSshHostGroup } from "@app/ee/services/ssh-host-group/ssh-host-group-schema";
|
||||||
|
import { EHostGroupMembershipFilter } from "@app/ee/services/ssh-host-group/ssh-host-group-types";
|
||||||
import { SSH_HOST_GROUPS } from "@app/lib/api-docs";
|
import { SSH_HOST_GROUPS } from "@app/lib/api-docs";
|
||||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
import { slugSchema } from "@app/server/lib/schemas";
|
import { slugSchema } from "@app/server/lib/schemas";
|
||||||
@@ -35,17 +37,17 @@ export const registerSshHostGroupRouter = async (server: FastifyZodProvider) =>
|
|||||||
actorOrgId: req.permission.orgId
|
actorOrgId: req.permission.orgId
|
||||||
});
|
});
|
||||||
|
|
||||||
// await server.services.auditLog.createAuditLog({
|
await server.services.auditLog.createAuditLog({
|
||||||
// ...req.auditLogInfo,
|
...req.auditLogInfo,
|
||||||
// projectId: host.projectId,
|
projectId: sshHostGroup.projectId,
|
||||||
// event: {
|
event: {
|
||||||
// type: EventType.GET_SSH_HOST,
|
type: EventType.GET_SSH_HOST_GROUP,
|
||||||
// metadata: {
|
metadata: {
|
||||||
// sshHostId: host.id,
|
sshHostGroupId: sshHostGroup.id,
|
||||||
// hostname: host.hostname
|
name: sshHostGroup.name
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// });
|
});
|
||||||
|
|
||||||
return sshHostGroup;
|
return sshHostGroup;
|
||||||
}
|
}
|
||||||
@@ -80,24 +82,18 @@ export const registerSshHostGroupRouter = async (server: FastifyZodProvider) =>
|
|||||||
actorOrgId: req.permission.orgId
|
actorOrgId: req.permission.orgId
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO: audit logs
|
await server.services.auditLog.createAuditLog({
|
||||||
// await server.services.auditLog.createAuditLog({
|
...req.auditLogInfo,
|
||||||
// ...req.auditLogInfo,
|
projectId: sshHostGroup.projectId,
|
||||||
// projectId: host.projectId,
|
event: {
|
||||||
// event: {
|
type: EventType.CREATE_SSH_HOST_GROUP,
|
||||||
// type: EventType.CREATE_SSH_HOST,
|
metadata: {
|
||||||
// metadata: {
|
sshHostGroupId: sshHostGroup.id,
|
||||||
// sshHostId: host.id,
|
name: sshHostGroup.name,
|
||||||
// hostname: host.hostname,
|
loginMappings: sshHostGroup.loginMappings
|
||||||
// alias: host.alias ?? null,
|
}
|
||||||
// userCertTtl: host.userCertTtl,
|
}
|
||||||
// hostCertTtl: host.hostCertTtl,
|
});
|
||||||
// loginMappings: host.loginMappings,
|
|
||||||
// userSshCaId: host.userSshCaId,
|
|
||||||
// hostSshCaId: host.hostSshCaId
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
|
|
||||||
return sshHostGroup;
|
return sshHostGroup;
|
||||||
}
|
}
|
||||||
@@ -135,23 +131,18 @@ export const registerSshHostGroupRouter = async (server: FastifyZodProvider) =>
|
|||||||
actorOrgId: req.permission.orgId
|
actorOrgId: req.permission.orgId
|
||||||
});
|
});
|
||||||
|
|
||||||
// await server.services.auditLog.createAuditLog({
|
await server.services.auditLog.createAuditLog({
|
||||||
// ...req.auditLogInfo,
|
...req.auditLogInfo,
|
||||||
// projectId: host.projectId,
|
projectId: sshHostGroup.projectId,
|
||||||
// event: {
|
event: {
|
||||||
// type: EventType.UPDATE_SSH_HOST,
|
type: EventType.UPDATE_SSH_HOST_GROUP,
|
||||||
// metadata: {
|
metadata: {
|
||||||
// sshHostId: host.id,
|
sshHostGroupId: sshHostGroup.id,
|
||||||
// hostname: host.hostname,
|
name: sshHostGroup.name,
|
||||||
// alias: host.alias,
|
loginMappings: sshHostGroup.loginMappings
|
||||||
// userCertTtl: host.userCertTtl,
|
}
|
||||||
// hostCertTtl: host.hostCertTtl,
|
}
|
||||||
// loginMappings: host.loginMappings,
|
});
|
||||||
// userSshCaId: host.userSshCaId,
|
|
||||||
// hostSshCaId: host.hostSshCaId
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
|
|
||||||
return sshHostGroup;
|
return sshHostGroup;
|
||||||
}
|
}
|
||||||
@@ -183,18 +174,17 @@ export const registerSshHostGroupRouter = async (server: FastifyZodProvider) =>
|
|||||||
actorOrgId: req.permission.orgId
|
actorOrgId: req.permission.orgId
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO: audit log
|
await server.services.auditLog.createAuditLog({
|
||||||
// await server.services.auditLog.createAuditLog({
|
...req.auditLogInfo,
|
||||||
// ...req.auditLogInfo,
|
projectId: sshHostGroup.projectId,
|
||||||
// projectId: host.projectId,
|
event: {
|
||||||
// event: {
|
type: EventType.DELETE_SSH_HOST_GROUP,
|
||||||
// type: EventType.DELETE_SSH_HOST,
|
metadata: {
|
||||||
// metadata: {
|
sshHostGroupId: sshHostGroup.id,
|
||||||
// sshHostId: host.id,
|
name: sshHostGroup.name
|
||||||
// hostname: host.hostname
|
}
|
||||||
// }
|
}
|
||||||
// }
|
});
|
||||||
// });
|
|
||||||
|
|
||||||
return sshHostGroup;
|
return sshHostGroup;
|
||||||
}
|
}
|
||||||
@@ -211,20 +201,30 @@ export const registerSshHostGroupRouter = async (server: FastifyZodProvider) =>
|
|||||||
sshHostGroupId: z.string().describe(SSH_HOST_GROUPS.GET.sshHostGroupId)
|
sshHostGroupId: z.string().describe(SSH_HOST_GROUPS.GET.sshHostGroupId)
|
||||||
}),
|
}),
|
||||||
querystring: z.object({
|
querystring: z.object({
|
||||||
offset: z.coerce.number().min(0).max(100).default(0).describe(SSH_HOST_GROUPS.LIST_HOSTS.offset),
|
filter: z.nativeEnum(EHostGroupMembershipFilter).optional().describe(SSH_HOST_GROUPS.GET.filter)
|
||||||
limit: z.coerce.number().min(1).max(100).default(10).describe(SSH_HOST_GROUPS.LIST_HOSTS.limit)
|
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
hosts: z.array(sanitizedSshHost),
|
hosts: sanitizedSshHost
|
||||||
|
.pick({
|
||||||
|
id: true,
|
||||||
|
hostname: true,
|
||||||
|
alias: true
|
||||||
|
})
|
||||||
|
.merge(
|
||||||
|
z.object({
|
||||||
|
isPartOfGroup: z.boolean(),
|
||||||
|
joinedGroupAt: z.date().nullable()
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.array(),
|
||||||
totalCount: z.number()
|
totalCount: z.number()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
console.log("list hosts in group pre");
|
const { sshHostGroup, hosts, totalCount } = await server.services.sshHostGroup.listSshHostGroupHosts({
|
||||||
const { hosts, totalCount } = await server.services.sshHostGroup.listSshHostGroupHosts({
|
|
||||||
sshHostGroupId: req.params.sshHostGroupId,
|
sshHostGroupId: req.params.sshHostGroupId,
|
||||||
actor: req.permission.type,
|
actor: req.permission.type,
|
||||||
actorId: req.permission.id,
|
actorId: req.permission.id,
|
||||||
@@ -232,20 +232,17 @@ export const registerSshHostGroupRouter = async (server: FastifyZodProvider) =>
|
|||||||
actorOrgId: req.permission.orgId,
|
actorOrgId: req.permission.orgId,
|
||||||
...req.query
|
...req.query
|
||||||
});
|
});
|
||||||
console.log("list hosts in group post");
|
|
||||||
|
|
||||||
// TODO: audit logs
|
await server.services.auditLog.createAuditLog({
|
||||||
// await server.services.auditLog.createAuditLog({
|
...req.auditLogInfo,
|
||||||
// ...req.auditLogInfo,
|
projectId: sshHostGroup.projectId,
|
||||||
// projectId: host.projectId,
|
event: {
|
||||||
// event: {
|
type: EventType.GET_SSH_HOST_GROUP_HOSTS,
|
||||||
// type: EventType.GET_SSH_HOST,
|
metadata: {
|
||||||
// metadata: {
|
sshHostGroupId: req.params.sshHostGroupId
|
||||||
// sshHostId: host.id,
|
}
|
||||||
// hostname: host.hostname
|
}
|
||||||
// }
|
});
|
||||||
// }
|
|
||||||
// });
|
|
||||||
|
|
||||||
return { hosts, totalCount };
|
return { hosts, totalCount };
|
||||||
}
|
}
|
||||||
@@ -270,9 +267,7 @@ export const registerSshHostGroupRouter = async (server: FastifyZodProvider) =>
|
|||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
console.log("add host to group pre");
|
const { sshHostGroup, sshHost } = await server.services.sshHostGroup.addHostToSshHostGroup({
|
||||||
|
|
||||||
const host = await server.services.sshHostGroup.addHostToSshHostGroup({
|
|
||||||
sshHostGroupId: req.params.sshHostGroupId,
|
sshHostGroupId: req.params.sshHostGroupId,
|
||||||
hostId: req.params.hostId,
|
hostId: req.params.hostId,
|
||||||
actor: req.permission.type,
|
actor: req.permission.type,
|
||||||
@@ -281,22 +276,20 @@ export const registerSshHostGroupRouter = async (server: FastifyZodProvider) =>
|
|||||||
actorOrgId: req.permission.orgId
|
actorOrgId: req.permission.orgId
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log("add host to group post");
|
await server.services.auditLog.createAuditLog({
|
||||||
|
...req.auditLogInfo,
|
||||||
|
projectId: sshHost.projectId,
|
||||||
|
event: {
|
||||||
|
type: EventType.ADD_HOST_TO_SSH_HOST_GROUP,
|
||||||
|
metadata: {
|
||||||
|
sshHostGroupId: sshHostGroup.id,
|
||||||
|
sshHostId: sshHost.id,
|
||||||
|
hostname: sshHost.hostname
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// TODO: audit logs
|
return sshHost;
|
||||||
// await server.services.auditLog.createAuditLog({
|
|
||||||
// ...req.auditLogInfo,
|
|
||||||
// projectId: host.projectId,
|
|
||||||
// event: {
|
|
||||||
// type: EventType.GET_SSH_HOST,
|
|
||||||
// metadata: {
|
|
||||||
// sshHostId: host.id,
|
|
||||||
// hostname: host.hostname
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
|
|
||||||
return host;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -319,9 +312,7 @@ export const registerSshHostGroupRouter = async (server: FastifyZodProvider) =>
|
|||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
console.log("remove host from group pre");
|
const { sshHostGroup, sshHost } = await server.services.sshHostGroup.removeHostFromSshHostGroup({
|
||||||
|
|
||||||
const host = await server.services.sshHostGroup.removeHostFromSshHostGroup({
|
|
||||||
sshHostGroupId: req.params.sshHostGroupId,
|
sshHostGroupId: req.params.sshHostGroupId,
|
||||||
hostId: req.params.hostId,
|
hostId: req.params.hostId,
|
||||||
actor: req.permission.type,
|
actor: req.permission.type,
|
||||||
@@ -330,22 +321,20 @@ export const registerSshHostGroupRouter = async (server: FastifyZodProvider) =>
|
|||||||
actorOrgId: req.permission.orgId
|
actorOrgId: req.permission.orgId
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log("remove host from group post");
|
await server.services.auditLog.createAuditLog({
|
||||||
|
...req.auditLogInfo,
|
||||||
|
projectId: sshHost.projectId,
|
||||||
|
event: {
|
||||||
|
type: EventType.REMOVE_HOST_FROM_SSH_HOST_GROUP,
|
||||||
|
metadata: {
|
||||||
|
sshHostGroupId: sshHostGroup.id,
|
||||||
|
sshHostId: sshHost.id,
|
||||||
|
hostname: sshHost.hostname
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// TODO: audit logs
|
return sshHost;
|
||||||
// await server.services.auditLog.createAuditLog({
|
|
||||||
// ...req.auditLogInfo,
|
|
||||||
// projectId: host.projectId,
|
|
||||||
// event: {
|
|
||||||
// type: EventType.GET_SSH_HOST,
|
|
||||||
// metadata: {
|
|
||||||
// sshHostId: host.id,
|
|
||||||
// hostname: host.hostname
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
|
|
||||||
return host;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
import { SshCaStatus, SshCertType } from "@app/ee/services/ssh/ssh-certificate-authority-types";
|
import { SshCaStatus, SshCertType } from "@app/ee/services/ssh/ssh-certificate-authority-types";
|
||||||
import { SshCertKeyAlgorithm } from "@app/ee/services/ssh-certificate/ssh-certificate-types";
|
import { SshCertKeyAlgorithm } from "@app/ee/services/ssh-certificate/ssh-certificate-types";
|
||||||
import { SshCertTemplateStatus } from "@app/ee/services/ssh-certificate-template/ssh-certificate-template-types";
|
import { SshCertTemplateStatus } from "@app/ee/services/ssh-certificate-template/ssh-certificate-template-types";
|
||||||
|
import { TLoginMapping } from "@app/ee/services/ssh-host/ssh-host-types";
|
||||||
import { SymmetricKeyAlgorithm } from "@app/lib/crypto/cipher";
|
import { SymmetricKeyAlgorithm } from "@app/lib/crypto/cipher";
|
||||||
import { AsymmetricKeyAlgorithm, SigningAlgorithm } from "@app/lib/crypto/sign/types";
|
import { AsymmetricKeyAlgorithm, SigningAlgorithm } from "@app/lib/crypto/sign/types";
|
||||||
import { TProjectPermission } from "@app/lib/types";
|
import { TProjectPermission } from "@app/lib/types";
|
||||||
@@ -191,12 +192,19 @@ export enum EventType {
|
|||||||
UPDATE_SSH_CERTIFICATE_TEMPLATE = "update-ssh-certificate-template",
|
UPDATE_SSH_CERTIFICATE_TEMPLATE = "update-ssh-certificate-template",
|
||||||
DELETE_SSH_CERTIFICATE_TEMPLATE = "delete-ssh-certificate-template",
|
DELETE_SSH_CERTIFICATE_TEMPLATE = "delete-ssh-certificate-template",
|
||||||
GET_SSH_CERTIFICATE_TEMPLATE = "get-ssh-certificate-template",
|
GET_SSH_CERTIFICATE_TEMPLATE = "get-ssh-certificate-template",
|
||||||
|
GET_SSH_HOST = "get-ssh-host",
|
||||||
CREATE_SSH_HOST = "create-ssh-host",
|
CREATE_SSH_HOST = "create-ssh-host",
|
||||||
UPDATE_SSH_HOST = "update-ssh-host",
|
UPDATE_SSH_HOST = "update-ssh-host",
|
||||||
DELETE_SSH_HOST = "delete-ssh-host",
|
DELETE_SSH_HOST = "delete-ssh-host",
|
||||||
GET_SSH_HOST = "get-ssh-host",
|
|
||||||
ISSUE_SSH_HOST_USER_CERT = "issue-ssh-host-user-cert",
|
ISSUE_SSH_HOST_USER_CERT = "issue-ssh-host-user-cert",
|
||||||
ISSUE_SSH_HOST_HOST_CERT = "issue-ssh-host-host-cert",
|
ISSUE_SSH_HOST_HOST_CERT = "issue-ssh-host-host-cert",
|
||||||
|
GET_SSH_HOST_GROUP = "get-ssh-host-group",
|
||||||
|
CREATE_SSH_HOST_GROUP = "create-ssh-host-group",
|
||||||
|
UPDATE_SSH_HOST_GROUP = "update-ssh-host-group",
|
||||||
|
DELETE_SSH_HOST_GROUP = "delete-ssh-host-group",
|
||||||
|
GET_SSH_HOST_GROUP_HOSTS = "get-ssh-host-group-hosts",
|
||||||
|
ADD_HOST_TO_SSH_HOST_GROUP = "add-host-to-ssh-host-group",
|
||||||
|
REMOVE_HOST_FROM_SSH_HOST_GROUP = "remove-host-from-ssh-host-group",
|
||||||
CREATE_CA = "create-certificate-authority",
|
CREATE_CA = "create-certificate-authority",
|
||||||
GET_CA = "get-certificate-authority",
|
GET_CA = "get-certificate-authority",
|
||||||
UPDATE_CA = "update-certificate-authority",
|
UPDATE_CA = "update-certificate-authority",
|
||||||
@@ -1499,12 +1507,7 @@ interface CreateSshHost {
|
|||||||
alias: string | null;
|
alias: string | null;
|
||||||
userCertTtl: string;
|
userCertTtl: string;
|
||||||
hostCertTtl: string;
|
hostCertTtl: string;
|
||||||
loginMappings: {
|
loginMappings: TLoginMapping[];
|
||||||
loginUser: string;
|
|
||||||
allowedPrincipals: {
|
|
||||||
usernames: string[];
|
|
||||||
};
|
|
||||||
}[];
|
|
||||||
userSshCaId: string;
|
userSshCaId: string;
|
||||||
hostSshCaId: string;
|
hostSshCaId: string;
|
||||||
};
|
};
|
||||||
@@ -1518,12 +1521,7 @@ interface UpdateSshHost {
|
|||||||
alias?: string | null;
|
alias?: string | null;
|
||||||
userCertTtl?: string;
|
userCertTtl?: string;
|
||||||
hostCertTtl?: string;
|
hostCertTtl?: string;
|
||||||
loginMappings?: {
|
loginMappings?: TLoginMapping[];
|
||||||
loginUser: string;
|
|
||||||
allowedPrincipals: {
|
|
||||||
usernames: string[];
|
|
||||||
};
|
|
||||||
}[];
|
|
||||||
userSshCaId?: string;
|
userSshCaId?: string;
|
||||||
hostSshCaId?: string;
|
hostSshCaId?: string;
|
||||||
};
|
};
|
||||||
@@ -1567,6 +1565,65 @@ interface IssueSshHostHostCert {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface GetSshHostGroupEvent {
|
||||||
|
type: EventType.GET_SSH_HOST_GROUP;
|
||||||
|
metadata: {
|
||||||
|
sshHostGroupId: string;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CreateSshHostGroupEvent {
|
||||||
|
type: EventType.CREATE_SSH_HOST_GROUP;
|
||||||
|
metadata: {
|
||||||
|
sshHostGroupId: string;
|
||||||
|
name: string;
|
||||||
|
loginMappings: TLoginMapping[];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UpdateSshHostGroupEvent {
|
||||||
|
type: EventType.UPDATE_SSH_HOST_GROUP;
|
||||||
|
metadata: {
|
||||||
|
sshHostGroupId: string;
|
||||||
|
name?: string;
|
||||||
|
loginMappings?: TLoginMapping[];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DeleteSshHostGroupEvent {
|
||||||
|
type: EventType.DELETE_SSH_HOST_GROUP;
|
||||||
|
metadata: {
|
||||||
|
sshHostGroupId: string;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface GetSshHostGroupHostsEvent {
|
||||||
|
type: EventType.GET_SSH_HOST_GROUP_HOSTS;
|
||||||
|
metadata: {
|
||||||
|
sshHostGroupId: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AddHostToSshHostGroupEvent {
|
||||||
|
type: EventType.ADD_HOST_TO_SSH_HOST_GROUP;
|
||||||
|
metadata: {
|
||||||
|
sshHostGroupId: string;
|
||||||
|
sshHostId: string;
|
||||||
|
hostname: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RemoveHostFromSshHostGroupEvent {
|
||||||
|
type: EventType.REMOVE_HOST_FROM_SSH_HOST_GROUP;
|
||||||
|
metadata: {
|
||||||
|
sshHostGroupId: string;
|
||||||
|
sshHostId: string;
|
||||||
|
hostname: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
interface CreateCa {
|
interface CreateCa {
|
||||||
type: EventType.CREATE_CA;
|
type: EventType.CREATE_CA;
|
||||||
metadata: {
|
metadata: {
|
||||||
@@ -2753,6 +2810,13 @@ export type Event =
|
|||||||
| CreateAppConnectionEvent
|
| CreateAppConnectionEvent
|
||||||
| UpdateAppConnectionEvent
|
| UpdateAppConnectionEvent
|
||||||
| DeleteAppConnectionEvent
|
| DeleteAppConnectionEvent
|
||||||
|
| GetSshHostGroupEvent
|
||||||
|
| CreateSshHostGroupEvent
|
||||||
|
| UpdateSshHostGroupEvent
|
||||||
|
| DeleteSshHostGroupEvent
|
||||||
|
| GetSshHostGroupHostsEvent
|
||||||
|
| AddHostToSshHostGroupEvent
|
||||||
|
| RemoveHostFromSshHostGroupEvent
|
||||||
| CreateSharedSecretEvent
|
| CreateSharedSecretEvent
|
||||||
| DeleteSharedSecretEvent
|
| DeleteSharedSecretEvent
|
||||||
| ReadSharedSecretEvent
|
| ReadSharedSecretEvent
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import { DatabaseError } from "@app/lib/errors";
|
|||||||
import { groupBy, unique } from "@app/lib/fn";
|
import { groupBy, unique } from "@app/lib/fn";
|
||||||
import { ormify } from "@app/lib/knex";
|
import { ormify } from "@app/lib/knex";
|
||||||
|
|
||||||
|
import { EHostGroupMembershipFilter } from "./ssh-host-group-types";
|
||||||
|
|
||||||
export type TSshHostGroupDALFactory = ReturnType<typeof sshHostGroupDALFactory>;
|
export type TSshHostGroupDALFactory = ReturnType<typeof sshHostGroupDALFactory>;
|
||||||
|
|
||||||
export const sshHostGroupDALFactory = (db: TDbClient) => {
|
export const sshHostGroupDALFactory = (db: TDbClient) => {
|
||||||
@@ -13,6 +15,7 @@ export const sshHostGroupDALFactory = (db: TDbClient) => {
|
|||||||
|
|
||||||
const findSshHostGroupsWithLoginMappings = async (projectId: string, tx?: Knex) => {
|
const findSshHostGroupsWithLoginMappings = async (projectId: string, tx?: Knex) => {
|
||||||
try {
|
try {
|
||||||
|
// First, get all the SSH host groups with their login mappings
|
||||||
const rows = await (tx || db.replicaNode())(TableName.SshHostGroup)
|
const rows = await (tx || db.replicaNode())(TableName.SshHostGroup)
|
||||||
.leftJoin(
|
.leftJoin(
|
||||||
TableName.SshHostLoginUser,
|
TableName.SshHostLoginUser,
|
||||||
@@ -38,6 +41,25 @@ export const sshHostGroupDALFactory = (db: TDbClient) => {
|
|||||||
|
|
||||||
const hostsGrouped = groupBy(rows, (r) => r.sshHostGroupId);
|
const hostsGrouped = groupBy(rows, (r) => r.sshHostGroupId);
|
||||||
|
|
||||||
|
const hostGroupIds = Object.keys(hostsGrouped);
|
||||||
|
|
||||||
|
type HostCountRow = {
|
||||||
|
sshHostGroupId: string;
|
||||||
|
host_count: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const hostCountsQuery = (await (tx ||
|
||||||
|
db
|
||||||
|
.replicaNode()(TableName.SshHostGroupMembership)
|
||||||
|
.select(`${TableName.SshHostGroupMembership}.sshHostGroupId`, db.raw(`count(*) as host_count`))
|
||||||
|
.whereIn(`${TableName.SshHostGroupMembership}.sshHostGroupId`, hostGroupIds)
|
||||||
|
.groupBy(`${TableName.SshHostGroupMembership}.sshHostGroupId`))) as HostCountRow[];
|
||||||
|
|
||||||
|
const hostCountsMap = hostCountsQuery.reduce<Record<string, number>>((acc, { sshHostGroupId, host_count }) => {
|
||||||
|
acc[sshHostGroupId] = Number(host_count);
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
|
||||||
return Object.values(hostsGrouped).map((hostRows) => {
|
return Object.values(hostsGrouped).map((hostRows) => {
|
||||||
const { sshHostGroupId, name } = hostRows[0];
|
const { sshHostGroupId, name } = hostRows[0];
|
||||||
const loginMappingGrouped = groupBy(
|
const loginMappingGrouped = groupBy(
|
||||||
@@ -54,7 +76,8 @@ export const sshHostGroupDALFactory = (db: TDbClient) => {
|
|||||||
id: sshHostGroupId,
|
id: sshHostGroupId,
|
||||||
projectId,
|
projectId,
|
||||||
name,
|
name,
|
||||||
loginMappings
|
loginMappings,
|
||||||
|
hostCount: hostCountsMap[sshHostGroupId] ?? 0
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -116,21 +139,40 @@ export const sshHostGroupDALFactory = (db: TDbClient) => {
|
|||||||
const findAllSshHostsInGroup = async ({
|
const findAllSshHostsInGroup = async ({
|
||||||
sshHostGroupId,
|
sshHostGroupId,
|
||||||
offset = 0,
|
offset = 0,
|
||||||
limit
|
limit,
|
||||||
|
filter
|
||||||
}: {
|
}: {
|
||||||
sshHostGroupId: string;
|
sshHostGroupId: string;
|
||||||
offset?: number;
|
offset?: number;
|
||||||
limit?: number;
|
limit?: number;
|
||||||
|
filter?: EHostGroupMembershipFilter;
|
||||||
}) => {
|
}) => {
|
||||||
try {
|
try {
|
||||||
|
const sshHostGroup = await db
|
||||||
|
.replicaNode()(TableName.SshHostGroup)
|
||||||
|
.where(`${TableName.SshHostGroup}.id`, sshHostGroupId)
|
||||||
|
.select("projectId")
|
||||||
|
.first();
|
||||||
|
|
||||||
|
if (!sshHostGroup) {
|
||||||
|
throw new Error(`SSH host group with ID ${sshHostGroupId} not found`);
|
||||||
|
}
|
||||||
|
|
||||||
const query = db
|
const query = db
|
||||||
.replicaNode()(TableName.SshHostGroupMembership)
|
.replicaNode()(TableName.SshHost)
|
||||||
.where(`${TableName.SshHostGroupMembership}.sshHostGroupId`, sshHostGroupId)
|
.where(`${TableName.SshHost}.projectId`, sshHostGroup.projectId)
|
||||||
.join(TableName.SshHost, `${TableName.SshHostGroupMembership}.sshHostId`, `${TableName.SshHost}.id`)
|
.leftJoin(TableName.SshHostGroupMembership, (bd) => {
|
||||||
|
bd.on(`${TableName.SshHostGroupMembership}.sshHostId`, "=", `${TableName.SshHost}.id`).andOn(
|
||||||
|
`${TableName.SshHostGroupMembership}.sshHostGroupId`,
|
||||||
|
"=",
|
||||||
|
db.raw("?", [sshHostGroupId])
|
||||||
|
);
|
||||||
|
})
|
||||||
.select(
|
.select(
|
||||||
db.ref("id").withSchema(TableName.SshHost),
|
db.ref("id").withSchema(TableName.SshHost),
|
||||||
db.ref("hostname").withSchema(TableName.SshHost),
|
db.ref("hostname").withSchema(TableName.SshHost),
|
||||||
db.ref("alias").withSchema(TableName.SshHost),
|
db.ref("alias").withSchema(TableName.SshHost),
|
||||||
|
db.ref("sshHostGroupId").withSchema(TableName.SshHostGroupMembership),
|
||||||
db.ref("createdAt").withSchema(TableName.SshHostGroupMembership).as("joinedGroupAt"),
|
db.ref("createdAt").withSchema(TableName.SshHostGroupMembership).as("joinedGroupAt"),
|
||||||
db.raw(`count(*) OVER() as total_count`)
|
db.raw(`count(*) OVER() as total_count`)
|
||||||
)
|
)
|
||||||
@@ -141,13 +183,28 @@ export const sshHostGroupDALFactory = (db: TDbClient) => {
|
|||||||
void query.limit(limit);
|
void query.limit(limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (filter) {
|
||||||
|
switch (filter) {
|
||||||
|
case EHostGroupMembershipFilter.GROUP_MEMBERS:
|
||||||
|
void query.andWhere(`${TableName.SshHostGroupMembership}.createdAt`, "is not", null);
|
||||||
|
break;
|
||||||
|
case EHostGroupMembershipFilter.NON_GROUP_MEMBERS:
|
||||||
|
void query.andWhere(`${TableName.SshHostGroupMembership}.createdAt`, "is", null);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const hosts = await query;
|
const hosts = await query;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
hosts: hosts.map(({ id, hostname, alias }) => ({
|
hosts: hosts.map(({ id, hostname, alias, sshHostGroupId: memberGroupId, joinedGroupAt }) => ({
|
||||||
id,
|
id,
|
||||||
hostname,
|
hostname,
|
||||||
alias
|
alias,
|
||||||
|
isPartOfGroup: !!memberGroupId,
|
||||||
|
joinedGroupAt
|
||||||
})),
|
})),
|
||||||
// @ts-expect-error col select is raw and not strongly typed
|
// @ts-expect-error col select is raw and not strongly typed
|
||||||
totalCount: Number(hosts?.[0]?.total_count ?? 0)
|
totalCount: Number(hosts?.[0]?.total_count ?? 0)
|
||||||
|
|||||||
@@ -226,7 +226,8 @@ export const sshHostGroupServiceFactory = ({
|
|||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
filter
|
||||||
}: TListSshHostGroupHostsDTO) => {
|
}: TListSshHostGroupHostsDTO) => {
|
||||||
const sshHostGroup = await sshHostGroupDAL.findSshHostGroupByIdWithLoginMappings(sshHostGroupId);
|
const sshHostGroup = await sshHostGroupDAL.findSshHostGroupByIdWithLoginMappings(sshHostGroupId);
|
||||||
if (!sshHostGroup) throw new NotFoundError({ message: `SSH host group with ID '${sshHostGroupId}' not found` });
|
if (!sshHostGroup) throw new NotFoundError({ message: `SSH host group with ID '${sshHostGroupId}' not found` });
|
||||||
@@ -242,10 +243,8 @@ export const sshHostGroupServiceFactory = ({
|
|||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SshHostGroups);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SshHostGroups);
|
||||||
|
|
||||||
// TODO: check
|
const { hosts, totalCount } = await sshHostGroupDAL.findAllSshHostsInGroup({ sshHostGroupId, filter });
|
||||||
const { hosts, totalCount } = await sshHostGroupDAL.findAllSshHostsInGroup({ sshHostGroupId });
|
return { sshHostGroup, hosts, totalCount };
|
||||||
console.log("hosts: ", hosts);
|
|
||||||
return { hosts, totalCount };
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const addHostToSshHostGroup = async ({
|
const addHostToSshHostGroup = async ({
|
||||||
@@ -259,14 +258,14 @@ export const sshHostGroupServiceFactory = ({
|
|||||||
const sshHostGroup = await sshHostGroupDAL.findSshHostGroupByIdWithLoginMappings(sshHostGroupId);
|
const sshHostGroup = await sshHostGroupDAL.findSshHostGroupByIdWithLoginMappings(sshHostGroupId);
|
||||||
if (!sshHostGroup) throw new NotFoundError({ message: `SSH host group with ID '${sshHostGroupId}' not found` });
|
if (!sshHostGroup) throw new NotFoundError({ message: `SSH host group with ID '${sshHostGroupId}' not found` });
|
||||||
|
|
||||||
const host = await sshHostDAL.findSshHostByIdWithLoginMappings(hostId);
|
const sshHost = await sshHostDAL.findSshHostByIdWithLoginMappings(hostId);
|
||||||
if (!host) {
|
if (!sshHost) {
|
||||||
throw new NotFoundError({
|
throw new NotFoundError({
|
||||||
message: `SSH host with ID ${hostId} not found`
|
message: `SSH host with ID ${hostId} not found`
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sshHostGroup.projectId !== host.projectId) {
|
if (sshHostGroup.projectId !== sshHost.projectId) {
|
||||||
throw new NotFoundError({
|
throw new NotFoundError({
|
||||||
message: `SSH host with ID ${hostId} not found in project ${sshHostGroup.projectId}`
|
message: `SSH host with ID ${hostId} not found in project ${sshHostGroup.projectId}`
|
||||||
});
|
});
|
||||||
@@ -286,7 +285,7 @@ export const sshHostGroupServiceFactory = ({
|
|||||||
|
|
||||||
await sshHostGroupMembershipDAL.create({ sshHostGroupId, sshHostId: hostId });
|
await sshHostGroupMembershipDAL.create({ sshHostGroupId, sshHostId: hostId });
|
||||||
|
|
||||||
return host;
|
return { sshHostGroup, sshHost };
|
||||||
};
|
};
|
||||||
|
|
||||||
const removeHostFromSshHostGroup = async ({
|
const removeHostFromSshHostGroup = async ({
|
||||||
@@ -297,17 +296,21 @@ export const sshHostGroupServiceFactory = ({
|
|||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId
|
||||||
}: TRemoveHostFromSshHostGroupDTO) => {
|
}: TRemoveHostFromSshHostGroupDTO) => {
|
||||||
|
console.log("removeHostFromSshHostGroup args: ", {
|
||||||
|
sshHostGroupId,
|
||||||
|
hostId
|
||||||
|
});
|
||||||
const sshHostGroup = await sshHostGroupDAL.findSshHostGroupByIdWithLoginMappings(sshHostGroupId);
|
const sshHostGroup = await sshHostGroupDAL.findSshHostGroupByIdWithLoginMappings(sshHostGroupId);
|
||||||
if (!sshHostGroup) throw new NotFoundError({ message: `SSH host group with ID '${sshHostGroupId}' not found` });
|
if (!sshHostGroup) throw new NotFoundError({ message: `SSH host group with ID '${sshHostGroupId}' not found` });
|
||||||
|
|
||||||
const host = await sshHostDAL.findSshHostByIdWithLoginMappings(hostId);
|
const sshHost = await sshHostDAL.findSshHostByIdWithLoginMappings(hostId);
|
||||||
if (!host) {
|
if (!sshHost) {
|
||||||
throw new NotFoundError({
|
throw new NotFoundError({
|
||||||
message: `SSH host with ID ${hostId} not found`
|
message: `SSH host with ID ${hostId} not found`
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sshHostGroup.projectId !== host.projectId) {
|
if (sshHostGroup.projectId !== sshHost.projectId) {
|
||||||
throw new NotFoundError({
|
throw new NotFoundError({
|
||||||
message: `SSH host with ID ${hostId} not found in project ${sshHostGroup.projectId}`
|
message: `SSH host with ID ${hostId} not found in project ${sshHostGroup.projectId}`
|
||||||
});
|
});
|
||||||
@@ -336,9 +339,13 @@ export const sshHostGroupServiceFactory = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log("boom: ", {
|
||||||
|
sshHostGroupMembership
|
||||||
|
});
|
||||||
|
|
||||||
await sshHostGroupMembershipDAL.deleteById(sshHostGroupMembership.id);
|
await sshHostGroupMembershipDAL.deleteById(sshHostGroupMembership.id);
|
||||||
|
|
||||||
return host;
|
return { sshHostGroup, sshHost };
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -1,13 +1,9 @@
|
|||||||
|
import { TLoginMapping } from "@app/ee/services/ssh-host/ssh-host-types";
|
||||||
import { TGenericPermission, TProjectPermission } from "@app/lib/types";
|
import { TGenericPermission, TProjectPermission } from "@app/lib/types";
|
||||||
|
|
||||||
export type TCreateSshHostGroupDTO = {
|
export type TCreateSshHostGroupDTO = {
|
||||||
name: string;
|
name: string;
|
||||||
loginMappings: {
|
loginMappings: TLoginMapping[];
|
||||||
loginUser: string;
|
|
||||||
allowedPrincipals: {
|
|
||||||
usernames: string[];
|
|
||||||
};
|
|
||||||
}[];
|
|
||||||
} & TProjectPermission;
|
} & TProjectPermission;
|
||||||
|
|
||||||
export type TUpdateSshHostGroupDTO = {
|
export type TUpdateSshHostGroupDTO = {
|
||||||
@@ -30,6 +26,7 @@ export type TDeleteSshHostGroupDTO = {
|
|||||||
} & TGenericPermission;
|
} & TGenericPermission;
|
||||||
export type TListSshHostGroupHostsDTO = {
|
export type TListSshHostGroupHostsDTO = {
|
||||||
sshHostGroupId: string;
|
sshHostGroupId: string;
|
||||||
|
filter?: EHostGroupMembershipFilter;
|
||||||
} & TGenericPermission;
|
} & TGenericPermission;
|
||||||
|
|
||||||
export type TAddHostToSshHostGroupDTO = {
|
export type TAddHostToSshHostGroupDTO = {
|
||||||
@@ -41,3 +38,8 @@ export type TRemoveHostFromSshHostGroupDTO = {
|
|||||||
sshHostGroupId: string;
|
sshHostGroupId: string;
|
||||||
hostId: string;
|
hostId: string;
|
||||||
} & TGenericPermission;
|
} & TGenericPermission;
|
||||||
|
|
||||||
|
export enum EHostGroupMembershipFilter {
|
||||||
|
GROUP_MEMBERS = "group-members",
|
||||||
|
NON_GROUP_MEMBERS = "non-group-members"
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import { TUserDALFactory } from "@app/services/user/user-dal";
|
|||||||
|
|
||||||
export type TListSshHostsDTO = Omit<TProjectPermission, "projectId">;
|
export type TListSshHostsDTO = Omit<TProjectPermission, "projectId">;
|
||||||
|
|
||||||
type LoginMapping = {
|
export type TLoginMapping = {
|
||||||
loginUser: string;
|
loginUser: string;
|
||||||
allowedPrincipals: {
|
allowedPrincipals: {
|
||||||
usernames: string[];
|
usernames: string[];
|
||||||
@@ -21,7 +21,7 @@ export type TCreateSshHostDTO = {
|
|||||||
alias?: string;
|
alias?: string;
|
||||||
userCertTtl: string;
|
userCertTtl: string;
|
||||||
hostCertTtl: string;
|
hostCertTtl: string;
|
||||||
loginMappings: LoginMapping[];
|
loginMappings: TLoginMapping[];
|
||||||
userSshCaId?: string;
|
userSshCaId?: string;
|
||||||
hostSshCaId?: string;
|
hostSshCaId?: string;
|
||||||
} & TProjectPermission;
|
} & TProjectPermission;
|
||||||
@@ -32,7 +32,7 @@ export type TUpdateSshHostDTO = {
|
|||||||
alias?: string;
|
alias?: string;
|
||||||
userCertTtl?: string;
|
userCertTtl?: string;
|
||||||
hostCertTtl?: string;
|
hostCertTtl?: string;
|
||||||
loginMappings?: LoginMapping[];
|
loginMappings?: TLoginMapping[];
|
||||||
} & Omit<TProjectPermission, "projectId">;
|
} & Omit<TProjectPermission, "projectId">;
|
||||||
|
|
||||||
export type TGetSshHostDTO = {
|
export type TGetSshHostDTO = {
|
||||||
@@ -54,7 +54,7 @@ export type TIssueSshHostHostCertDTO = {
|
|||||||
} & Omit<TProjectPermission, "projectId">;
|
} & Omit<TProjectPermission, "projectId">;
|
||||||
|
|
||||||
type BaseCreateSshLoginMappingsDTO = {
|
type BaseCreateSshLoginMappingsDTO = {
|
||||||
loginMappings: LoginMapping[];
|
loginMappings: TLoginMapping[];
|
||||||
sshHostLoginUserDAL: Pick<TSshHostLoginUserDALFactory, "create" | "transaction">;
|
sshHostLoginUserDAL: Pick<TSshHostLoginUserDALFactory, "create" | "transaction">;
|
||||||
sshHostLoginUserMappingDAL: Pick<TSshHostLoginUserMappingDALFactory, "insertMany">;
|
sshHostLoginUserMappingDAL: Pick<TSshHostLoginUserMappingDALFactory, "insertMany">;
|
||||||
userDAL: Pick<TUserDALFactory, "find">;
|
userDAL: Pick<TUserDALFactory, "find">;
|
||||||
|
|||||||
@@ -1387,7 +1387,8 @@ export const SSH_CERTIFICATE_TEMPLATES = {
|
|||||||
|
|
||||||
export const SSH_HOST_GROUPS = {
|
export const SSH_HOST_GROUPS = {
|
||||||
GET: {
|
GET: {
|
||||||
sshHostGroupId: "The ID of the SSH host group to get."
|
sshHostGroupId: "The ID of the SSH host group to get.",
|
||||||
|
filter: "The filter to apply to the SSH hosts in the SSH host group."
|
||||||
},
|
},
|
||||||
CREATE: {
|
CREATE: {
|
||||||
projectId: "The ID of the project to create the SSH host group in.",
|
projectId: "The ID of the project to create the SSH host group in.",
|
||||||
|
|||||||
@@ -666,7 +666,8 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
|||||||
200: z.object({
|
200: z.object({
|
||||||
groups: z.array(
|
groups: z.array(
|
||||||
sanitizedSshHostGroup.extend({
|
sanitizedSshHostGroup.extend({
|
||||||
loginMappings: z.array(loginMappingSchema)
|
loginMappings: z.array(loginMappingSchema),
|
||||||
|
hostCount: z.number()
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -299,9 +299,9 @@ export const ROUTE_PATHS = Object.freeze({
|
|||||||
"/ssh/$projectId/ca/$caId",
|
"/ssh/$projectId/ca/$caId",
|
||||||
"/_authenticate/_inject-org-details/_org-layout/ssh/$projectId/_ssh-layout/ca/$caId"
|
"/_authenticate/_inject-org-details/_org-layout/ssh/$projectId/_ssh-layout/ca/$caId"
|
||||||
),
|
),
|
||||||
SshGroupDetailsByIDPage: setRoute(
|
SshHostGroupDetailsByIDPage: setRoute(
|
||||||
"/ssh/$projectId/ssh-groups/$groupId",
|
"/ssh/$projectId/ssh-host-groups/$sshHostGroupId",
|
||||||
"/_authenticate/_inject-org-details/_org-layout/ssh/$projectId/_ssh-layout/groups/$groupId"
|
"/_authenticate/_inject-org-details/_org-layout/ssh/$projectId/_ssh-layout/ssh-host-groups/$sshHostGroupId"
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
Public: {
|
Public: {
|
||||||
|
|||||||
@@ -1,2 +1,8 @@
|
|||||||
export { useCreateSshHostGroup, useDeleteSshHostGroup, useUpdateSshHostGroup } from "./mutations";
|
export {
|
||||||
export { useGetSshHostGroupById } from "./queries";
|
useAddHostToSshHostGroup,
|
||||||
|
useCreateSshHostGroup,
|
||||||
|
useDeleteSshHostGroup,
|
||||||
|
useRemoveHostFromSshHostGroup,
|
||||||
|
useUpdateSshHostGroup
|
||||||
|
} from "./mutations";
|
||||||
|
export { useGetSshHostGroupById, useListSshHostGroupHosts } from "./queries";
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { useMutation, useQueryClient } from "@tanstack/react-query";
|
|||||||
import { apiRequest } from "@app/config/request";
|
import { apiRequest } from "@app/config/request";
|
||||||
|
|
||||||
import { workspaceKeys } from "../workspace/query-keys";
|
import { workspaceKeys } from "../workspace/query-keys";
|
||||||
|
import { sshHostGroupKeys } from "./queries";
|
||||||
import {
|
import {
|
||||||
TCreateSshHostGroupDTO,
|
TCreateSshHostGroupDTO,
|
||||||
TDeleteSshHostGroupDTO,
|
TDeleteSshHostGroupDTO,
|
||||||
@@ -17,10 +18,13 @@ export const useCreateSshHostGroup = () => {
|
|||||||
const { data: hostGroup } = await apiRequest.post("/api/v1/ssh/host-groups", body);
|
const { data: hostGroup } = await apiRequest.post("/api/v1/ssh/host-groups", body);
|
||||||
return hostGroup;
|
return hostGroup;
|
||||||
},
|
},
|
||||||
onSuccess: ({ projectId }) => {
|
onSuccess: ({ projectId, id }) => {
|
||||||
queryClient.invalidateQueries({
|
queryClient.invalidateQueries({
|
||||||
queryKey: workspaceKeys.getWorkspaceSshHostGroups(projectId)
|
queryKey: workspaceKeys.getWorkspaceSshHostGroups(projectId)
|
||||||
});
|
});
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: sshHostGroupKeys.getSshHostGroupById(id)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -35,10 +39,13 @@ export const useUpdateSshHostGroup = () => {
|
|||||||
);
|
);
|
||||||
return hostGroup;
|
return hostGroup;
|
||||||
},
|
},
|
||||||
onSuccess: ({ projectId }) => {
|
onSuccess: ({ projectId }, { sshHostGroupId }) => {
|
||||||
queryClient.invalidateQueries({
|
queryClient.invalidateQueries({
|
||||||
queryKey: workspaceKeys.getWorkspaceSshHostGroups(projectId)
|
queryKey: workspaceKeys.getWorkspaceSshHostGroups(projectId)
|
||||||
});
|
});
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: sshHostGroupKeys.getSshHostGroupById(sshHostGroupId)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -52,10 +59,41 @@ export const useDeleteSshHostGroup = () => {
|
|||||||
);
|
);
|
||||||
return hostGroup;
|
return hostGroup;
|
||||||
},
|
},
|
||||||
onSuccess: ({ projectId }) => {
|
onSuccess: ({ projectId }, { sshHostGroupId }) => {
|
||||||
queryClient.invalidateQueries({
|
queryClient.invalidateQueries({
|
||||||
queryKey: workspaceKeys.getWorkspaceSshHostGroups(projectId)
|
queryKey: workspaceKeys.getWorkspaceSshHostGroups(projectId)
|
||||||
});
|
});
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: sshHostGroupKeys.getSshHostGroupById(sshHostGroupId)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useAddHostToSshHostGroup = () => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
return useMutation<void, object, { sshHostGroupId: string; sshHostId: string }>({
|
||||||
|
mutationFn: async ({ sshHostGroupId, sshHostId }) => {
|
||||||
|
await apiRequest.post(`/api/v1/ssh/host-groups/${sshHostGroupId}/hosts/${sshHostId}`);
|
||||||
|
},
|
||||||
|
onSuccess: (_, { sshHostGroupId }) => {
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: sshHostGroupKeys.forSshHostGroupHosts(sshHostGroupId)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useRemoveHostFromSshHostGroup = () => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
return useMutation<void, object, { sshHostGroupId: string; sshHostId: string }>({
|
||||||
|
mutationFn: async ({ sshHostGroupId, sshHostId }) => {
|
||||||
|
await apiRequest.delete(`/api/v1/ssh/host-groups/${sshHostGroupId}/hosts/${sshHostId}`);
|
||||||
|
},
|
||||||
|
onSuccess: (_, { sshHostGroupId }) => {
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: sshHostGroupKeys.forSshHostGroupHosts(sshHostGroupId)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,10 +2,20 @@ import { useQuery } from "@tanstack/react-query";
|
|||||||
|
|
||||||
import { apiRequest } from "@app/config/request";
|
import { apiRequest } from "@app/config/request";
|
||||||
|
|
||||||
import { TSshHostGroup } from "./types";
|
import { EHostGroupMembershipFilter, TListSshHostGroupHostsResponse, TSshHostGroup } from "./types";
|
||||||
|
|
||||||
export const sshHostGroupKeys = {
|
export const sshHostGroupKeys = {
|
||||||
getSshHostGroupById: (sshHostGroupId: string) => [{ sshHostGroupId }, "ssh-host-group"]
|
getSshHostGroupById: (sshHostGroupId: string) => [{ sshHostGroupId }, "ssh-host-group"],
|
||||||
|
allSshHostGroupHosts: () => ["ssh-host-group-hosts"] as const,
|
||||||
|
forSshHostGroupHosts: (sshHostGroupId: string) =>
|
||||||
|
[...sshHostGroupKeys.allSshHostGroupHosts(), sshHostGroupId] as const,
|
||||||
|
specificSshHostGroupHosts: ({
|
||||||
|
sshHostGroupId,
|
||||||
|
filter
|
||||||
|
}: {
|
||||||
|
sshHostGroupId: string;
|
||||||
|
filter?: EHostGroupMembershipFilter;
|
||||||
|
}) => [...sshHostGroupKeys.forSshHostGroupHosts(sshHostGroupId), { filter }] as const
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useGetSshHostGroupById = (sshHostGroupId: string) => {
|
export const useGetSshHostGroupById = (sshHostGroupId: string) => {
|
||||||
@@ -20,3 +30,31 @@ export const useGetSshHostGroupById = (sshHostGroupId: string) => {
|
|||||||
enabled: Boolean(sshHostGroupId)
|
enabled: Boolean(sshHostGroupId)
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useListSshHostGroupHosts = ({
|
||||||
|
sshHostGroupId,
|
||||||
|
filter
|
||||||
|
}: {
|
||||||
|
sshHostGroupId: string;
|
||||||
|
filter?: EHostGroupMembershipFilter;
|
||||||
|
}) => {
|
||||||
|
return useQuery({
|
||||||
|
queryKey: sshHostGroupKeys.specificSshHostGroupHosts({ sshHostGroupId, filter }),
|
||||||
|
queryFn: async () => {
|
||||||
|
const params = new URLSearchParams({
|
||||||
|
...(filter ? { filter } : {})
|
||||||
|
});
|
||||||
|
|
||||||
|
const { data } = await apiRequest.get<TListSshHostGroupHostsResponse>(
|
||||||
|
`/api/v1/ssh/host-groups/${sshHostGroupId}/hosts`,
|
||||||
|
{
|
||||||
|
params
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
enabled: Boolean(sshHostGroupId),
|
||||||
|
staleTime: 0,
|
||||||
|
gcTime: 0
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { TSshHost } from "../sshHost/types";
|
||||||
|
|
||||||
export type TSshHostGroup = {
|
export type TSshHostGroup = {
|
||||||
id: string;
|
id: string;
|
||||||
projectId: string;
|
projectId: string;
|
||||||
@@ -35,3 +37,13 @@ export type TUpdateSshHostGroupDTO = {
|
|||||||
export type TDeleteSshHostGroupDTO = {
|
export type TDeleteSshHostGroupDTO = {
|
||||||
sshHostGroupId: string;
|
sshHostGroupId: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type TListSshHostGroupHostsResponse = {
|
||||||
|
hosts: TSshHost[];
|
||||||
|
totalCount: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export enum EHostGroupMembershipFilter {
|
||||||
|
GROUP_MEMBERS = "group-members",
|
||||||
|
NON_GROUP_MEMBERS = "non-group-members"
|
||||||
|
}
|
||||||
|
|||||||
@@ -877,7 +877,7 @@ export const useListWorkspaceSshHostGroups = (projectId: string) => {
|
|||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
const {
|
const {
|
||||||
data: { groups }
|
data: { groups }
|
||||||
} = await apiRequest.get<{ groups: TSshHostGroup[] }>(
|
} = await apiRequest.get<{ groups: (TSshHostGroup & { hostCount: number })[] }>(
|
||||||
`/api/v2/workspace/${projectId}/ssh-host-groups`
|
`/api/v2/workspace/${projectId}/ssh-host-groups`
|
||||||
);
|
);
|
||||||
return groups;
|
return groups;
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
import { createFileRoute } from '@tanstack/react-router'
|
|
||||||
|
|
||||||
import { SshGroupDetailsByIDPage } from './SshGroupDetailsByIDPage'
|
|
||||||
|
|
||||||
export const Route = createFileRoute(
|
|
||||||
'/_authenticate/_inject-org-details/_org-layout/ssh/$projectId/_ssh-layout/ssh-host-groups/$sshHostGroupId',
|
|
||||||
)({
|
|
||||||
component: SshGroupDetailsByIDPage,
|
|
||||||
})
|
|
||||||
+15
-8
@@ -1,6 +1,7 @@
|
|||||||
import { Helmet } from "react-helmet";
|
import { Helmet } from "react-helmet";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useNavigate } from "@tanstack/react-router";
|
import { useNavigate, useParams } from "@tanstack/react-router";
|
||||||
|
import { twMerge } from "tailwind-merge";
|
||||||
|
|
||||||
import { createNotification } from "@app/components/notifications";
|
import { createNotification } from "@app/components/notifications";
|
||||||
import { ProjectPermissionCan } from "@app/components/permissions";
|
import { ProjectPermissionCan } from "@app/components/permissions";
|
||||||
@@ -20,15 +21,18 @@ import { useDeleteSshHostGroup, useGetSshHostGroupById } from "@app/hooks/api";
|
|||||||
import { ProjectType } from "@app/hooks/api/workspace/types";
|
import { ProjectType } from "@app/hooks/api/workspace/types";
|
||||||
import { usePopUp } from "@app/hooks/usePopUp";
|
import { usePopUp } from "@app/hooks/usePopUp";
|
||||||
|
|
||||||
|
import { SshHostGroupModal } from "../SshHostsPage/components/SshHostGroupModal";
|
||||||
|
import { SshHostGroupDetailsSection, SshHostGroupHostsSection } from "./components";
|
||||||
|
|
||||||
const Page = () => {
|
const Page = () => {
|
||||||
const { currentWorkspace } = useWorkspace();
|
const { currentWorkspace } = useWorkspace();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const projectId = currentWorkspace?.id || "";
|
const projectId = currentWorkspace?.id || "";
|
||||||
const groupId = useParams({
|
const sshHostGroupId = useParams({
|
||||||
from: ROUTE_PATHS.Ssh.SshGroupDetailsByIDPage.id,
|
from: ROUTE_PATHS.Ssh.SshHostGroupDetailsByIDPage.id,
|
||||||
select: (el) => el.groupId
|
select: (el) => el.sshHostGroupId
|
||||||
});
|
});
|
||||||
const { data } = useGetSshHostGroupById(groupId);
|
const { data } = useGetSshHostGroupById(sshHostGroupId);
|
||||||
|
|
||||||
const { mutateAsync: deleteSshHostGroup } = useDeleteSshHostGroup();
|
const { mutateAsync: deleteSshHostGroup } = useDeleteSshHostGroup();
|
||||||
|
|
||||||
@@ -105,10 +109,13 @@ const Page = () => {
|
|||||||
</PageHeader>
|
</PageHeader>
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
<div className="mr-4 w-96">
|
<div className="mr-4 w-96">
|
||||||
<SshHostGroupDetailsSection groupId={groupId} handlePopUpOpen={handlePopUpOpen} />
|
<SshHostGroupDetailsSection
|
||||||
|
sshHostGroupId={sshHostGroupId}
|
||||||
|
handlePopUpOpen={handlePopUpOpen}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<SshHostsSection groupId={groupId} />
|
<SshHostGroupHostsSection sshHostGroupId={sshHostGroupId} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -127,7 +134,7 @@ const Page = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SshGroupDetailsByIDPage = () => {
|
export const SshHostGroupDetailsByIDPage = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
+126
@@ -0,0 +1,126 @@
|
|||||||
|
import { faServer } from "@fortawesome/free-solid-svg-icons";
|
||||||
|
|
||||||
|
import { createNotification } from "@app/components/notifications";
|
||||||
|
import { ProjectPermissionCan } from "@app/components/permissions";
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
EmptyState,
|
||||||
|
Modal,
|
||||||
|
ModalContent,
|
||||||
|
Table,
|
||||||
|
TableContainer,
|
||||||
|
TableSkeleton,
|
||||||
|
TBody,
|
||||||
|
Td,
|
||||||
|
Th,
|
||||||
|
THead,
|
||||||
|
Tr
|
||||||
|
} from "@app/components/v2";
|
||||||
|
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/context";
|
||||||
|
import { useAddHostToSshHostGroup, useListSshHostGroupHosts } from "@app/hooks/api";
|
||||||
|
import { EHostGroupMembershipFilter } from "@app/hooks/api/sshHostGroup/types";
|
||||||
|
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
popUp: UsePopUpState<["addHostGroupMembers"]>;
|
||||||
|
handlePopUpToggle: (
|
||||||
|
popUpName: keyof UsePopUpState<["addHostGroupMembers"]>,
|
||||||
|
state?: boolean
|
||||||
|
) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const AddHostGroupMemberModal = ({ popUp, handlePopUpToggle }: Props) => {
|
||||||
|
const popUpData = popUp?.addHostGroupMembers?.data as {
|
||||||
|
sshHostGroupId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const { data, isPending } = useListSshHostGroupHosts({
|
||||||
|
sshHostGroupId: popUpData?.sshHostGroupId,
|
||||||
|
filter: EHostGroupMembershipFilter.NON_GROUP_MEMBERS
|
||||||
|
});
|
||||||
|
const { mutateAsync: addHostToSshHostGroup } = useAddHostToSshHostGroup();
|
||||||
|
|
||||||
|
const handleAddHost = async (sshHostId: string) => {
|
||||||
|
try {
|
||||||
|
if (!popUpData?.sshHostGroupId) {
|
||||||
|
createNotification({
|
||||||
|
text: "Some data is missing, please refresh the page and try again",
|
||||||
|
type: "error"
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await addHostToSshHostGroup({
|
||||||
|
sshHostGroupId: popUpData.sshHostGroupId,
|
||||||
|
sshHostId
|
||||||
|
});
|
||||||
|
|
||||||
|
createNotification({
|
||||||
|
text: "Successfully added host to the group",
|
||||||
|
type: "success"
|
||||||
|
});
|
||||||
|
} catch {
|
||||||
|
createNotification({
|
||||||
|
text: "Failed to add host to the group",
|
||||||
|
type: "error"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
isOpen={popUp?.addHostGroupMembers?.isOpen}
|
||||||
|
onOpenChange={(isOpen) => {
|
||||||
|
handlePopUpToggle("addHostGroupMembers", isOpen);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ModalContent title="Add Hosts to Group">
|
||||||
|
<TableContainer>
|
||||||
|
<Table>
|
||||||
|
<THead>
|
||||||
|
<Tr>
|
||||||
|
<Th>Alias</Th>
|
||||||
|
<Th>Hostname</Th>
|
||||||
|
<Th />
|
||||||
|
</Tr>
|
||||||
|
</THead>
|
||||||
|
<TBody>
|
||||||
|
{isPending && <TableSkeleton columns={3} innerKey="ssh-hosts" />}
|
||||||
|
{!isPending &&
|
||||||
|
data?.hosts?.map((host) => {
|
||||||
|
return (
|
||||||
|
<Tr className="items-center" key={`host-${host.id}`}>
|
||||||
|
<Td>{host.alias ?? "-"}</Td>
|
||||||
|
<Td>{host.hostname}</Td>
|
||||||
|
<Td className="flex justify-end">
|
||||||
|
<ProjectPermissionCan
|
||||||
|
I={ProjectPermissionActions.Edit}
|
||||||
|
a={ProjectPermissionSub.SshHostGroups}
|
||||||
|
>
|
||||||
|
{(isAllowed) => (
|
||||||
|
<Button
|
||||||
|
isLoading={isPending}
|
||||||
|
isDisabled={!isAllowed}
|
||||||
|
colorSchema="primary"
|
||||||
|
variant="outline_bg"
|
||||||
|
type="submit"
|
||||||
|
onClick={() => handleAddHost(host.id)}
|
||||||
|
>
|
||||||
|
Add
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</ProjectPermissionCan>
|
||||||
|
</Td>
|
||||||
|
</Tr>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</TBody>
|
||||||
|
</Table>
|
||||||
|
{!isPending && !data?.hosts?.length && (
|
||||||
|
<EmptyState title="No hosts available to add to the SSH host group" icon={faServer} />
|
||||||
|
)}
|
||||||
|
</TableContainer>
|
||||||
|
</ModalContent>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
+84
@@ -0,0 +1,84 @@
|
|||||||
|
import { faCheck, faCopy, faPencil } from "@fortawesome/free-solid-svg-icons";
|
||||||
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
|
||||||
|
import { ProjectPermissionCan } from "@app/components/permissions";
|
||||||
|
import { IconButton, Tooltip } from "@app/components/v2";
|
||||||
|
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/context";
|
||||||
|
import { useTimedReset } from "@app/hooks";
|
||||||
|
import { useGetSshHostGroupById } from "@app/hooks/api";
|
||||||
|
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
sshHostGroupId: string;
|
||||||
|
handlePopUpOpen: (popUpName: keyof UsePopUpState<["sshHostGroup"]>, data?: object) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const SshHostGroupDetailsSection = ({ sshHostGroupId, handlePopUpOpen }: Props) => {
|
||||||
|
const [copyTextId, isCopyingId, setCopyTextId] = useTimedReset<string>({
|
||||||
|
initialState: "Copy ID to clipboard"
|
||||||
|
});
|
||||||
|
|
||||||
|
const { data: sshHostGroup } = useGetSshHostGroupById(sshHostGroupId);
|
||||||
|
|
||||||
|
return sshHostGroup ? (
|
||||||
|
<div className="rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
|
||||||
|
<div className="flex items-center justify-between border-b border-mineshaft-400 pb-4">
|
||||||
|
<h3 className="text-lg font-semibold text-mineshaft-100">SSH Host Group Details</h3>
|
||||||
|
<ProjectPermissionCan
|
||||||
|
I={ProjectPermissionActions.Edit}
|
||||||
|
a={ProjectPermissionSub.SshHostGroups}
|
||||||
|
>
|
||||||
|
{(isAllowed) => {
|
||||||
|
return (
|
||||||
|
<Tooltip content="Edit SSH Host Group">
|
||||||
|
<IconButton
|
||||||
|
isDisabled={!isAllowed}
|
||||||
|
ariaLabel="edit icon"
|
||||||
|
variant="plain"
|
||||||
|
className="group relative"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
handlePopUpOpen("sshHostGroup", {
|
||||||
|
sshHostGroupId: sshHostGroup.id
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faPencil} />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
</ProjectPermissionCan>
|
||||||
|
</div>
|
||||||
|
<div className="pt-4">
|
||||||
|
<div className="mb-4">
|
||||||
|
<p className="text-sm font-semibold text-mineshaft-300">SSH Host Group ID</p>
|
||||||
|
<div className="group flex align-top">
|
||||||
|
<p className="text-sm text-mineshaft-300">{sshHostGroup.id}</p>
|
||||||
|
<div className="opacity-0 transition-opacity duration-300 group-hover:opacity-100">
|
||||||
|
<Tooltip content={copyTextId}>
|
||||||
|
<IconButton
|
||||||
|
ariaLabel="copy icon"
|
||||||
|
variant="plain"
|
||||||
|
className="group relative ml-2"
|
||||||
|
onClick={() => {
|
||||||
|
navigator.clipboard.writeText(sshHostGroup.id);
|
||||||
|
setCopyTextId("Copied");
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={isCopyingId ? faCheck : faCopy} />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p className="text-sm font-semibold text-mineshaft-300">Name</p>
|
||||||
|
<p className="text-sm text-mineshaft-300">{sshHostGroup.name}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div />
|
||||||
|
);
|
||||||
|
};
|
||||||
+89
@@ -0,0 +1,89 @@
|
|||||||
|
import { faPlus } from "@fortawesome/free-solid-svg-icons";
|
||||||
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
|
||||||
|
import { createNotification } from "@app/components/notifications";
|
||||||
|
import { ProjectPermissionCan } from "@app/components/permissions";
|
||||||
|
import { DeleteActionModal, IconButton } from "@app/components/v2";
|
||||||
|
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/context";
|
||||||
|
import { usePopUp } from "@app/hooks";
|
||||||
|
import { useRemoveHostFromSshHostGroup } from "@app/hooks/api";
|
||||||
|
|
||||||
|
import { AddHostGroupMemberModal } from "./AddHostGroupMemberModal";
|
||||||
|
import { SshHostGroupHostsTable } from "./SshHostGroupHostsTable";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
sshHostGroupId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const SshHostGroupHostsSection = ({ sshHostGroupId }: Props) => {
|
||||||
|
const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([
|
||||||
|
"removeHostFromSshHostGroup",
|
||||||
|
"addHostGroupMembers"
|
||||||
|
] as const);
|
||||||
|
|
||||||
|
const { mutateAsync: removeHostFromGroup } = useRemoveHostFromSshHostGroup();
|
||||||
|
|
||||||
|
const onRemoveSshHostSubmit = async (sshHostId: string) => {
|
||||||
|
try {
|
||||||
|
await removeHostFromGroup({
|
||||||
|
sshHostId,
|
||||||
|
sshHostGroupId
|
||||||
|
});
|
||||||
|
|
||||||
|
await createNotification({
|
||||||
|
text: "Successfully removed host from SSH group",
|
||||||
|
type: "success"
|
||||||
|
});
|
||||||
|
|
||||||
|
handlePopUpClose("removeHostFromSshHostGroup");
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
createNotification({
|
||||||
|
text: "Failed to remove host from SSH group",
|
||||||
|
type: "error"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="h-full rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
|
||||||
|
<div className="flex items-center justify-between border-b border-mineshaft-400 pb-4">
|
||||||
|
<h3 className="text-lg font-semibold text-mineshaft-100">SSH Hosts</h3>
|
||||||
|
<ProjectPermissionCan I={ProjectPermissionActions.Create} a={ProjectPermissionSub.SshHosts}>
|
||||||
|
{(isAllowed) => (
|
||||||
|
<IconButton
|
||||||
|
ariaLabel="add host"
|
||||||
|
variant="plain"
|
||||||
|
className="group relative"
|
||||||
|
onClick={() =>
|
||||||
|
handlePopUpOpen("addHostGroupMembers", {
|
||||||
|
sshHostGroupId
|
||||||
|
})
|
||||||
|
}
|
||||||
|
isDisabled={!isAllowed}
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faPlus} />
|
||||||
|
</IconButton>
|
||||||
|
)}
|
||||||
|
</ProjectPermissionCan>
|
||||||
|
</div>
|
||||||
|
<div className="py-4">
|
||||||
|
<SshHostGroupHostsTable sshHostGroupId={sshHostGroupId} handlePopUpOpen={handlePopUpOpen} />
|
||||||
|
</div>
|
||||||
|
<AddHostGroupMemberModal popUp={popUp} handlePopUpToggle={handlePopUpToggle} />
|
||||||
|
<DeleteActionModal
|
||||||
|
isOpen={popUp.removeHostFromSshHostGroup.isOpen}
|
||||||
|
title={`Are you sure want to remove the host ${
|
||||||
|
(popUp?.removeHostFromSshHostGroup?.data as { hostname: string })?.hostname || ""
|
||||||
|
} from this SSH group?`}
|
||||||
|
onChange={(isOpen) => handlePopUpToggle("removeHostFromSshHostGroup", isOpen)}
|
||||||
|
deleteKey="confirm"
|
||||||
|
onDeleteApproved={() =>
|
||||||
|
onRemoveSshHostSubmit(
|
||||||
|
(popUp?.removeHostFromSshHostGroup?.data as { sshHostId: string })?.sshHostId
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
+94
@@ -0,0 +1,94 @@
|
|||||||
|
import { faServer, faUserMinus } from "@fortawesome/free-solid-svg-icons";
|
||||||
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
|
||||||
|
import { ProjectPermissionCan } from "@app/components/permissions";
|
||||||
|
import {
|
||||||
|
EmptyState,
|
||||||
|
IconButton,
|
||||||
|
Table,
|
||||||
|
TableContainer,
|
||||||
|
TableSkeleton,
|
||||||
|
TBody,
|
||||||
|
Td,
|
||||||
|
Th,
|
||||||
|
THead,
|
||||||
|
Tooltip,
|
||||||
|
Tr
|
||||||
|
} from "@app/components/v2";
|
||||||
|
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/context";
|
||||||
|
import { useListSshHostGroupHosts } from "@app/hooks/api";
|
||||||
|
import { EHostGroupMembershipFilter } from "@app/hooks/api/sshHostGroup/types";
|
||||||
|
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
sshHostGroupId: string;
|
||||||
|
handlePopUpOpen: (
|
||||||
|
popUpName: keyof UsePopUpState<["removeHostFromSshHostGroup"]>,
|
||||||
|
data?: object
|
||||||
|
) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const SshHostGroupHostsTable = ({ sshHostGroupId, handlePopUpOpen }: Props) => {
|
||||||
|
const { data, isPending } = useListSshHostGroupHosts({
|
||||||
|
sshHostGroupId,
|
||||||
|
filter: EHostGroupMembershipFilter.GROUP_MEMBERS
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<TableContainer>
|
||||||
|
<Table>
|
||||||
|
<THead>
|
||||||
|
<Tr>
|
||||||
|
<Th>Alias</Th>
|
||||||
|
<Th>Hostname</Th>
|
||||||
|
<Th />
|
||||||
|
</Tr>
|
||||||
|
</THead>
|
||||||
|
<TBody>
|
||||||
|
{isPending && <TableSkeleton columns={2} innerKey="ssh-host-group-hosts" />}
|
||||||
|
{!isPending &&
|
||||||
|
data?.hosts.map((host) => {
|
||||||
|
return (
|
||||||
|
<Tr className="h-10" key={`host-${host.id}`}>
|
||||||
|
<Td>{host.alias ?? "-"}</Td>
|
||||||
|
<Td>{host.hostname}</Td>
|
||||||
|
<Td className="flex justify-end">
|
||||||
|
<ProjectPermissionCan
|
||||||
|
I={ProjectPermissionActions.Edit}
|
||||||
|
a={ProjectPermissionSub.SshHostGroups}
|
||||||
|
>
|
||||||
|
{(isAllowed) => (
|
||||||
|
<Tooltip content="Remove user from group">
|
||||||
|
<IconButton
|
||||||
|
isDisabled={!isAllowed}
|
||||||
|
ariaLabel="Remove user from group"
|
||||||
|
onClick={() =>
|
||||||
|
handlePopUpOpen("removeHostFromSshHostGroup", {
|
||||||
|
sshHostId: host.id,
|
||||||
|
hostname: host.hostname
|
||||||
|
})
|
||||||
|
}
|
||||||
|
variant="plain"
|
||||||
|
colorSchema="danger"
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faUserMinus} />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
|
</ProjectPermissionCan>
|
||||||
|
</Td>
|
||||||
|
</Tr>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</TBody>
|
||||||
|
</Table>
|
||||||
|
{!isPending && !data?.hosts?.length && (
|
||||||
|
<EmptyState title="No hosts have been added to this SSH host group" icon={faServer} />
|
||||||
|
)}
|
||||||
|
</TableContainer>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SshHostGroupHostsTable;
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export { SshHostGroupDetailsSection } from "./SshHostGroupDetailsSection";
|
||||||
|
export { SshHostGroupHostsSection } from "./SshHostGroupHostsSection";
|
||||||
|
export { SshHostGroupHostsTable } from "./SshHostGroupHostsTable";
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import { createFileRoute } from "@tanstack/react-router";
|
||||||
|
|
||||||
|
import { SshHostGroupDetailsByIDPage } from "./SshHostGroupDetailsByIDPage";
|
||||||
|
|
||||||
|
export const Route = createFileRoute(
|
||||||
|
"/_authenticate/_inject-org-details/_org-layout/ssh/$projectId/_ssh-layout/ssh-host-groups/$sshHostGroupId"
|
||||||
|
)({
|
||||||
|
component: SshHostGroupDetailsByIDPage
|
||||||
|
});
|
||||||
@@ -1,175 +0,0 @@
|
|||||||
import { useState } from "react";
|
|
||||||
import { faMagnifyingGlass, faUsers } from "@fortawesome/free-solid-svg-icons";
|
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|
||||||
|
|
||||||
import { createNotification } from "@app/components/notifications";
|
|
||||||
import { OrgPermissionCan } from "@app/components/permissions";
|
|
||||||
import {
|
|
||||||
Button,
|
|
||||||
EmptyState,
|
|
||||||
Input,
|
|
||||||
Modal,
|
|
||||||
ModalContent,
|
|
||||||
Pagination,
|
|
||||||
Table,
|
|
||||||
TableContainer,
|
|
||||||
TableSkeleton,
|
|
||||||
TBody,
|
|
||||||
Td,
|
|
||||||
Th,
|
|
||||||
THead,
|
|
||||||
Tr
|
|
||||||
} from "@app/components/v2";
|
|
||||||
import { OrgPermissionGroupActions, OrgPermissionSubjects } from "@app/context";
|
|
||||||
import { useDebounce, useResetPageHelper } from "@app/hooks";
|
|
||||||
import { useAddUserToGroup, useListGroupUsers } from "@app/hooks/api";
|
|
||||||
import { EFilterReturnedUsers } from "@app/hooks/api/groups/types";
|
|
||||||
import { UsePopUpState } from "@app/hooks/usePopUp";
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
popUp: UsePopUpState<["sshHostGroupHosts"]>;
|
|
||||||
handlePopUpToggle: (
|
|
||||||
popUpName: keyof UsePopUpState<["sshHostGroupHosts"]>,
|
|
||||||
state?: boolean
|
|
||||||
) => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const SshHostGroupHostsModal = ({ popUp, handlePopUpToggle }: Props) => {
|
|
||||||
const [page, setPage] = useState(1);
|
|
||||||
const [perPage, setPerPage] = useState(10);
|
|
||||||
const [searchMemberFilter, setSearchMemberFilter] = useState("");
|
|
||||||
const [debouncedSearch] = useDebounce(searchMemberFilter);
|
|
||||||
|
|
||||||
const popUpData = popUp?.addGroupMembers?.data as {
|
|
||||||
groupId: string;
|
|
||||||
slug: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
const offset = (page - 1) * perPage;
|
|
||||||
const { data, isPending } = useListGroupUsers({
|
|
||||||
id: popUpData?.groupId,
|
|
||||||
groupSlug: popUpData?.slug,
|
|
||||||
offset,
|
|
||||||
limit: perPage,
|
|
||||||
search: debouncedSearch,
|
|
||||||
filter: EFilterReturnedUsers.NON_MEMBERS
|
|
||||||
});
|
|
||||||
|
|
||||||
const { totalCount = 0 } = data ?? {};
|
|
||||||
|
|
||||||
useResetPageHelper({
|
|
||||||
totalCount,
|
|
||||||
offset,
|
|
||||||
setPage
|
|
||||||
});
|
|
||||||
|
|
||||||
const { mutateAsync: addUserToGroupMutateAsync } = useAddUserToGroup();
|
|
||||||
|
|
||||||
const handleAddHost = async (hostId: string) => {
|
|
||||||
try {
|
|
||||||
if (!popUpData?.slug) {
|
|
||||||
createNotification({
|
|
||||||
text: "Some data is missing, please refresh the page and try again",
|
|
||||||
type: "error"
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// await addUserToGroupMutateAsync({
|
|
||||||
// groupId: popUpData.groupId,
|
|
||||||
// username,
|
|
||||||
// slug: popUpData.slug
|
|
||||||
// });
|
|
||||||
|
|
||||||
createNotification({
|
|
||||||
text: "Successfully assigned host to the SSH host group",
|
|
||||||
type: "success"
|
|
||||||
});
|
|
||||||
} catch {
|
|
||||||
createNotification({
|
|
||||||
text: "Failed to assign host to the SSH host group",
|
|
||||||
type: "error"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Modal
|
|
||||||
isOpen={popUp?.sshHostGroupHosts?.isOpen}
|
|
||||||
onOpenChange={(isOpen) => {
|
|
||||||
handlePopUpToggle("sshHostGroupHosts", isOpen);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ModalContent title="Add Hosts to Group">
|
|
||||||
<Input
|
|
||||||
value={searchMemberFilter}
|
|
||||||
onChange={(e) => setSearchMemberFilter(e.target.value)}
|
|
||||||
leftIcon={<FontAwesomeIcon icon={faMagnifyingGlass} />}
|
|
||||||
placeholder="Search members..."
|
|
||||||
/>
|
|
||||||
<TableContainer className="mt-4">
|
|
||||||
<Table>
|
|
||||||
<THead>
|
|
||||||
<Tr>
|
|
||||||
<Th>User</Th>
|
|
||||||
<Th />
|
|
||||||
</Tr>
|
|
||||||
</THead>
|
|
||||||
<TBody>
|
|
||||||
{isPending && <TableSkeleton columns={2} innerKey="group-users" />}
|
|
||||||
{!isPending &&
|
|
||||||
data?.users?.map(({ id, firstName, lastName, username }) => {
|
|
||||||
return (
|
|
||||||
<Tr className="items-center" key={`group-user-${id}`}>
|
|
||||||
<Td>
|
|
||||||
<p>{`${firstName ?? "-"} ${lastName ?? ""}`}</p>
|
|
||||||
<p>{username}</p>
|
|
||||||
</Td>
|
|
||||||
<Td className="flex justify-end">
|
|
||||||
<OrgPermissionCan
|
|
||||||
I={OrgPermissionGroupActions.Edit}
|
|
||||||
a={OrgPermissionSubjects.Groups}
|
|
||||||
>
|
|
||||||
{(isAllowed) => {
|
|
||||||
return (
|
|
||||||
<Button
|
|
||||||
isLoading={isPending}
|
|
||||||
isDisabled={!isAllowed}
|
|
||||||
colorSchema="primary"
|
|
||||||
variant="outline_bg"
|
|
||||||
type="submit"
|
|
||||||
onClick={() => handleAddMember(username)}
|
|
||||||
>
|
|
||||||
Assign
|
|
||||||
</Button>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
</OrgPermissionCan>
|
|
||||||
</Td>
|
|
||||||
</Tr>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</TBody>
|
|
||||||
</Table>
|
|
||||||
{!isPending && totalCount > 0 && (
|
|
||||||
<Pagination
|
|
||||||
count={totalCount}
|
|
||||||
page={page}
|
|
||||||
perPage={perPage}
|
|
||||||
onChangePage={(newPage) => setPage(newPage)}
|
|
||||||
onChangePerPage={(newPerPage) => setPerPage(newPerPage)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{!isPending && !data?.users?.length && (
|
|
||||||
<EmptyState
|
|
||||||
title={
|
|
||||||
debouncedSearch ? "No users match search" : "All users are already in the group"
|
|
||||||
}
|
|
||||||
icon={faUsers}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</TableContainer>
|
|
||||||
</ModalContent>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -43,7 +43,7 @@ export const SshHostGroupsTable = ({ handlePopUpOpen }: Props) => {
|
|||||||
<THead>
|
<THead>
|
||||||
<Tr>
|
<Tr>
|
||||||
<Th>Name</Th>
|
<Th>Name</Th>
|
||||||
<Th>Hosts</Th>
|
<Th># Hosts</Th>
|
||||||
<Th>Login User - Authorized Principals Mapping</Th>
|
<Th>Login User - Authorized Principals Mapping</Th>
|
||||||
<Th />
|
<Th />
|
||||||
</Tr>
|
</Tr>
|
||||||
@@ -60,16 +60,16 @@ export const SshHostGroupsTable = ({ handlePopUpOpen }: Props) => {
|
|||||||
key={`ssh-host-group-${group.id}`}
|
key={`ssh-host-group-${group.id}`}
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
navigate({
|
navigate({
|
||||||
to: `/${ProjectType.SSH}/$projectId/ssh-groups/$sshGroupId` as const,
|
to: `/${ProjectType.SSH}/$projectId/ssh-host-groups/$sshHostGroupId` as const,
|
||||||
params: {
|
params: {
|
||||||
projectId: currentWorkspace.id,
|
projectId: currentWorkspace.id,
|
||||||
sshGroupId: group.id
|
sshHostGroupId: group.id
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Td>{group.name}</Td>
|
<Td>{group.name}</Td>
|
||||||
<Td>-</Td>
|
<Td>{group.hostCount}</Td>
|
||||||
<Td>
|
<Td>
|
||||||
{group.loginMappings.length === 0 ? (
|
{group.loginMappings.length === 0 ? (
|
||||||
<span className="italic text-mineshaft-400">None</span>
|
<span className="italic text-mineshaft-400">None</span>
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ import { Route as projectRoleDetailsBySlugPageRouteCertManagerImport } from './p
|
|||||||
import { Route as certManagerPkiCollectionDetailsByIDPageRoutesImport } from './pages/cert-manager/PkiCollectionDetailsByIDPage/routes'
|
import { Route as certManagerPkiCollectionDetailsByIDPageRoutesImport } from './pages/cert-manager/PkiCollectionDetailsByIDPage/routes'
|
||||||
import { Route as projectMemberDetailsByIDPageRouteCertManagerImport } from './pages/project/MemberDetailsByIDPage/route-cert-manager'
|
import { Route as projectMemberDetailsByIDPageRouteCertManagerImport } from './pages/project/MemberDetailsByIDPage/route-cert-manager'
|
||||||
import { Route as projectIdentityDetailsByIDPageRouteCertManagerImport } from './pages/project/IdentityDetailsByIDPage/route-cert-manager'
|
import { Route as projectIdentityDetailsByIDPageRouteCertManagerImport } from './pages/project/IdentityDetailsByIDPage/route-cert-manager'
|
||||||
import { Route as sshSshGroupDetailsByIDPageRouteImport } from './pages/ssh/SshGroupDetailsByIDPage/route'
|
import { Route as sshSshHostGroupDetailsByIDPageRouteImport } from './pages/ssh/SshHostGroupDetailsByIDPage/route'
|
||||||
import { Route as sshSshCaByIDPageRouteImport } from './pages/ssh/SshCaByIDPage/route'
|
import { Route as sshSshCaByIDPageRouteImport } from './pages/ssh/SshCaByIDPage/route'
|
||||||
import { Route as secretManagerSecretDashboardPageRouteImport } from './pages/secret-manager/SecretDashboardPage/route'
|
import { Route as secretManagerSecretDashboardPageRouteImport } from './pages/secret-manager/SecretDashboardPage/route'
|
||||||
import { Route as secretManagerIntegrationsSelectIntegrationAuthPageRouteImport } from './pages/secret-manager/integrations/SelectIntegrationAuthPage/route'
|
import { Route as secretManagerIntegrationsSelectIntegrationAuthPageRouteImport } from './pages/secret-manager/integrations/SelectIntegrationAuthPage/route'
|
||||||
@@ -1002,8 +1002,8 @@ const projectIdentityDetailsByIDPageRouteCertManagerRoute =
|
|||||||
getParentRoute: () => certManagerLayoutRoute,
|
getParentRoute: () => certManagerLayoutRoute,
|
||||||
} as any)
|
} as any)
|
||||||
|
|
||||||
const sshSshGroupDetailsByIDPageRouteRoute =
|
const sshSshHostGroupDetailsByIDPageRouteRoute =
|
||||||
sshSshGroupDetailsByIDPageRouteImport.update({
|
sshSshHostGroupDetailsByIDPageRouteImport.update({
|
||||||
id: '/ssh-host-groups/$sshHostGroupId',
|
id: '/ssh-host-groups/$sshHostGroupId',
|
||||||
path: '/ssh-host-groups/$sshHostGroupId',
|
path: '/ssh-host-groups/$sshHostGroupId',
|
||||||
getParentRoute: () => sshLayoutRoute,
|
getParentRoute: () => sshLayoutRoute,
|
||||||
@@ -2391,7 +2391,7 @@ declare module '@tanstack/react-router' {
|
|||||||
id: '/_authenticate/_inject-org-details/_org-layout/ssh/$projectId/_ssh-layout/ssh-host-groups/$sshHostGroupId'
|
id: '/_authenticate/_inject-org-details/_org-layout/ssh/$projectId/_ssh-layout/ssh-host-groups/$sshHostGroupId'
|
||||||
path: '/ssh-host-groups/$sshHostGroupId'
|
path: '/ssh-host-groups/$sshHostGroupId'
|
||||||
fullPath: '/ssh/$projectId/ssh-host-groups/$sshHostGroupId'
|
fullPath: '/ssh/$projectId/ssh-host-groups/$sshHostGroupId'
|
||||||
preLoaderRoute: typeof sshSshGroupDetailsByIDPageRouteImport
|
preLoaderRoute: typeof sshSshHostGroupDetailsByIDPageRouteImport
|
||||||
parentRoute: typeof sshLayoutImport
|
parentRoute: typeof sshLayoutImport
|
||||||
}
|
}
|
||||||
'/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout/identities/$identityId': {
|
'/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout/identities/$identityId': {
|
||||||
@@ -3561,7 +3561,7 @@ interface sshLayoutRouteChildren {
|
|||||||
sshSettingsPageRouteRoute: typeof sshSettingsPageRouteRoute
|
sshSettingsPageRouteRoute: typeof sshSettingsPageRouteRoute
|
||||||
projectAccessControlPageRouteSshRoute: typeof projectAccessControlPageRouteSshRoute
|
projectAccessControlPageRouteSshRoute: typeof projectAccessControlPageRouteSshRoute
|
||||||
sshSshCaByIDPageRouteRoute: typeof sshSshCaByIDPageRouteRoute
|
sshSshCaByIDPageRouteRoute: typeof sshSshCaByIDPageRouteRoute
|
||||||
sshSshGroupDetailsByIDPageRouteRoute: typeof sshSshGroupDetailsByIDPageRouteRoute
|
sshSshHostGroupDetailsByIDPageRouteRoute: typeof sshSshHostGroupDetailsByIDPageRouteRoute
|
||||||
projectIdentityDetailsByIDPageRouteSshRoute: typeof projectIdentityDetailsByIDPageRouteSshRoute
|
projectIdentityDetailsByIDPageRouteSshRoute: typeof projectIdentityDetailsByIDPageRouteSshRoute
|
||||||
projectMemberDetailsByIDPageRouteSshRoute: typeof projectMemberDetailsByIDPageRouteSshRoute
|
projectMemberDetailsByIDPageRouteSshRoute: typeof projectMemberDetailsByIDPageRouteSshRoute
|
||||||
projectRoleDetailsBySlugPageRouteSshRoute: typeof projectRoleDetailsBySlugPageRouteSshRoute
|
projectRoleDetailsBySlugPageRouteSshRoute: typeof projectRoleDetailsBySlugPageRouteSshRoute
|
||||||
@@ -3574,7 +3574,8 @@ const sshLayoutRouteChildren: sshLayoutRouteChildren = {
|
|||||||
sshSettingsPageRouteRoute: sshSettingsPageRouteRoute,
|
sshSettingsPageRouteRoute: sshSettingsPageRouteRoute,
|
||||||
projectAccessControlPageRouteSshRoute: projectAccessControlPageRouteSshRoute,
|
projectAccessControlPageRouteSshRoute: projectAccessControlPageRouteSshRoute,
|
||||||
sshSshCaByIDPageRouteRoute: sshSshCaByIDPageRouteRoute,
|
sshSshCaByIDPageRouteRoute: sshSshCaByIDPageRouteRoute,
|
||||||
sshSshGroupDetailsByIDPageRouteRoute: sshSshGroupDetailsByIDPageRouteRoute,
|
sshSshHostGroupDetailsByIDPageRouteRoute:
|
||||||
|
sshSshHostGroupDetailsByIDPageRouteRoute,
|
||||||
projectIdentityDetailsByIDPageRouteSshRoute:
|
projectIdentityDetailsByIDPageRouteSshRoute:
|
||||||
projectIdentityDetailsByIDPageRouteSshRoute,
|
projectIdentityDetailsByIDPageRouteSshRoute,
|
||||||
projectMemberDetailsByIDPageRouteSshRoute:
|
projectMemberDetailsByIDPageRouteSshRoute:
|
||||||
@@ -3883,7 +3884,7 @@ export interface FileRoutesByFullPath {
|
|||||||
'/secret-manager/$projectId/integrations/select-integration-auth': typeof secretManagerIntegrationsSelectIntegrationAuthPageRouteRoute
|
'/secret-manager/$projectId/integrations/select-integration-auth': typeof secretManagerIntegrationsSelectIntegrationAuthPageRouteRoute
|
||||||
'/secret-manager/$projectId/secrets/$envSlug': typeof secretManagerSecretDashboardPageRouteRoute
|
'/secret-manager/$projectId/secrets/$envSlug': typeof secretManagerSecretDashboardPageRouteRoute
|
||||||
'/ssh/$projectId/ca/$caId': typeof sshSshCaByIDPageRouteRoute
|
'/ssh/$projectId/ca/$caId': typeof sshSshCaByIDPageRouteRoute
|
||||||
'/ssh/$projectId/ssh-host-groups/$sshHostGroupId': typeof sshSshGroupDetailsByIDPageRouteRoute
|
'/ssh/$projectId/ssh-host-groups/$sshHostGroupId': typeof sshSshHostGroupDetailsByIDPageRouteRoute
|
||||||
'/cert-manager/$projectId/identities/$identityId': typeof projectIdentityDetailsByIDPageRouteCertManagerRoute
|
'/cert-manager/$projectId/identities/$identityId': typeof projectIdentityDetailsByIDPageRouteCertManagerRoute
|
||||||
'/cert-manager/$projectId/members/$membershipId': typeof projectMemberDetailsByIDPageRouteCertManagerRoute
|
'/cert-manager/$projectId/members/$membershipId': typeof projectMemberDetailsByIDPageRouteCertManagerRoute
|
||||||
'/cert-manager/$projectId/pki-collections/$collectionId': typeof certManagerPkiCollectionDetailsByIDPageRoutesRoute
|
'/cert-manager/$projectId/pki-collections/$collectionId': typeof certManagerPkiCollectionDetailsByIDPageRoutesRoute
|
||||||
@@ -4060,7 +4061,7 @@ export interface FileRoutesByTo {
|
|||||||
'/secret-manager/$projectId/integrations/select-integration-auth': typeof secretManagerIntegrationsSelectIntegrationAuthPageRouteRoute
|
'/secret-manager/$projectId/integrations/select-integration-auth': typeof secretManagerIntegrationsSelectIntegrationAuthPageRouteRoute
|
||||||
'/secret-manager/$projectId/secrets/$envSlug': typeof secretManagerSecretDashboardPageRouteRoute
|
'/secret-manager/$projectId/secrets/$envSlug': typeof secretManagerSecretDashboardPageRouteRoute
|
||||||
'/ssh/$projectId/ca/$caId': typeof sshSshCaByIDPageRouteRoute
|
'/ssh/$projectId/ca/$caId': typeof sshSshCaByIDPageRouteRoute
|
||||||
'/ssh/$projectId/ssh-host-groups/$sshHostGroupId': typeof sshSshGroupDetailsByIDPageRouteRoute
|
'/ssh/$projectId/ssh-host-groups/$sshHostGroupId': typeof sshSshHostGroupDetailsByIDPageRouteRoute
|
||||||
'/cert-manager/$projectId/identities/$identityId': typeof projectIdentityDetailsByIDPageRouteCertManagerRoute
|
'/cert-manager/$projectId/identities/$identityId': typeof projectIdentityDetailsByIDPageRouteCertManagerRoute
|
||||||
'/cert-manager/$projectId/members/$membershipId': typeof projectMemberDetailsByIDPageRouteCertManagerRoute
|
'/cert-manager/$projectId/members/$membershipId': typeof projectMemberDetailsByIDPageRouteCertManagerRoute
|
||||||
'/cert-manager/$projectId/pki-collections/$collectionId': typeof certManagerPkiCollectionDetailsByIDPageRoutesRoute
|
'/cert-manager/$projectId/pki-collections/$collectionId': typeof certManagerPkiCollectionDetailsByIDPageRoutesRoute
|
||||||
@@ -4255,7 +4256,7 @@ export interface FileRoutesById {
|
|||||||
'/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/integrations/select-integration-auth': typeof secretManagerIntegrationsSelectIntegrationAuthPageRouteRoute
|
'/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/integrations/select-integration-auth': typeof secretManagerIntegrationsSelectIntegrationAuthPageRouteRoute
|
||||||
'/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/secrets/$envSlug': typeof secretManagerSecretDashboardPageRouteRoute
|
'/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/secrets/$envSlug': typeof secretManagerSecretDashboardPageRouteRoute
|
||||||
'/_authenticate/_inject-org-details/_org-layout/ssh/$projectId/_ssh-layout/ca/$caId': typeof sshSshCaByIDPageRouteRoute
|
'/_authenticate/_inject-org-details/_org-layout/ssh/$projectId/_ssh-layout/ca/$caId': typeof sshSshCaByIDPageRouteRoute
|
||||||
'/_authenticate/_inject-org-details/_org-layout/ssh/$projectId/_ssh-layout/ssh-host-groups/$sshHostGroupId': typeof sshSshGroupDetailsByIDPageRouteRoute
|
'/_authenticate/_inject-org-details/_org-layout/ssh/$projectId/_ssh-layout/ssh-host-groups/$sshHostGroupId': typeof sshSshHostGroupDetailsByIDPageRouteRoute
|
||||||
'/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout/identities/$identityId': typeof projectIdentityDetailsByIDPageRouteCertManagerRoute
|
'/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout/identities/$identityId': typeof projectIdentityDetailsByIDPageRouteCertManagerRoute
|
||||||
'/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout/members/$membershipId': typeof projectMemberDetailsByIDPageRouteCertManagerRoute
|
'/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout/members/$membershipId': typeof projectMemberDetailsByIDPageRouteCertManagerRoute
|
||||||
'/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout/pki-collections/$collectionId': typeof certManagerPkiCollectionDetailsByIDPageRoutesRoute
|
'/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout/pki-collections/$collectionId': typeof certManagerPkiCollectionDetailsByIDPageRoutesRoute
|
||||||
@@ -5579,7 +5580,7 @@ export const routeTree = rootRoute
|
|||||||
"parent": "/_authenticate/_inject-org-details/_org-layout/ssh/$projectId/_ssh-layout"
|
"parent": "/_authenticate/_inject-org-details/_org-layout/ssh/$projectId/_ssh-layout"
|
||||||
},
|
},
|
||||||
"/_authenticate/_inject-org-details/_org-layout/ssh/$projectId/_ssh-layout/ssh-host-groups/$sshHostGroupId": {
|
"/_authenticate/_inject-org-details/_org-layout/ssh/$projectId/_ssh-layout/ssh-host-groups/$sshHostGroupId": {
|
||||||
"filePath": "ssh/SshGroupDetailsByIDPage/route.tsx",
|
"filePath": "ssh/SshHostGroupDetailsByIDPage/route.tsx",
|
||||||
"parent": "/_authenticate/_inject-org-details/_org-layout/ssh/$projectId/_ssh-layout"
|
"parent": "/_authenticate/_inject-org-details/_org-layout/ssh/$projectId/_ssh-layout"
|
||||||
},
|
},
|
||||||
"/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout/identities/$identityId": {
|
"/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout/identities/$identityId": {
|
||||||
|
|||||||
@@ -312,7 +312,7 @@ const sshRoutes = route("/ssh/$projectId", [
|
|||||||
route("/certificates", "ssh/SshCertsPage/route.tsx"),
|
route("/certificates", "ssh/SshCertsPage/route.tsx"),
|
||||||
route("/cas", "ssh/SshCasPage/route.tsx"),
|
route("/cas", "ssh/SshCasPage/route.tsx"),
|
||||||
route("/ca/$caId", "ssh/SshCaByIDPage/route.tsx"),
|
route("/ca/$caId", "ssh/SshCaByIDPage/route.tsx"),
|
||||||
route("/ssh-host-groups/$sshHostGroupId", "ssh/SshGroupDetailsByIDPage/route.tsx"),
|
route("/ssh-host-groups/$sshHostGroupId", "ssh/SshHostGroupDetailsByIDPage/route.tsx"),
|
||||||
route("/settings", "ssh/SettingsPage/route.tsx"),
|
route("/settings", "ssh/SettingsPage/route.tsx"),
|
||||||
route("/access-management", "project/AccessControlPage/route-ssh.tsx"),
|
route("/access-management", "project/AccessControlPage/route-ssh.tsx"),
|
||||||
route("/roles/$roleSlug", "project/RoleDetailsBySlugPage/route-ssh.tsx"),
|
route("/roles/$roleSlug", "project/RoleDetailsBySlugPage/route-ssh.tsx"),
|
||||||
|
|||||||
Reference in New Issue
Block a user