eptaadmin-sdk 0.1.8 → 0.1.10
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.
- package/README.md +2 -2
- package/package.json +1 -1
- package/src/index.js +13 -28
package/README.md
CHANGED
|
@@ -76,11 +76,11 @@ If your project needs row-aligned records, build them yourself from the columns
|
|
|
76
76
|
|
|
77
77
|
## Image values
|
|
78
78
|
|
|
79
|
-
A column of type "image" stores each value as a URL. The
|
|
79
|
+
A column of type "image" stores each value as a URL. The server rewrites these to absolute, **signed** URLs before returning them (`{baseUrl}/api/v1/workspaces/{slug}/uploads/{file}?sig=...`) — safe to drop straight into an `<img>`/`<video>` src, since the signature (not your personal API key) is what authorizes the request. A leaked link only ever exposes that one file, never your account's broader read access — but unlike a short-lived token, it doesn't expire on its own (revoking it means rotating the server's signing secret, which invalidates every signed link at once):
|
|
80
80
|
|
|
81
81
|
```js
|
|
82
82
|
const avatars = await client.getValue("acme/clients/avatar");
|
|
83
|
-
// ["https://your-eptaadmin-instance.example.com/api/v1/workspaces/acme/uploads/6f2dff985af5d290.png"]
|
|
83
|
+
// ["https://your-eptaadmin-instance.example.com/api/v1/workspaces/acme/uploads/6f2dff985af5d290.png?sig=..."]
|
|
84
84
|
```
|
|
85
85
|
|
|
86
86
|
## Build-time prefetch (for static/SPA builds)
|
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -11,33 +11,18 @@ export class EptaAdminError extends Error {
|
|
|
11
11
|
}
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
// Image column values
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
//
|
|
18
|
-
//
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
// The key travels as a "?apiKey=" query parameter, not an Authorization
|
|
23
|
-
// header, because the whole point of this URL is to be usable directly as
|
|
24
|
-
// an <img>/<video> src — a browser's native resource fetch for those never
|
|
25
|
-
// attaches custom headers, so a header-only scheme would make the URL this
|
|
26
|
-
// returns unusable for that (the single most common reason to want it).
|
|
27
|
-
const uploadUrlPattern = /^\/workspaces\/([^/]+)\/uploads\/(.+)$/;
|
|
28
|
-
|
|
29
|
-
function resolveImageURL(value, baseUrl, apiKey) {
|
|
30
|
-
if (typeof value !== "string") return value;
|
|
31
|
-
const match = value.match(uploadUrlPattern);
|
|
32
|
-
if (!match) return value;
|
|
33
|
-
const url = `${baseUrl}/api/v1/workspaces/${match[1]}/uploads/${match[2]}`;
|
|
34
|
-
return apiKey ? `${url}?apiKey=${encodeURIComponent(apiKey)}` : url;
|
|
14
|
+
// Image column values arrive from the API already rewritten server-side
|
|
15
|
+
// into a self-contained, signed public path (see upload_signing.go) — a
|
|
16
|
+
// personal API key is never embedded in it, so it stays safe to drop
|
|
17
|
+
// straight into an <img>/<video> src, unlike the account's actual API key.
|
|
18
|
+
// This just needs to turn that relative path into an absolute URL.
|
|
19
|
+
function resolveImageURL(value, baseUrl) {
|
|
20
|
+
if (typeof value !== "string" || !value.startsWith("/")) return value;
|
|
21
|
+
return baseUrl + value;
|
|
35
22
|
}
|
|
36
23
|
|
|
37
|
-
function resolveImageURLs(value, baseUrl
|
|
38
|
-
return Array.isArray(value)
|
|
39
|
-
? value.map((v) => resolveImageURL(v, baseUrl, apiKey))
|
|
40
|
-
: resolveImageURL(value, baseUrl, apiKey);
|
|
24
|
+
function resolveImageURLs(value, baseUrl) {
|
|
25
|
+
return Array.isArray(value) ? value.map((v) => resolveImageURL(v, baseUrl)) : resolveImageURL(value, baseUrl);
|
|
41
26
|
}
|
|
42
27
|
|
|
43
28
|
// Populated by build tooling (see eptaadmin-sdk/vite) via a bundler `define`
|
|
@@ -104,7 +89,7 @@ export class EptaAdminClient {
|
|
|
104
89
|
`/api/v1/workspaces/${encodeURIComponent(workspaceSlug)}/datasources/${encodeURIComponent(dataSourceSlug)}`
|
|
105
90
|
);
|
|
106
91
|
for (const key of Object.keys(body.columns || {})) {
|
|
107
|
-
body.columns[key] = resolveImageURLs(body.columns[key], this.baseUrl
|
|
92
|
+
body.columns[key] = resolveImageURLs(body.columns[key], this.baseUrl);
|
|
108
93
|
}
|
|
109
94
|
return body;
|
|
110
95
|
}
|
|
@@ -136,7 +121,7 @@ export class EptaAdminClient {
|
|
|
136
121
|
if (index !== undefined && result === undefined) {
|
|
137
122
|
throw new EptaAdminError(`index ${index} out of range for column "${column}"`, 404);
|
|
138
123
|
}
|
|
139
|
-
return resolveImageURLs(result, this.baseUrl
|
|
124
|
+
return resolveImageURLs(result, this.baseUrl);
|
|
140
125
|
}
|
|
141
126
|
|
|
142
127
|
let url =
|
|
@@ -147,6 +132,6 @@ export class EptaAdminClient {
|
|
|
147
132
|
url += `/${encodeURIComponent(index)}`;
|
|
148
133
|
}
|
|
149
134
|
const body = await this._request(url);
|
|
150
|
-
return resolveImageURLs(index !== undefined ? body.value : body.values, this.baseUrl
|
|
135
|
+
return resolveImageURLs(index !== undefined ? body.value : body.values, this.baseUrl);
|
|
151
136
|
}
|
|
152
137
|
}
|