Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/src/packages/util/db-schema/organizations.ts
Views: 687
/*1Table of organizations.23WARNING: Organizations are far from actually being implemented4fully in Cocalc! I just figure defining this can't hurt to get5the ball rollowing.6*/78import { Table } from "./types";9import { checkAccountName as checkOrganizationName } from "./name-rules";1011Table({12name: "organizations",13fields: {14organization_id: {15type: "uuid",16desc: "The uuid that determines this organization",17},18created: {19type: "timestamp",20desc: "When the organization was created.",21},22deleted: {23type: "boolean",24desc: "True if this organization has been deleted.",25},26name: {27type: "string",28pg_type: "VARCHAR(39)",29desc: "The name of this organization (used for URL's). This is optional but globally unique across all organizations *and* accounts. It can be between 1 and 39 characters from a-z A-Z 0-9 - and must not start with a dash.",30},31title: {32type: "string",33pg_type: "VARCHAR(254)",34desc: "Title of this organization",35},36description: {37type: "string",38pg_type: "VARCHAR(254)",39desc: "Description of this organization.",40},41link: {42type: "string",43pg_type: "VARCHAR(254)",44desc: "Optional URL of this organization (e.g., their webpage).",45},46email_address: {47type: "string",48pg_type: "VARCHAR(254)",49desc: "Optional email address to reach this organization.",50},51api_key: {52type: "string",53desc: "Optional API key that grants full API access to all projects that this organization owns. Key is of the form 'sk_9QabcrqJFy7JIhvAGih5c6Nb', where the random part is 24 characters (base 62).",54},55profile: {56type: "map",57desc: "Information related to displaying an avatar for this organization.",58},59users: {60type: "map",61desc: "This is a map from account_id to 'owner' | 'member'.",62},63invitations: {64type: "map",65desc: "This is a map from account_id to {created:timestamp, status:'pending'|'invited'|'accepted'|'denied', emailed:timestamp}",66},67},68rules: {69desc: "All organizations.",70primary_key: "organization_id",71pg_indexes: [72"(lower(title) text_pattern_ops)",73"(lower(description) text_pattern_ops)",74"api_key",75],76pg_unique_indexes: [77"LOWER(name)", // see comments for accounts table.78],79user_query: {80get: {81throttle_changes: 500,82pg_where: [{ "organization_id = $::UUID": "organization_id" }],83fields: {84organization_id: null,85email_address: null,86name: "",87title: "",88description: "",89profile: {90image: undefined,91color: undefined,92},93created: null,94},95},96set: {97fields: {98organization_id: true,99name: true,100title: true,101description: true,102profile: true,103},104required_fields: {105organization_id: true,106},107async check_hook(db, obj, account_id, _project_id, cb) {108// Check that account_id is a member of this organization109// via a db query, since otherwise no permission to do anything.110if (111!(await db.accountIsInOrganization({112account_id,113organization_id: obj["organization_id"],114}))115) {116cb(`account must be a member of the organization`);117return;118}119120if (obj["name"] != null) {121try {122checkOrganizationName(obj["name"]);123} catch (err) {124cb(err.toString());125return;126}127const id = await db.nameToAccountOrOrganization(obj["name"]);128if (id != null && id != account_id) {129cb(130`name "${obj["name"]}" is already taken by another organization or account`131);132return;133}134}135136// Hook to truncate some text fields to at most 254 characters, to avoid137// further trouble down the line.138for (const field of ["title", "description", "email_address"]) {139if (obj[field] != null) {140obj[field] = obj[field].slice(0, 254);141}142}143cb();144},145},146},147},148});149150151