@howone/sdk 0.1.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.
- package/README.md +129 -0
- package/dist/index.d.mts +434 -0
- package/dist/index.d.ts +434 -0
- package/dist/index.js +1499 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1438 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +39 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1499 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
AuthGuard: () => AuthGuard,
|
|
34
|
+
DefaultErrorFallback: () => DefaultErrorFallback,
|
|
35
|
+
ErrorBoundary: () => ErrorBoundary,
|
|
36
|
+
FloatingButton: () => FloatingButton,
|
|
37
|
+
Loading: () => Loading,
|
|
38
|
+
LoadingSpinner: () => LoadingSpinner,
|
|
39
|
+
LoginForm: () => LoginForm,
|
|
40
|
+
aiRequest: () => aiRequest,
|
|
41
|
+
aiWorkflow: () => aiWorkflow,
|
|
42
|
+
canAccessArtifact: () => canAccessArtifact,
|
|
43
|
+
createAIWorkflowClient: () => createAIWorkflowClient,
|
|
44
|
+
createAIWorkflowClientAxios: () => createAIWorkflowClientAxios,
|
|
45
|
+
createArtifactsClient: () => createArtifactsClient,
|
|
46
|
+
createClient: () => createClient,
|
|
47
|
+
getCodeStatus: () => getCodeStatus,
|
|
48
|
+
injectEarlyErrorHandler: () => injectEarlyErrorHandler,
|
|
49
|
+
loginWithEmailCode: () => loginWithEmailCode,
|
|
50
|
+
request: () => request,
|
|
51
|
+
sendEmailVerificationCode: () => sendEmailVerificationCode,
|
|
52
|
+
unifiedAuth: () => unifiedAuth,
|
|
53
|
+
unifiedOAuth: () => unifiedOAuth,
|
|
54
|
+
useDebounce: () => useDebounce,
|
|
55
|
+
useIsMobile: () => useIsMobile,
|
|
56
|
+
useLocalStorage: () => useLocalStorage,
|
|
57
|
+
workflowRequest: () => workflowRequest
|
|
58
|
+
});
|
|
59
|
+
module.exports = __toCommonJS(index_exports);
|
|
60
|
+
|
|
61
|
+
// src/components/FloatingButton.tsx
|
|
62
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
63
|
+
var FloatingButton = ({
|
|
64
|
+
text = "Made in Howone",
|
|
65
|
+
onClick,
|
|
66
|
+
className = ""
|
|
67
|
+
}) => {
|
|
68
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
69
|
+
"button",
|
|
70
|
+
{
|
|
71
|
+
onClick,
|
|
72
|
+
className: `fixed flex bg-white gap-2 items-center right-4 z-50 text-black px-3 py-2 rounded-lg shadow-lg transition-colors duration-200 ${className}`,
|
|
73
|
+
style: {
|
|
74
|
+
fontSize: "14px",
|
|
75
|
+
fontWeight: "bold",
|
|
76
|
+
bottom: "28px"
|
|
77
|
+
},
|
|
78
|
+
children: [
|
|
79
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("img", { width: 20, src: "https://sxwxqoixnnklnpeutjrj.supabase.co/storage/v1/object/public/create-x/logo/logo-sm.svg", alt: "" }),
|
|
80
|
+
text
|
|
81
|
+
]
|
|
82
|
+
}
|
|
83
|
+
);
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
// src/services/unified-oauth.ts
|
|
87
|
+
var UnifiedAuthService = class {
|
|
88
|
+
constructor() {
|
|
89
|
+
this.API_BASE_URL = "https://create-x-backend.fly.dev";
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* 初始化 Google 登录流程
|
|
93
|
+
* @returns 包含 token 和用户信息的 Promise
|
|
94
|
+
*/
|
|
95
|
+
async initiateGoogleLogin() {
|
|
96
|
+
const currentUrl = window.location.href;
|
|
97
|
+
const state = {
|
|
98
|
+
from_url: currentUrl,
|
|
99
|
+
app_id: this.generateAppId(),
|
|
100
|
+
timestamp: Date.now()
|
|
101
|
+
};
|
|
102
|
+
const stateString = encodeURIComponent(JSON.stringify(state));
|
|
103
|
+
try {
|
|
104
|
+
const response = await fetch(`${this.API_BASE_URL}/api/auth/google?state=${stateString}`);
|
|
105
|
+
const data = await response.json();
|
|
106
|
+
if (!data.data.success || !data.data.authUrl) {
|
|
107
|
+
throw new Error("Failed to get OAuth URL");
|
|
108
|
+
}
|
|
109
|
+
return this.openOAuthPopup(data.data.authUrl);
|
|
110
|
+
} catch (error) {
|
|
111
|
+
throw error;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* 打开 OAuth 认证弹窗
|
|
116
|
+
* @param authUrl 认证 URL
|
|
117
|
+
* @returns 包含 token 和用户信息的 Promise
|
|
118
|
+
*/
|
|
119
|
+
openOAuthPopup(authUrl) {
|
|
120
|
+
return new Promise((resolve, reject) => {
|
|
121
|
+
const width = 500;
|
|
122
|
+
const height = 600;
|
|
123
|
+
const left = window.screen.width / 2 - width / 2;
|
|
124
|
+
const top = window.screen.height / 2 - height / 2;
|
|
125
|
+
const popup = window.open(
|
|
126
|
+
authUrl,
|
|
127
|
+
"oauth-popup",
|
|
128
|
+
`width=${width},height=${height},left=${left},top=${top},scrollbars=yes,resizable=yes`
|
|
129
|
+
);
|
|
130
|
+
if (!popup) {
|
|
131
|
+
reject(new Error("Unable to open popup, please check browser popup settings"));
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
let popupClosed = false;
|
|
135
|
+
let popupCheckInterval = null;
|
|
136
|
+
const checkPopupStatus = () => {
|
|
137
|
+
try {
|
|
138
|
+
if (!popup || popup.closed) {
|
|
139
|
+
if (!popupClosed) {
|
|
140
|
+
popupClosed = true;
|
|
141
|
+
if (popupCheckInterval) {
|
|
142
|
+
clearInterval(popupCheckInterval);
|
|
143
|
+
popupCheckInterval = null;
|
|
144
|
+
}
|
|
145
|
+
window.removeEventListener("message", messageHandler);
|
|
146
|
+
reject(new Error("User cancelled login"));
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
} catch (e) {
|
|
150
|
+
if (!popupClosed) {
|
|
151
|
+
popupClosed = true;
|
|
152
|
+
if (popupCheckInterval) {
|
|
153
|
+
clearInterval(popupCheckInterval);
|
|
154
|
+
popupCheckInterval = null;
|
|
155
|
+
}
|
|
156
|
+
window.removeEventListener("message", messageHandler);
|
|
157
|
+
reject(new Error("User cancelled login"));
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
popupCheckInterval = window.setInterval(checkPopupStatus, 1e3);
|
|
162
|
+
const messageHandler = (event) => {
|
|
163
|
+
const validOrigins = [
|
|
164
|
+
window.location.origin,
|
|
165
|
+
"https://create-x-backend.fly.dev"
|
|
166
|
+
];
|
|
167
|
+
const isValidOrigin = validOrigins.some(
|
|
168
|
+
(origin) => event.origin === origin || event.origin.includes("localhost") || event.origin.includes("127.0.0.1") || event.origin.includes("fly.dev")
|
|
169
|
+
);
|
|
170
|
+
if (!isValidOrigin) {
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
if (event.data && event.data.type === "OAUTH_SUCCESS") {
|
|
174
|
+
popupClosed = true;
|
|
175
|
+
if (popupCheckInterval) {
|
|
176
|
+
clearInterval(popupCheckInterval);
|
|
177
|
+
popupCheckInterval = null;
|
|
178
|
+
}
|
|
179
|
+
window.removeEventListener("message", messageHandler);
|
|
180
|
+
try {
|
|
181
|
+
if (popup) popup.close();
|
|
182
|
+
} catch (e) {
|
|
183
|
+
}
|
|
184
|
+
resolve(event.data.payload);
|
|
185
|
+
} else if (event.data && event.data.type === "OAUTH_ERROR") {
|
|
186
|
+
popupClosed = true;
|
|
187
|
+
if (popupCheckInterval) {
|
|
188
|
+
clearInterval(popupCheckInterval);
|
|
189
|
+
popupCheckInterval = null;
|
|
190
|
+
}
|
|
191
|
+
window.removeEventListener("message", messageHandler);
|
|
192
|
+
try {
|
|
193
|
+
if (popup) popup.close();
|
|
194
|
+
} catch (e) {
|
|
195
|
+
}
|
|
196
|
+
reject(new Error(event.data.error || "login failed"));
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
window.addEventListener("message", messageHandler);
|
|
200
|
+
setTimeout(() => {
|
|
201
|
+
if (!popupClosed) {
|
|
202
|
+
popupClosed = true;
|
|
203
|
+
if (popupCheckInterval) {
|
|
204
|
+
clearInterval(popupCheckInterval);
|
|
205
|
+
popupCheckInterval = null;
|
|
206
|
+
}
|
|
207
|
+
window.removeEventListener("message", messageHandler);
|
|
208
|
+
try {
|
|
209
|
+
if (popup) popup.close();
|
|
210
|
+
} catch (e) {
|
|
211
|
+
}
|
|
212
|
+
reject(new Error("login timeout"));
|
|
213
|
+
}
|
|
214
|
+
}, 5 * 60 * 1e3);
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* 初始化 GitHub 登录流程
|
|
219
|
+
* @returns 包含 token 和用户信息的 Promise
|
|
220
|
+
*/
|
|
221
|
+
async initiateGitHubLogin() {
|
|
222
|
+
const currentUrl = window.location.href;
|
|
223
|
+
const state = {
|
|
224
|
+
from_url: currentUrl,
|
|
225
|
+
app_id: this.generateAppId(),
|
|
226
|
+
timestamp: Date.now()
|
|
227
|
+
};
|
|
228
|
+
const stateString = encodeURIComponent(JSON.stringify(state));
|
|
229
|
+
try {
|
|
230
|
+
const response = await fetch(`${this.API_BASE_URL}/api/auth/github?state=${stateString}`);
|
|
231
|
+
const data = await response.json();
|
|
232
|
+
if (!data.data.success || !data.data.authUrl) {
|
|
233
|
+
throw new Error("Failed to get GitHub OAuth URL");
|
|
234
|
+
}
|
|
235
|
+
return this.openOAuthPopup(data.data.authUrl);
|
|
236
|
+
} catch (error) {
|
|
237
|
+
throw error;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* 发送邮箱验证码
|
|
242
|
+
* @param email 邮箱地址
|
|
243
|
+
* @param appName 应用名称(可选)
|
|
244
|
+
* @returns 发送结果 Promise
|
|
245
|
+
*/
|
|
246
|
+
async sendEmailVerificationCode(email, appName) {
|
|
247
|
+
try {
|
|
248
|
+
const currentUrl = window.location.origin;
|
|
249
|
+
const appId = this.generateAppId();
|
|
250
|
+
const response = await fetch(`${this.API_BASE_URL}/api/auth/email/send-code`, {
|
|
251
|
+
method: "POST",
|
|
252
|
+
headers: {
|
|
253
|
+
"Content-Type": "application/json"
|
|
254
|
+
},
|
|
255
|
+
body: JSON.stringify({
|
|
256
|
+
email,
|
|
257
|
+
from_url: currentUrl,
|
|
258
|
+
app_id: appId,
|
|
259
|
+
app_name: appName
|
|
260
|
+
})
|
|
261
|
+
});
|
|
262
|
+
const data = await response.json();
|
|
263
|
+
if (!response.ok) {
|
|
264
|
+
throw new Error(JSON.stringify(data));
|
|
265
|
+
}
|
|
266
|
+
if (data.code === 0 && data.data && data.data.success === false) {
|
|
267
|
+
throw new Error(JSON.stringify(data));
|
|
268
|
+
}
|
|
269
|
+
if (data.code === 429) {
|
|
270
|
+
throw new Error(JSON.stringify(data));
|
|
271
|
+
}
|
|
272
|
+
return data;
|
|
273
|
+
} catch (error) {
|
|
274
|
+
if (error instanceof Error && error.message.startsWith("{")) {
|
|
275
|
+
throw error;
|
|
276
|
+
}
|
|
277
|
+
throw new Error(error instanceof Error ? error.message : "\u7F51\u7EDC\u9519\u8BEF\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5");
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* 邮箱验证码登录
|
|
282
|
+
* @param email 邮箱地址
|
|
283
|
+
* @param code 验证码
|
|
284
|
+
* @returns 登录结果 Promise
|
|
285
|
+
*/
|
|
286
|
+
async loginWithEmailCode(email, code) {
|
|
287
|
+
try {
|
|
288
|
+
const currentUrl = window.location.origin;
|
|
289
|
+
const appId = this.generateAppId();
|
|
290
|
+
const response = await fetch(`${this.API_BASE_URL}/api/auth/email/login`, {
|
|
291
|
+
method: "POST",
|
|
292
|
+
headers: {
|
|
293
|
+
"Content-Type": "application/json"
|
|
294
|
+
},
|
|
295
|
+
body: JSON.stringify({
|
|
296
|
+
email,
|
|
297
|
+
code,
|
|
298
|
+
from_url: currentUrl,
|
|
299
|
+
app_id: appId
|
|
300
|
+
})
|
|
301
|
+
});
|
|
302
|
+
const data = await response.json();
|
|
303
|
+
if (!response.ok) {
|
|
304
|
+
throw new Error(JSON.stringify(data));
|
|
305
|
+
}
|
|
306
|
+
if (data.code === 0 && data.data && data.data.success === false) {
|
|
307
|
+
throw new Error(JSON.stringify(data));
|
|
308
|
+
}
|
|
309
|
+
return data;
|
|
310
|
+
} catch (error) {
|
|
311
|
+
if (error instanceof Error && error.message.startsWith("{")) {
|
|
312
|
+
throw error;
|
|
313
|
+
}
|
|
314
|
+
throw new Error(error instanceof Error ? error.message : "\u7F51\u7EDC\u9519\u8BEF\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5");
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* 获取验证码状态(调试用)
|
|
319
|
+
* @param email 邮箱地址
|
|
320
|
+
* @returns 验证码状态 Promise
|
|
321
|
+
*/
|
|
322
|
+
async getCodeStatus(email) {
|
|
323
|
+
try {
|
|
324
|
+
const response = await fetch(
|
|
325
|
+
`${this.API_BASE_URL}/api/auth/email/code-status?email=${encodeURIComponent(email)}`
|
|
326
|
+
);
|
|
327
|
+
return await response.json();
|
|
328
|
+
} catch (error) {
|
|
329
|
+
return { success: false, error: error instanceof Error ? error.message : "\u7F51\u7EDC\u9519\u8BEF" };
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
generateAppId() {
|
|
333
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
334
|
+
let appId = urlParams.get("appId");
|
|
335
|
+
return appId || "app";
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* 检查 OAuth 回调
|
|
339
|
+
* 从 URL 参数中获取 token 和错误信息
|
|
340
|
+
* @returns 包含 token、用户信息和错误的对象
|
|
341
|
+
*/
|
|
342
|
+
checkOAuthCallback() {
|
|
343
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
344
|
+
const token = urlParams.get("token");
|
|
345
|
+
const error = urlParams.get("error");
|
|
346
|
+
const userParam = urlParams.get("user");
|
|
347
|
+
if (token) {
|
|
348
|
+
let user = null;
|
|
349
|
+
if (userParam) {
|
|
350
|
+
try {
|
|
351
|
+
user = JSON.parse(decodeURIComponent(userParam));
|
|
352
|
+
} catch (e) {
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
this.saveAuthData(token, user);
|
|
356
|
+
window.history.replaceState({}, document.title, window.location.pathname);
|
|
357
|
+
return { success: true, token, user };
|
|
358
|
+
}
|
|
359
|
+
if (error) {
|
|
360
|
+
window.history.replaceState({}, document.title, window.location.pathname);
|
|
361
|
+
return { success: false, error: decodeURIComponent(error) };
|
|
362
|
+
}
|
|
363
|
+
const savedAuth = this.getSavedAuthData();
|
|
364
|
+
if (savedAuth && savedAuth.token) {
|
|
365
|
+
return { success: true, ...savedAuth };
|
|
366
|
+
}
|
|
367
|
+
return { success: false };
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* 验证 token 是否有效
|
|
371
|
+
* @param token JWT token
|
|
372
|
+
* @returns 包含有效状态和用户信息的 Promise
|
|
373
|
+
*/
|
|
374
|
+
async verifyToken(token) {
|
|
375
|
+
try {
|
|
376
|
+
const response = await fetch(`${this.API_BASE_URL}/api/auth/verify`, {
|
|
377
|
+
method: "GET",
|
|
378
|
+
headers: {
|
|
379
|
+
"Authorization": `Bearer ${token}`
|
|
380
|
+
}
|
|
381
|
+
});
|
|
382
|
+
if (response.ok) {
|
|
383
|
+
const data = await response.json();
|
|
384
|
+
if (data.success) {
|
|
385
|
+
return { valid: true, user: data.user };
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
return { valid: false };
|
|
389
|
+
} catch (error) {
|
|
390
|
+
return { valid: false };
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
394
|
+
* 从本地存储获取认证数据
|
|
395
|
+
*/
|
|
396
|
+
getSavedAuthData() {
|
|
397
|
+
try {
|
|
398
|
+
const token = localStorage.getItem("auth_token");
|
|
399
|
+
if (!token) return null;
|
|
400
|
+
let user = null;
|
|
401
|
+
const userJson = localStorage.getItem("auth_user");
|
|
402
|
+
if (userJson) {
|
|
403
|
+
try {
|
|
404
|
+
user = JSON.parse(userJson);
|
|
405
|
+
} catch (e) {
|
|
406
|
+
console.error("Failed to parse saved user data:", e);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
return { token, user };
|
|
410
|
+
} catch (e) {
|
|
411
|
+
console.error("Failed to get auth data from localStorage:", e);
|
|
412
|
+
return null;
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* 保存认证数据到本地存储
|
|
417
|
+
*/
|
|
418
|
+
saveAuthData(token, user) {
|
|
419
|
+
try {
|
|
420
|
+
localStorage.setItem("auth_token", token);
|
|
421
|
+
localStorage.setItem("auth_user", JSON.stringify(user || {}));
|
|
422
|
+
} catch (e) {
|
|
423
|
+
console.error("Failed to save auth data to localStorage:", e);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
/**
|
|
427
|
+
* 退出登录
|
|
428
|
+
* @param token JWT token
|
|
429
|
+
*/
|
|
430
|
+
async logout(token) {
|
|
431
|
+
try {
|
|
432
|
+
await fetch(`${this.API_BASE_URL}/api/auth/logout`, {
|
|
433
|
+
method: "POST",
|
|
434
|
+
headers: {
|
|
435
|
+
"Authorization": `Bearer ${token}`
|
|
436
|
+
}
|
|
437
|
+
});
|
|
438
|
+
} catch (error) {
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
};
|
|
442
|
+
var unifiedAuth = new UnifiedAuthService();
|
|
443
|
+
var unifiedOAuth = unifiedAuth;
|
|
444
|
+
function sendEmailVerificationCode(email, appName) {
|
|
445
|
+
return unifiedAuth.sendEmailVerificationCode(email, appName);
|
|
446
|
+
}
|
|
447
|
+
function loginWithEmailCode(email, code) {
|
|
448
|
+
return unifiedAuth.loginWithEmailCode(email, code);
|
|
449
|
+
}
|
|
450
|
+
function getCodeStatus(email) {
|
|
451
|
+
return unifiedAuth.getCodeStatus(email);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// src/services/ai-workflow.ts
|
|
455
|
+
var AIWorkflowClient = class {
|
|
456
|
+
constructor(options = {}) {
|
|
457
|
+
this.baseUrl = options.baseUrl?.replace(/\/+$/, "") || "";
|
|
458
|
+
this.apiKey = options.apiKey;
|
|
459
|
+
this.headers = { "Content-Type": "application/json", ...options.headers || {} };
|
|
460
|
+
this.fetchImpl = options.fetchImpl || fetch.bind(globalThis);
|
|
461
|
+
}
|
|
462
|
+
buildHeaders(extra) {
|
|
463
|
+
const h = { ...this.headers, ...extra || {} };
|
|
464
|
+
if (this.apiKey && !h["Authorization"]) {
|
|
465
|
+
h["Authorization"] = `Bearer ${this.apiKey}`;
|
|
466
|
+
}
|
|
467
|
+
return h;
|
|
468
|
+
}
|
|
469
|
+
async safeJson(resp) {
|
|
470
|
+
try {
|
|
471
|
+
return await resp.json();
|
|
472
|
+
} catch (_e) {
|
|
473
|
+
return null;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
/**
|
|
477
|
+
* 按 ID 执行工作流:POST {baseUrl}/workflow/{workflowId}/execute
|
|
478
|
+
* body: { input, options }
|
|
479
|
+
*/
|
|
480
|
+
async executeWorkflow(workflowId, input, options) {
|
|
481
|
+
if (!this.baseUrl) {
|
|
482
|
+
throw new Error("AI workflow client requires a baseUrl (e.g. https://evoagentx-server.fly.dev)");
|
|
483
|
+
}
|
|
484
|
+
const url = `${this.baseUrl}/workflow/${encodeURIComponent(workflowId)}/execute`;
|
|
485
|
+
try {
|
|
486
|
+
const res = await this.fetchImpl(url, {
|
|
487
|
+
method: "POST",
|
|
488
|
+
headers: this.buildHeaders(),
|
|
489
|
+
body: JSON.stringify({ input, options })
|
|
490
|
+
});
|
|
491
|
+
const data = await this.safeJson(res);
|
|
492
|
+
if (!res.ok) {
|
|
493
|
+
return { success: false, error: data?.error || `HTTP ${res.status}` };
|
|
494
|
+
}
|
|
495
|
+
return data || { success: true };
|
|
496
|
+
} catch (error) {
|
|
497
|
+
return { success: false, error: error instanceof Error ? error.message : "Network error" };
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
};
|
|
501
|
+
function createAIWorkflowClient(options = {}) {
|
|
502
|
+
return new AIWorkflowClient(options);
|
|
503
|
+
}
|
|
504
|
+
var aiWorkflow = createAIWorkflowClient({ baseUrl: "https://evoagentx-server" });
|
|
505
|
+
|
|
506
|
+
// src/services/request/index.ts
|
|
507
|
+
var import_axios = __toESM(require("axios"));
|
|
508
|
+
var Request = class {
|
|
509
|
+
constructor(config) {
|
|
510
|
+
this.abortControllers = /* @__PURE__ */ new Map();
|
|
511
|
+
this.instance = import_axios.default.create({
|
|
512
|
+
...config,
|
|
513
|
+
validateStatus: (status) => {
|
|
514
|
+
return status >= 200 && status < 300;
|
|
515
|
+
}
|
|
516
|
+
});
|
|
517
|
+
this.interceptors = config.interceptors;
|
|
518
|
+
this.instance.interceptors.request.use(
|
|
519
|
+
this.interceptors?.requestInterceptor,
|
|
520
|
+
this.interceptors?.requestInterceptorCatch
|
|
521
|
+
);
|
|
522
|
+
this.instance.interceptors.response.use(
|
|
523
|
+
this.interceptors?.responseInterceptor,
|
|
524
|
+
this.interceptors?.responseInterceptorCatch
|
|
525
|
+
);
|
|
526
|
+
this.instance.interceptors.request.use(
|
|
527
|
+
(config2) => {
|
|
528
|
+
return config2;
|
|
529
|
+
},
|
|
530
|
+
(err) => {
|
|
531
|
+
return Promise.reject(err);
|
|
532
|
+
}
|
|
533
|
+
);
|
|
534
|
+
this.instance.interceptors.response.use(
|
|
535
|
+
(res) => {
|
|
536
|
+
return res.data;
|
|
537
|
+
},
|
|
538
|
+
(err) => {
|
|
539
|
+
if (import_axios.default.isCancel(err)) {
|
|
540
|
+
return Promise.reject({
|
|
541
|
+
isCanceled: true,
|
|
542
|
+
message: "request canceled",
|
|
543
|
+
originalError: err
|
|
544
|
+
});
|
|
545
|
+
}
|
|
546
|
+
if (err.response?.data?.error) {
|
|
547
|
+
return Promise.reject(err.response.data.error);
|
|
548
|
+
}
|
|
549
|
+
return Promise.reject(err);
|
|
550
|
+
}
|
|
551
|
+
);
|
|
552
|
+
}
|
|
553
|
+
cancelRequest(url) {
|
|
554
|
+
this.abortControllers.forEach((controller, key) => {
|
|
555
|
+
if (key.includes(url)) {
|
|
556
|
+
controller.abort();
|
|
557
|
+
this.abortControllers.delete(key);
|
|
558
|
+
}
|
|
559
|
+
});
|
|
560
|
+
}
|
|
561
|
+
cancelAllRequests() {
|
|
562
|
+
this.abortControllers.forEach((controller) => {
|
|
563
|
+
controller.abort();
|
|
564
|
+
});
|
|
565
|
+
this.abortControllers.clear();
|
|
566
|
+
}
|
|
567
|
+
request(config) {
|
|
568
|
+
const controller = new AbortController();
|
|
569
|
+
const url = config.url || "";
|
|
570
|
+
const method = config.method || "GET";
|
|
571
|
+
const key = `${method}:${url}`;
|
|
572
|
+
this.abortControllers.set(key, controller);
|
|
573
|
+
config.signal = controller.signal;
|
|
574
|
+
return new Promise((resolve, reject) => {
|
|
575
|
+
if (config.interceptors?.requestInterceptor) {
|
|
576
|
+
config = config.interceptors.requestInterceptor(config);
|
|
577
|
+
}
|
|
578
|
+
this.instance.request(config).then((res) => {
|
|
579
|
+
this.abortControllers.delete(key);
|
|
580
|
+
if (config.interceptors?.responseInterceptor) {
|
|
581
|
+
res = config.interceptors.responseInterceptor(res);
|
|
582
|
+
}
|
|
583
|
+
resolve(res);
|
|
584
|
+
}).catch((err) => {
|
|
585
|
+
this.abortControllers.delete(key);
|
|
586
|
+
reject(err);
|
|
587
|
+
});
|
|
588
|
+
});
|
|
589
|
+
}
|
|
590
|
+
get(config) {
|
|
591
|
+
return this.request({ ...config, method: "GET" });
|
|
592
|
+
}
|
|
593
|
+
post(config) {
|
|
594
|
+
return this.request({ ...config, method: "POST" });
|
|
595
|
+
}
|
|
596
|
+
delete(config) {
|
|
597
|
+
return this.request({ ...config, method: "DELETE" });
|
|
598
|
+
}
|
|
599
|
+
put(config) {
|
|
600
|
+
return this.request({ ...config, method: "PUT" });
|
|
601
|
+
}
|
|
602
|
+
patch(config) {
|
|
603
|
+
return this.request({ ...config, method: "PATCH" });
|
|
604
|
+
}
|
|
605
|
+
};
|
|
606
|
+
var request_default = Request;
|
|
607
|
+
|
|
608
|
+
// src/services/ai-workflow-axios.ts
|
|
609
|
+
function createAIWorkflowClientAxios(options = {}) {
|
|
610
|
+
const basePath = options.basePath ?? "/api";
|
|
611
|
+
const baseUrl = (options.baseUrl || "https://evoagentx-server.fly.dev").replace(/\/+$/, "");
|
|
612
|
+
const baseAPI = `${baseUrl}${basePath.startsWith("/") ? "" : "/"}${basePath}`.replace(/\/+$/, "");
|
|
613
|
+
const client = options.requestInstance || new request_default({
|
|
614
|
+
baseURL: baseAPI,
|
|
615
|
+
timeout: options.timeout ?? 6e4,
|
|
616
|
+
interceptors: {
|
|
617
|
+
requestInterceptor: (config) => {
|
|
618
|
+
config.headers = config.headers || {};
|
|
619
|
+
if (options.apiKey && !config.headers["Authorization"]) {
|
|
620
|
+
config.headers["Authorization"] = `Bearer ${options.apiKey}`;
|
|
621
|
+
}
|
|
622
|
+
if (options.headers) {
|
|
623
|
+
config.headers = { ...config.headers || {}, ...options.headers };
|
|
624
|
+
}
|
|
625
|
+
return config;
|
|
626
|
+
},
|
|
627
|
+
requestInterceptorCatch: (err) => Promise.reject(err),
|
|
628
|
+
responseInterceptor: (res) => res,
|
|
629
|
+
responseInterceptorCatch: (err) => Promise.reject(err)
|
|
630
|
+
}
|
|
631
|
+
});
|
|
632
|
+
return {
|
|
633
|
+
async executeWorkflow(workflowId, input, opts) {
|
|
634
|
+
const url = `${baseUrl}/workflow/${encodeURIComponent(workflowId)}/execute`;
|
|
635
|
+
const data = await client.post({ url, data: { input, options: opts } });
|
|
636
|
+
return data;
|
|
637
|
+
}
|
|
638
|
+
};
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
// src/services/artifact-types.ts
|
|
642
|
+
function canAccessArtifact(a, ctx = {}) {
|
|
643
|
+
if (!a) return false;
|
|
644
|
+
if (a.visibility === "public") return true;
|
|
645
|
+
if (a.visibility === "project") {
|
|
646
|
+
if (ctx.projectId && a.projectId && ctx.projectId === a.projectId) return true;
|
|
647
|
+
if (ctx.tokenScopes && ctx.tokenScopes.includes("project:read")) return true;
|
|
648
|
+
return false;
|
|
649
|
+
}
|
|
650
|
+
if (a.visibility === "private") {
|
|
651
|
+
if (ctx.userId && a.ownerId && ctx.userId === a.ownerId) return true;
|
|
652
|
+
if (ctx.tokenScopes && (ctx.tokenScopes.includes("artifact:read:all") || ctx.tokenScopes.includes("admin"))) return true;
|
|
653
|
+
return false;
|
|
654
|
+
}
|
|
655
|
+
if (a.visibility === "shared") {
|
|
656
|
+
if (ctx.userId && a.sharedWith && a.sharedWith.includes(ctx.userId)) return true;
|
|
657
|
+
if (ctx.tokenScopes && (ctx.tokenScopes.includes("artifact:read:all") || ctx.tokenScopes.includes("admin"))) return true;
|
|
658
|
+
return false;
|
|
659
|
+
}
|
|
660
|
+
return false;
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
// src/services/artifacts-client.ts
|
|
664
|
+
function createArtifactsClient(req) {
|
|
665
|
+
return {
|
|
666
|
+
async create(input) {
|
|
667
|
+
return await req.post({ url: "/artifacts", data: input });
|
|
668
|
+
},
|
|
669
|
+
async list(query) {
|
|
670
|
+
return await req.get({ url: "/artifacts", params: query });
|
|
671
|
+
},
|
|
672
|
+
async get(id) {
|
|
673
|
+
return await req.get({ url: `/artifacts/${encodeURIComponent(id)}` });
|
|
674
|
+
},
|
|
675
|
+
async setVisibility(id, visibility) {
|
|
676
|
+
await req.patch({ url: `/artifacts/${encodeURIComponent(id)}/visibility`, data: { visibility } });
|
|
677
|
+
},
|
|
678
|
+
async delete(id) {
|
|
679
|
+
await req.delete({ url: `/artifacts/${encodeURIComponent(id)}` });
|
|
680
|
+
},
|
|
681
|
+
// convenience local check (server is authoritative)
|
|
682
|
+
canAccessLocal(a, ctx) {
|
|
683
|
+
if (!a) return false;
|
|
684
|
+
if (a.visibility === "public") return true;
|
|
685
|
+
if (a.visibility === "project") return Boolean(ctx.projectId && a.projectId && ctx.projectId === a.projectId);
|
|
686
|
+
if (a.visibility === "private") return Boolean(ctx.userId && a.ownerId && ctx.userId === a.ownerId);
|
|
687
|
+
if (a.visibility === "shared") return Boolean(ctx.userId && a.sharedWith && a.sharedWith.includes(ctx.userId));
|
|
688
|
+
return false;
|
|
689
|
+
}
|
|
690
|
+
};
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
// src/services/index.ts
|
|
694
|
+
var request = new request_default({
|
|
695
|
+
baseURL: "https://create-x-backend.fly.dev/api",
|
|
696
|
+
timeout: 6e4,
|
|
697
|
+
interceptors: {
|
|
698
|
+
requestInterceptor: (config) => {
|
|
699
|
+
config.headers = config.headers || {};
|
|
700
|
+
return config;
|
|
701
|
+
},
|
|
702
|
+
requestInterceptorCatch: (err) => {
|
|
703
|
+
return Promise.reject(err);
|
|
704
|
+
},
|
|
705
|
+
responseInterceptor: (res) => {
|
|
706
|
+
return res;
|
|
707
|
+
},
|
|
708
|
+
responseInterceptorCatch: (err) => {
|
|
709
|
+
return Promise.reject(err);
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
});
|
|
713
|
+
var aiRequest = new request_default({
|
|
714
|
+
baseURL: "https://evoagentx-server.fly.dev",
|
|
715
|
+
timeout: 6e5,
|
|
716
|
+
interceptors: {
|
|
717
|
+
requestInterceptor: (config) => {
|
|
718
|
+
config.headers = config.headers || {};
|
|
719
|
+
return config;
|
|
720
|
+
},
|
|
721
|
+
requestInterceptorCatch: (err) => {
|
|
722
|
+
return Promise.reject(err);
|
|
723
|
+
},
|
|
724
|
+
responseInterceptor: (res) => {
|
|
725
|
+
return res;
|
|
726
|
+
},
|
|
727
|
+
responseInterceptorCatch: (err) => {
|
|
728
|
+
return Promise.reject(err);
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
});
|
|
732
|
+
var workflowRequest = aiRequest;
|
|
733
|
+
function createClient(opts) {
|
|
734
|
+
const biz = opts?.requestInstance || request;
|
|
735
|
+
const ai = opts?.aiRequestInstance || aiRequest;
|
|
736
|
+
let token = null;
|
|
737
|
+
function applyToken(t) {
|
|
738
|
+
try {
|
|
739
|
+
try {
|
|
740
|
+
if (biz && biz.instance) {
|
|
741
|
+
if (t) biz.instance.defaults.headers.common["Authorization"] = `Bearer ${t}`;
|
|
742
|
+
else delete biz.instance.defaults.headers.common["Authorization"];
|
|
743
|
+
}
|
|
744
|
+
if (ai && ai.instance) {
|
|
745
|
+
if (t) ai.instance.defaults.headers.common["Authorization"] = `Bearer ${t}`;
|
|
746
|
+
else delete ai.instance.defaults.headers.common["Authorization"];
|
|
747
|
+
}
|
|
748
|
+
} catch (_e) {
|
|
749
|
+
}
|
|
750
|
+
} catch (_e) {
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
const runtimeMode = (() => {
|
|
754
|
+
if (opts?.mode) return opts.mode;
|
|
755
|
+
try {
|
|
756
|
+
if (typeof window !== "undefined") return window.self !== window.top ? "embedded" : "standalone";
|
|
757
|
+
} catch (_e) {
|
|
758
|
+
return "standalone";
|
|
759
|
+
}
|
|
760
|
+
return "standalone";
|
|
761
|
+
})();
|
|
762
|
+
async function initToken() {
|
|
763
|
+
if (opts?.auth?.getToken) {
|
|
764
|
+
try {
|
|
765
|
+
const t = await opts.auth.getToken();
|
|
766
|
+
if (t) {
|
|
767
|
+
token = t;
|
|
768
|
+
applyToken(token);
|
|
769
|
+
return;
|
|
770
|
+
}
|
|
771
|
+
} catch (_e) {
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
if (runtimeMode === "embedded" && opts?.auth?.tokenInjection) {
|
|
775
|
+
const allowed = opts.auth.tokenInjection.allowedOrigins || [];
|
|
776
|
+
const waitMs = opts.auth.tokenInjection.waitMs ?? 3e3;
|
|
777
|
+
const tokenFromPostMessage = await new Promise((resolve) => {
|
|
778
|
+
let resolved = false;
|
|
779
|
+
function handler(ev) {
|
|
780
|
+
try {
|
|
781
|
+
if (!ev.data || typeof ev.data !== "object") return;
|
|
782
|
+
if (ev.data.type !== "howone:token") return;
|
|
783
|
+
if (allowed.length > 0 && !allowed.includes(ev.origin)) return;
|
|
784
|
+
const t = ev.data.token;
|
|
785
|
+
if (t && !resolved) {
|
|
786
|
+
resolved = true;
|
|
787
|
+
window.removeEventListener("message", handler);
|
|
788
|
+
resolve(String(t));
|
|
789
|
+
}
|
|
790
|
+
} catch (_e) {
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
window.addEventListener("message", handler);
|
|
794
|
+
setTimeout(() => {
|
|
795
|
+
if (!resolved) {
|
|
796
|
+
resolved = true;
|
|
797
|
+
window.removeEventListener("message", handler);
|
|
798
|
+
resolve(null);
|
|
799
|
+
}
|
|
800
|
+
}, waitMs);
|
|
801
|
+
});
|
|
802
|
+
if (tokenFromPostMessage) {
|
|
803
|
+
token = tokenFromPostMessage;
|
|
804
|
+
applyToken(token);
|
|
805
|
+
return;
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
try {
|
|
810
|
+
if (typeof window !== "undefined") {
|
|
811
|
+
void initToken();
|
|
812
|
+
}
|
|
813
|
+
} catch (_e) {
|
|
814
|
+
}
|
|
815
|
+
return {
|
|
816
|
+
request: biz,
|
|
817
|
+
aiRequest: ai,
|
|
818
|
+
workflowRequest: ai,
|
|
819
|
+
// artifact helpers using artifacts-client
|
|
820
|
+
artifacts: createArtifactsClient(biz),
|
|
821
|
+
// auth helpers
|
|
822
|
+
auth: {
|
|
823
|
+
setToken: (t) => {
|
|
824
|
+
token = t;
|
|
825
|
+
applyToken(t);
|
|
826
|
+
},
|
|
827
|
+
getToken: () => token,
|
|
828
|
+
isAuthenticated: () => Boolean(token),
|
|
829
|
+
// minimal login/logout stubs - consumers can override behavior
|
|
830
|
+
login: (redirect) => {
|
|
831
|
+
if (opts?.authRequired) {
|
|
832
|
+
if (typeof window !== "undefined") {
|
|
833
|
+
const loc = redirect || window.location.href;
|
|
834
|
+
window.location.href = `/login?redirect=${encodeURIComponent(loc)}`;
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
},
|
|
838
|
+
logout: () => {
|
|
839
|
+
token = null;
|
|
840
|
+
applyToken(null);
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
};
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
// src/components/auth/LoginForm.tsx
|
|
847
|
+
var import_react = require("react");
|
|
848
|
+
var import_react2 = require("@iconify/react");
|
|
849
|
+
var import_lucide_react = require("lucide-react");
|
|
850
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
851
|
+
var LoginForm = ({
|
|
852
|
+
onLoginSuccess,
|
|
853
|
+
appName = "AI Application Platform",
|
|
854
|
+
className = ""
|
|
855
|
+
}) => {
|
|
856
|
+
const [email, setEmail] = (0, import_react.useState)("");
|
|
857
|
+
const [code, setCode] = (0, import_react.useState)("");
|
|
858
|
+
const [validationErrors, setValidationErrors] = (0, import_react.useState)({});
|
|
859
|
+
const [isAnyLoading, setIsAnyLoading] = (0, import_react.useState)(false);
|
|
860
|
+
const [codeSent, setCodeSent] = (0, import_react.useState)(false);
|
|
861
|
+
const [loginError, setLoginError] = (0, import_react.useState)(null);
|
|
862
|
+
const googleLogin = async () => {
|
|
863
|
+
console.log("Google login clicked");
|
|
864
|
+
};
|
|
865
|
+
const githubLogin = async () => {
|
|
866
|
+
console.log("GitHub login clicked");
|
|
867
|
+
};
|
|
868
|
+
const sendEmailCode = async (email2) => {
|
|
869
|
+
console.log("Send code to:", email2, "for app:", appName);
|
|
870
|
+
setCodeSent(true);
|
|
871
|
+
};
|
|
872
|
+
const loginWithEmail = async (email2, code2) => {
|
|
873
|
+
console.log("Login with email:", email2, "code:", code2);
|
|
874
|
+
if (onLoginSuccess) onLoginSuccess();
|
|
875
|
+
};
|
|
876
|
+
const clearError = () => {
|
|
877
|
+
setLoginError(null);
|
|
878
|
+
setValidationErrors({});
|
|
879
|
+
};
|
|
880
|
+
(0, import_react.useEffect)(() => {
|
|
881
|
+
if (loginError) {
|
|
882
|
+
try {
|
|
883
|
+
const errorObj = JSON.parse(loginError);
|
|
884
|
+
const newErrors = {};
|
|
885
|
+
if (errorObj.code === 429) {
|
|
886
|
+
newErrors.form = errorObj.msg || "\u8BF7\u6C42\u8FC7\u4E8E\u9891\u7E41\uFF0C\u8BF7\u7A0D\u540E\u518D\u8BD5";
|
|
887
|
+
} else if (errorObj.data?.code === 401 && errorObj.data?.message) {
|
|
888
|
+
if (codeSent) {
|
|
889
|
+
newErrors.code = errorObj.data.message;
|
|
890
|
+
} else {
|
|
891
|
+
newErrors.form = errorObj.data.message;
|
|
892
|
+
}
|
|
893
|
+
} else if (errorObj.msg) {
|
|
894
|
+
newErrors.form = errorObj.msg;
|
|
895
|
+
} else if (errorObj.data?.message) {
|
|
896
|
+
newErrors.form = errorObj.data.message;
|
|
897
|
+
} else {
|
|
898
|
+
newErrors.form = "\u53D1\u751F\u672A\u77E5\u9519\u8BEF\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5";
|
|
899
|
+
}
|
|
900
|
+
setValidationErrors(newErrors);
|
|
901
|
+
} catch (e) {
|
|
902
|
+
setValidationErrors({ form: loginError });
|
|
903
|
+
}
|
|
904
|
+
const timer = setTimeout(() => {
|
|
905
|
+
clearError();
|
|
906
|
+
}, 8e3);
|
|
907
|
+
return () => clearTimeout(timer);
|
|
908
|
+
}
|
|
909
|
+
}, [loginError, codeSent]);
|
|
910
|
+
const handleSubmit = async (e) => {
|
|
911
|
+
e.preventDefault();
|
|
912
|
+
const formData = new FormData(e.currentTarget);
|
|
913
|
+
setValidationErrors({});
|
|
914
|
+
if (!codeSent) {
|
|
915
|
+
const emailValue = formData.get("email");
|
|
916
|
+
if (!emailValue) {
|
|
917
|
+
setValidationErrors({ email: "Please enter your email address" });
|
|
918
|
+
return;
|
|
919
|
+
}
|
|
920
|
+
setIsAnyLoading(true);
|
|
921
|
+
try {
|
|
922
|
+
await sendEmailCode(emailValue);
|
|
923
|
+
setEmail(emailValue);
|
|
924
|
+
} catch (error) {
|
|
925
|
+
setLoginError(error instanceof Error ? error.message : "Failed to send code");
|
|
926
|
+
} finally {
|
|
927
|
+
setIsAnyLoading(false);
|
|
928
|
+
}
|
|
929
|
+
} else {
|
|
930
|
+
const codeValue = code;
|
|
931
|
+
if (!codeValue || codeValue.length !== 6) {
|
|
932
|
+
setValidationErrors({ code: "Please enter the 6-digit verification code" });
|
|
933
|
+
return;
|
|
934
|
+
}
|
|
935
|
+
setIsAnyLoading(true);
|
|
936
|
+
try {
|
|
937
|
+
await loginWithEmail(email, codeValue);
|
|
938
|
+
} catch (error) {
|
|
939
|
+
setLoginError(error instanceof Error ? error.message : "Login failed");
|
|
940
|
+
} finally {
|
|
941
|
+
setIsAnyLoading(false);
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
};
|
|
945
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: `w-full space-y-6 ${className}`, children: [
|
|
946
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "space-y-1", children: [
|
|
947
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h1", { className: "text-3xl font-bold text-gray-900", children: "Welcome Back!" }),
|
|
948
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-sm text-gray-600", children: "Please enter log in details below" })
|
|
949
|
+
] }),
|
|
950
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("form", { className: "space-y-5", onSubmit: handleSubmit, children: [
|
|
951
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "space-y-2", children: [
|
|
952
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { htmlFor: "email", className: "text-gray-700 font-medium text-sm block", children: "Email" }),
|
|
953
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
954
|
+
"input",
|
|
955
|
+
{
|
|
956
|
+
id: "email",
|
|
957
|
+
type: "email",
|
|
958
|
+
name: "email",
|
|
959
|
+
value: email,
|
|
960
|
+
onChange: (e) => {
|
|
961
|
+
const value = e.target.value;
|
|
962
|
+
setEmail(value);
|
|
963
|
+
if (validationErrors.email) {
|
|
964
|
+
const newErrors = { ...validationErrors };
|
|
965
|
+
delete newErrors.email;
|
|
966
|
+
setValidationErrors(newErrors);
|
|
967
|
+
}
|
|
968
|
+
},
|
|
969
|
+
required: true,
|
|
970
|
+
"aria-invalid": !!validationErrors.email,
|
|
971
|
+
className: "w-full text-sm bg-gray-50 rounded-md h-11 px-3 hover:bg-gray-100 focus:bg-white border border-gray-200 focus:border-gray-400 focus:outline-none",
|
|
972
|
+
placeholder: ""
|
|
973
|
+
}
|
|
974
|
+
),
|
|
975
|
+
validationErrors.email && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-red-600 text-sm", children: validationErrors.email })
|
|
976
|
+
] }),
|
|
977
|
+
codeSent && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "space-y-2", children: [
|
|
978
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { className: "text-gray-700 font-medium text-sm block", children: "Verification Code" }),
|
|
979
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
980
|
+
"input",
|
|
981
|
+
{
|
|
982
|
+
type: "text",
|
|
983
|
+
maxLength: 6,
|
|
984
|
+
value: code,
|
|
985
|
+
onChange: (e) => {
|
|
986
|
+
const value = e.target.value.replace(/\D/g, "");
|
|
987
|
+
setCode(value);
|
|
988
|
+
if (validationErrors.code) {
|
|
989
|
+
const newErrors = { ...validationErrors };
|
|
990
|
+
delete newErrors.code;
|
|
991
|
+
setValidationErrors(newErrors);
|
|
992
|
+
}
|
|
993
|
+
},
|
|
994
|
+
"aria-invalid": !!validationErrors.code,
|
|
995
|
+
className: "w-full text-center text-sm bg-gray-50 rounded-md h-11 px-3 hover:bg-gray-100 focus:bg-white border border-gray-200 focus:border-gray-400 focus:outline-none tracking-widest",
|
|
996
|
+
placeholder: "000000"
|
|
997
|
+
}
|
|
998
|
+
),
|
|
999
|
+
validationErrors.code && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-red-600 text-sm", children: validationErrors.code })
|
|
1000
|
+
] }),
|
|
1001
|
+
validationErrors.form && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "text-red-600 text-sm p-3 bg-red-50 rounded-md border border-red-200", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center", children: [
|
|
1002
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react2.Icon, { icon: "material-symbols:error", className: "text-red-600 mr-2" }),
|
|
1003
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: validationErrors.form })
|
|
1004
|
+
] }) }),
|
|
1005
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
1006
|
+
"button",
|
|
1007
|
+
{
|
|
1008
|
+
type: "submit",
|
|
1009
|
+
className: "w-full h-11 bg-black hover:bg-gray-800 text-white font-medium rounded-md text-sm disabled:opacity-70 flex items-center justify-center",
|
|
1010
|
+
disabled: isAnyLoading || !email || codeSent && code.length !== 6,
|
|
1011
|
+
"aria-busy": isAnyLoading,
|
|
1012
|
+
children: [
|
|
1013
|
+
isAnyLoading && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react.Loader2, { className: "mr-2 h-4 w-4 animate-spin" }),
|
|
1014
|
+
codeSent ? isAnyLoading ? "Signing in..." : "Sign in" : isAnyLoading ? "Sending..." : "Send Code"
|
|
1015
|
+
]
|
|
1016
|
+
}
|
|
1017
|
+
)
|
|
1018
|
+
] }),
|
|
1019
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "space-y-3", children: [
|
|
1020
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "relative", children: [
|
|
1021
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "absolute inset-0 flex items-center", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "w-full border-t border-gray-200" }) }),
|
|
1022
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "relative flex justify-center text-xs", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "px-4 text-gray-500", children: "or continue" }) })
|
|
1023
|
+
] }),
|
|
1024
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
1025
|
+
"button",
|
|
1026
|
+
{
|
|
1027
|
+
className: "w-full h-11 bg-white hover:bg-gray-50 text-gray-900 hover:text-gray-900 border border-gray-200 hover:border-gray-300 font-medium rounded-md text-sm disabled:opacity-70 flex items-center justify-center",
|
|
1028
|
+
onClick: () => {
|
|
1029
|
+
if (!isAnyLoading) {
|
|
1030
|
+
clearError();
|
|
1031
|
+
googleLogin();
|
|
1032
|
+
}
|
|
1033
|
+
},
|
|
1034
|
+
disabled: isAnyLoading,
|
|
1035
|
+
children: [
|
|
1036
|
+
isAnyLoading ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react.Loader2, { className: "mr-2 h-4 w-4 animate-spin" }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react2.Icon, { icon: "flat-color-icons:google", className: "w-6 h-6 mr-2" }),
|
|
1037
|
+
isAnyLoading ? "Connecting..." : "Log in with Google"
|
|
1038
|
+
]
|
|
1039
|
+
}
|
|
1040
|
+
),
|
|
1041
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
1042
|
+
"button",
|
|
1043
|
+
{
|
|
1044
|
+
className: "w-full h-11 bg-black hover:bg-gray-800 text-white font-medium rounded-md text-sm disabled:opacity-70 flex items-center justify-center",
|
|
1045
|
+
onClick: () => {
|
|
1046
|
+
if (!isAnyLoading) {
|
|
1047
|
+
clearError();
|
|
1048
|
+
githubLogin();
|
|
1049
|
+
}
|
|
1050
|
+
},
|
|
1051
|
+
disabled: isAnyLoading,
|
|
1052
|
+
children: [
|
|
1053
|
+
isAnyLoading ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react.Loader2, { className: "mr-2 h-4 w-4 animate-spin" }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react2.Icon, { icon: "mdi:github", className: "w-6 h-6 mr-2" }),
|
|
1054
|
+
isAnyLoading ? "Connecting..." : "Log in with GitHub"
|
|
1055
|
+
]
|
|
1056
|
+
}
|
|
1057
|
+
)
|
|
1058
|
+
] })
|
|
1059
|
+
] });
|
|
1060
|
+
};
|
|
1061
|
+
|
|
1062
|
+
// src/components/auth/AuthGuard.tsx
|
|
1063
|
+
var import_react_router_dom = require("react-router-dom");
|
|
1064
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
1065
|
+
var AuthGuard = ({
|
|
1066
|
+
children,
|
|
1067
|
+
fallback,
|
|
1068
|
+
redirectTo = "/login",
|
|
1069
|
+
requireAuth = true,
|
|
1070
|
+
roles = [],
|
|
1071
|
+
permissions = []
|
|
1072
|
+
}) => {
|
|
1073
|
+
const isAuthenticated = false;
|
|
1074
|
+
const user = null;
|
|
1075
|
+
const isLoading = false;
|
|
1076
|
+
const location = (0, import_react_router_dom.useLocation)();
|
|
1077
|
+
if (isLoading) {
|
|
1078
|
+
if (fallback) {
|
|
1079
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_jsx_runtime3.Fragment, { children: fallback });
|
|
1080
|
+
}
|
|
1081
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "min-h-screen flex items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600" }) });
|
|
1082
|
+
}
|
|
1083
|
+
if (requireAuth && !isAuthenticated) {
|
|
1084
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_router_dom.Navigate, { to: redirectTo, state: { from: location }, replace: true });
|
|
1085
|
+
}
|
|
1086
|
+
if (roles.length > 0 && user !== null) {
|
|
1087
|
+
const currentUser = user;
|
|
1088
|
+
if (currentUser.roles) {
|
|
1089
|
+
const hasRole = roles.some((role) => currentUser.roles.includes(role));
|
|
1090
|
+
if (!hasRole) {
|
|
1091
|
+
if (fallback) {
|
|
1092
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_jsx_runtime3.Fragment, { children: fallback });
|
|
1093
|
+
}
|
|
1094
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "min-h-screen flex items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "text-center", children: [
|
|
1095
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h1", { className: "text-2xl font-bold text-gray-900 mb-2", children: "Access Denied" }),
|
|
1096
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "text-gray-600", children: "You don't have the required role to access this page." })
|
|
1097
|
+
] }) });
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
if (permissions.length > 0 && user !== null) {
|
|
1102
|
+
const currentUser = user;
|
|
1103
|
+
if (currentUser.permissions) {
|
|
1104
|
+
const hasPermission = permissions.some((permission) => currentUser.permissions.includes(permission));
|
|
1105
|
+
if (!hasPermission) {
|
|
1106
|
+
if (fallback) {
|
|
1107
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_jsx_runtime3.Fragment, { children: fallback });
|
|
1108
|
+
}
|
|
1109
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "min-h-screen flex items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "text-center", children: [
|
|
1110
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h1", { className: "text-2xl font-bold text-gray-900 mb-2", children: "Access Denied" }),
|
|
1111
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "text-gray-600", children: "You don't have the required permission to access this page." })
|
|
1112
|
+
] }) });
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_jsx_runtime3.Fragment, { children });
|
|
1117
|
+
};
|
|
1118
|
+
|
|
1119
|
+
// src/components/ui/Loading.tsx
|
|
1120
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
1121
|
+
var Loading = ({
|
|
1122
|
+
size = "md",
|
|
1123
|
+
text = "Loading...",
|
|
1124
|
+
className = "",
|
|
1125
|
+
fullScreen = false
|
|
1126
|
+
}) => {
|
|
1127
|
+
const sizeClasses = {
|
|
1128
|
+
sm: "h-4 w-4",
|
|
1129
|
+
md: "h-8 w-8",
|
|
1130
|
+
lg: "h-12 w-12"
|
|
1131
|
+
};
|
|
1132
|
+
const containerClasses = fullScreen ? "fixed inset-0 flex items-center justify-center bg-white/80 backdrop-blur-sm z-50" : "flex items-center justify-center p-4";
|
|
1133
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: `${containerClasses} ${className}`, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "text-center", children: [
|
|
1134
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1135
|
+
"div",
|
|
1136
|
+
{
|
|
1137
|
+
className: `animate-spin rounded-full border-2 border-gray-300 border-t-blue-600 mx-auto ${sizeClasses[size]}`
|
|
1138
|
+
}
|
|
1139
|
+
),
|
|
1140
|
+
text && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "mt-2 text-sm text-gray-600", children: text })
|
|
1141
|
+
] }) });
|
|
1142
|
+
};
|
|
1143
|
+
var LoadingSpinner = ({
|
|
1144
|
+
size = "md",
|
|
1145
|
+
className = ""
|
|
1146
|
+
}) => {
|
|
1147
|
+
const sizeClasses = {
|
|
1148
|
+
sm: "h-4 w-4",
|
|
1149
|
+
md: "h-8 w-8",
|
|
1150
|
+
lg: "h-12 w-12"
|
|
1151
|
+
};
|
|
1152
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1153
|
+
"div",
|
|
1154
|
+
{
|
|
1155
|
+
className: `animate-spin rounded-full border-2 border-gray-300 border-t-blue-600 ${sizeClasses[size]} ${className}`
|
|
1156
|
+
}
|
|
1157
|
+
);
|
|
1158
|
+
};
|
|
1159
|
+
|
|
1160
|
+
// src/components/ui/ErrorBoundary.tsx
|
|
1161
|
+
var import_react3 = require("react");
|
|
1162
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
1163
|
+
var ErrorBoundary = class extends import_react3.Component {
|
|
1164
|
+
constructor(props) {
|
|
1165
|
+
super(props);
|
|
1166
|
+
this.handleRetry = () => {
|
|
1167
|
+
this.setState({ hasError: false, error: void 0, errorInfo: void 0 });
|
|
1168
|
+
};
|
|
1169
|
+
this.state = { hasError: false };
|
|
1170
|
+
}
|
|
1171
|
+
static getDerivedStateFromError(error) {
|
|
1172
|
+
return { hasError: true, error };
|
|
1173
|
+
}
|
|
1174
|
+
componentDidCatch(error, errorInfo) {
|
|
1175
|
+
this.setState({
|
|
1176
|
+
error,
|
|
1177
|
+
errorInfo
|
|
1178
|
+
});
|
|
1179
|
+
this.props.onError?.(error, errorInfo);
|
|
1180
|
+
}
|
|
1181
|
+
render() {
|
|
1182
|
+
if (this.state.hasError) {
|
|
1183
|
+
if (this.props.fallback) {
|
|
1184
|
+
const FallbackComponent = this.props.fallback;
|
|
1185
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(FallbackComponent, { error: this.state.error, retry: this.handleRetry });
|
|
1186
|
+
}
|
|
1187
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "min-h-[400px] flex items-center justify-center p-4", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "text-center max-w-md", children: [
|
|
1188
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "text-red-500 text-6xl mb-4", children: "\u26A0\uFE0F" }),
|
|
1189
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("h2", { className: "text-xl font-semibold text-gray-900 mb-2", children: "Something went wrong" }),
|
|
1190
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "text-gray-600 mb-4", children: "An unexpected error occurred. Please try refreshing the page." }),
|
|
1191
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1192
|
+
"button",
|
|
1193
|
+
{
|
|
1194
|
+
onClick: this.handleRetry,
|
|
1195
|
+
className: "px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition-colors",
|
|
1196
|
+
children: "Try Again"
|
|
1197
|
+
}
|
|
1198
|
+
),
|
|
1199
|
+
false
|
|
1200
|
+
] }) });
|
|
1201
|
+
}
|
|
1202
|
+
return this.props.children;
|
|
1203
|
+
}
|
|
1204
|
+
};
|
|
1205
|
+
var DefaultErrorFallback = ({ retry }) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "min-h-[200px] flex items-center justify-center p-4", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "text-center", children: [
|
|
1206
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "text-red-500 text-4xl mb-2", children: "\u26A0\uFE0F" }),
|
|
1207
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "text-gray-600 mb-2", children: "Something went wrong" }),
|
|
1208
|
+
retry && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1209
|
+
"button",
|
|
1210
|
+
{
|
|
1211
|
+
onClick: retry,
|
|
1212
|
+
className: "px-3 py-1 bg-blue-600 text-white text-sm rounded hover:bg-blue-700 transition-colors",
|
|
1213
|
+
children: "Retry"
|
|
1214
|
+
}
|
|
1215
|
+
)
|
|
1216
|
+
] }) });
|
|
1217
|
+
|
|
1218
|
+
// src/hooks/use-mobile.ts
|
|
1219
|
+
var React3 = __toESM(require("react"));
|
|
1220
|
+
var MOBILE_BREAKPOINT = 768;
|
|
1221
|
+
function useIsMobile() {
|
|
1222
|
+
const [isMobile, setIsMobile] = React3.useState(void 0);
|
|
1223
|
+
React3.useEffect(() => {
|
|
1224
|
+
const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);
|
|
1225
|
+
const onChange = () => {
|
|
1226
|
+
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
|
|
1227
|
+
};
|
|
1228
|
+
mql.addEventListener("change", onChange);
|
|
1229
|
+
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
|
|
1230
|
+
return () => mql.removeEventListener("change", onChange);
|
|
1231
|
+
}, []);
|
|
1232
|
+
return !!isMobile;
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
// src/hooks/use-local-storage.ts
|
|
1236
|
+
var import_react4 = require("react");
|
|
1237
|
+
function useLocalStorage(key, initialValue) {
|
|
1238
|
+
const [storedValue, setStoredValue] = (0, import_react4.useState)(() => {
|
|
1239
|
+
try {
|
|
1240
|
+
const item = window.localStorage.getItem(key);
|
|
1241
|
+
return item ? JSON.parse(item) : initialValue;
|
|
1242
|
+
} catch (error) {
|
|
1243
|
+
console.error(`Error reading localStorage key "${key}":`, error);
|
|
1244
|
+
return initialValue;
|
|
1245
|
+
}
|
|
1246
|
+
});
|
|
1247
|
+
const setValue = (value) => {
|
|
1248
|
+
try {
|
|
1249
|
+
const valueToStore = value instanceof Function ? value(storedValue) : value;
|
|
1250
|
+
setStoredValue(valueToStore);
|
|
1251
|
+
window.localStorage.setItem(key, JSON.stringify(valueToStore));
|
|
1252
|
+
} catch (error) {
|
|
1253
|
+
console.error(`Error setting localStorage key "${key}":`, error);
|
|
1254
|
+
}
|
|
1255
|
+
};
|
|
1256
|
+
const removeValue = () => {
|
|
1257
|
+
try {
|
|
1258
|
+
window.localStorage.removeItem(key);
|
|
1259
|
+
setStoredValue(initialValue);
|
|
1260
|
+
} catch (error) {
|
|
1261
|
+
console.error(`Error removing localStorage key "${key}":`, error);
|
|
1262
|
+
}
|
|
1263
|
+
};
|
|
1264
|
+
return [storedValue, setValue, removeValue];
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
// src/hooks/use-debounce.ts
|
|
1268
|
+
var import_react5 = require("react");
|
|
1269
|
+
function useDebounce(value, delay) {
|
|
1270
|
+
const [debouncedValue, setDebouncedValue] = (0, import_react5.useState)(value);
|
|
1271
|
+
(0, import_react5.useEffect)(() => {
|
|
1272
|
+
const handler = setTimeout(() => {
|
|
1273
|
+
setDebouncedValue(value);
|
|
1274
|
+
}, delay);
|
|
1275
|
+
return () => {
|
|
1276
|
+
clearTimeout(handler);
|
|
1277
|
+
};
|
|
1278
|
+
}, [value, delay]);
|
|
1279
|
+
return debouncedValue;
|
|
1280
|
+
}
|
|
1281
|
+
|
|
1282
|
+
// src/utils/earlyErrorHandler.ts
|
|
1283
|
+
function injectEarlyErrorHandler() {
|
|
1284
|
+
if (typeof window === "undefined" || typeof document === "undefined") return;
|
|
1285
|
+
const code = `
|
|
1286
|
+
(function () {
|
|
1287
|
+
const isIframe = window.self !== window.top;
|
|
1288
|
+
console.log("isIframe", isIframe);
|
|
1289
|
+
|
|
1290
|
+
console.log("[\u65E9\u671F\u9519\u8BEF\u5904\u7406] \u521D\u59CB\u5316");
|
|
1291
|
+
|
|
1292
|
+
window.__SEND_ERROR_TO_PARENT__ = function (error, details) {
|
|
1293
|
+
try {
|
|
1294
|
+
const errorPayload = {
|
|
1295
|
+
message: error instanceof Error ? error.message : String(error),
|
|
1296
|
+
stack: error instanceof Error ? error.stack : undefined,
|
|
1297
|
+
filename: details?.file || details?.filename,
|
|
1298
|
+
lineno: details?.line,
|
|
1299
|
+
colno: details?.column,
|
|
1300
|
+
timestamp: Date.now(),
|
|
1301
|
+
type: details?.type || "compile-error",
|
|
1302
|
+
details: details,
|
|
1303
|
+
};
|
|
1304
|
+
|
|
1305
|
+
window.parent.postMessage({ type: "ERROR_EVENT", payload: errorPayload }, "*");
|
|
1306
|
+
console.log("[\u65E9\u671F\u9519\u8BEF\u5904\u7406] \u9519\u8BEF\u5DF2\u53D1\u9001\u5230\u7236\u7A97\u53E3:", errorPayload);
|
|
1307
|
+
} catch (e) {
|
|
1308
|
+
console.error("[\u65E9\u671F\u9519\u8BEF\u5904\u7406] \u53D1\u9001\u9519\u8BEF\u5230\u7236\u7A97\u53E3\u5931\u8D25:", e);
|
|
1309
|
+
}
|
|
1310
|
+
};
|
|
1311
|
+
|
|
1312
|
+
function setupViteOverlayObserver() {
|
|
1313
|
+
if (!window.MutationObserver) return;
|
|
1314
|
+
|
|
1315
|
+
console.log("[\u65E9\u671F\u9519\u8BEF\u5904\u7406] \u8BBE\u7F6E Vite \u9519\u8BEF overlay \u76D1\u542C\u5668");
|
|
1316
|
+
|
|
1317
|
+
function processViteErrorOverlay(overlay) {
|
|
1318
|
+
try {
|
|
1319
|
+
if (overlay.hasAttribute("data-error-sent")) return;
|
|
1320
|
+
const shadowRoot = overlay.shadowRoot;
|
|
1321
|
+
if (!shadowRoot) return;
|
|
1322
|
+
|
|
1323
|
+
const messageElement = shadowRoot.querySelector("pre.message");
|
|
1324
|
+
const fileElement = shadowRoot.querySelector("pre.file");
|
|
1325
|
+
const frameElements = shadowRoot.querySelectorAll("pre.frame");
|
|
1326
|
+
const stackElement = shadowRoot.querySelector("pre.stack");
|
|
1327
|
+
|
|
1328
|
+
const message = messageElement ? messageElement.textContent : "";
|
|
1329
|
+
const file = fileElement ? fileElement.textContent : "";
|
|
1330
|
+
const frames = Array.from(frameElements).map((el) => el.textContent).join("
|
|
1331
|
+
");
|
|
1332
|
+
const stack = stackElement ? stackElement.textContent : "";
|
|
1333
|
+
|
|
1334
|
+
let line, column;
|
|
1335
|
+
const fileStr = file || "";
|
|
1336
|
+
const lineColMatch = fileStr.match(/(?:line|at|:)(?:.*?)(?::| )(d+)(?::| )(d+)/i);
|
|
1337
|
+
if (lineColMatch) {
|
|
1338
|
+
line = parseInt(lineColMatch[1], 10);
|
|
1339
|
+
column = parseInt(lineColMatch[2], 10);
|
|
1340
|
+
}
|
|
1341
|
+
|
|
1342
|
+
const messageStr = message || "";
|
|
1343
|
+
const isReactError =
|
|
1344
|
+
messageStr.includes("React") ||
|
|
1345
|
+
messageStr.includes("JSX") ||
|
|
1346
|
+
messageStr.includes("Unexpected token") ||
|
|
1347
|
+
messageStr.includes("vite:react-babel");
|
|
1348
|
+
|
|
1349
|
+
const viteError = new Error(messageStr);
|
|
1350
|
+
|
|
1351
|
+
window.__SEND_ERROR_TO_PARENT__(viteError, {
|
|
1352
|
+
file,
|
|
1353
|
+
line,
|
|
1354
|
+
column,
|
|
1355
|
+
frames,
|
|
1356
|
+
stack,
|
|
1357
|
+
isReactError,
|
|
1358
|
+
type: isReactError ? "react-syntax" : "vite-error-overlay",
|
|
1359
|
+
});
|
|
1360
|
+
|
|
1361
|
+
overlay.setAttribute("data-error-sent", "true");
|
|
1362
|
+
} catch (e) {
|
|
1363
|
+
console.error("[\u65E9\u671F\u9519\u8BEF\u5904\u7406] \u5904\u7406 vite-error-overlay \u5931\u8D25:", e);
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
function checkExistingOverlays() {
|
|
1368
|
+
const viteErrorOverlays = document.querySelectorAll("vite-error-overlay");
|
|
1369
|
+
if (viteErrorOverlays.length > 0) {
|
|
1370
|
+
viteErrorOverlays.forEach((overlay) => {
|
|
1371
|
+
if (!overlay.hasAttribute("data-error-sent")) {
|
|
1372
|
+
processViteErrorOverlay(overlay);
|
|
1373
|
+
}
|
|
1374
|
+
});
|
|
1375
|
+
}
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1378
|
+
const observer = new MutationObserver((mutations) => {
|
|
1379
|
+
for (const mutation of mutations) {
|
|
1380
|
+
if (mutation.addedNodes.length > 0) {
|
|
1381
|
+
for (const node of Array.from(mutation.addedNodes)) {
|
|
1382
|
+
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
1383
|
+
const element = node;
|
|
1384
|
+
if (element.tagName === "VITE-ERROR-OVERLAY" && !element.hasAttribute("data-error-sent")) {
|
|
1385
|
+
setTimeout(() => processViteErrorOverlay(element), 100);
|
|
1386
|
+
}
|
|
1387
|
+
const viteErrorOverlays = element.querySelectorAll("vite-error-overlay");
|
|
1388
|
+
for (const viteErrorOverlay of Array.from(viteErrorOverlays)) {
|
|
1389
|
+
if (!viteErrorOverlay.hasAttribute("data-error-sent")) {
|
|
1390
|
+
setTimeout(() => processViteErrorOverlay(viteErrorOverlay), 100);
|
|
1391
|
+
}
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1396
|
+
}
|
|
1397
|
+
});
|
|
1398
|
+
|
|
1399
|
+
observer.observe(document.documentElement, { childList: true, subtree: true, attributes: true, characterData: true });
|
|
1400
|
+
setTimeout(checkExistingOverlays, 500);
|
|
1401
|
+
setInterval(checkExistingOverlays, 2000);
|
|
1402
|
+
}
|
|
1403
|
+
|
|
1404
|
+
window.addEventListener(
|
|
1405
|
+
"error",
|
|
1406
|
+
function (event) {
|
|
1407
|
+
const target = event && (event.target || event.srcElement);
|
|
1408
|
+
const tagName = target && target.tagName;
|
|
1409
|
+
if (tagName) {
|
|
1410
|
+
const url = (target && (target.src || target.href || target.currentSrc)) || (target && target.getAttribute && (target.getAttribute('src') || target.getAttribute('href')));
|
|
1411
|
+
const preview = target && target.outerHTML ? String(target.outerHTML).slice(0, 300) : undefined;
|
|
1412
|
+
const resourceError = new Error('Resource load error: ' + tagName + ' ' + (url || 'unknown'));
|
|
1413
|
+
window.__SEND_ERROR_TO_PARENT__(resourceError, { filename: url, tagName: tagName, element: preview, type: "resource" });
|
|
1414
|
+
return;
|
|
1415
|
+
}
|
|
1416
|
+
|
|
1417
|
+
var stack = (event && event.error && event.error.stack) || "";
|
|
1418
|
+
var message = typeof event.message === "string" ? event.message : "";
|
|
1419
|
+
if ((stack && stack.indexOf('error-handler') !== -1) || !message) return;
|
|
1420
|
+
|
|
1421
|
+
window.__SEND_ERROR_TO_PARENT__(event.error || new Error(event.message), { filename: event.filename, line: event.lineno, column: event.colno, type: "runtime" });
|
|
1422
|
+
},
|
|
1423
|
+
true
|
|
1424
|
+
);
|
|
1425
|
+
|
|
1426
|
+
window.addEventListener("unhandledrejection", function (event) {
|
|
1427
|
+
const reason = event.reason;
|
|
1428
|
+
window.__SEND_ERROR_TO_PARENT__(reason instanceof Error ? reason : new Error(String(reason)), { type: "promise" });
|
|
1429
|
+
});
|
|
1430
|
+
|
|
1431
|
+
setupViteOverlayObserver();
|
|
1432
|
+
|
|
1433
|
+
if (!window.__CONSOLE_ERROR_INTERCEPTED__) {
|
|
1434
|
+
window.__CONSOLE_ERROR_INTERCEPTED__ = true;
|
|
1435
|
+
const originalConsoleError = console.error;
|
|
1436
|
+
console.error = function () {
|
|
1437
|
+
try {
|
|
1438
|
+
var args = Array.prototype.slice.call(arguments);
|
|
1439
|
+
var first = args[0];
|
|
1440
|
+
var firstStr = typeof first === "string" ? first : "";
|
|
1441
|
+
var isReact = firstStr.indexOf("React") !== -1;
|
|
1442
|
+
var isVite = firstStr.indexOf("[vite]") !== -1 || firstStr.indexOf("Failed to reload") !== -1;
|
|
1443
|
+
if (isReact || isVite) {
|
|
1444
|
+
var joined = args.map(function (a) {
|
|
1445
|
+
if (a instanceof Error) return a.message;
|
|
1446
|
+
if (typeof a === "string") return a;
|
|
1447
|
+
if (a && typeof a.message === "string") return a.message;
|
|
1448
|
+
try { return JSON.stringify(a); } catch (e) { return String(a); }
|
|
1449
|
+
}).join(" ");
|
|
1450
|
+
var errorForReport = new Error(joined);
|
|
1451
|
+
window.__SEND_ERROR_TO_PARENT__(errorForReport, { type: "runtime", viteErrorType: "console" });
|
|
1452
|
+
}
|
|
1453
|
+
} catch (e) { void e }
|
|
1454
|
+
return originalConsoleError.apply(console, arguments);
|
|
1455
|
+
};
|
|
1456
|
+
}
|
|
1457
|
+
|
|
1458
|
+
console.log("[\u65E9\u671F\u9519\u8BEF\u5904\u7406] \u521D\u59CB\u5316\u5B8C\u6210");
|
|
1459
|
+
})();
|
|
1460
|
+
`;
|
|
1461
|
+
try {
|
|
1462
|
+
const script = document.createElement("script");
|
|
1463
|
+
script.type = "text/javascript";
|
|
1464
|
+
script.text = code;
|
|
1465
|
+
const parent = document.head || document.documentElement;
|
|
1466
|
+
parent.prepend(script);
|
|
1467
|
+
} catch (e) {
|
|
1468
|
+
void e;
|
|
1469
|
+
}
|
|
1470
|
+
}
|
|
1471
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
1472
|
+
0 && (module.exports = {
|
|
1473
|
+
AuthGuard,
|
|
1474
|
+
DefaultErrorFallback,
|
|
1475
|
+
ErrorBoundary,
|
|
1476
|
+
FloatingButton,
|
|
1477
|
+
Loading,
|
|
1478
|
+
LoadingSpinner,
|
|
1479
|
+
LoginForm,
|
|
1480
|
+
aiRequest,
|
|
1481
|
+
aiWorkflow,
|
|
1482
|
+
canAccessArtifact,
|
|
1483
|
+
createAIWorkflowClient,
|
|
1484
|
+
createAIWorkflowClientAxios,
|
|
1485
|
+
createArtifactsClient,
|
|
1486
|
+
createClient,
|
|
1487
|
+
getCodeStatus,
|
|
1488
|
+
injectEarlyErrorHandler,
|
|
1489
|
+
loginWithEmailCode,
|
|
1490
|
+
request,
|
|
1491
|
+
sendEmailVerificationCode,
|
|
1492
|
+
unifiedAuth,
|
|
1493
|
+
unifiedOAuth,
|
|
1494
|
+
useDebounce,
|
|
1495
|
+
useIsMobile,
|
|
1496
|
+
useLocalStorage,
|
|
1497
|
+
workflowRequest
|
|
1498
|
+
});
|
|
1499
|
+
//# sourceMappingURL=index.js.map
|