s6663296/pet-lost-found
0
1<!DOCTYPE html>
2<html lang="zh-TW">
3
4<head>
5 <meta charset="UTF-8">
6 <meta name="viewport" content="width=device-width, initial-scale=1.0">
7 <title>Supabase 連接測試</title>
8 <style>
9 body {
10 font-family: Arial, sans-serif;
11 max-width: 800px;
12 margin: 50px auto;
13 padding: 20px;
14 background-color: #f5f5f5;
15 }
16
17 .card {
18 background: white;
19 border-radius: 10px;
20 padding: 30px;
21 box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
22 margin-bottom: 20px;
23 }
24
25 h1 {
26 color: #2d756f;
27 text-align: center;
28 }
29
30 .status {
31 padding: 15px;
32 border-radius: 8px;
33 margin: 10px 0;
34 font-weight: bold;
35 }
36
37 .success {
38 background-color: #d4edda;
39 color: #155724;
40 border: 1px solid #c3e6cb;
41 }
42
43 .error {
44 background-color: #f8d7da;
45 color: #721c24;
46 border: 1px solid #f5c6cb;
47 }
48
49 .warning {
50 background-color: #fff3cd;
51 color: #856404;
52 border: 1px solid #ffeeba;
53 }
54
55 .info {
56 background-color: #d1ecf1;
57 color: #0c5460;
58 border: 1px solid #bee5eb;
59 }
60
61 button {
62 background-color: #2d756f;
63 color: white;
64 border: none;
65 padding: 12px 24px;
66 border-radius: 5px;
67 cursor: pointer;
68 font-size: 16px;
69 margin: 5px;
70 }
71
72 button:hover {
73 background-color: #25655c;
74 }
75
76 button:disabled {
77 background-color: #ccc;
78 cursor: not-allowed;
79 }
80
81 pre {
82 background: #f8f9fa;
83 padding: 15px;
84 border-radius: 5px;
85 overflow-x: auto;
86 font-size: 14px;
87 }
88
89 .test-item {
90 display: flex;
91 align-items: center;
92 padding: 10px 0;
93 border-bottom: 1px solid #eee;
94 }
95
96 .test-item:last-child {
97 border-bottom: none;
98 }
99
100 .test-name {
101 flex: 1;
102 }
103
104 .test-result {
105 padding: 5px 15px;
106 border-radius: 20px;
107 font-size: 14px;
108 }
109
110 .spinner {
111 display: inline-block;
112 width: 20px;
113 height: 20px;
114 border: 3px solid #f3f3f3;
115 border-top: 3px solid #2d756f;
116 border-radius: 50%;
117 animation: spin 1s linear infinite;
118 }
119
120 @keyframes spin {
121 0% {
122 transform: rotate(0deg);
123 }
124
125 100% {
126 transform: rotate(360deg);
127 }
128 }
129 </style>
130</head>
131
132<body>
133 <h1>🔌 Supabase 連接測試</h1>
134
135 <div class="card">
136 <h2>Step 1: 檢查配置</h2>
137 <div id="config-check">
138 <div class="status info">點擊下方按鈕開始測試</div>
139 </div>
140 <button onclick="runAllTests()">🚀 開始測試連接</button>
141 </div>
142
143 <div class="card">
144 <h2>Step 2: 測試結果</h2>
145 <div id="test-results">
146 <div class="test-item">
147 <span class="test-name">1. Supabase SDK 載入</span>
148 <span id="test-sdk" class="test-result">待測試</span>
149 </div>
150 <div class="test-item">
151 <span class="test-name">2. 配置檔案載入</span>
152 <span id="test-config" class="test-result">待測試</span>
153 </div>
154 <div class="test-item">
155 <span class="test-name">3. 資料庫連接</span>
156 <span id="test-connection" class="test-result">待測試</span>
157 </div>
158 <div class="test-item">
159 <span class="test-name">4. 資料表存在</span>
160 <span id="test-tables" class="test-result">待測試</span>
161 </div>
162 <div class="test-item">
163 <span class="test-name">5. Storage 連接</span>
164 <span id="test-storage" class="test-result">待測試</span>
165 </div>
166 </div>
167 </div>
168
169 <div class="card">
170 <h2>Step 3: 詳細資訊</h2>
171 <pre id="details">測試後將顯示詳細資訊...</pre>
172 </div>
173
174 <div class="card">
175 <h2>快速修復</h2>
176 <p>如果連接失敗,請檢查:</p>
177 <ol>
178 <li>打開 <code>supabase-config.js</code></li>
179 <li>確認 <code>SUPABASE_URL</code> 是否正確(格式:https://xxxxx.supabase.co)</li>
180 <li>確認 <code>SUPABASE_ANON_KEY</code> 是否正確</li>
181 <li>確認已在 Supabase Dashboard 執行了 SQL 建表</li>
182 </ol>
183 </div>
184
185 <!-- Supabase SDK -->
186 <script src="https://cdn.jsdelivr.net/npm/@supabase/supabase-js@2"></script>
187 <script src="supabase-config.js"></script>
188
189 <script>
190 const details = [];
191
192 function setTestResult(elementId, success, message) {
193 const el = document.getElementById(elementId);
194 if (success === 'loading') {
195 el.innerHTML = '<span class="spinner"></span>';
196 el.style.background = '#f8f9fa';
197 } else if (success) {
198 el.textContent = '✅ 通過';
199 el.style.background = '#d4edda';
200 el.style.color = '#155724';
201 } else {
202 el.textContent = '❌ 失敗';
203 el.style.background = '#f8d7da';
204 el.style.color = '#721c24';
205 }
206 if (message) {
207 details.push(message);
208 document.getElementById('details').textContent = details.join('\n\n');
209 }
210 }
211
212 async function runAllTests() {
213 details.length = 0;
214 document.getElementById('config-check').innerHTML = '<div class="status info">測試中...</div>';
215
216 // Test 1: SDK 載入
217 setTestResult('test-sdk', 'loading');
218 setTestResult('test-config', 'loading');
219 setTestResult('test-connection', 'loading');
220 setTestResult('test-tables', 'loading');
221 setTestResult('test-storage', 'loading');
222
223 await new Promise(r => setTimeout(r, 500));
224
225 // Test 1: Check SDK
226 if (typeof window.supabase !== 'undefined') {
227 setTestResult('test-sdk', true, '✅ Test 1: Supabase SDK 已成功載入');
228 } else {
229 setTestResult('test-sdk', false, '❌ Test 1: Supabase SDK 未載入\n → 檢查網路連接');
230 document.getElementById('config-check').innerHTML = '<div class="status error">SDK 載入失敗</div>';
231 return;
232 }
233
234 // Test 2: Check Config
235 await new Promise(r => setTimeout(r, 300));
236 try {
237 // 直接檢查 supabase 客戶端是否已初始化
238 if (typeof supabase !== 'undefined' && supabase.auth) {
239 setTestResult('test-config', true,
240 '✅ Test 2: 配置檔案已載入\n' +
241 ' Supabase 客戶端已初始化');
242 } else {
243 setTestResult('test-config', false,
244 '❌ Test 2: Supabase 客戶端未初始化\n' +
245 ' → 請檢查 supabase-config.js 是否正確');
246 return;
247 }
248 } catch (e) {
249 setTestResult('test-config', false, '❌ Test 2: supabase-config.js 載入錯誤: ' + e.message);
250 return;
251 }
252
253 // Test 3: Database Connection
254 await new Promise(r => setTimeout(r, 300));
255 try {
256 const { data, error } = await supabase.from('users').select('count', { count: 'exact', head: true });
257 if (error) throw error;
258 setTestResult('test-connection', true, '✅ Test 3: 資料庫連接成功');
259 } catch (err) {
260 if (err.message.includes('relation') || err.message.includes('does not exist')) {
261 setTestResult('test-connection', true, '✅ Test 3: 資料庫連接成功(但資料表未建立)');
262 setTestResult('test-tables', false,
263 '❌ Test 4: 資料表不存在\n' +
264 ' → 請到 Supabase Dashboard → SQL Editor\n' +
265 ' → 執行 supabase-setup.sql 的內容');
266 setTestResult('test-storage', 'loading');
267 } else {
268 setTestResult('test-connection', false,
269 '❌ Test 3: 資料庫連接失敗\n' +
270 ' 錯誤: ' + err.message);
271 setTestResult('test-tables', false);
272 setTestResult('test-storage', false);
273 document.getElementById('config-check').innerHTML = '<div class="status error">連接失敗</div>';
274 return;
275 }
276 }
277
278 // Test 4: Tables Exist
279 await new Promise(r => setTimeout(r, 300));
280 try {
281 const tables = ['users', 'cases', 'case_photos', 'reports', 'admins'];
282 let allExist = true;
283 let existingTables = [];
284 let missingTables = [];
285
286 for (const table of tables) {
287 const { error } = await supabase.from(table).select('count', { count: 'exact', head: true });
288 if (error && (error.message.includes('relation') || error.message.includes('does not exist'))) {
289 allExist = false;
290 missingTables.push(table);
291 } else {
292 existingTables.push(table);
293 }
294 }
295
296 if (allExist) {
297 setTestResult('test-tables', true, '✅ Test 4: 所有資料表已存在\n Tables: ' + tables.join(', '));
298 } else {
299 setTestResult('test-tables', false,
300 '⚠️ Test 4: 部分資料表缺失\n' +
301 ' 已存在: ' + (existingTables.length ? existingTables.join(', ') : '無') + '\n' +
302 ' 缺少: ' + missingTables.join(', ') + '\n' +
303 ' → 請執行 supabase-setup.sql');
304 }
305 } catch (err) {
306 setTestResult('test-tables', false, '❌ Test 4: 無法檢查資料表');
307 }
308
309 // Test 5: Storage
310 await new Promise(r => setTimeout(r, 300));
311 try {
312 const { data, error } = await supabase.storage.listBuckets();
313 if (error) throw error;
314
315 const petPhotosBucket = data.find(b => b.name === 'pet-photos');
316 if (petPhotosBucket) {
317 setTestResult('test-storage', true, '✅ Test 5: Storage bucket "pet-photos" 已存在');
318 } else {
319 setTestResult('test-storage', false,
320 '⚠️ Test 5: Storage bucket "pet-photos" 不存在\n' +
321 ' → 現有 buckets: ' + (data.map(b => b.name).join(', ') || '無') + '\n' +
322 ' → 請到 Storage 建立名為 pet-photos 的 bucket');
323 }
324 } catch (err) {
325 setTestResult('test-storage', false, '❌ Test 5: Storage 連接失敗\n ' + err.message);
326 }
327
328 // Final Status
329 const allPassed = !document.getElementById('details').textContent.includes('❌');
330 if (allPassed) {
331 document.getElementById('config-check').innerHTML = '<div class="status success">🎉 所有測試通過!Supabase 已成功連接!</div>';
332 } else {
333 document.getElementById('config-check').innerHTML = '<div class="status warning">⚠️ 部分測試未通過,請查看詳細資訊</div>';
334 }
335 }
336 </script>
337</body>
338
339</html>