mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-08-23 14:36:41 +00:00
fix: deduplicate shared hosts (#1098)
This commit is contained in:
@@ -26,6 +26,28 @@ export interface HostListAccessEntry {
|
||||
permissionLevel: string;
|
||||
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 & {
|
||||
ownerId: string;
|
||||
isShared: boolean;
|
||||
@@ -103,9 +125,15 @@ export class HostResolutionRepository {
|
||||
.from(hosts)
|
||||
.where(eq(hosts.userId, userId));
|
||||
|
||||
const sharedHostIds = Array.from(
|
||||
new Set(accessEntries.map((access) => access.hostId)),
|
||||
);
|
||||
const accessByHostId = new Map<number, HostListAccessEntry>();
|
||||
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 =
|
||||
sharedHostIds.length > 0
|
||||
? await this.context.drizzle
|
||||
@@ -125,7 +153,7 @@ export class HostResolutionRepository {
|
||||
permissionLevel: undefined,
|
||||
expiresAt: undefined,
|
||||
})),
|
||||
...accessEntries.flatMap((access) => {
|
||||
...Array.from(accessByHostId.values()).flatMap((access) => {
|
||||
const host = sharedHostsById.get(access.hostId);
|
||||
if (!host || host.userId === userId) {
|
||||
return [];
|
||||
|
||||
@@ -297,7 +297,12 @@ describe("HostResolutionRepository", () => {
|
||||
const repository = await createRepository();
|
||||
|
||||
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: 999, permissionLevel: "view", expiresAt: null },
|
||||
]);
|
||||
@@ -316,8 +321,8 @@ describe("HostResolutionRepository", () => {
|
||||
userId: "user-1",
|
||||
ownerId: "user-1",
|
||||
isShared: true,
|
||||
permissionLevel: "execute",
|
||||
expiresAt: null,
|
||||
permissionLevel: "manage",
|
||||
expiresAt: "2026-07-01T00:00:00.000Z",
|
||||
});
|
||||
expect(DataCrypto.decryptRecord).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user