mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-08-29 18:31:33 +00:00
fix: deduplicate shared hosts (#1098)
This commit is contained in:
@@ -26,6 +26,28 @@ export interface HostListAccessEntry {
|
|||||||
permissionLevel: string;
|
permissionLevel: string;
|
||||||
expiresAt: string | null;
|
expiresAt: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const HOST_PERMISSION_RANK: Record<string, number> = {
|
||||||
|
connect: 1,
|
||||||
|
view: 2,
|
||||||
|
edit: 3,
|
||||||
|
manage: 4,
|
||||||
|
};
|
||||||
|
|
||||||
|
function preferHostAccess(
|
||||||
|
current: HostListAccessEntry,
|
||||||
|
candidate: HostListAccessEntry,
|
||||||
|
): HostListAccessEntry {
|
||||||
|
const currentRank = HOST_PERMISSION_RANK[current.permissionLevel] ?? 0;
|
||||||
|
const candidateRank = HOST_PERMISSION_RANK[candidate.permissionLevel] ?? 0;
|
||||||
|
if (candidateRank !== currentRank) {
|
||||||
|
return candidateRank > currentRank ? candidate : current;
|
||||||
|
}
|
||||||
|
if (current.expiresAt === null) return current;
|
||||||
|
if (candidate.expiresAt === null) return candidate;
|
||||||
|
return candidate.expiresAt > current.expiresAt ? candidate : current;
|
||||||
|
}
|
||||||
|
|
||||||
export type HostListRow = HostResolutionHostRecord & {
|
export type HostListRow = HostResolutionHostRecord & {
|
||||||
ownerId: string;
|
ownerId: string;
|
||||||
isShared: boolean;
|
isShared: boolean;
|
||||||
@@ -103,9 +125,15 @@ export class HostResolutionRepository {
|
|||||||
.from(hosts)
|
.from(hosts)
|
||||||
.where(eq(hosts.userId, userId));
|
.where(eq(hosts.userId, userId));
|
||||||
|
|
||||||
const sharedHostIds = Array.from(
|
const accessByHostId = new Map<number, HostListAccessEntry>();
|
||||||
new Set(accessEntries.map((access) => access.hostId)),
|
for (const access of accessEntries) {
|
||||||
);
|
const current = accessByHostId.get(access.hostId);
|
||||||
|
accessByHostId.set(
|
||||||
|
access.hostId,
|
||||||
|
current ? preferHostAccess(current, access) : access,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const sharedHostIds = Array.from(accessByHostId.keys());
|
||||||
const sharedHostRows =
|
const sharedHostRows =
|
||||||
sharedHostIds.length > 0
|
sharedHostIds.length > 0
|
||||||
? await this.context.drizzle
|
? await this.context.drizzle
|
||||||
@@ -125,7 +153,7 @@ export class HostResolutionRepository {
|
|||||||
permissionLevel: undefined,
|
permissionLevel: undefined,
|
||||||
expiresAt: undefined,
|
expiresAt: undefined,
|
||||||
})),
|
})),
|
||||||
...accessEntries.flatMap((access) => {
|
...Array.from(accessByHostId.values()).flatMap((access) => {
|
||||||
const host = sharedHostsById.get(access.hostId);
|
const host = sharedHostsById.get(access.hostId);
|
||||||
if (!host || host.userId === userId) {
|
if (!host || host.userId === userId) {
|
||||||
return [];
|
return [];
|
||||||
|
|||||||
@@ -297,7 +297,12 @@ describe("HostResolutionRepository", () => {
|
|||||||
const repository = await createRepository();
|
const repository = await createRepository();
|
||||||
|
|
||||||
const rows = await repository.listHostRowsForAccessList("user-2", [
|
const rows = await repository.listHostRowsForAccessList("user-2", [
|
||||||
{ hostId: 1, permissionLevel: "execute", expiresAt: null },
|
{ hostId: 1, permissionLevel: "view", expiresAt: null },
|
||||||
|
{
|
||||||
|
hostId: 1,
|
||||||
|
permissionLevel: "manage",
|
||||||
|
expiresAt: "2026-07-01T00:00:00.000Z",
|
||||||
|
},
|
||||||
{ hostId: 3, permissionLevel: "view", expiresAt: null },
|
{ hostId: 3, permissionLevel: "view", expiresAt: null },
|
||||||
{ hostId: 999, permissionLevel: "view", expiresAt: null },
|
{ hostId: 999, permissionLevel: "view", expiresAt: null },
|
||||||
]);
|
]);
|
||||||
@@ -316,8 +321,8 @@ describe("HostResolutionRepository", () => {
|
|||||||
userId: "user-1",
|
userId: "user-1",
|
||||||
ownerId: "user-1",
|
ownerId: "user-1",
|
||||||
isShared: true,
|
isShared: true,
|
||||||
permissionLevel: "execute",
|
permissionLevel: "manage",
|
||||||
expiresAt: null,
|
expiresAt: "2026-07-01T00:00:00.000Z",
|
||||||
});
|
});
|
||||||
expect(DataCrypto.decryptRecord).not.toHaveBeenCalled();
|
expect(DataCrypto.decryptRecord).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user