@xuda.io/drive_module 1.1.1501 → 1.1.1502

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/index.mjs +72 -3
  2. package/package.json +1 -1
package/index.mjs CHANGED
@@ -1923,6 +1923,69 @@ export const upload_drive_file = async (req, job_id, headers = {}, file_obj) =>
1923
1923
  };
1924
1924
  const check_existing_file_ret = await check_drive_file(check_req);
1925
1925
 
1926
+ // OVERWRITE IN PLACE. Callers that mean "update this file" (the office plugins editing a
1927
+ // deck / document the user named) opt in with req.overwrite. Without it the block below
1928
+ // dedupes the name, so "update /Presentations/deck.pptx" quietly produced
1929
+ // "deck (1).pptx" and the file the user was looking at never changed.
1930
+ //
1931
+ // The existing doc is REUSED rather than replaced: _id, server_file_name and the bucket
1932
+ // key all stay, so every link already handed out (a chat file card, a shared URL, an
1933
+ // agent's saved reference) still resolves to the edited bytes. Only the mutable facts
1934
+ // move. preview_stat goes back to 1 so the rendered preview is regenerated from the new
1935
+ // content instead of showing the old slides for ever.
1936
+ //
1937
+ // Bucket-backed drives only. The datacenter / is_deployment branches ship bytes over scp
1938
+ // to a path derived from the same name, so an overwrite there would need its own handling;
1939
+ // until it does, those fall through to the dedupe rather than half-writing.
1940
+ if (check_existing_file_ret.code < 0 && req.overwrite) {
1941
+ try {
1942
+ const existing = await get_drive_doc('file', drive_type, app_id, uid, path.join(doc.file_path, originalname), headers);
1943
+ const existing_key = existing?.server_file_name;
1944
+ const app_obj_ret = drive_type === 'workspace' ? await db_module.get_app_obj(app_id) : null;
1945
+ const scp_backed = !!(app_obj_ret?.data?.app_type === 'datacenter' || app_obj_ret?.data?.is_deployment);
1946
+
1947
+ if (existing_key && drive_type !== 'studio' && !scp_backed) {
1948
+ const ref_ow = drive_type === 'user' ? await get_account_default_project_id(uid) : await _common.get_project_app_id(req.app_id, true);
1949
+ existing.bucket = {
1950
+ [`${process.env.XUDA_HOSTNAME}`]: await upload_file_to_spaces(tempPath, ref_ow, drive_type, existing_key),
1951
+ };
1952
+ existing.size = doc.size;
1953
+ existing.mime = doc.mime;
1954
+ existing.file_ext = doc.file_ext;
1955
+ existing.ts = Date.now();
1956
+ // Back to 1 (queued) so the office->pdf render re-runs against the new bytes;
1957
+ // without it the viewer would keep showing the slides from before the edit.
1958
+ // preview_link is not stored, it is derived per read by preview_links_for.
1959
+ existing.preview_stat = doc.preview_stat;
1960
+
1961
+ const save_ow =
1962
+ drive_type === 'user' ? await save_user_drive_file(uid, existing) : await db_module.save_app_couch_doc(await _common.get_project_app_id(req.app_id), existing);
1963
+ if (save_ow.code < 0) throw new Error(save_ow.data);
1964
+
1965
+ await fs_module.unlink(tempPath).catch(() => {});
1966
+ if (existing.preview_stat === 1) {
1967
+ generate_drive_file_preview({ app_id: req.app_id, drive_type, uid, doc_id: existing._id }).catch(() => {});
1968
+ }
1969
+
1970
+ return {
1971
+ code: 760,
1972
+ data: {
1973
+ server_file_name: existing_key,
1974
+ filename: existing.originalname,
1975
+ file_url: get_file_url(drive_type, existing_key, ref_ow),
1976
+ file_path: existing.file_path,
1977
+ bucket: existing.bucket,
1978
+ },
1979
+ overwritten: true,
1980
+ };
1981
+ }
1982
+ } catch (err) {
1983
+ // An overwrite that cannot be completed must not lose the upload: fall through to the
1984
+ // dedupe path below so the bytes still land somewhere the user can reach.
1985
+ console.error('[upload_drive_file] overwrite failed, falling back to a new name:', err?.message || err);
1986
+ }
1987
+ }
1988
+
1926
1989
  if (check_existing_file_ret.code < 0) {
1927
1990
  const rename_file_name = async (i) => {
1928
1991
  if (i > 1000) {
@@ -5056,7 +5119,10 @@ export const drive_set_plan = async function (req) {
5056
5119
  const account = await _drive_account_doc(uid);
5057
5120
  if (!account._id) return { code: -404, data: 'account not found' };
5058
5121
  const current = _drive_account_plan(account);
5059
- if (current === plan_id) return await get_drive_plans({ uid });
5122
+ // UI-192: a switch-off asks for the FREE tier, so an account already sitting on free
5123
+ // matches here and would come back unchanged with the switch still on. Asking to
5124
+ // deactivate is never "no change": clearing the chosen stamp is the change.
5125
+ if (current === plan_id && req?.deactivate !== true) return await get_drive_plans({ uid });
5060
5126
 
5061
5127
  // Downgrade guard. Taking space away from an account that is already using it
5062
5128
  // would drop it straight into metered overage on files it cannot see a way to
@@ -5072,7 +5138,7 @@ export const drive_set_plan = async function (req) {
5072
5138
 
5073
5139
  const paid = Number(plan.price) > 0;
5074
5140
  if (paid) {
5075
- const ret = await stripe_ms.add_subscription_item({ uid, plan_id });
5141
+ const ret = await stripe_ms.add_subscription_item({ uid, plan_id, deactivate: req?.deactivate === true });
5076
5142
  if (!ret || ret.code < 0) return { code: ret?.code === -402 || ret?.code === -3 ? -402 : -1, data: ret?.data || 'could not change the plan' };
5077
5143
  } else if (account.stripe_subscription_items?.[DRIVE_CATEGORY]) {
5078
5144
  const ret = await stripe_ms.remove_subscription_item({ uid, category: DRIVE_CATEGORY });
@@ -5082,7 +5148,10 @@ export const drive_set_plan = async function (req) {
5082
5148
  // Re-read: the stripe call just wrote stripe_subscription_items on this doc.
5083
5149
  const fresh = await _drive_account_doc(uid);
5084
5150
  fresh.drive_plan = plan_id;
5085
- fresh.drive_plan_changed = Date.now();
5151
+ // UI-192: see the note in shipping_set_plan. A chosen free tier now reads as switched
5152
+ // on, so switching off has to clear the stamp that recorded the choice.
5153
+ if (req?.deactivate === true) delete fresh.drive_plan_changed;
5154
+ else fresh.drive_plan_changed = Date.now();
5086
5155
  await db_module.save_couch_doc('xuda_accounts', fresh);
5087
5156
 
5088
5157
  return await get_drive_plans({ uid });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xuda.io/drive_module",
3
- "version": "1.1.1501",
3
+ "version": "1.1.1502",
4
4
  "description": "Xuda Drive Server Module",
5
5
  "main": "index.mjs",
6
6
  "dependencies": {