@cocalc/server
Version:
CoCalc server functionality: functions used by either the hub and the next.js server
63 lines (58 loc) • 1.84 kB
text/typescript
/* Returns users of the sandbox that are idle. */
import getPool from "@cocalc/database/pool";
import { is_valid_uuid_string as isValid } from "@cocalc/util/misc";
export default async function idleSandboxUsers(
project_id: string,
idleTimeoutSeconds: number = 60 * 10
): Promise<string[]> {
if (!isValid(project_id)) {
throw Error("invalid project_id");
}
const pool = getPool("long"); // long cache makes sense here.
const { rows } = await pool.query(
`SELECT users, last_active, sandbox FROM projects WHERE project_id=$1`,
[project_id]
);
if (rows.length == 0) {
// no such project -- nothing to do
return [];
}
if (!rows[0].sandbox) {
// not a sandbox, so nothing to do
return [];
}
const idleUsers: string[] = [];
const { users, last_active } = rows[0];
const now = new Date().valueOf();
const cutoff = now - idleTimeoutSeconds * 1000;
const addToLastActive: string[] = []; // these got added to sandbox but no activity being tracked yet, so we initialize it.
for (const account_id in users ?? {}) {
const active = new Date(last_active?.[account_id] ?? 0).valueOf();
if (!active) {
addToLastActive.push(account_id);
} else if (active <= cutoff && users[account_id]?.["group"] != "owner") {
idleUsers.push(account_id);
}
}
if (addToLastActive.length > 0) {
await updateLastActive(project_id, addToLastActive);
}
return idleUsers;
}
async function updateLastActive(
project_id: string,
users: string[]
): Promise<void> {
const pool = getPool();
const now = new Date();
const X: any = {};
for (const account_id of users) {
X[account_id] = now;
}
await pool.query(
`UPDATE projects SET last_active = COALESCE(last_active, '{}') || '${JSON.stringify(
X
)}' WHERE project_id=$1`,
[project_id]
);
}