@directus/api
Version:
Directus is a real-time API and App dashboard for managing SQL database content
126 lines (124 loc) • 4.05 kB
JavaScript
import database_default from "../../../database/index.js";
import { fetchAccessRoles } from "../../../utils/fetch-user-count/fetch-access-roles.js";
import { AccessService } from "../../../services/access.js";
import { getSchema } from "../../../utils/get-schema.js";
import { UsersService } from "../../../services/users.js";
import "../../../services/index.js";
import { toBoolean } from "@directus/utils";
import { USER_INACTIVE_LICENSE_STATUS } from "@directus/constants";
//#region src/license/entitlements/lib/seats.ts
/**
* Group access rows to the admin/app users and roles that occupy a seat.
*/
function getSeatUsersAndRoles(accessRows) {
const adminRoles = /* @__PURE__ */ new Set();
const appRoles = /* @__PURE__ */ new Set();
const adminUsers = /* @__PURE__ */ new Set();
const appUsers = /* @__PURE__ */ new Set();
const appUsersByRole = /* @__PURE__ */ new Map();
for (const accessRow of accessRows) {
const { admin_access, app_access } = accessRow["policy"] || {};
const isAdmin = toBoolean(admin_access);
const isApp = !isAdmin && toBoolean(app_access);
if (!isAdmin && !isApp) continue;
if (accessRow["user"] && accessRow["user"].status === "active") {
const { id, role } = accessRow["user"];
if (isAdmin) {
adminUsers.add(id);
appUsers.delete(id);
} else if (adminUsers.has(id) === false && (!role || adminRoles.has(role) === false)) {
appUsers.add(id);
if (role) {
const roleUsers = appUsersByRole.get(role) ?? /* @__PURE__ */ new Set();
appUsersByRole.set(role, roleUsers.add(id));
}
}
}
if (accessRow["role"]) if (isAdmin) {
adminRoles.add(accessRow["role"]);
for (const id of appUsersByRole.get(accessRow["role"]) ?? []) appUsers.delete(id);
} else appRoles.add(accessRow["role"]);
}
return {
adminUsers,
appUsers,
adminRoles,
appRoles
};
}
async function getActiveSeats(opts) {
const knex = opts?.knex ?? database_default();
const schema = await getSchema({ database: knex });
const { adminUsers, appUsers, adminRoles, appRoles } = getSeatUsersAndRoles(await new AccessService({
schema,
knex
}).readByQuery({
fields: [
"role",
"user.id",
"user.status",
"user.role",
"policy.app_access",
"policy.admin_access"
],
limit: -1
}));
const { adminRoles: allAdminRoles, appRoles: allAppRoles } = await fetchAccessRoles({
adminRoles,
appRoles
}, { knex });
const usersService = new UsersService({
schema,
knex
});
const adminFilters = [{ _or: [{ id: { _in: Array.from(adminUsers) } }, { role: { _in: Array.from(allAdminRoles) } }] }, { status: { _eq: "active" } }];
const appFilters = [
{ _or: [{ id: { _in: Array.from(appUsers) } }, { role: { _in: Array.from(allAppRoles) } }] },
{ id: { _nin: Array.from(adminUsers) } },
{ _or: [{ role: { _nin: Array.from(allAdminRoles) } }, { role: { _null: true } }] },
{ status: { _eq: "active" } }
];
if (opts?.adminId) {
adminFilters.push({ id: { _neq: opts.adminId } });
appFilters.push({ id: { _neq: opts.adminId } });
}
const adminCandidates = await usersService.readByQuery({
fields: [
"id",
"first_name",
"last_name",
"avatar",
"email"
],
filter: { _and: adminFilters },
limit: -1
});
return [...await usersService.readByQuery({
fields: [
"id",
"first_name",
"last_name",
"avatar",
"email"
],
filter: { _and: appFilters },
limit: -1
}), ...adminCandidates.map((admin) => ({
...admin,
admin: true
}))];
}
async function countActiveSeats(opts) {
return (await getActiveSeats(opts)).length;
}
async function resolveSeats(seats, ctx) {
if (!ctx?.accountability?.user) return;
const usersService = new UsersService({
schema: await getSchema(),
accountability: ctx.accountability
});
const users = seats.filter((user_id) => user_id !== ctx.accountability.user);
await Promise.allSettled(users.map((user_id) => usersService.updateOne(user_id, { status: USER_INACTIVE_LICENSE_STATUS })));
}
//#endregion
export { countActiveSeats, getActiveSeats, getSeatUsersAndRoles, resolveSeats };