vibe-gx 1.0.4 → 1.0.6
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/package.json +1 -1
- package/utils/core/handler.js +17 -4
- package/utils/core/parser.js +42 -8
package/package.json
CHANGED
package/utils/core/handler.js
CHANGED
|
@@ -140,21 +140,34 @@ export async function runIntercept(intercept, req, res, isRoute = true) {
|
|
|
140
140
|
*/
|
|
141
141
|
export function handleError(error, req, res) {
|
|
142
142
|
const isDev = process.env.NODE_ENV !== "production";
|
|
143
|
+
const message = error.message || "Unknown error";
|
|
143
144
|
|
|
144
145
|
// Log error (full stack in dev, message only in production)
|
|
145
146
|
if (isDev) {
|
|
146
147
|
console.error("[VIBE ERROR]:", error);
|
|
147
148
|
} else {
|
|
148
|
-
console.error("[VIBE ERROR]:",
|
|
149
|
+
console.error("[VIBE ERROR]:", message);
|
|
149
150
|
}
|
|
150
151
|
|
|
151
152
|
if (!res.headersSent) {
|
|
152
|
-
|
|
153
|
+
// Determine status code based on error type
|
|
154
|
+
let statusCode = 500;
|
|
155
|
+
let errorType = "Internal Server Error";
|
|
156
|
+
|
|
157
|
+
if (message.includes("exceeds max size")) {
|
|
158
|
+
statusCode = 413;
|
|
159
|
+
errorType = "Payload Too Large";
|
|
160
|
+
} else if (message.includes("not allowed")) {
|
|
161
|
+
statusCode = 415;
|
|
162
|
+
errorType = "Unsupported Media Type";
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
res.writeHead(statusCode, { "content-type": "application/json" });
|
|
153
166
|
|
|
154
167
|
// Only expose error details in development
|
|
155
168
|
const responseBody = isDev
|
|
156
|
-
? { error:
|
|
157
|
-
: { error:
|
|
169
|
+
? { error: errorType, message }
|
|
170
|
+
: { error: errorType };
|
|
158
171
|
|
|
159
172
|
res.end(JSON.stringify(responseBody));
|
|
160
173
|
}
|
package/utils/core/parser.js
CHANGED
|
@@ -54,6 +54,19 @@ function parseMultipart(req, res, media, options, resolve, reject) {
|
|
|
54
54
|
let bb;
|
|
55
55
|
let fileError = null;
|
|
56
56
|
const streaming = media.streaming === true;
|
|
57
|
+
let pendingWrites = 0;
|
|
58
|
+
let busboyFinished = false;
|
|
59
|
+
|
|
60
|
+
// Helper to check if we're done
|
|
61
|
+
const checkComplete = () => {
|
|
62
|
+
if (busboyFinished && pendingWrites === 0) {
|
|
63
|
+
if (fileError) {
|
|
64
|
+
reject(fileError);
|
|
65
|
+
} else {
|
|
66
|
+
resolve();
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
};
|
|
57
70
|
|
|
58
71
|
try {
|
|
59
72
|
bb = busboy({
|
|
@@ -75,9 +88,15 @@ function parseMultipart(req, res, media, options, resolve, reject) {
|
|
|
75
88
|
const { filename, mimeType } = info;
|
|
76
89
|
if (!filename) return file.resume();
|
|
77
90
|
|
|
78
|
-
// File type validation
|
|
91
|
+
// File type validation - support wildcards like "image/*"
|
|
79
92
|
if (media.allowedTypes && Array.isArray(media.allowedTypes)) {
|
|
80
|
-
|
|
93
|
+
const isAllowed = media.allowedTypes.some((allowed) => {
|
|
94
|
+
if (allowed.endsWith("/*")) {
|
|
95
|
+
return mimeType.startsWith(allowed.slice(0, -1));
|
|
96
|
+
}
|
|
97
|
+
return allowed === mimeType;
|
|
98
|
+
});
|
|
99
|
+
if (!isAllowed) {
|
|
81
100
|
fileError = new Error(
|
|
82
101
|
`File type '${mimeType}' not allowed. Allowed: ${media.allowedTypes.join(", ")}`,
|
|
83
102
|
);
|
|
@@ -92,6 +111,8 @@ function parseMultipart(req, res, media, options, resolve, reject) {
|
|
|
92
111
|
}
|
|
93
112
|
|
|
94
113
|
// BUFFERING MODE: Write to disk
|
|
114
|
+
pendingWrites++;
|
|
115
|
+
|
|
95
116
|
const parent = media.public ? options.publicFolder || "" : "";
|
|
96
117
|
const dest = path.resolve(
|
|
97
118
|
path.join(parent, media.dest || (media.public ? "uploads" : "private")),
|
|
@@ -103,6 +124,8 @@ function parseMultipart(req, res, media, options, resolve, reject) {
|
|
|
103
124
|
!dest.startsWith(path.resolve(options.publicFolder || ""))
|
|
104
125
|
) {
|
|
105
126
|
console.warn("Attempted upload outside public folder, skipping");
|
|
127
|
+
pendingWrites--;
|
|
128
|
+
checkComplete();
|
|
106
129
|
return file.resume();
|
|
107
130
|
}
|
|
108
131
|
|
|
@@ -110,6 +133,8 @@ function parseMultipart(req, res, media, options, resolve, reject) {
|
|
|
110
133
|
if (!fs.existsSync(dest)) fs.mkdirSync(dest, { recursive: true });
|
|
111
134
|
} catch (err) {
|
|
112
135
|
console.error("Failed to create upload folder:", err);
|
|
136
|
+
pendingWrites--;
|
|
137
|
+
checkComplete();
|
|
113
138
|
return file.resume();
|
|
114
139
|
}
|
|
115
140
|
|
|
@@ -136,18 +161,28 @@ function parseMultipart(req, res, media, options, resolve, reject) {
|
|
|
136
161
|
);
|
|
137
162
|
file.unpipe(writeStream);
|
|
138
163
|
writeStream.end();
|
|
164
|
+
// IMPORTANT: Resume the file stream to drain remaining data
|
|
165
|
+
// This allows busboy to finish parsing the multipart request
|
|
166
|
+
file.resume();
|
|
139
167
|
// Clean up partial file
|
|
140
|
-
fs.unlink(filePath, () => {
|
|
168
|
+
fs.unlink(filePath, () => {
|
|
169
|
+
pendingWrites--;
|
|
170
|
+
checkComplete();
|
|
171
|
+
});
|
|
141
172
|
});
|
|
142
173
|
|
|
143
174
|
file.on("error", (err) => {
|
|
144
175
|
console.error("File stream error:", err);
|
|
145
176
|
writeStream.end();
|
|
177
|
+
pendingWrites--;
|
|
178
|
+
checkComplete();
|
|
146
179
|
});
|
|
147
180
|
|
|
148
181
|
writeStream.on("error", (err) => {
|
|
149
182
|
console.error("Write stream error:", err);
|
|
150
183
|
file.resume();
|
|
184
|
+
pendingWrites--;
|
|
185
|
+
checkComplete();
|
|
151
186
|
});
|
|
152
187
|
|
|
153
188
|
writeStream.on("finish", () => {
|
|
@@ -160,6 +195,8 @@ function parseMultipart(req, res, media, options, resolve, reject) {
|
|
|
160
195
|
size,
|
|
161
196
|
});
|
|
162
197
|
}
|
|
198
|
+
pendingWrites--;
|
|
199
|
+
checkComplete();
|
|
163
200
|
});
|
|
164
201
|
|
|
165
202
|
file.pipe(writeStream);
|
|
@@ -172,11 +209,8 @@ function parseMultipart(req, res, media, options, resolve, reject) {
|
|
|
172
209
|
});
|
|
173
210
|
|
|
174
211
|
bb.on("finish", () => {
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
} else {
|
|
178
|
-
resolve();
|
|
179
|
-
}
|
|
212
|
+
busboyFinished = true;
|
|
213
|
+
checkComplete();
|
|
180
214
|
});
|
|
181
215
|
|
|
182
216
|
req.pipe(bb);
|