From 735e039b25d86e01aa64a136d180ed00d9927e78 Mon Sep 17 00:00:00 2001 From: Harald Schilly Date: Fri, 12 Jul 2024 12:18:25 +0200 Subject: [PATCH] hub/delete-project: add explicit delete_project_data setting --- .../database/postgres/delete-projects.ts | 43 ++++++++++++------- src/packages/util/db-schema/site-defaults.ts | 7 +++ 2 files changed, 35 insertions(+), 15 deletions(-) diff --git a/src/packages/database/postgres/delete-projects.ts b/src/packages/database/postgres/delete-projects.ts index 701e831839a..480cca7a528 100644 --- a/src/packages/database/postgres/delete-projects.ts +++ b/src/packages/database/postgres/delete-projects.ts @@ -130,12 +130,18 @@ export async function cleanup_old_projects_data( ) { const settings = await getServerSettings(); const on_prem = settings.kucalc === KUCALC_ON_PREMISES; + const delete_data = settings.delete_project_data; const L0 = log.extend("cleanup_old_projects_data"); const L = L0.debug; - L("args", { max_run_m, on_prem }); - const start_ts = new Date(); + L("args", { max_run_m, on_prem, delete_data }); + + if (!delete_data) { + L(`deleting project data is disabled ('delete_project_data' setting).`); + return; + } + const start_ts = new Date(); const pool = getPool(); let numSyncStr = 0; @@ -171,27 +177,34 @@ export async function cleanup_old_projects_data( numProj += 1; let delRows = 0; + // Clean up data *on* a given project. For now, remove all site licenses. + await pool.query( + `UPDATE projects SET site_license = NULL WHERE project_id = $1`, + [project_id], + ); + if (on_prem) { L2(`delete all project files`); await deleteProjectFiles(L2, project_id); L2(`deleting all shared files`); - // this is something like /shared/projects/${project_id} - const shared_path = pathToFiles(project_id, ""); - await fs.rm(shared_path, { recursive: true, force: true }); - - // for now, on-prem only as well. This gets rid of all sorts of data in tables specific to the given project. - delRows += await delete_associated_project_data(L2, project_id); + try { + // this is something like /shared/projects/${project_id} + const shared_path = pathToFiles(project_id, ""); + await fs.rm(shared_path, { recursive: true, force: true }); + } catch (err) { + L2(`Unable to delete shared files: ${err}`); + } } + // This gets rid of all sorts of data in tables specific to the given project. + delRows += await delete_associated_project_data(L2, project_id); + // now, that we're done with that project, mark it as state.state ->> 'deleted' - // in addition to the flag "deleted = true" - await callback2(db.set_project_state, { - project_id, - state: "deleted", - }); + // in addition to the flag "deleted = true". This also updates the state.time timestamp. + await callback2(db.set_project_state, { project_id, state: "deleted" }); L2( - `finished deleting project data | deleted ${delRows} entries | setting state.state="deleted"`, + `finished deleting project data | deleted ${delRows} entries | state.state="deleted"`, ); } @@ -247,7 +260,7 @@ async function delete_associated_project_data( const { rowsDeleted } = await bulkDelete({ table: "listings", field: "project_id", - id: "project_id", // TODO listings has a more complex ID, is this a problem? + id: "project_id", // TODO listings has a more complex ID, which means this gets rid of everything in one go. should be fine, though. value: project_id, }); total += rowsDeleted; diff --git a/src/packages/util/db-schema/site-defaults.ts b/src/packages/util/db-schema/site-defaults.ts index 6b93f775b75..ddfe3889dd4 100644 --- a/src/packages/util/db-schema/site-defaults.ts +++ b/src/packages/util/db-schema/site-defaults.ts @@ -80,6 +80,7 @@ export type SiteSettingsKeys = | "require_license_to_create_project" | "google_analytics" | "kucalc" + | "delete_project_data" | "dns" | "datastore" | "ssh_gateway" @@ -652,6 +653,12 @@ export const site_settings_conf: SiteSettings = { to_val: split_iframe_comm_hosts, to_display: num_dns_hosts, }, + delete_project_data : { + name :"Delete Project Data", + desc: "When a project has been marked as deleted, also actually delete associated data from the database and (for OnPrem) also its files.", + default: "no", + to_val: to_bool, + }, email_enabled: { name: "Email sending enabled", desc: "Controls visibility of UI elements and if any emails are sent. This is independent of any particular email configuration!",