@orthacms/media-provider-local 0.3.0 → 0.4.0
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":"
|
|
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
|
-
|
|
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
|
+
"version": "0.4.0",
|
|
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.
|
|
28
|
+
"@orthacms/media-server": "^0.4.0",
|
|
29
29
|
"tslib": "^2.3.0"
|
|
30
30
|
},
|
|
31
31
|
"publishConfig": {
|