Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.
Path: blob/master/src/packages/util/db-schema/groups.ts
Views: 791
/*1* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.2* License: MS-RSL – see LICENSE.md for details3*/45/*6Groups of cocalc accounts.7*/89import { Table } from "./types";10import { SCHEMA } from "./index";11import { uuid } from "../misc";1213export interface Group {14// primary key: a uuid15group_id: string;16// owners -- uuids of owners of the group17owner_account_ids?: string[];18// members -- uuids of members of the group19member_account_ids?: string[];20// the title21title?: string;22color?: string;23}2425export const MAX_TITLE_LENGTH = 1024;26export const MAX_COLOR_LENGTH = 30;2728Table({29name: "groups",30fields: {31group_id: {32type: "uuid",33desc: "Unique id of this group of accounts.",34},35owner_account_ids: {36type: "array",37pg_type: "UUID[]",38desc: "Unique id's of owners of this group. They can add/remove members or other owners. This can be null, e.g., for implicitly created groups (e.g., to send a group message), there's no need for a management.",39},40member_account_ids: {41type: "array",42pg_type: "UUID[]",43desc: "Unique id's of owners of this group. They can add/remove members or other owners.",44},45title: {46type: "string",47pg_type: `VARCHAR(${MAX_TITLE_LENGTH})`,48desc: "Title of this group of accounts",49},50color: {51type: "string",52desc: "A user configurable color.",53pg_type: `VARCHAR(${MAX_COLOR_LENGTH})`,54render: { type: "color", editable: true },55},56},57rules: {58primary_key: "group_id",59pg_indexes: [60"USING GIN (owner_account_ids)",61"USING GIN (member_account_ids)",62],63changefeed_keys: ["owner_account_ids"],64user_query: {65get: {66pg_where: [{ "$::UUID = ANY(owner_account_ids)": "account_id" }],67fields: {68group_id: null,69owner_account_ids: null,70member_account_ids: null,71title: null,72color: null,73},74},75set: {76fields: {77group_id: true,78owner_account_ids: true,79member_account_ids: true,80title: true,81color: true,82},83async check_hook(database, query, account_id, _project_id, cb) {84// for sets we have to manually check that the this user is an owner, because85// we didn't implement something like `project_id: "project_write"` which is86// usually used for validating writes. Also the where above is obviously87// only for gets and changefeeds.88try {89const client = database._client();90const { rows } = await client.query(91"SELECT COUNT(*) AS count FROM groups WHERE $1=ANY(owner_account_ids) AND group_id=$2",92[account_id, query?.group_id],93);94if (rows[0].count != 1) {95throw Error("user must be an owner of the group");96}97cb();98} catch (err) {99cb(`${err}`);100}101},102},103},104},105});106107// Use the create_groups virtual table to create a new group.108// We have to do this, since users shouldn't assign uuid's109// AND our check_hook above prevents a user from writing110// to a group if they don't already own it, and they don't111// own one they are creating.112// This is a get query, because you do a get for113// {group_id:null, owner_account_ids:...}114// and the group_id gets filled in with your new record's id.115Table({116name: "create_group",117rules: {118virtual: "groups",119primary_key: "group_id",120user_query: {121get: {122fields: {123group_id: null,124owner_account_ids: null,125member_account_ids: null,126title: null,127color: null,128},129async instead_of_query(database, opts, cb): Promise<void> {130try {131// server assigned:132const group_id = uuid();133const client = database._client();134const query = opts.query ?? {};135const owner_account_ids = [...query.owner_account_ids];136if (!owner_account_ids.includes(opts.account_id)) {137owner_account_ids.push(opts.account_id);138}139const { member_account_ids, title, color } = query;140await client.query(141"INSERT INTO groups(group_id, owner_account_ids, member_account_ids, title, color) VALUES($1,$2,$3,$4,$5)",142[group_id, owner_account_ids, member_account_ids, title, color],143);144cb(undefined, {145group_id,146owner_account_ids,147member_account_ids,148title: title?.slice(0, MAX_TITLE_LENGTH),149color: color?.slice(0, MAX_COLOR_LENGTH),150});151} catch (err) {152cb(`${err}`);153}154},155},156},157},158fields: SCHEMA.groups.fields,159});160161Table({162name: "crm_groups",163rules: {164virtual: "groups",165primary_key: "group_id",166user_query: {167get: {168admin: true,169fields: SCHEMA.groups.user_query?.get?.fields ?? {},170},171},172},173fields: SCHEMA.groups.fields,174});175176177