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/database/postgres/project-queries.ts
Views: 687
/*1* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.2* License: MS-RSL – see LICENSE.md for details3*/45import { omit } from "lodash";6import { PostgreSQL } from "./types";7import { callback2 } from "@cocalc/util/async-utils";8import { query } from "./query";9import debug from "debug";10const L = debug("hub:project-queries");11import { DUMMY_SECRET } from "@cocalc/util/consts";12import { DatastoreConfig } from "@cocalc/util/types";1314export async function project_has_network_access(15db: PostgreSQL,16project_id: string17): Promise<boolean> {18let x;19try {20x = await callback2(db.get_project, {21project_id,22columns: ["users", "settings"],23});24} catch (err) {25// error probably means there is no such project or project_id is badly formatted.26return false;27}28if (x.settings != null && x.settings.network) {29return true;30}31if (x.users != null) {32for (const account_id in x.users) {33if (34x.users[account_id] != null &&35x.users[account_id].upgrades != null &&36x.users[account_id].upgrades.network37) {38return true;39}40}41}42return false;43}4445// get/set/del datastore configurations in addons4647interface GetDSOpts {48db: PostgreSQL;49account_id: string;50project_id: string;51}5253async function get_datastore(54opts: GetDSOpts55): Promise<{ [key: string]: DatastoreConfig }> {56const { db, account_id, project_id } = opts;57const q: { users: any; addons?: any } = await query({58db,59table: "projects",60select: ["addons", "users"],61where: { project_id },62one: true,63});6465// this access test is absolutely critial to have! (only project queries set access_check to false)66if (q.users[account_id] == null) throw Error(`access denied`);6768return q.addons?.datastore;69}7071export async function project_datastore_set(72db: PostgreSQL,73account_id: string,74project_id: string,75config: any76): Promise<void> {77// L("project_datastore_set", config);7879if (config.name == null) throw Error("configuration 'name' is not defined");80if (typeof config.type !== "string")81throw Error(82"configuration 'type' is not defined (must be 'gcs', 'sshfs', ...)"83);8485// check data from user86for (const [key, val] of Object.entries(config)) {87if (typeof val !== "string" && typeof val !== "boolean") {88throw new Error(`Invalid value -- '${key}' is not a valid type`);89}90if (typeof val === "string" && val.length > 100000) {91throw new Error(`Invalid value -- '${key}' is too long`);92}93}9495const old_name = config.__old_name;96const conf_new = omit(config, "name", "secret", "__old_name");9798// this is implicitly a test if the user has access to modify this -- don't catch it99const ds_prev = await get_datastore({ db, account_id, project_id });100101// there is a situation where datastore is renamed, i.e. "name" is a new one,102// while the previous secret is stored under a different key. So, if __old_name103// is set, we pick that one instead.104const prev_name = old_name != null ? old_name : config.name;105106// if a user wants to update the settings, they don't need to have the secret.107// an empty value or the dummy text signals to keep the secret as it is...108if (109ds_prev != null &&110ds_prev[prev_name] != null &&111(config.secret === DUMMY_SECRET || config.secret === "")112) {113conf_new.secret = ds_prev[prev_name].secret;114} else {115conf_new.secret = Buffer.from(config.secret ?? "").toString("base64");116}117118await query({119db,120query: "UPDATE projects",121where: { "project_id = $::UUID": project_id },122jsonb_merge: { addons: { datastore: { [config.name]: conf_new } } },123});124}125126export async function project_datastore_del(127db: PostgreSQL,128account_id: string,129project_id: string,130name: string131): Promise<void> {132L("project_datastore_del", name);133if (typeof name !== "string" || name.length == 0) {134throw Error("Datastore name not properly set.");135}136137// this is implicitly a test if the user has access to modify this -- don't catch it138const ds = await get_datastore({ db, account_id, project_id });139delete ds[name];140await query({141db,142query: "UPDATE projects",143where: { "project_id = $::UUID": project_id },144jsonb_set: { addons: { datastore: ds } },145});146}147148export async function project_datastore_get(149db: PostgreSQL,150account_id: string,151project_id: string152): Promise<any> {153try {154const ds = await get_datastore({155db,156account_id,157project_id,158});159if (ds != null) {160for (const [k, v] of Object.entries(ds)) {161ds[k] = omit(v, "secret") as any;162}163}164return {165addons: { datastore: ds },166};167} catch (err) {168return { type: "error", error: `${err}` };169}170}171172173