GitHub Actions commited on
Commit
4ab7df8
·
1 Parent(s): 74a041f

Deploy from GitHub Actions [DEV] - 2025-10-31 05:57:32

Browse files
app/refresh-moments/improvement-result/improvement-proposal-client.tsx CHANGED
@@ -11,6 +11,7 @@ import { RegulationModal } from '@/components/improvement-result/regulation-moda
11
  import { Bread } from '@/components/parts/bread';
12
  import { ErrorDisplay } from '@/components/parts/error-display';
13
  import { Button } from '@/components/ui/button';
 
14
  import { useEnvironment } from '@/hooks/use-environment';
15
  import { useImprovementResultExpressTestMode } from '@/hooks/use-improvement-result-express-test-mode';
16
  import { useNotificationPermission } from '@/hooks/use-notification-permission';
@@ -505,6 +506,13 @@ function ImprovementProposalContent({ isDummyMode: propIsDummyMode = false, isEx
505
  dummyMode: isDummyMode,
506
  });
507
 
 
 
 
 
 
 
 
508
  // function markChangedWords(original: Record<string, unknown>, regulated: Record<string, unknown>): Record<string, unknown> {
509
  // if (!regulated) return original;
510
 
@@ -693,8 +701,8 @@ function ImprovementProposalContent({ isDummyMode: propIsDummyMode = false, isEx
693
  }
694
  };
695
 
696
- // ローディング状態を統合
697
- const isLoading = isProposalsLoading || isAnalysisLoading || proposalTabsLoading;
698
 
699
  // エラー判定のデバッグ
700
  useEffect(() => {
@@ -739,8 +747,8 @@ function ImprovementProposalContent({ isDummyMode: propIsDummyMode = false, isEx
739
  );
740
  }
741
 
742
- // エラー表示(エラーフラグのみで判定、errorオブジェクトは必須でない
743
- if (proposalTabsIsError && !proposalTabsLoading) {
744
  const errorMessage = proposalTabsError instanceof Error ? proposalTabsError.message : 'データの取得に失敗しました';
745
  return (
746
  <div className="flex flex-col space-y-10">
 
11
  import { Bread } from '@/components/parts/bread';
12
  import { ErrorDisplay } from '@/components/parts/error-display';
13
  import { Button } from '@/components/ui/button';
14
+ import { useDelayedErrorDisplay } from '@/hooks/use-delayed-error-display';
15
  import { useEnvironment } from '@/hooks/use-environment';
16
  import { useImprovementResultExpressTestMode } from '@/hooks/use-improvement-result-express-test-mode';
17
  import { useNotificationPermission } from '@/hooks/use-notification-permission';
 
506
  dummyMode: isDummyMode,
507
  });
508
 
509
+ // 遅延エラー表示Hook(90秒間ローディング表示を継続)
510
+ const { shouldShowError, shouldShowLoading } = useDelayedErrorDisplay({
511
+ error: proposalTabsError,
512
+ isLoading: proposalTabsLoading,
513
+ delayMs: 90000, // 90秒(本番設定)
514
+ });
515
+
516
  // function markChangedWords(original: Record<string, unknown>, regulated: Record<string, unknown>): Record<string, unknown> {
517
  // if (!regulated) return original;
518
 
 
701
  }
702
  };
703
 
704
+ // ローディング状態を統合(遅延エラー表示を考慮)
705
+ const isLoading = isProposalsLoading || isAnalysisLoading || shouldShowLoading;
706
 
707
  // エラー判定のデバッグ
708
  useEffect(() => {
 
747
  );
748
  }
749
 
750
+ // エラー表示(90秒遅延後に表示
751
+ if (shouldShowError) {
752
  const errorMessage = proposalTabsError instanceof Error ? proposalTabsError.message : 'データの取得に失敗しました';
753
  return (
754
  <div className="flex flex-col space-y-10">
components/pages/index/pre-analysis-check.tsx CHANGED
@@ -6,6 +6,7 @@ import Image from 'next/image';
6
 
7
  import { usePreAnalysis } from '@/api-client/gradio-proxy/use-pre-analysis';
8
  import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table';
 
9
  import { useGlobalStore } from '@/store/global';
10
  import { useInputStore } from '@/store/input';
11
  import { useResultStore } from '@/store/result';
@@ -40,7 +41,7 @@ export default function PreAnalysisCheck({ retryButtonRef }: PreAnalysisCheckPro
40
  const [localCompetitionUrls] = useState(competitionUrls);
41
  const [isFieldsChanged, setIsFieldsChanged] = useState(false);
42
  const [errorMessage, setErrorMessage] = useState<string>('');
43
- const [showError, setShowError] = useState(false);
44
  const [imageErrors, setImageErrors] = useState<{ [url: string]: boolean }>({});
45
 
46
  // メールアドレスが変わったらuserIdentifierを更新
@@ -99,6 +100,13 @@ export default function PreAnalysisCheck({ retryButtonRef }: PreAnalysisCheckPro
99
  userIdentifier: getUserIdentifier(),
100
  });
101
 
 
 
 
 
 
 
 
102
  const onSubmit = useCallback(async () => {
103
  console.log('[DEBUG] onSubmit called');
104
  console.log('[DEBUG] dummyMode:', dummyMode);
@@ -156,9 +164,8 @@ export default function PreAnalysisCheck({ retryButtonRef }: PreAnalysisCheckPro
156
  setShowAnalysisError(true);
157
  setGetScoreLoading(false);
158
 
159
- // ローカル状態も設定(後方互換性のため)
160
  setErrorMessage(message);
161
- setShowError(true);
162
  }
163
  }, [dummyMode, checkUrlFvErrors, localUpdatedCompetitionUrls, setGetScoreLoading, preAnalysis, router, setAnalysisError, setShowAnalysisError]);
164
 
@@ -349,14 +356,16 @@ export default function PreAnalysisCheck({ retryButtonRef }: PreAnalysisCheckPro
349
  )}
350
 
351
  {/* エラー表示とリトライボタン */}
352
- {showError && (
353
  <div className="flex min-h-[400px] flex-col items-center justify-center p-8">
354
  <div className="mx-auto max-w-md text-center">
355
  <AlertTriangle className="mx-auto mb-4 h-16 w-16 text-red-500" />
356
  <h2 className="mb-2 text-2xl font-semibold text-gray-900">エラーが発生しました</h2>
357
  <p className="mb-6 text-gray-600">
358
  申し訳ございません。予期しないエラーが発生しました。
359
- {errorMessage && <span className="mt-2 block text-sm text-gray-500">{errorMessage}</span>}
 
 
360
  </p>
361
  <div className="flex justify-center gap-3">
362
  <Button
 
6
 
7
  import { usePreAnalysis } from '@/api-client/gradio-proxy/use-pre-analysis';
8
  import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table';
9
+ import { useDelayedErrorDisplay } from '@/hooks/use-delayed-error-display';
10
  import { useGlobalStore } from '@/store/global';
11
  import { useInputStore } from '@/store/input';
12
  import { useResultStore } from '@/store/result';
 
41
  const [localCompetitionUrls] = useState(competitionUrls);
42
  const [isFieldsChanged, setIsFieldsChanged] = useState(false);
43
  const [errorMessage, setErrorMessage] = useState<string>('');
44
+ const [showError, setShowError] = useState(false); // バリデーションエラー用(即座に表示)
45
  const [imageErrors, setImageErrors] = useState<{ [url: string]: boolean }>({});
46
 
47
  // メールアドレスが変わったらuserIdentifierを更新
 
100
  userIdentifier: getUserIdentifier(),
101
  });
102
 
103
+ // 90秒遅延エラー表示
104
+ const { shouldShowError } = useDelayedErrorDisplay({
105
+ error: preAnalysis.error,
106
+ isLoading: preAnalysis.isLoading,
107
+ delayMs: 90000, // 90秒(本番設定)
108
+ });
109
+
110
  const onSubmit = useCallback(async () => {
111
  console.log('[DEBUG] onSubmit called');
112
  console.log('[DEBUG] dummyMode:', dummyMode);
 
164
  setShowAnalysisError(true);
165
  setGetScoreLoading(false);
166
 
167
+ // ローカル状態も設定(カスタマイズされたエラーメッセージのため)
168
  setErrorMessage(message);
 
169
  }
170
  }, [dummyMode, checkUrlFvErrors, localUpdatedCompetitionUrls, setGetScoreLoading, preAnalysis, router, setAnalysisError, setShowAnalysisError]);
171
 
 
356
  )}
357
 
358
  {/* エラー表示とリトライボタン */}
359
+ {(shouldShowError || showError) && (
360
  <div className="flex min-h-[400px] flex-col items-center justify-center p-8">
361
  <div className="mx-auto max-w-md text-center">
362
  <AlertTriangle className="mx-auto mb-4 h-16 w-16 text-red-500" />
363
  <h2 className="mb-2 text-2xl font-semibold text-gray-900">エラーが発生しました</h2>
364
  <p className="mb-6 text-gray-600">
365
  申し訳ございません。予期しないエラーが発生しました。
366
+ {(preAnalysis.error?.message || errorMessage) && (
367
+ <span className="mt-2 block text-sm text-gray-500">{preAnalysis.error?.message || errorMessage}</span>
368
+ )}
369
  </p>
370
  <div className="flex justify-center gap-3">
371
  <Button
components/pages/updatedui/pre-analysis-check-update.tsx CHANGED
@@ -6,6 +6,7 @@ import Image from 'next/image';
6
 
7
  import { usePreAnalysis } from '@/api-client/gradio-proxy/use-pre-analysis';
8
  import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table';
 
9
  import { useGlobalStore } from '@/store/global';
10
  import { useInputStore } from '@/store/input';
11
  import { useResultStore } from '@/store/result';
@@ -31,7 +32,7 @@ export default function PreAnalysisCheckUpdate() {
31
  competitionUrls.filter((url) => !checkUrlFvErrors.includes(url)),
32
  );
33
  const [errorMessage, setErrorMessage] = useState<string>('');
34
- const [showError, setShowError] = useState(false);
35
  const { tempImages, commonDict } = useResultStore();
36
  const [imageErrors, setImageErrors] = useState<{ [url: string]: boolean }>({});
37
 
@@ -91,6 +92,13 @@ export default function PreAnalysisCheckUpdate() {
91
  userIdentifier: getUserIdentifier(),
92
  });
93
 
 
 
 
 
 
 
 
94
  const onSubmit = useCallback(async () => {
95
  console.log('[DEBUG] onSubmit called');
96
  console.log('[DEBUG] dummyMode:', dummyMode);
@@ -138,9 +146,8 @@ export default function PreAnalysisCheckUpdate() {
138
  setShowAnalysisError(true);
139
  setGetScoreLoading(false);
140
 
141
- // ローカル状態も設定(後方互換性のため)
142
  setErrorMessage(message);
143
- setShowError(true);
144
  }
145
  }, [dummyMode, localUpdatedCompetitionUrls, checkUrlFvErrors, setGetScoreLoading, preAnalysis, router, setAnalysisError, setShowAnalysisError]);
146
 
@@ -315,14 +322,16 @@ export default function PreAnalysisCheckUpdate() {
315
  )}
316
 
317
  {/* エラー表示とリトライボタン */}
318
- {showError && (
319
  <div className="flex min-h-[400px] flex-col items-center justify-center p-8">
320
  <div className="mx-auto max-w-md text-center">
321
  <AlertTriangle className="mx-auto mb-4 h-16 w-16 text-red-500" />
322
  <h2 className="mb-2 text-2xl font-semibold text-gray-900">エラーが発生しました</h2>
323
  <p className="mb-6 text-gray-600">
324
  申し訳ございません。予期しないエラーが発生しました。
325
- {errorMessage && <span className="mt-2 block text-sm text-gray-500">{errorMessage}</span>}
 
 
326
  </p>
327
  <div className="flex justify-center gap-3">
328
  <Button
 
6
 
7
  import { usePreAnalysis } from '@/api-client/gradio-proxy/use-pre-analysis';
8
  import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table';
9
+ import { useDelayedErrorDisplay } from '@/hooks/use-delayed-error-display';
10
  import { useGlobalStore } from '@/store/global';
11
  import { useInputStore } from '@/store/input';
12
  import { useResultStore } from '@/store/result';
 
32
  competitionUrls.filter((url) => !checkUrlFvErrors.includes(url)),
33
  );
34
  const [errorMessage, setErrorMessage] = useState<string>('');
35
+ const [showError, setShowError] = useState(false); // バリデーションエラー用(即座に表示)
36
  const { tempImages, commonDict } = useResultStore();
37
  const [imageErrors, setImageErrors] = useState<{ [url: string]: boolean }>({});
38
 
 
92
  userIdentifier: getUserIdentifier(),
93
  });
94
 
95
+ // 90秒遅延エラー表示
96
+ const { shouldShowError } = useDelayedErrorDisplay({
97
+ error: preAnalysis.error,
98
+ isLoading: preAnalysis.isLoading,
99
+ delayMs: 90000, // 90秒(本番設定)
100
+ });
101
+
102
  const onSubmit = useCallback(async () => {
103
  console.log('[DEBUG] onSubmit called');
104
  console.log('[DEBUG] dummyMode:', dummyMode);
 
146
  setShowAnalysisError(true);
147
  setGetScoreLoading(false);
148
 
149
+ // ローカル状態も設定(カスタマイズされたエラーメッセージのため)
150
  setErrorMessage(message);
 
151
  }
152
  }, [dummyMode, localUpdatedCompetitionUrls, checkUrlFvErrors, setGetScoreLoading, preAnalysis, router, setAnalysisError, setShowAnalysisError]);
153
 
 
322
  )}
323
 
324
  {/* エラー表示とリトライボタン */}
325
+ {(shouldShowError || showError) && (
326
  <div className="flex min-h-[400px] flex-col items-center justify-center p-8">
327
  <div className="mx-auto max-w-md text-center">
328
  <AlertTriangle className="mx-auto mb-4 h-16 w-16 text-red-500" />
329
  <h2 className="mb-2 text-2xl font-semibold text-gray-900">エラーが発生しました</h2>
330
  <p className="mb-6 text-gray-600">
331
  申し訳ございません。予期しないエラーが発生しました。
332
+ {(preAnalysis.error?.message || errorMessage) && (
333
+ <span className="mt-2 block text-sm text-gray-500">{preAnalysis.error?.message || errorMessage}</span>
334
+ )}
335
  </p>
336
  <div className="flex justify-center gap-3">
337
  <Button
hooks/use-delayed-error-display.ts ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { useEffect, useState } from 'react';
2
+
3
+ export interface UseDelayedErrorDisplayOptions {
4
+ error: Error | null;
5
+ isLoading: boolean;
6
+ delayMs?: number; // デフォルト90000ms (90秒)
7
+ }
8
+
9
+ export interface UseDelayedErrorDisplayResult {
10
+ shouldShowError: boolean;
11
+ shouldShowLoading: boolean;
12
+ delayedError: Error | null;
13
+ }
14
+
15
+ /**
16
+ * エラー発生後も指定時間(デフォルト90秒)はローディング表示を継続し、
17
+ * 時間経過後にエラーを表示するHook
18
+ *
19
+ * @param options エラー、ローディング状態、遅延時間(ミリ秒)
20
+ * @returns エラー表示すべきか、ローディング表示すべきか、遅延後のエラー
21
+ */
22
+ export function useDelayedErrorDisplay({
23
+ error,
24
+ isLoading,
25
+ delayMs = 90000, // 90秒(本番設定)
26
+ }: UseDelayedErrorDisplayOptions): UseDelayedErrorDisplayResult {
27
+ const [errorTimestamp, setErrorTimestamp] = useState<number | null>(null);
28
+ const [shouldShowError, setShouldShowError] = useState(false);
29
+
30
+ // エラーが発生した時刻を記録
31
+ useEffect(() => {
32
+ if (error && !errorTimestamp) {
33
+ setErrorTimestamp(Date.now());
34
+ setShouldShowError(false);
35
+ } else if (!error) {
36
+ // エラーがクリアされた場合はリセット
37
+ setErrorTimestamp(null);
38
+ setShouldShowError(false);
39
+ }
40
+ }, [error, errorTimestamp]);
41
+
42
+ // 指定時間経過後にエラーを表示
43
+ useEffect(() => {
44
+ if (!errorTimestamp || !error || shouldShowError) {
45
+ // すでにエラー表示状態の場合は何もしない
46
+ return;
47
+ }
48
+
49
+ const checkElapsed = () => {
50
+ const elapsed = Date.now() - errorTimestamp;
51
+ if (elapsed >= delayMs) {
52
+ setShouldShowError(true);
53
+ }
54
+ };
55
+
56
+ // 初回チェック
57
+ checkElapsed();
58
+
59
+ // 1秒ごとにチェック
60
+ const interval = setInterval(checkElapsed, 1000);
61
+
62
+ return () => clearInterval(interval);
63
+ }, [errorTimestamp, error, delayMs, shouldShowError]);
64
+
65
+ // ローディング表示判定:
66
+ // - 通常ローディング中、または
67
+ // - エラーが発生しているが、まだ遅延時間内
68
+ const shouldShowLoading = isLoading || (error !== null && errorTimestamp !== null && !shouldShowError);
69
+
70
+ return {
71
+ shouldShowError,
72
+ shouldShowLoading,
73
+ delayedError: shouldShowError ? error : null,
74
+ };
75
+ }
server/routes/gradio-proxy.ts CHANGED
@@ -10,6 +10,7 @@ import { scoreStep3RequestSchema } from '@/schema/gradio-proxy/score-step3';
10
  import { summaryRequestSchema } from '@/schema/gradio-proxy/summary';
11
  import { themeByMomentRequestSchema } from '@/schema/gradio-proxy/theme-by-moment';
12
  import { visScoreRequestSchema } from '@/schema/gradio-proxy/vis-score';
 
13
  import { getDummyData } from '@/server/utils/dummy-data';
14
  import {
15
  getCheckUrl,
@@ -28,6 +29,33 @@ import {
28
  import { zValidator } from '@hono/zod-validator';
29
  import { Hono } from 'hono';
30
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
  export const gradioProxyRoute = new Hono()
32
  // check_url API (POST /check-url)
33
  .post('/check-url', zValidator('json', checkUrlRequestSchema), async (c) => {
@@ -71,24 +99,11 @@ export const gradioProxyRoute = new Hono()
71
  return c.json(result);
72
  } catch (error) {
73
  console.error('[CHECK_URL API] Error:', error);
74
-
75
- let errorMessage = '不明なエラーが発生しました。';
76
- let statusCode = 500;
77
-
78
- if (error instanceof Error) {
79
- errorMessage = error.message;
80
-
81
- // タイムアウトエラー
82
- if (error.message.includes('タイムアウト') || error.message.includes('timeout')) {
83
- errorMessage = 'タイムアウトが発生しました。もう一度やり直してください。';
84
- statusCode = 408;
85
- }
86
- }
87
-
88
  return c.json(
89
  {
90
  status: 'error',
91
- message: errorMessage,
92
  },
93
  statusCode as 400 | 408 | 500,
94
  );
@@ -144,23 +159,12 @@ export const gradioProxyRoute = new Hono()
144
 
145
  return c.json(result);
146
  } catch (error) {
147
- let errorMessage = '不明なエラーが発生しました。';
148
- let statusCode = 500;
149
-
150
- if (error instanceof Error) {
151
- errorMessage = error.message;
152
-
153
- // タイムアウトエラー
154
- if (error.message.includes('タイムアウト') || error.message.includes('timeout')) {
155
- errorMessage = 'タイムアウトが発生しました。もう一度やり直してください。';
156
- statusCode = 408;
157
- }
158
- }
159
-
160
  return c.json(
161
  {
162
  status: 'error',
163
- message: errorMessage,
164
  },
165
  statusCode as 400 | 408 | 500,
166
  );
@@ -202,22 +206,12 @@ export const gradioProxyRoute = new Hono()
202
 
203
  return c.json(result);
204
  } catch (error) {
205
- let errorMessage = '不明なエラーが発生しました。';
206
- let statusCode = 500;
207
-
208
- if (error instanceof Error) {
209
- errorMessage = error.message;
210
-
211
- if (error.message.includes('タイムアウト') || error.message.includes('timeout')) {
212
- errorMessage = 'タイムアウトが発生しました。もう一度やり直してください。';
213
- statusCode = 408;
214
- }
215
- }
216
-
217
  return c.json(
218
  {
219
  status: 'error',
220
- message: errorMessage,
221
  },
222
  statusCode as 400 | 408 | 500,
223
  );
@@ -261,22 +255,12 @@ export const gradioProxyRoute = new Hono()
261
 
262
  return c.json(result);
263
  } catch (error) {
264
- let errorMessage = '不明なエラーが発生しました。';
265
- let statusCode = 500;
266
-
267
- if (error instanceof Error) {
268
- errorMessage = error.message;
269
-
270
- if (error.message.includes('タイムアウト') || error.message.includes('timeout')) {
271
- errorMessage = 'タイムアウトが発生しました。もう一度やり直してください。';
272
- statusCode = 408;
273
- }
274
- }
275
-
276
  return c.json(
277
  {
278
  status: 'error',
279
- message: errorMessage,
280
  },
281
  statusCode as 400 | 408 | 500,
282
  );
@@ -317,22 +301,12 @@ export const gradioProxyRoute = new Hono()
317
 
318
  return c.json(result);
319
  } catch (error) {
320
- let errorMessage = '不明なエラーが発生しました。';
321
- let statusCode = 500;
322
-
323
- if (error instanceof Error) {
324
- errorMessage = error.message;
325
-
326
- if (error.message.includes('タイムアウト') || error.message.includes('timeout')) {
327
- errorMessage = 'タイムアウトが発生しました。もう一度やり直してください。';
328
- statusCode = 408;
329
- }
330
- }
331
-
332
  return c.json(
333
  {
334
  status: 'error',
335
- message: errorMessage,
336
  },
337
  statusCode as 400 | 408 | 500,
338
  );
@@ -376,22 +350,12 @@ export const gradioProxyRoute = new Hono()
376
 
377
  return c.json(result);
378
  } catch (error) {
379
- let errorMessage = '不明なエラーが発生しました。';
380
- let statusCode = 500;
381
-
382
- if (error instanceof Error) {
383
- errorMessage = error.message;
384
-
385
- if (error.message.includes('タイムアウト') || error.message.includes('timeout')) {
386
- errorMessage = 'タイムアウトが発生しました。もう一度やり直してください。';
387
- statusCode = 408;
388
- }
389
- }
390
-
391
  return c.json(
392
  {
393
  status: 'error',
394
- message: errorMessage,
395
  },
396
  statusCode as 400 | 408 | 500,
397
  );
@@ -747,22 +711,12 @@ export const gradioProxyRoute = new Hono()
747
 
748
  return c.json(result);
749
  } catch (error) {
750
- let errorMessage = '不明なエラーが発生しました。';
751
- let statusCode = 500;
752
-
753
- if (error instanceof Error) {
754
- errorMessage = error.message;
755
-
756
- if (error.message.includes('タイムアウト') || error.message.includes('timeout')) {
757
- errorMessage = 'タイムアウトが発生しました。もう一度やり直してください。';
758
- statusCode = 408;
759
- }
760
- }
761
-
762
  return c.json(
763
  {
764
  status: 'error',
765
- message: errorMessage,
766
  },
767
  statusCode as 400 | 408 | 500,
768
  );
 
10
  import { summaryRequestSchema } from '@/schema/gradio-proxy/summary';
11
  import { themeByMomentRequestSchema } from '@/schema/gradio-proxy/theme-by-moment';
12
  import { visScoreRequestSchema } from '@/schema/gradio-proxy/vis-score';
13
+ import { AppError } from '@/lib/errors';
14
  import { getDummyData } from '@/server/utils/dummy-data';
15
  import {
16
  getCheckUrl,
 
29
  import { zValidator } from '@hono/zod-validator';
30
  import { Hono } from 'hono';
31
 
32
+ /**
33
+ * Gradio APIエラーを統一的に処理するヘルパー関数
34
+ */
35
+ function handleGradioError(error: unknown): { statusCode: number; message: string } {
36
+ let errorMessage = '不明なエラーが発生しました。';
37
+ let statusCode = 500;
38
+
39
+ if (error instanceof AppError) {
40
+ errorMessage = error.message;
41
+ statusCode = error.statusCode || 500;
42
+ } else if (error instanceof Error) {
43
+ errorMessage = error.message;
44
+
45
+ // タイムアウトエラー
46
+ if (error.message.includes('タイムアウト') || error.message.includes('timeout')) {
47
+ errorMessage = 'タイムアウトが発生しました。もう一度やり直してください。';
48
+ statusCode = 408;
49
+ }
50
+
51
+ if ('statusCode' in error && typeof error.statusCode === 'number') {
52
+ statusCode = error.statusCode;
53
+ }
54
+ }
55
+
56
+ return { statusCode, message: errorMessage };
57
+ }
58
+
59
  export const gradioProxyRoute = new Hono()
60
  // check_url API (POST /check-url)
61
  .post('/check-url', zValidator('json', checkUrlRequestSchema), async (c) => {
 
99
  return c.json(result);
100
  } catch (error) {
101
  console.error('[CHECK_URL API] Error:', error);
102
+ const { statusCode, message } = handleGradioError(error);
 
 
 
 
 
 
 
 
 
 
 
 
 
103
  return c.json(
104
  {
105
  status: 'error',
106
+ message,
107
  },
108
  statusCode as 400 | 408 | 500,
109
  );
 
159
 
160
  return c.json(result);
161
  } catch (error) {
162
+ console.error('[SCORE_STEP3 API] Error:', error);
163
+ const { statusCode, message } = handleGradioError(error);
 
 
 
 
 
 
 
 
 
 
 
164
  return c.json(
165
  {
166
  status: 'error',
167
+ message,
168
  },
169
  statusCode as 400 | 408 | 500,
170
  );
 
206
 
207
  return c.json(result);
208
  } catch (error) {
209
+ console.error('[VIS_SCORE API] Error:', error);
210
+ const { statusCode, message } = handleGradioError(error);
 
 
 
 
 
 
 
 
 
 
211
  return c.json(
212
  {
213
  status: 'error',
214
+ message,
215
  },
216
  statusCode as 400 | 408 | 500,
217
  );
 
255
 
256
  return c.json(result);
257
  } catch (error) {
258
+ console.error('[SUMMARY API] Error:', error);
259
+ const { statusCode, message } = handleGradioError(error);
 
 
 
 
 
 
 
 
 
 
260
  return c.json(
261
  {
262
  status: 'error',
263
+ message,
264
  },
265
  statusCode as 400 | 408 | 500,
266
  );
 
301
 
302
  return c.json(result);
303
  } catch (error) {
304
+ console.error('[POX API] Error:', error);
305
+ const { statusCode, message } = handleGradioError(error);
 
 
 
 
 
 
 
 
 
 
306
  return c.json(
307
  {
308
  status: 'error',
309
+ message,
310
  },
311
  statusCode as 400 | 408 | 500,
312
  );
 
350
 
351
  return c.json(result);
352
  } catch (error) {
353
+ console.error('[EXCEL API] Error:', error);
354
+ const { statusCode, message } = handleGradioError(error);
 
 
 
 
 
 
 
 
 
 
355
  return c.json(
356
  {
357
  status: 'error',
358
+ message,
359
  },
360
  statusCode as 400 | 408 | 500,
361
  );
 
711
 
712
  return c.json(result);
713
  } catch (error) {
714
+ console.error('[REFRESH_MOMENT API] Error:', error);
715
+ const { statusCode, message } = handleGradioError(error);
 
 
 
 
 
 
 
 
 
 
716
  return c.json(
717
  {
718
  status: 'error',
719
+ message,
720
  },
721
  statusCode as 400 | 408 | 500,
722
  );