appflare 0.2.26 → 0.2.28
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/cli/templates/core/client/storage.ts +28 -13
- package/cli/templates/core/client/types.ts +3 -1
- package/cli/templates/handlers/generators/context/storage-api.ts +0 -52
- package/cli/templates/handlers/generators/registration/modules/storage.ts +41 -38
- package/cli/templates/handlers/generators/types/context.ts +0 -1
- package/dist/cli/index.js +116 -149
- package/dist/cli/index.mjs +116 -149
- package/package.json +1 -1
|
@@ -67,6 +67,26 @@ export function createStorageClient(
|
|
|
67
67
|
): StorageClient {
|
|
68
68
|
return {
|
|
69
69
|
upload: async (args) => {
|
|
70
|
+
let base64BodyToSend: string | undefined;
|
|
71
|
+
|
|
72
|
+
if (args.body instanceof File) {
|
|
73
|
+
const buffer = await args.body.arrayBuffer();
|
|
74
|
+
const bytes = new Uint8Array(buffer);
|
|
75
|
+
let binary = "";
|
|
76
|
+
const chunkSize = 8192;
|
|
77
|
+
for (let i = 0; i < bytes.length; i += chunkSize) {
|
|
78
|
+
const chunk = bytes.subarray(i, i + chunkSize);
|
|
79
|
+
binary += String.fromCharCode(...chunk);
|
|
80
|
+
}
|
|
81
|
+
base64BodyToSend = btoa(binary);
|
|
82
|
+
} else if (typeof args.body === "string") {
|
|
83
|
+
base64BodyToSend = btoa(args.body);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (args.base64Body) {
|
|
87
|
+
base64BodyToSend = args.base64Body;
|
|
88
|
+
}
|
|
89
|
+
|
|
70
90
|
const headers = await createAuthorizedHeaders(
|
|
71
91
|
{
|
|
72
92
|
"content-type": "application/json",
|
|
@@ -77,7 +97,12 @@ export function createStorageClient(
|
|
|
77
97
|
const response = await request(\`\${endpoint}/storage/upload\`, {
|
|
78
98
|
method: "POST",
|
|
79
99
|
headers,
|
|
80
|
-
body: JSON.stringify(
|
|
100
|
+
body: JSON.stringify({
|
|
101
|
+
path: args.path,
|
|
102
|
+
contentType: args.contentType,
|
|
103
|
+
expiresIn: args.expiresIn,
|
|
104
|
+
base64Body: base64BodyToSend,
|
|
105
|
+
}),
|
|
81
106
|
});
|
|
82
107
|
if (!response.ok) {
|
|
83
108
|
throw new Error(await response.text());
|
|
@@ -105,24 +130,14 @@ export function createStorageClient(
|
|
|
105
130
|
|
|
106
131
|
return (await response.json()) as StorageSignedUrlResponse;
|
|
107
132
|
},
|
|
108
|
-
preview:
|
|
133
|
+
preview: (args) => {
|
|
109
134
|
const query = new URLSearchParams({
|
|
110
135
|
path: args.path,
|
|
111
136
|
...(typeof args.expiresIn === "number"
|
|
112
137
|
? { expiresIn: String(args.expiresIn) }
|
|
113
138
|
: {}),
|
|
114
139
|
});
|
|
115
|
-
|
|
116
|
-
\`\${endpoint}/storage/preview?\${query.toString()}\`,
|
|
117
|
-
{
|
|
118
|
-
headers: await createAuthorizedHeaders(undefined, onGetAuthToken),
|
|
119
|
-
},
|
|
120
|
-
);
|
|
121
|
-
if (!response.ok) {
|
|
122
|
-
throw new Error(await response.text());
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
return (await response.json()) as StorageSignedUrlResponse;
|
|
140
|
+
return \`\${endpoint}/storage/download?\${query.toString()}\`;
|
|
126
141
|
},
|
|
127
142
|
delete: async (args) => {
|
|
128
143
|
const query = new URLSearchParams({
|
|
@@ -148,6 +148,8 @@ export type StorageClient = {
|
|
|
148
148
|
path: string;
|
|
149
149
|
contentType?: string;
|
|
150
150
|
expiresIn?: number;
|
|
151
|
+
base64Body?: string;
|
|
152
|
+
body?: string | File;
|
|
151
153
|
}) => Promise<StorageSignedUrlResponse>;
|
|
152
154
|
download: (args: {
|
|
153
155
|
path: string;
|
|
@@ -157,7 +159,7 @@ export type StorageClient = {
|
|
|
157
159
|
preview: (args: {
|
|
158
160
|
path: string;
|
|
159
161
|
expiresIn?: number;
|
|
160
|
-
}) =>
|
|
162
|
+
}) => string;
|
|
161
163
|
delete: (args: { path: string }) => Promise<{ ok: boolean; path: string }>;
|
|
162
164
|
list: (args?: {
|
|
163
165
|
prefix?: string;
|
|
@@ -32,28 +32,6 @@ function createStorageApi(
|
|
|
32
32
|
contentType: args.contentType,
|
|
33
33
|
});
|
|
34
34
|
|
|
35
|
-
if (args.returnSignedUrlOnly) {
|
|
36
|
-
const currentBucket = requireBucket();
|
|
37
|
-
if (typeof currentBucket.createPresignedUrl !== "function") {
|
|
38
|
-
throw new Error("R2 createPresignedUrl is unavailable for this runtime binding");
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const signedRequest = buildSignedRequest(
|
|
42
|
-
{
|
|
43
|
-
path,
|
|
44
|
-
method: "PUT",
|
|
45
|
-
contentType: args.contentType,
|
|
46
|
-
expiresIn: args.expiresIn,
|
|
47
|
-
},
|
|
48
|
-
path,
|
|
49
|
-
);
|
|
50
|
-
const signedUrl = await currentBucket.createPresignedUrl(signedRequest, {
|
|
51
|
-
expiresIn: args.expiresIn ?? 60 * 5,
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
return signedUrl.toString();
|
|
55
|
-
}
|
|
56
|
-
|
|
57
35
|
return requireBucket().put(path, args.body, {
|
|
58
36
|
httpMetadata: {
|
|
59
37
|
...(args.httpMetadata ?? {}),
|
|
@@ -98,36 +76,6 @@ function createStorageApi(
|
|
|
98
76
|
include: args.include,
|
|
99
77
|
});
|
|
100
78
|
},
|
|
101
|
-
signedUrl: async (args) => {
|
|
102
|
-
const path = normalizeStoragePath(args.path);
|
|
103
|
-
const requestMethod = args.method ?? "GET";
|
|
104
|
-
const method: StorageMethod =
|
|
105
|
-
requestMethod === "PUT"
|
|
106
|
-
? "put"
|
|
107
|
-
: requestMethod === "DELETE"
|
|
108
|
-
? "delete"
|
|
109
|
-
: args.downloadAsAttachment === false
|
|
110
|
-
? "preview"
|
|
111
|
-
: "download";
|
|
112
|
-
|
|
113
|
-
await assertAuthorized({
|
|
114
|
-
path: "/" + path,
|
|
115
|
-
method,
|
|
116
|
-
contentType: args.contentType,
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
const currentBucket = requireBucket();
|
|
120
|
-
if (typeof currentBucket.createPresignedUrl !== "function") {
|
|
121
|
-
throw new Error("R2 createPresignedUrl is unavailable for this runtime binding");
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
const signedRequest = buildSignedRequest(args, path);
|
|
125
|
-
const signedUrl = await currentBucket.createPresignedUrl(signedRequest, {
|
|
126
|
-
expiresIn: args.expiresIn ?? 60 * 5,
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
return signedUrl.toString();
|
|
130
|
-
},
|
|
131
79
|
};
|
|
132
80
|
}
|
|
133
81
|
`;
|
|
@@ -37,25 +37,34 @@ export function registerGeneratedStorageRoutes(
|
|
|
37
37
|
const path = toStoragePath(String(body.path ?? ""));
|
|
38
38
|
const contentType =
|
|
39
39
|
typeof body.contentType === "string" ? body.contentType : undefined;
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
40
|
+
const encodedBody = typeof body.body === "string" ? body.body : undefined;
|
|
41
|
+
const base64Body = typeof body.base64Body === "string" ? body.base64Body : undefined;
|
|
42
|
+
|
|
43
|
+
if (!base64Body && !encodedBody) {
|
|
44
|
+
return c.json(
|
|
45
|
+
{ message: "File content is required. Provide either 'body' or 'base64Body' field." },
|
|
46
|
+
400,
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const uploadBody = base64Body
|
|
51
|
+
? Uint8Array.from(atob(base64Body), (char) => char.charCodeAt(0))
|
|
52
|
+
: encodedBody
|
|
53
|
+
? Uint8Array.from(atob(encodedBody), (char) => char.charCodeAt(0))
|
|
54
|
+
: new Uint8Array();
|
|
44
55
|
|
|
45
|
-
|
|
56
|
+
await ctx.storage.put({
|
|
46
57
|
path,
|
|
47
|
-
body:
|
|
58
|
+
body: uploadBody,
|
|
48
59
|
contentType,
|
|
49
|
-
expiresIn,
|
|
50
|
-
returnSignedUrlOnly: true,
|
|
51
60
|
});
|
|
52
61
|
|
|
53
62
|
return c.json({
|
|
54
|
-
url,
|
|
63
|
+
url: null,
|
|
55
64
|
method: "PUT",
|
|
56
65
|
path,
|
|
57
66
|
contentType,
|
|
58
|
-
|
|
67
|
+
uploaded: true,
|
|
59
68
|
}, 200);
|
|
60
69
|
} catch (error) {
|
|
61
70
|
if (error instanceof AppflareHandledError) {
|
|
@@ -74,28 +83,28 @@ export function registerGeneratedStorageRoutes(
|
|
|
74
83
|
try {
|
|
75
84
|
const path = readStoragePath(c);
|
|
76
85
|
const fileName = c.req.query("fileName") ?? undefined;
|
|
77
|
-
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
downloadAsAttachment: true,
|
|
83
|
-
fileName,
|
|
84
|
-
});
|
|
86
|
+
|
|
87
|
+
const file = await ctx.storage.get({ path });
|
|
88
|
+
if (!file) {
|
|
89
|
+
return c.json({ message: "File not found" }, 404);
|
|
90
|
+
}
|
|
85
91
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
+
const headers: Record<string, string> = {};
|
|
93
|
+
if (file.contentType) {
|
|
94
|
+
headers["content-type"] = file.contentType;
|
|
95
|
+
}
|
|
96
|
+
if (fileName) {
|
|
97
|
+
headers["content-disposition"] = \`attachment; filename="\${fileName}"\`;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return c.body(file.body, 200, headers);
|
|
92
101
|
} catch (error) {
|
|
93
102
|
if (error instanceof AppflareHandledError) {
|
|
94
103
|
return c.json(error.payload, error.status as any);
|
|
95
104
|
}
|
|
96
105
|
|
|
97
106
|
return c.json(
|
|
98
|
-
{ message: (error as Error).message ?? "Unable to
|
|
107
|
+
{ message: (error as Error).message ?? "Unable to download file" },
|
|
99
108
|
400,
|
|
100
109
|
);
|
|
101
110
|
}
|
|
@@ -106,19 +115,13 @@ export function registerGeneratedStorageRoutes(
|
|
|
106
115
|
try {
|
|
107
116
|
const path = readStoragePath(c);
|
|
108
117
|
const expiresIn = parseExpiresIn(c.req.query("expiresIn"));
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
return c.json({
|
|
117
|
-
url,
|
|
118
|
-
method: "GET",
|
|
119
|
-
path,
|
|
120
|
-
disposition: "inline",
|
|
121
|
-
}, 200);
|
|
118
|
+
|
|
119
|
+
// For now, we'll return an error since signed URLs are not available
|
|
120
|
+
// In the future, we could implement direct file serving here
|
|
121
|
+
return c.json(
|
|
122
|
+
{ message: "Preview via signed URL is not available in this runtime. Use direct file access instead." },
|
|
123
|
+
501,
|
|
124
|
+
);
|
|
122
125
|
} catch (error) {
|
|
123
126
|
if (error instanceof AppflareHandledError) {
|
|
124
127
|
return c.json(error.payload, error.status as any);
|
|
@@ -49,7 +49,6 @@ export type AppflareStorage = {
|
|
|
49
49
|
get: (args: StorageGetArgs) => Promise<unknown | null>;
|
|
50
50
|
delete: (args: StorageDeleteArgs) => Promise<void>;
|
|
51
51
|
list: (args?: StorageListArgs) => Promise<unknown>;
|
|
52
|
-
signedUrl: (args: StorageSignedUrlArgs) => Promise<string>;
|
|
53
52
|
};
|
|
54
53
|
|
|
55
54
|
export type AppflareContext = {
|