@orthacms/media-provider-local 0.3.0 → 0.4.1

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.
@@ -3,8 +3,6 @@ import type { StorageProvider } from '@orthacms/media-server';
3
3
  export interface LocalStorageConfig {
4
4
  /** Directory blobs live under (absolute or project-relative). */
5
5
  rootDir: string;
6
- /** Base path the browser hits to stream a blob (reserved for direct URLs). */
7
- publicBasePath: string;
8
6
  }
9
7
  /**
10
8
  * Raised when a storage key resolves outside the provider's `rootDir`.
@@ -1 +1 @@
1
- {"version":3,"file":"local-storage-provider.d.ts","sourceRoot":"","sources":["../../src/lib/local-storage-provider.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAER,eAAe,EAElB,MAAM,wBAAwB,CAAC;AAEhC,0CAA0C;AAC1C,MAAM,WAAW,kBAAkB;IAC/B,iEAAiE;IACjE,OAAO,EAAE,MAAM,CAAC;IAChB,8EAA8E;IAC9E,cAAc,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;;;;;;GAUG;AACH,qBAAa,0BAA2B,SAAQ,KAAK;IACrC,QAAQ,CAAC,UAAU,EAAE,MAAM;gBAAlB,UAAU,EAAE,MAAM;CAI1C;AAgFD;;;;;;;;;;;;GAYG;AACH,wBAAgB,0BAA0B,CACtC,MAAM,EAAE,kBAAkB,GAC3B,eAAe,CA2IjB"}
1
+ {"version":3,"file":"local-storage-provider.d.ts","sourceRoot":"","sources":["../../src/lib/local-storage-provider.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAER,eAAe,EAElB,MAAM,wBAAwB,CAAC;AAEhC,0CAA0C;AAC1C,MAAM,WAAW,kBAAkB;IAC/B,iEAAiE;IACjE,OAAO,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;;GAUG;AACH,qBAAa,0BAA2B,SAAQ,KAAK;IACrC,QAAQ,CAAC,UAAU,EAAE,MAAM;gBAAlB,UAAU,EAAE,MAAM;CAI1C;AAgFD;;;;;;;;;;;;GAYG;AACH,wBAAgB,0BAA0B,CACtC,MAAM,EAAE,kBAAkB,GAC3B,eAAe,CA2JjB"}
@@ -8,6 +8,7 @@ const promises_1 = require("node:fs/promises");
8
8
  const node_path_1 = require("node:path");
9
9
  const node_stream_1 = require("node:stream");
10
10
  const promises_2 = require("node:stream/promises");
11
+ const media_server_1 = require("@orthacms/media-server");
11
12
  /**
12
13
  * Raised when a storage key resolves outside the provider's `rootDir`.
13
14
  *
@@ -150,6 +151,18 @@ function createLocalStorageProvider(config) {
150
151
  return target;
151
152
  };
152
153
  return {
154
+ id: 'local',
155
+ capabilities: {
156
+ // A filesystem cannot mint a URL the browser may fetch on its own:
157
+ // serving `rootDir` statically would hand every blob to anyone
158
+ // holding a key, and keys are returned in API responses. Downloads
159
+ // stay proxied through the app's own route.
160
+ directUrl: false,
161
+ // Accepted and dropped — a filesystem has nowhere to put it. The
162
+ // authoritative type stays `media_asset.mime_type`.
163
+ contentTypeMetadata: false,
164
+ streamingPut: true
165
+ },
153
166
  async put(object) {
154
167
  const storageKey = keyFor(object.workspaceId, object.assetId, object.fileName, object.isVariant ?? false);
155
168
  const target = absolute(storageKey);
@@ -190,7 +203,23 @@ function createLocalStorageProvider(config) {
190
203
  // response was already a streaming 200 that could no longer become
191
204
  // a 404. Opening the descriptor here moves the failure back in
192
205
  // front of the response.
193
- const handle = await (0, promises_1.open)(absolute(storageKey), 'r');
206
+ //
207
+ // The *kind* of rejection is the port's too, not this backend's:
208
+ // a raw `ENOENT` reaching the controller made a missing blob a
209
+ // **500**, where the row exists and the bytes do not — from the
210
+ // caller's side indistinguishable from a missing asset, which the
211
+ // route already answers 404 for. `ObjectNotFoundError` is what
212
+ // `to-http.ts` maps; `provider-s3` raises the same for `NoSuchKey`.
213
+ //
214
+ // Only `ENOENT`/`ENOTDIR` — a permission error or a full descriptor
215
+ // table is an operator's problem, and reporting it as "not found"
216
+ // would hide an outage behind a plausible 404.
217
+ const handle = await (0, promises_1.open)(absolute(storageKey), 'r').catch((error) => {
218
+ if (error.code === 'ENOENT' || error.code === 'ENOTDIR') {
219
+ throw new media_server_1.ObjectNotFoundError(storageKey, error);
220
+ }
221
+ throw error;
222
+ });
194
223
  const stats = await handle.stat();
195
224
  if (!stats.isFile()) {
196
225
  await handle.close();
@@ -204,20 +233,6 @@ function createLocalStorageProvider(config) {
204
233
  const target = absolute(storageKey);
205
234
  await (0, promises_1.rm)(target, { force: true });
206
235
  await pruneEmptyDirectories((0, node_path_1.dirname)(target), root);
207
- },
208
- async url(storageKey) {
209
- // NOTE: no route serves this path today — downloads go through
210
- // `GET /api/media/assets/:id/raw`, which resolves the key from the
211
- // asset row *after* checking workspace membership. Nothing calls
212
- // `url()`; it exists because the port promises a direct URL that a
213
- // signed-URL backend (S3) can give and a filesystem cannot.
214
- //
215
- // Whoever implements the static-serving mode: it must not be an
216
- // `express.static` over `rootDir`. That would serve every blob to
217
- // anyone who can guess a key and would bypass the membership check
218
- // wholesale — the keys are uuids, but they are also handed out in
219
- // API responses.
220
- return `${config.publicBasePath}/blob/${encodeURIComponent(storageKey)}`;
221
236
  }
222
237
  };
223
238
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orthacms/media-provider-local",
3
- "version": "0.3.0",
3
+ "version": "0.4.1",
4
4
  "description": "@orthacms/media-provider-local — part of Ortha CMS.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/ortha-source/ortha-cms/tree/main/packages/media/provider-local",
@@ -25,7 +25,7 @@
25
25
  "dist"
26
26
  ],
27
27
  "dependencies": {
28
- "@orthacms/media-server": "^0.3.0",
28
+ "@orthacms/media-server": "^0.4.1",
29
29
  "tslib": "^2.3.0"
30
30
  },
31
31
  "publishConfig": {