next-tinacms-dos 0.0.0-c8b1d84-20241003015733 → 0.0.0-c9e72f6-20251216031141
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 +1 -1
- package/dist/handlers.js +44 -71
- package/dist/index.js +122 -125
- package/package.json +9 -9
- package/dist/index.mjs +0 -130
package/README.md
CHANGED
|
@@ -92,7 +92,7 @@ Call `createMediaHandler` to set up routes and connect your instance of the Medi
|
|
|
92
92
|
|
|
93
93
|
Import `isAuthorized` from [`@tinacms/auth`](https://github.com/tinacms/tinacms/tree/main/packages/%40tinacms/auth).
|
|
94
94
|
|
|
95
|
-
The `authorized` key will make it so only authorized users within
|
|
95
|
+
The `authorized` key will make it so only authorized users within TinaCloud can upload and make media edits.
|
|
96
96
|
|
|
97
97
|
|
|
98
98
|
```
|
package/dist/handlers.js
CHANGED
|
@@ -1,46 +1,21 @@
|
|
|
1
|
-
var __create = Object.create;
|
|
2
|
-
var __defProp = Object.defineProperty;
|
|
3
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
6
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
-
var __export = (target, all) => {
|
|
8
|
-
for (var name in all)
|
|
9
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
-
};
|
|
11
|
-
var __copyProps = (to, from, except, desc) => {
|
|
12
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
-
for (let key of __getOwnPropNames(from))
|
|
14
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
-
}
|
|
17
|
-
return to;
|
|
18
|
-
};
|
|
19
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
20
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
21
|
-
mod
|
|
22
|
-
));
|
|
23
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
24
|
-
|
|
25
1
|
// src/handlers.ts
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
var import_util = require("util");
|
|
2
|
+
import {
|
|
3
|
+
S3Client,
|
|
4
|
+
ListObjectsCommand,
|
|
5
|
+
PutObjectCommand,
|
|
6
|
+
DeleteObjectCommand
|
|
7
|
+
} from "@aws-sdk/client-s3";
|
|
8
|
+
import path from "path";
|
|
9
|
+
import fs from "fs";
|
|
10
|
+
import multer from "multer";
|
|
11
|
+
import { promisify } from "util";
|
|
37
12
|
var mediaHandlerConfig = {
|
|
38
13
|
api: {
|
|
39
14
|
bodyParser: false
|
|
40
15
|
}
|
|
41
16
|
};
|
|
42
17
|
var createMediaHandler = (config, options) => {
|
|
43
|
-
const client = new
|
|
18
|
+
const client = new S3Client(config.config);
|
|
44
19
|
const bucket = config.bucket;
|
|
45
20
|
let mediaRoot = config.mediaRoot || "";
|
|
46
21
|
if (mediaRoot) {
|
|
@@ -51,7 +26,7 @@ var createMediaHandler = (config, options) => {
|
|
|
51
26
|
mediaRoot = mediaRoot.substr(1);
|
|
52
27
|
}
|
|
53
28
|
}
|
|
54
|
-
let cdnUrl =
|
|
29
|
+
let cdnUrl = options?.cdnUrl || config.config.endpoint.toString().replace(/http(s|):\/\//i, `https://${bucket}.`);
|
|
55
30
|
cdnUrl = cdnUrl + (cdnUrl.endsWith("/") ? "" : "/");
|
|
56
31
|
return async (req, res) => {
|
|
57
32
|
const isAuthorized = await config.authorized(req, res);
|
|
@@ -72,10 +47,11 @@ var createMediaHandler = (config, options) => {
|
|
|
72
47
|
};
|
|
73
48
|
};
|
|
74
49
|
async function uploadMedia(req, res, client, bucket, mediaRoot, cdnUrl) {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
50
|
+
const upload = promisify(
|
|
51
|
+
multer({
|
|
52
|
+
storage: multer.diskStorage({
|
|
53
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
54
|
+
// @ts-ignore
|
|
79
55
|
directory: (req2, file, cb) => {
|
|
80
56
|
cb(null, "/tmp");
|
|
81
57
|
},
|
|
@@ -88,20 +64,19 @@ async function uploadMedia(req, res, client, bucket, mediaRoot, cdnUrl) {
|
|
|
88
64
|
await upload(req, res);
|
|
89
65
|
const { directory } = req.body;
|
|
90
66
|
let prefix = directory.replace(/^\//, "").replace(/\/$/, "");
|
|
91
|
-
if (prefix)
|
|
92
|
-
prefix = prefix + "/";
|
|
67
|
+
if (prefix) prefix = prefix + "/";
|
|
93
68
|
const filePath = req.file.path;
|
|
94
|
-
const fileType =
|
|
95
|
-
const blob =
|
|
96
|
-
const filename =
|
|
69
|
+
const fileType = req.file?.mimetype;
|
|
70
|
+
const blob = fs.readFileSync(filePath);
|
|
71
|
+
const filename = path.basename(filePath);
|
|
97
72
|
const params = {
|
|
98
73
|
Bucket: bucket,
|
|
99
|
-
Key: mediaRoot ?
|
|
74
|
+
Key: mediaRoot ? path.join(mediaRoot, prefix + filename) : prefix + filename,
|
|
100
75
|
Body: blob,
|
|
101
76
|
ACL: "public-read",
|
|
102
77
|
ContentType: fileType || "application/octet-stream"
|
|
103
78
|
};
|
|
104
|
-
const command = new
|
|
79
|
+
const command = new PutObjectCommand(params);
|
|
105
80
|
try {
|
|
106
81
|
const src = cdnUrl + prefix + filename;
|
|
107
82
|
await client.send(command);
|
|
@@ -115,7 +90,7 @@ async function uploadMedia(req, res, client, bucket, mediaRoot, cdnUrl) {
|
|
|
115
90
|
"400x400": src,
|
|
116
91
|
"1000x1000": src
|
|
117
92
|
},
|
|
118
|
-
src: cdnUrl + (mediaRoot ?
|
|
93
|
+
src: cdnUrl + (mediaRoot ? path.join(mediaRoot, prefix + filename) : prefix + filename)
|
|
119
94
|
});
|
|
120
95
|
} catch (e) {
|
|
121
96
|
console.error("Error uploading media");
|
|
@@ -140,7 +115,6 @@ function stripMediaRoot(mediaRoot, key) {
|
|
|
140
115
|
return keyParts.join("/");
|
|
141
116
|
}
|
|
142
117
|
async function listMedia(req, res, client, bucket, mediaRoot, cdnUrl) {
|
|
143
|
-
var _a;
|
|
144
118
|
try {
|
|
145
119
|
const {
|
|
146
120
|
directory = "",
|
|
@@ -148,18 +122,17 @@ async function listMedia(req, res, client, bucket, mediaRoot, cdnUrl) {
|
|
|
148
122
|
offset
|
|
149
123
|
} = req.query;
|
|
150
124
|
let prefix = directory.replace(/^\//, "").replace(/\/$/, "");
|
|
151
|
-
if (prefix)
|
|
152
|
-
prefix = prefix + "/";
|
|
125
|
+
if (prefix) prefix = prefix + "/";
|
|
153
126
|
const params = {
|
|
154
127
|
Bucket: bucket,
|
|
155
128
|
Delimiter: "/",
|
|
156
|
-
Prefix: mediaRoot ?
|
|
157
|
-
Marker: offset
|
|
129
|
+
Prefix: mediaRoot ? path.join(mediaRoot, prefix) : prefix,
|
|
130
|
+
Marker: offset?.toString(),
|
|
158
131
|
MaxKeys: directory && !offset ? +limit + 1 : +limit
|
|
159
132
|
};
|
|
160
|
-
const response = await client.send(new
|
|
133
|
+
const response = await client.send(new ListObjectsCommand(params));
|
|
161
134
|
const items = [];
|
|
162
|
-
|
|
135
|
+
response.CommonPrefixes?.forEach(({ Prefix }) => {
|
|
163
136
|
const strippedPrefix = stripMediaRoot(mediaRoot, Prefix);
|
|
164
137
|
if (!strippedPrefix) {
|
|
165
138
|
return;
|
|
@@ -167,8 +140,8 @@ async function listMedia(req, res, client, bucket, mediaRoot, cdnUrl) {
|
|
|
167
140
|
items.push({
|
|
168
141
|
id: Prefix,
|
|
169
142
|
type: "dir",
|
|
170
|
-
filename:
|
|
171
|
-
directory:
|
|
143
|
+
filename: path.basename(strippedPrefix),
|
|
144
|
+
directory: path.dirname(strippedPrefix)
|
|
172
145
|
});
|
|
173
146
|
});
|
|
174
147
|
items.push(
|
|
@@ -190,23 +163,24 @@ async function listMedia(req, res, client, bucket, mediaRoot, cdnUrl) {
|
|
|
190
163
|
}
|
|
191
164
|
}
|
|
192
165
|
var findErrorMessage = (e) => {
|
|
193
|
-
if (typeof e == "string")
|
|
194
|
-
|
|
195
|
-
if (e.message)
|
|
196
|
-
return e.message;
|
|
197
|
-
if (e.error && e.error.message)
|
|
198
|
-
return e.error.message;
|
|
166
|
+
if (typeof e == "string") return e;
|
|
167
|
+
if (e.message) return e.message;
|
|
168
|
+
if (e.error && e.error.message) return e.error.message;
|
|
199
169
|
return "an error occurred";
|
|
200
170
|
};
|
|
201
171
|
async function deleteAsset(req, res, client, bucket) {
|
|
202
172
|
const { media } = req.query;
|
|
203
|
-
|
|
173
|
+
let [, objectKey] = media;
|
|
174
|
+
const objectKeyIsSplit = media && media.length > 2 && typeof media !== "string";
|
|
175
|
+
if (objectKeyIsSplit) {
|
|
176
|
+
objectKey = media.slice(1).join("/");
|
|
177
|
+
}
|
|
204
178
|
const params = {
|
|
205
179
|
Bucket: bucket,
|
|
206
180
|
Key: objectKey
|
|
207
181
|
};
|
|
208
182
|
try {
|
|
209
|
-
const data = await client.send(new
|
|
183
|
+
const data = await client.send(new DeleteObjectCommand(params));
|
|
210
184
|
res.json(data);
|
|
211
185
|
} catch (err) {
|
|
212
186
|
console.error("Error deleting media");
|
|
@@ -219,8 +193,8 @@ async function deleteAsset(req, res, client, bucket) {
|
|
|
219
193
|
function getDOSToTinaFunc(cdnUrl, mediaRoot) {
|
|
220
194
|
return function dosToTina(file) {
|
|
221
195
|
const strippedKey = stripMediaRoot(mediaRoot, file.Key);
|
|
222
|
-
const filename =
|
|
223
|
-
const directory =
|
|
196
|
+
const filename = path.basename(strippedKey);
|
|
197
|
+
const directory = path.dirname(strippedKey) + "/";
|
|
224
198
|
const src = cdnUrl + file.Key;
|
|
225
199
|
return {
|
|
226
200
|
id: file.Key,
|
|
@@ -236,8 +210,7 @@ function getDOSToTinaFunc(cdnUrl, mediaRoot) {
|
|
|
236
210
|
};
|
|
237
211
|
};
|
|
238
212
|
}
|
|
239
|
-
|
|
240
|
-
0 && (module.exports = {
|
|
213
|
+
export {
|
|
241
214
|
createMediaHandler,
|
|
242
215
|
mediaHandlerConfig
|
|
243
|
-
}
|
|
216
|
+
};
|
package/dist/index.js
CHANGED
|
@@ -1,133 +1,130 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
this.ERR_TYPE = "MediaListError";
|
|
9
|
-
this.title = config.title;
|
|
10
|
-
this.docsLink = config.docsLink;
|
|
11
|
-
}
|
|
1
|
+
import { DEFAULT_MEDIA_UPLOAD_TYPES } from "tinacms";
|
|
2
|
+
class MediaListError extends Error {
|
|
3
|
+
constructor(config) {
|
|
4
|
+
super(config.message);
|
|
5
|
+
this.ERR_TYPE = "MediaListError";
|
|
6
|
+
this.title = config.title;
|
|
7
|
+
this.docsLink = config.docsLink;
|
|
12
8
|
}
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
const fileRes = await res.json();
|
|
77
|
-
await new Promise((resolve) => {
|
|
78
|
-
setTimeout(resolve, 2e3);
|
|
79
|
-
});
|
|
80
|
-
newFiles.push(fileRes);
|
|
9
|
+
}
|
|
10
|
+
const E_DEFAULT = new MediaListError({
|
|
11
|
+
title: "An Error Occurred",
|
|
12
|
+
message: "Something went wrong fetching your media from Digital Ocean Space.",
|
|
13
|
+
docsLink: "https://tina.io/docs/r/digital-ocean-spaces"
|
|
14
|
+
});
|
|
15
|
+
const E_UNAUTHORIZED = new MediaListError({
|
|
16
|
+
title: "Unauthorized",
|
|
17
|
+
message: "You don't have access to this resource.",
|
|
18
|
+
docsLink: "https://tina.io/docs/r/digital-ocean-spaces"
|
|
19
|
+
});
|
|
20
|
+
const E_CONFIG = new MediaListError({
|
|
21
|
+
title: "Missing Credentials",
|
|
22
|
+
message: "Unable to connect to Digital Ocean Space because one or more environment variables are missing.",
|
|
23
|
+
docsLink: "https://tina.io/docs/r/digital-ocean-spaces/"
|
|
24
|
+
});
|
|
25
|
+
const E_KEY_FAIL = new MediaListError({
|
|
26
|
+
title: "Bad Credentials",
|
|
27
|
+
message: "Unable to connect to Digital Ocean Space because one or more environment variables are misconfigured.",
|
|
28
|
+
docsLink: "https://tina.io/docs/r/digital-ocean-spaces/"
|
|
29
|
+
});
|
|
30
|
+
const E_BAD_ROUTE = new MediaListError({
|
|
31
|
+
title: "Bad Route",
|
|
32
|
+
message: "The Digital Ocean Space API route is missing or misconfigured.",
|
|
33
|
+
docsLink: "https://tina.io/docs/r/digital-ocean-spaces/#set-up-api-routes"
|
|
34
|
+
});
|
|
35
|
+
const interpretErrorMessage = (message) => {
|
|
36
|
+
switch (message) {
|
|
37
|
+
case "Must supply cloud_name":
|
|
38
|
+
case "Must supply api_key":
|
|
39
|
+
case "Must supply api_secret":
|
|
40
|
+
return E_CONFIG;
|
|
41
|
+
case "unknown api_key":
|
|
42
|
+
return E_KEY_FAIL;
|
|
43
|
+
default:
|
|
44
|
+
return E_DEFAULT;
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
class DOSMediaStore {
|
|
48
|
+
constructor() {
|
|
49
|
+
this.fetchFunction = (input, init) => {
|
|
50
|
+
return fetch(input, init);
|
|
51
|
+
};
|
|
52
|
+
this.accept = DEFAULT_MEDIA_UPLOAD_TYPES;
|
|
53
|
+
this.parse = (img) => {
|
|
54
|
+
return img.src;
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
async persist(media) {
|
|
58
|
+
const newFiles = [];
|
|
59
|
+
for (const item of media) {
|
|
60
|
+
const { file, directory } = item;
|
|
61
|
+
const formData = new FormData();
|
|
62
|
+
formData.append("file", file);
|
|
63
|
+
formData.append("directory", directory);
|
|
64
|
+
formData.append("filename", file.name);
|
|
65
|
+
const res = await this.fetchFunction(`/api/dos/media`, {
|
|
66
|
+
method: "POST",
|
|
67
|
+
body: formData
|
|
68
|
+
});
|
|
69
|
+
if (res.status != 200) {
|
|
70
|
+
const responseData = await res.json();
|
|
71
|
+
throw new Error(responseData.message);
|
|
81
72
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
await this.fetchFunction(`/api/dos/media/${encodeURIComponent(media.id)}`, {
|
|
86
|
-
method: "DELETE"
|
|
73
|
+
const fileRes = await res.json();
|
|
74
|
+
await new Promise((resolve) => {
|
|
75
|
+
setTimeout(resolve, 2e3);
|
|
87
76
|
});
|
|
77
|
+
newFiles.push(fileRes);
|
|
88
78
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
throw error;
|
|
102
|
-
}
|
|
103
|
-
const { items, offset } = await response.json();
|
|
104
|
-
return {
|
|
105
|
-
items: items.map((item) => item),
|
|
106
|
-
nextOffset: offset
|
|
107
|
-
};
|
|
79
|
+
return newFiles;
|
|
80
|
+
}
|
|
81
|
+
async delete(media) {
|
|
82
|
+
await this.fetchFunction(`/api/dos/media/${encodeURIComponent(media.id)}`, {
|
|
83
|
+
method: "DELETE"
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
async list(options) {
|
|
87
|
+
const query = this.buildQuery(options);
|
|
88
|
+
const response = await this.fetchFunction("/api/dos/media" + query);
|
|
89
|
+
if (response.status == 401) {
|
|
90
|
+
throw E_UNAUTHORIZED;
|
|
108
91
|
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
return `?${params}`;
|
|
92
|
+
if (response.status == 404) {
|
|
93
|
+
throw E_BAD_ROUTE;
|
|
112
94
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
this.client = client;
|
|
118
|
-
this.fetchFunction = async (input, init) => {
|
|
119
|
-
try {
|
|
120
|
-
const url = input.toString();
|
|
121
|
-
const query = `${url.includes("?") ? "&" : "?"}clientID=${client.clientId}`;
|
|
122
|
-
const res = client.authProvider.fetchWithToken(url + query, init);
|
|
123
|
-
return res;
|
|
124
|
-
} catch (error) {
|
|
125
|
-
console.error(error);
|
|
126
|
-
}
|
|
127
|
-
};
|
|
95
|
+
if (response.status >= 500) {
|
|
96
|
+
const { e } = await response.json();
|
|
97
|
+
const error = interpretErrorMessage(e);
|
|
98
|
+
throw error;
|
|
128
99
|
}
|
|
100
|
+
const { items, offset } = await response.json();
|
|
101
|
+
return {
|
|
102
|
+
items: items.map((item) => item),
|
|
103
|
+
nextOffset: offset
|
|
104
|
+
};
|
|
129
105
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
}
|
|
106
|
+
buildQuery(options) {
|
|
107
|
+
const params = Object.keys(options).filter((key) => options[key] !== "" && options[key] !== void 0).map((key) => `${key}=${options[key]}`).join("&");
|
|
108
|
+
return `?${params}`;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
class TinaCloudDOSMediaStore extends DOSMediaStore {
|
|
112
|
+
constructor(client) {
|
|
113
|
+
super();
|
|
114
|
+
this.client = client;
|
|
115
|
+
this.fetchFunction = async (input, init) => {
|
|
116
|
+
try {
|
|
117
|
+
const url = input.toString();
|
|
118
|
+
const query = `${url.includes("?") ? "&" : "?"}clientID=${client.clientId}`;
|
|
119
|
+
const res = client.authProvider.fetchWithToken(url + query, init);
|
|
120
|
+
return res;
|
|
121
|
+
} catch (error) {
|
|
122
|
+
console.error(error);
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
export {
|
|
128
|
+
DOSMediaStore,
|
|
129
|
+
TinaCloudDOSMediaStore
|
|
130
|
+
};
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "next-tinacms-dos",
|
|
3
|
-
"version": "0.0.0-
|
|
3
|
+
"version": "0.0.0-c9e72f6-20251216031141",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
|
-
"module": "dist/index.
|
|
5
|
+
"module": "./dist/index.js",
|
|
6
6
|
"files": [
|
|
7
7
|
"dist"
|
|
8
8
|
],
|
|
@@ -18,18 +18,18 @@
|
|
|
18
18
|
]
|
|
19
19
|
},
|
|
20
20
|
"peerDependencies": {
|
|
21
|
-
"tinacms": "0.0.0-
|
|
21
|
+
"tinacms": "0.0.0-c9e72f6-20251216031141"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"@types/crypto-js": "^3.1.47",
|
|
25
25
|
"@types/js-cookie": "^2.2.7",
|
|
26
|
-
"@types/node": "^22.
|
|
27
|
-
"next": "14.2.
|
|
26
|
+
"@types/node": "^22.13.1",
|
|
27
|
+
"next": "14.2.35",
|
|
28
28
|
"react": "^18.3.1",
|
|
29
29
|
"react-dom": "^18.3.1",
|
|
30
|
-
"typescript": "^5.
|
|
31
|
-
"@tinacms/scripts": "1.2
|
|
32
|
-
"tinacms": "0.0.0-
|
|
30
|
+
"typescript": "^5.7.3",
|
|
31
|
+
"@tinacms/scripts": "1.4.2",
|
|
32
|
+
"tinacms": "0.0.0-c9e72f6-20251216031141"
|
|
33
33
|
},
|
|
34
34
|
"publishConfig": {
|
|
35
35
|
"registry": "https://registry.npmjs.org"
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"directory": "packages/next-tinacms-dos"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@aws-sdk/client-s3": "^3.
|
|
42
|
+
"@aws-sdk/client-s3": "^3.744.0",
|
|
43
43
|
"multer": "1.4.5-lts.1"
|
|
44
44
|
},
|
|
45
45
|
"scripts": {
|
package/dist/index.mjs
DELETED
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
import { DEFAULT_MEDIA_UPLOAD_TYPES } from "tinacms";
|
|
2
|
-
class MediaListError extends Error {
|
|
3
|
-
constructor(config) {
|
|
4
|
-
super(config.message);
|
|
5
|
-
this.ERR_TYPE = "MediaListError";
|
|
6
|
-
this.title = config.title;
|
|
7
|
-
this.docsLink = config.docsLink;
|
|
8
|
-
}
|
|
9
|
-
}
|
|
10
|
-
const E_DEFAULT = new MediaListError({
|
|
11
|
-
title: "An Error Occurred",
|
|
12
|
-
message: "Something went wrong fetching your media from Digital Ocean Space.",
|
|
13
|
-
docsLink: "https://tina.io/packages/next-tinacms-dos"
|
|
14
|
-
});
|
|
15
|
-
const E_UNAUTHORIZED = new MediaListError({
|
|
16
|
-
title: "Unauthorized",
|
|
17
|
-
message: "You don't have access to this resource.",
|
|
18
|
-
docsLink: "https://tina.io/packages/next-tinacms-dos"
|
|
19
|
-
});
|
|
20
|
-
const E_CONFIG = new MediaListError({
|
|
21
|
-
title: "Missing Credentials",
|
|
22
|
-
message: "Unable to connect to Digital Ocean Space because one or more environment variables are missing.",
|
|
23
|
-
docsLink: "https://tina.io/docs/media-dos/"
|
|
24
|
-
});
|
|
25
|
-
const E_KEY_FAIL = new MediaListError({
|
|
26
|
-
title: "Bad Credentials",
|
|
27
|
-
message: "Unable to connect to Digital Ocean Space because one or more environment variables are misconfigured.",
|
|
28
|
-
docsLink: "https://tina.io/docs/media-dos/"
|
|
29
|
-
});
|
|
30
|
-
const E_BAD_ROUTE = new MediaListError({
|
|
31
|
-
title: "Bad Route",
|
|
32
|
-
message: "The Digital Ocean Space API route is missing or misconfigured.",
|
|
33
|
-
docsLink: "https://tina.io/packages/next-tinacms-dos/#set-up-api-routes"
|
|
34
|
-
});
|
|
35
|
-
const interpretErrorMessage = (message) => {
|
|
36
|
-
switch (message) {
|
|
37
|
-
case "Must supply cloud_name":
|
|
38
|
-
case "Must supply api_key":
|
|
39
|
-
case "Must supply api_secret":
|
|
40
|
-
return E_CONFIG;
|
|
41
|
-
case "unknown api_key":
|
|
42
|
-
return E_KEY_FAIL;
|
|
43
|
-
default:
|
|
44
|
-
return E_DEFAULT;
|
|
45
|
-
}
|
|
46
|
-
};
|
|
47
|
-
class DOSMediaStore {
|
|
48
|
-
constructor() {
|
|
49
|
-
this.fetchFunction = (input, init) => {
|
|
50
|
-
return fetch(input, init);
|
|
51
|
-
};
|
|
52
|
-
this.accept = DEFAULT_MEDIA_UPLOAD_TYPES;
|
|
53
|
-
this.parse = (img) => {
|
|
54
|
-
return img.src;
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
async persist(media) {
|
|
58
|
-
const newFiles = [];
|
|
59
|
-
for (const item of media) {
|
|
60
|
-
const { file, directory } = item;
|
|
61
|
-
const formData = new FormData();
|
|
62
|
-
formData.append("file", file);
|
|
63
|
-
formData.append("directory", directory);
|
|
64
|
-
formData.append("filename", file.name);
|
|
65
|
-
const res = await this.fetchFunction(`/api/dos/media`, {
|
|
66
|
-
method: "POST",
|
|
67
|
-
body: formData
|
|
68
|
-
});
|
|
69
|
-
if (res.status != 200) {
|
|
70
|
-
const responseData = await res.json();
|
|
71
|
-
throw new Error(responseData.message);
|
|
72
|
-
}
|
|
73
|
-
const fileRes = await res.json();
|
|
74
|
-
await new Promise((resolve) => {
|
|
75
|
-
setTimeout(resolve, 2e3);
|
|
76
|
-
});
|
|
77
|
-
newFiles.push(fileRes);
|
|
78
|
-
}
|
|
79
|
-
return newFiles;
|
|
80
|
-
}
|
|
81
|
-
async delete(media) {
|
|
82
|
-
await this.fetchFunction(`/api/dos/media/${encodeURIComponent(media.id)}`, {
|
|
83
|
-
method: "DELETE"
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
async list(options) {
|
|
87
|
-
const query = this.buildQuery(options);
|
|
88
|
-
const response = await this.fetchFunction("/api/dos/media" + query);
|
|
89
|
-
if (response.status == 401) {
|
|
90
|
-
throw E_UNAUTHORIZED;
|
|
91
|
-
}
|
|
92
|
-
if (response.status == 404) {
|
|
93
|
-
throw E_BAD_ROUTE;
|
|
94
|
-
}
|
|
95
|
-
if (response.status >= 500) {
|
|
96
|
-
const { e } = await response.json();
|
|
97
|
-
const error = interpretErrorMessage(e);
|
|
98
|
-
throw error;
|
|
99
|
-
}
|
|
100
|
-
const { items, offset } = await response.json();
|
|
101
|
-
return {
|
|
102
|
-
items: items.map((item) => item),
|
|
103
|
-
nextOffset: offset
|
|
104
|
-
};
|
|
105
|
-
}
|
|
106
|
-
buildQuery(options) {
|
|
107
|
-
const params = Object.keys(options).filter((key) => options[key] !== "" && options[key] !== void 0).map((key) => `${key}=${options[key]}`).join("&");
|
|
108
|
-
return `?${params}`;
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
class TinaCloudDOSMediaStore extends DOSMediaStore {
|
|
112
|
-
constructor(client) {
|
|
113
|
-
super();
|
|
114
|
-
this.client = client;
|
|
115
|
-
this.fetchFunction = async (input, init) => {
|
|
116
|
-
try {
|
|
117
|
-
const url = input.toString();
|
|
118
|
-
const query = `${url.includes("?") ? "&" : "?"}clientID=${client.clientId}`;
|
|
119
|
-
const res = client.authProvider.fetchWithToken(url + query, init);
|
|
120
|
-
return res;
|
|
121
|
-
} catch (error) {
|
|
122
|
-
console.error(error);
|
|
123
|
-
}
|
|
124
|
-
};
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
export {
|
|
128
|
-
DOSMediaStore,
|
|
129
|
-
TinaCloudDOSMediaStore
|
|
130
|
-
};
|