release(main): 同步 dev 最新候选改动
Storage Persistence Guard / storage-persistence-audit (push) Has been cancelled
Client Quality Gates / Shared client and Web (push) Has been cancelled
Client Quality Gates / macOS Desktop (push) Has been cancelled
Client Quality Gates / Shared client and Web (pull_request) Has been cancelled
Client Quality Gates / macOS Desktop (pull_request) Has been cancelled
Storage Persistence Guard / storage-persistence-audit (pull_request) Has been cancelled
Storage Persistence Guard / storage-persistence-audit (push) Has been cancelled
Client Quality Gates / Shared client and Web (push) Has been cancelled
Client Quality Gates / macOS Desktop (push) Has been cancelled
Client Quality Gates / Shared client and Web (pull_request) Has been cancelled
Client Quality Gates / macOS Desktop (pull_request) Has been cancelled
Storage Persistence Guard / storage-persistence-audit (pull_request) Has been cancelled
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
import type { UserLoginActivity } from "../../types/api";
|
||||
|
||||
export type DisplayLoginActivity = UserLoginActivity & { grouped_count: number };
|
||||
|
||||
type ActivityStatus = NonNullable<UserLoginActivity["activity_status"]>;
|
||||
|
||||
const activityTime = (value: string) => new Date(value).getTime();
|
||||
|
||||
export const activitySourceKey = (item: UserLoginActivity) => {
|
||||
if (!item.login_ip) return `session:${item.id}`;
|
||||
return [item.client_type, item.login_ip].join("|");
|
||||
};
|
||||
|
||||
export const groupLoginActivitiesBySource = (
|
||||
activities: UserLoginActivity[],
|
||||
resolveStatus: (item: UserLoginActivity) => ActivityStatus,
|
||||
): DisplayLoginActivity[] => {
|
||||
const grouped = new Map<
|
||||
string,
|
||||
{ row: DisplayLoginActivity; firstLoginAt: string; hasOnlineSession: boolean }
|
||||
>();
|
||||
|
||||
activities.forEach((item) => {
|
||||
const key = activitySourceKey(item);
|
||||
const status = resolveStatus(item);
|
||||
const existing = grouped.get(key);
|
||||
if (!existing) {
|
||||
grouped.set(key, {
|
||||
row: { ...item, activity_status: status, grouped_count: 1 },
|
||||
firstLoginAt: item.login_at,
|
||||
hasOnlineSession: status === "ONLINE",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
existing.row.grouped_count += 1;
|
||||
existing.hasOnlineSession ||= status === "ONLINE";
|
||||
if (activityTime(item.login_at) < activityTime(existing.firstLoginAt)) {
|
||||
existing.firstLoginAt = item.login_at;
|
||||
}
|
||||
if (activityTime(item.last_seen_at) > activityTime(existing.row.last_seen_at)) {
|
||||
const groupedCount = existing.row.grouped_count;
|
||||
existing.row = { ...item, activity_status: status, grouped_count: groupedCount };
|
||||
}
|
||||
});
|
||||
|
||||
return Array.from(grouped.values())
|
||||
.map(({ row, firstLoginAt, hasOnlineSession }) => ({
|
||||
...row,
|
||||
login_at: firstLoginAt,
|
||||
activity_status: hasOnlineSession ? "ONLINE" : row.activity_status,
|
||||
ended_at: hasOnlineSession ? null : row.ended_at,
|
||||
end_reason: hasOnlineSession ? null : row.end_reason,
|
||||
}))
|
||||
.sort((left, right) => activityTime(right.last_seen_at) - activityTime(left.last_seen_at));
|
||||
};
|
||||
Reference in New Issue
Block a user