seawolf2357 commited on
Commit
7a25048
·
verified ·
1 Parent(s): bcd71ae

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +681 -117
index.html CHANGED
@@ -1,132 +1,696 @@
1
- <!-- Simplified API Tracking Script -->
2
- <script>
3
- (function() {
4
- // 추적 서버 설정
5
- const TRACKING_SERVER = 'https://seawolf2357-evcook.hf.space';
6
- const SITE_ID = '38e0a883';
7
-
8
- // 디바이스 ID 생성
9
- function getDeviceId() {
10
- let deviceId = localStorage.getItem('_tracker_device_id');
11
- if (!deviceId) {
12
- deviceId = 'DEV_' + Date.now() + '_' + Math.random().toString(36).substring(2, 9);
13
- localStorage.setItem('_tracker_device_id', deviceId);
14
- }
15
- return deviceId;
16
- }
17
-
18
- // 추적 데이터 전송 (단순화)
19
- async function sendTracking(eventType = 'pageview', eventData = {}) {
20
- const data = {
21
- siteId: SITE_ID,
22
- deviceId: getDeviceId(),
23
- eventType: eventType,
24
- eventData: eventData,
25
- pageUrl: window.location.href,
26
- pageTitle: document.title,
27
- referrer: document.referrer,
28
- userAgent: navigator.userAgent,
29
- screenResolution: screen.width + 'x' + screen.height,
30
- platform: navigator.platform,
31
- language: navigator.language,
32
- timestamp: new Date().toISOString()
33
- };
34
 
35
- console.log('[Tracker] Sending:', eventType);
 
 
 
 
 
36
 
37
- // 방법 1: Direct API with data_json parameter
38
- try {
39
- const response = await fetch(TRACKING_SERVER + '/api/predict/', {
40
- method: 'POST',
41
- headers: {
42
- 'Content-Type': 'application/json',
43
- },
44
- body: JSON.stringify({
45
- data: [JSON.stringify(data)], // data_json expects a string
46
- fn_index: 0
47
- })
48
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
 
50
- if (response.ok) {
51
- console.log('[Tracker] Success (Method 1)');
52
- return;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
  }
54
- } catch (e) {
55
- console.log('[Tracker] Method 1 error:', e);
56
  }
57
 
58
- // 방법 2: Using form data (alternative approach)
59
- try {
60
- const formData = new FormData();
61
- formData.append('data_json', JSON.stringify(data));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
 
63
- const response = await fetch(TRACKING_SERVER + '/api/process_tracking', {
64
- method: 'POST',
65
- body: formData
66
- });
 
67
 
68
- if (response.ok) {
69
- console.log('[Tracker] ✅ Success (Method 2)');
70
- return;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71
  }
72
- } catch (e) {
73
- console.log('[Tracker] Method 2 error:', e);
74
  }
75
 
76
- // 방법 3: Legacy endpoint
77
- try {
78
- const response = await fetch(TRACKING_SERVER + '/run/predict', {
79
- method: 'POST',
80
- headers: {
81
- 'Content-Type': 'application/json',
82
- },
83
- body: JSON.stringify({
84
- data: [JSON.stringify(data)],
85
- fn_index: 0
86
- })
87
- });
88
 
89
- if (response.ok) {
90
- console.log('[Tracker] ✅ Success (Method 3)');
91
- return;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
92
  }
93
- } catch (e) {
94
- console.log('[Tracker] Method 3 error:', e);
95
- }
96
-
97
- console.error('[Tracker] All methods failed');
98
- }
99
-
100
- // 전역 추적 객체
101
- window._tracker = {
102
- trackEvent: function(name, data) {
103
- sendTracking('custom', { name: name, data: data });
104
- },
105
-
106
- // 간단한 테스트 함수
107
- test: function() {
108
- console.log('[Tracker] Sending test event...');
109
- sendTracking('test', {
110
- message: 'This is a test event',
111
- timestamp: new Date().toISOString()
112
  });
 
 
 
113
  }
114
- };
115
-
116
- // 페이지 로드 추적
117
- sendTracking('pageview');
118
-
119
- // 스크롤 추적
120
- let scrolled = false;
121
- window.addEventListener('scroll', function() {
122
- if (!scrolled && window.scrollY > 100) {
123
- scrolled = true;
124
- sendTracking('scroll', { depth: window.scrollY });
125
- }
126
- });
127
-
128
- console.log('🔍 EV Cook Tracker Ready');
129
- console.log('Device ID:', getDeviceId());
130
- console.log('Test with: window._tracker.test()');
131
- })();
132
- </script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="ko">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>EV Cook Site - 방문자 추적 활성화</title>
7
+ <style>
8
+ * {
9
+ margin: 0;
10
+ padding: 0;
11
+ box-sizing: border-box;
12
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
 
14
+ body {
15
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
16
+ background: linear-gradient(135deg, #1e3c72 0%, #2a5298 100%);
17
+ min-height: 100vh;
18
+ color: #333;
19
+ }
20
 
21
+ .header {
22
+ background: rgba(255, 255, 255, 0.95);
23
+ padding: 20px 0;
24
+ box-shadow: 0 2px 10px rgba(0,0,0,0.1);
25
+ }
26
+
27
+ .header-content {
28
+ max-width: 1200px;
29
+ margin: 0 auto;
30
+ padding: 0 20px;
31
+ display: flex;
32
+ justify-content: space-between;
33
+ align-items: center;
34
+ }
35
+
36
+ .logo {
37
+ font-size: 28px;
38
+ font-weight: bold;
39
+ color: #1e3c72;
40
+ }
41
+
42
+ .nav {
43
+ display: flex;
44
+ gap: 30px;
45
+ }
46
+
47
+ .nav a {
48
+ color: #333;
49
+ text-decoration: none;
50
+ font-weight: 500;
51
+ transition: color 0.3s;
52
+ }
53
+
54
+ .nav a:hover {
55
+ color: #1e3c72;
56
+ }
57
+
58
+ .main-container {
59
+ max-width: 1200px;
60
+ margin: 50px auto;
61
+ padding: 0 20px;
62
+ }
63
+
64
+ .hero-section {
65
+ background: white;
66
+ border-radius: 20px;
67
+ padding: 60px;
68
+ text-align: center;
69
+ box-shadow: 0 20px 60px rgba(0,0,0,0.1);
70
+ margin-bottom: 40px;
71
+ }
72
+
73
+ .hero-section h1 {
74
+ font-size: 48px;
75
+ color: #1e3c72;
76
+ margin-bottom: 20px;
77
+ }
78
+
79
+ .hero-section p {
80
+ font-size: 20px;
81
+ color: #666;
82
+ margin-bottom: 30px;
83
+ }
84
+
85
+ .tracking-status {
86
+ background: #e8f5e9;
87
+ border: 2px solid #4caf50;
88
+ border-radius: 10px;
89
+ padding: 20px;
90
+ margin: 20px 0;
91
+ }
92
+
93
+ .tracking-status h3 {
94
+ color: #2e7d32;
95
+ margin-bottom: 10px;
96
+ }
97
+
98
+ .info-grid {
99
+ display: grid;
100
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
101
+ gap: 20px;
102
+ margin-top: 40px;
103
+ }
104
+
105
+ .info-card {
106
+ background: white;
107
+ padding: 30px;
108
+ border-radius: 15px;
109
+ box-shadow: 0 10px 30px rgba(0,0,0,0.1);
110
+ transition: transform 0.3s;
111
+ }
112
+
113
+ .info-card:hover {
114
+ transform: translateY(-5px);
115
+ }
116
+
117
+ .info-card h3 {
118
+ color: #1e3c72;
119
+ margin-bottom: 15px;
120
+ font-size: 24px;
121
+ }
122
+
123
+ .info-card p {
124
+ color: #666;
125
+ line-height: 1.6;
126
+ }
127
+
128
+ .status-indicator {
129
+ display: inline-flex;
130
+ align-items: center;
131
+ gap: 8px;
132
+ background: #f5f5f5;
133
+ padding: 8px 16px;
134
+ border-radius: 20px;
135
+ font-size: 14px;
136
+ }
137
+
138
+ .status-indicator.active {
139
+ background: #e8f5e9;
140
+ color: #2e7d32;
141
+ }
142
+
143
+ .status-dot {
144
+ width: 8px;
145
+ height: 8px;
146
+ border-radius: 50%;
147
+ background: #4caf50;
148
+ animation: pulse 2s infinite;
149
+ }
150
+
151
+ @keyframes pulse {
152
+ 0% {
153
+ box-shadow: 0 0 0 0 rgba(76, 175, 80, 0.7);
154
+ }
155
+ 70% {
156
+ box-shadow: 0 0 0 10px rgba(76, 175, 80, 0);
157
+ }
158
+ 100% {
159
+ box-shadow: 0 0 0 0 rgba(76, 175, 80, 0);
160
+ }
161
+ }
162
+
163
+ .tracking-details {
164
+ background: #f8f9fa;
165
+ padding: 20px;
166
+ border-radius: 10px;
167
+ margin-top: 20px;
168
+ font-family: monospace;
169
+ font-size: 14px;
170
+ }
171
+
172
+ .tracking-details .row {
173
+ display: flex;
174
+ justify-content: space-between;
175
+ padding: 8px 0;
176
+ border-bottom: 1px solid #e0e0e0;
177
+ }
178
+
179
+ .tracking-details .row:last-child {
180
+ border-bottom: none;
181
+ }
182
+
183
+ .tracking-details .label {
184
+ font-weight: bold;
185
+ color: #555;
186
+ }
187
+
188
+ .tracking-details .value {
189
+ color: #1e3c72;
190
+ }
191
+
192
+ .buttons {
193
+ display: flex;
194
+ gap: 15px;
195
+ margin-top: 30px;
196
+ justify-content: center;
197
+ flex-wrap: wrap;
198
+ }
199
+
200
+ button {
201
+ padding: 12px 30px;
202
+ background: #1e3c72;
203
+ color: white;
204
+ border: none;
205
+ border-radius: 8px;
206
+ font-size: 16px;
207
+ font-weight: 500;
208
+ cursor: pointer;
209
+ transition: all 0.3s;
210
+ }
211
+
212
+ button:hover {
213
+ background: #2a5298;
214
+ transform: translateY(-2px);
215
+ box-shadow: 0 5px 15px rgba(0,0,0,0.2);
216
+ }
217
+
218
+ button.secondary {
219
+ background: #757575;
220
+ }
221
+
222
+ button.secondary:hover {
223
+ background: #616161;
224
+ }
225
+
226
+ .footer {
227
+ text-align: center;
228
+ padding: 40px 20px;
229
+ color: white;
230
+ margin-top: 60px;
231
+ }
232
+
233
+ .console-hint {
234
+ background: #fff3cd;
235
+ border: 1px solid #ffeaa7;
236
+ padding: 15px;
237
+ border-radius: 8px;
238
+ margin: 20px 0;
239
+ text-align: center;
240
+ }
241
+
242
+ .console-hint code {
243
+ background: #f8f9fa;
244
+ padding: 2px 6px;
245
+ border-radius: 3px;
246
+ font-family: monospace;
247
+ }
248
+ </style>
249
+ </head>
250
+ <body>
251
+ <header class="header">
252
+ <div class="header-content">
253
+ <div class="logo">⚡ EV Cook</div>
254
+ <nav class="nav">
255
+ <a href="#home">홈</a>
256
+ <a href="#about">소개</a>
257
+ <a href="#tracking">추적정보</a>
258
+ <a href="#contact">문의</a>
259
+ </nav>
260
+ </div>
261
+ </header>
262
+
263
+ <div class="main-container">
264
+ <section class="hero-section">
265
+ <h1>EV Cook 추적 데모 사이트</h1>
266
+ <p>이 사이트는 방문자 추적 시스템이 활성화되어 있습니다</p>
267
 
268
+ <div class="tracking-status">
269
+ <h3>🔍 추적 상태</h3>
270
+ <div class="status-indicator active">
271
+ <span class="status-dot"></span>
272
+ <span>추적 활성화됨</span>
273
+ </div>
274
+
275
+ <div class="tracking-details">
276
+ <div class="row">
277
+ <span class="label">Device ID:</span>
278
+ <span class="value" id="deviceId">로딩 중...</span>
279
+ </div>
280
+ <div class="row">
281
+ <span class="label">추적 서버:</span>
282
+ <span class="value">seawolf2357-evcook.hf.space</span>
283
+ </div>
284
+ <div class="row">
285
+ <span class="label">사이트 ID:</span>
286
+ <span class="value">38e0a883</span>
287
+ </div>
288
+ <div class="row">
289
+ <span class="label">추적 시작:</span>
290
+ <span class="value" id="trackingStart">-</span>
291
+ </div>
292
+ </div>
293
+ </div>
294
+
295
+ <div class="buttons">
296
+ <button onclick="testTracking()">📊 추적 테스트</button>
297
+ <button onclick="sendCustomEvent()">🎯 커스텀 이벤트</button>
298
+ <button onclick="viewDetails()" class="secondary">📋 상세 정보</button>
299
+ <button onclick="checkServer()" class="secondary">🔍 서버 확인</button>
300
+ </div>
301
+ </section>
302
+
303
+ <div class="info-grid">
304
+ <div class="info-card">
305
+ <h3>🚀 실시간 추적</h3>
306
+ <p>페이지 방문, 클릭, 스크롤 등 모든 사용자 활동이 실시간으로 추적됩니다. 개발자 도구 콘솔에서 추적 로그를 확인할 수 있습니다.</p>
307
+ </div>
308
+ <div class="info-card">
309
+ <h3>🔒 개인정보 보호</h3>
310
+ <p>디바이스 ID는 브라우저 정보를 기반으로 생성되며, 개인을 식별할 수 있는 정보는 수집하지 않습니다.</p>
311
+ </div>
312
+ <div class="info-card">
313
+ <h3>📈 분석 대시보드</h3>
314
+ <p>수집된 데이터는 추적 서버의 대시보드에서 실시간으로 확인할 수 있습니다.</p>
315
+ </div>
316
+ </div>
317
+
318
+ <div class="console-hint">
319
+ 💡 <strong>개발자 도구 활용:</strong> <code>F12</code>를 눌러 Console 탭에서 추적 이벤트를 실시간으로 확인하세요!
320
+ </div>
321
+
322
+ <div id="detailsSection" style="display: none;">
323
+ <section class="hero-section" style="margin-top: 40px;">
324
+ <h2>📊 브라우저 상세 정보</h2>
325
+ <pre id="browserDetails" style="text-align: left; background: #f5f5f5; padding: 20px; border-radius: 8px; overflow-x: auto;"></pre>
326
+ </section>
327
+ </div>
328
+ </div>
329
+
330
+ <footer class="footer">
331
+ <p>&copy; 2025 EV Cook. 모든 권리 보유.</p>
332
+ <p>추적 서버: <a href="https://seawolf2357-evcook.hf.space" target="_blank" style="color: white;">seawolf2357-evcook.hf.space</a></p>
333
+ </footer>
334
+
335
+ <!-- Fixed Visitor Tracking Script -->
336
+ <script>
337
+ (function() {
338
+ // 추적 서버 설정
339
+ const TRACKING_SERVER = 'https://seawolf2357-evcook.hf.space';
340
+ const SITE_ID = '38e0a883'; // 서버에서 생성한 사이트 ID 사용
341
+
342
+ // 디바이스 ID 생성/조회
343
+ function getDeviceId() {
344
+ let deviceId = localStorage.getItem('_tracker_device_id');
345
+ if (!deviceId) {
346
+ // 하드웨어 기반 ID 생성
347
+ const components = [
348
+ screen.width + 'x' + screen.height,
349
+ navigator.hardwareConcurrency || 0,
350
+ navigator.deviceMemory || 0,
351
+ navigator.platform,
352
+ navigator.language,
353
+ new Date().getTimezoneOffset()
354
+ ];
355
+
356
+ // 간단한 해시 함수
357
+ let hash = 0;
358
+ const str = components.join('|');
359
+ for (let i = 0; i < str.length; i++) {
360
+ const char = str.charCodeAt(i);
361
+ hash = ((hash << 5) - hash) + char;
362
+ hash = hash & hash;
363
+ }
364
+
365
+ deviceId = 'DEV_' + Math.abs(hash).toString(36);
366
+ localStorage.setItem('_tracker_device_id', deviceId);
367
+ }
368
+ return deviceId;
369
+ }
370
+
371
+ // WebGL 정보 수집
372
+ function getWebGLInfo() {
373
+ try {
374
+ const canvas = document.createElement('canvas');
375
+ const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
376
+ if (!gl) return 'Not supported';
377
+
378
+ const debugInfo = gl.getExtension('WEBGL_debug_renderer_info');
379
+ if (debugInfo) {
380
+ return gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL);
381
+ }
382
+ return 'Unknown';
383
+ } catch(e) {
384
+ return 'Error';
385
  }
 
 
386
  }
387
 
388
+ // 추적 데이터 전송 (수정된 버전)
389
+ async function collectAndSend(eventType = 'pageview', eventData = {}) {
390
+ const data = {
391
+ siteId: SITE_ID,
392
+ deviceId: getDeviceId(),
393
+ eventType: eventType,
394
+ eventData: eventData,
395
+ pageUrl: window.location.href,
396
+ pageTitle: document.title,
397
+ referrer: document.referrer,
398
+ userAgent: navigator.userAgent,
399
+ screenResolution: screen.width + 'x' + screen.height,
400
+ platform: navigator.platform,
401
+ language: navigator.language,
402
+ timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
403
+ cpuCores: navigator.hardwareConcurrency || 0,
404
+ deviceMemory: navigator.deviceMemory || 0,
405
+ gpuInfo: getWebGLInfo(),
406
+ timestamp: new Date().toISOString()
407
+ };
408
 
409
+ console.log('[Tracker] Sending:', eventType);
410
+
411
+ // 여러 fn_index 시도 (TabbedInterface 때문에 인덱스가 다를 수 있음)
412
+ const fnIndexes = [0, 1, 2]; // 가능한 인덱스들
413
+ let success = false;
414
 
415
+ for (const fnIndex of fnIndexes) {
416
+ if (success) break;
417
+
418
+ try {
419
+ // 방법 1: /run/predict 엔드포인트
420
+ const response = await fetch(TRACKING_SERVER + '/run/predict', {
421
+ method: 'POST',
422
+ headers: {
423
+ 'Content-Type': 'application/json',
424
+ },
425
+ body: JSON.stringify({
426
+ data: [JSON.stringify(data)],
427
+ fn_index: fnIndex
428
+ })
429
+ });
430
+
431
+ if (response.ok) {
432
+ const result = await response.json();
433
+ console.log(`[Tracker] ✅ Success with fn_index=${fnIndex}:`, result);
434
+ success = true;
435
+
436
+ // 성공한 fn_index 저장
437
+ window._trackerFnIndex = fnIndex;
438
+ break;
439
+ } else if (response.status === 422) {
440
+ console.log(`[Tracker] fn_index=${fnIndex} not valid, trying next...`);
441
+ } else {
442
+ console.log(`[Tracker] fn_index=${fnIndex} error: ${response.status}`);
443
+ }
444
+ } catch (e) {
445
+ console.log(`[Tracker] fn_index=${fnIndex} failed:`, e.message);
446
+ }
447
+ }
448
+
449
+ // 방법 2: API 엔드포인트 직접 호출 (폴백)
450
+ if (!success) {
451
+ try {
452
+ const apiResponse = await fetch(TRACKING_SERVER + '/api/predict/', {
453
+ method: 'POST',
454
+ headers: {
455
+ 'Content-Type': 'application/json',
456
+ },
457
+ body: JSON.stringify({
458
+ data: [JSON.stringify(data)],
459
+ fn_index: window._trackerFnIndex || 1 // 이전에 성공한 인덱스 사용
460
+ })
461
+ });
462
+
463
+ if (apiResponse.ok) {
464
+ console.log('[Tracker] ✅ API method success');
465
+ success = true;
466
+ }
467
+ } catch (e) {
468
+ console.error('[Tracker] API method failed:', e);
469
+ }
470
+ }
471
+
472
+ // 방법 3: Queue/Join 방식 (Gradio 4.x)
473
+ if (!success) {
474
+ try {
475
+ const session_hash = Math.random().toString(36).substring(2, 15);
476
+ const queueResponse = await fetch(TRACKING_SERVER + '/queue/join', {
477
+ method: 'POST',
478
+ headers: {
479
+ 'Content-Type': 'application/json',
480
+ },
481
+ body: JSON.stringify({
482
+ data: [JSON.stringify(data)],
483
+ fn_index: window._trackerFnIndex || 1,
484
+ session_hash: session_hash
485
+ })
486
+ });
487
+
488
+ if (queueResponse.ok) {
489
+ console.log('[Tracker] ✅ Queue method success');
490
+ success = true;
491
+ }
492
+ } catch (e) {
493
+ console.error('[Tracker] Queue method failed:', e);
494
+ }
495
+ }
496
+
497
+ if (!success) {
498
+ console.error('[Tracker] ❌ All methods failed for event:', eventType);
499
  }
 
 
500
  }
501
 
502
+ // 전역 추적 객체
503
+ window._tracker = {
504
+ trackEvent: function(eventName, eventData) {
505
+ collectAndSend('custom', { name: eventName, data: eventData });
506
+ },
 
 
 
 
 
 
 
507
 
508
+ // 디버깅 도구
509
+ debug: {
510
+ getDeviceId: getDeviceId,
511
+
512
+ // 서버 구조 확인
513
+ checkConfig: async function() {
514
+ try {
515
+ const response = await fetch(TRACKING_SERVER + '/config');
516
+ const config = await response.json();
517
+ console.log('Server config:', config);
518
+
519
+ if (config.dependencies) {
520
+ console.log('Available functions:');
521
+ config.dependencies.forEach((dep, i) => {
522
+ console.log(`[${i}] ${dep.api_name || 'unnamed'}`);
523
+ });
524
+ }
525
+ } catch (e) {
526
+ console.error('Config check failed:', e);
527
+ }
528
+ },
529
+
530
+ // 테스트 전송
531
+ test: function() {
532
+ collectAndSend('debug_test', {
533
+ test: true,
534
+ timestamp: new Date().toISOString(),
535
+ random: Math.random()
536
+ });
537
+ }
538
  }
539
+ };
540
+
541
+ // 페이지 로드시 추적
542
+ if (document.readyState === 'loading') {
543
+ document.addEventListener('DOMContentLoaded', () => {
544
+ console.log('[Tracker] DOM loaded, sending pageview...');
545
+ collectAndSend();
 
 
 
 
 
 
 
 
 
 
 
 
546
  });
547
+ } else {
548
+ console.log('[Tracker] Page already loaded, sending pageview...');
549
+ collectAndSend();
550
  }
551
+
552
+ // SPA 지원 - URL 변경 감지
553
+ let lastUrl = location.href;
554
+ new MutationObserver(() => {
555
+ const url = location.href;
556
+ if (url !== lastUrl) {
557
+ lastUrl = url;
558
+ collectAndSend('navigation', { from: lastUrl, to: url });
559
+ }
560
+ }).observe(document, {subtree: true, childList: true});
561
+
562
+ // 스크롤 추적
563
+ let scrollTracked = false;
564
+ window.addEventListener('scroll', () => {
565
+ if (!scrollTracked && window.scrollY > 100) {
566
+ scrollTracked = true;
567
+ window._tracker.trackEvent('scroll', { depth: window.scrollY });
568
+ }
569
+ });
570
+
571
+ // 초기화 메시지
572
+ console.log('%c🔍 EV Cook Tracker v4.0 (Fixed)', 'color: #4CAF50; font-size: 16px; font-weight: bold;');
573
+ console.log('Server:', TRACKING_SERVER);
574
+ console.log('Site ID:', SITE_ID);
575
+ console.log('Device ID:', getDeviceId());
576
+ console.log('\n사용 가능한 명령어:');
577
+ console.log('- window._tracker.trackEvent(name, data)');
578
+ console.log('- window._tracker.debug.checkConfig()');
579
+ console.log('- window._tracker.debug.test()');
580
+ })();
581
+ </script>
582
+ <!-- End Visitor Tracking Script -->
583
+
584
+ <!-- 페이지 기능 스크립트 -->
585
+ <script>
586
+ // 페이지 로드시 실행
587
+ window.addEventListener('DOMContentLoaded', () => {
588
+ // Device ID 표시
589
+ setTimeout(() => {
590
+ const deviceId = localStorage.getItem('_tracker_device_id');
591
+ if (deviceId) {
592
+ document.getElementById('deviceId').textContent = deviceId;
593
+ }
594
+ document.getElementById('trackingStart').textContent = new Date().toLocaleString('ko-KR');
595
+ }, 500);
596
+ });
597
+
598
+ // 추적 테스트
599
+ function testTracking() {
600
+ if (window._tracker) {
601
+ window._tracker.trackEvent('test_button_click', {
602
+ button: 'tracking_test',
603
+ timestamp: Date.now(),
604
+ location: 'hero_section'
605
+ });
606
+
607
+ alert('✅ 추적 이벤트가 전송되었습니다!\n\n개발자 도구(F12) Console 탭에서 확인하세요.');
608
+ } else {
609
+ alert('❌ 추적 스크립트가 로드되지 않았습니다.');
610
+ }
611
+ }
612
+
613
+ // 커스텀 이벤트 전송
614
+ function sendCustomEvent() {
615
+ const eventName = prompt('이벤트 이름을 입력하세요:', 'custom_action');
616
+ if (eventName && window._tracker) {
617
+ const eventData = {
618
+ custom_name: eventName,
619
+ user_input: true,
620
+ timestamp: Date.now()
621
+ };
622
+
623
+ window._tracker.trackEvent(eventName, eventData);
624
+
625
+ alert(`✅ '${eventName}' 이벤트가 전송되었습니다!`);
626
+ console.log('[Custom Event]', eventName, eventData);
627
+ }
628
+ }
629
+
630
+ // 상세 정보 보기
631
+ function viewDetails() {
632
+ const details = {
633
+ tracking: {
634
+ deviceId: localStorage.getItem('_tracker_device_id'),
635
+ siteId: '38e0a883',
636
+ serverUrl: 'https://seawolf2357-evcook.hf.space'
637
+ },
638
+ browser: {
639
+ userAgent: navigator.userAgent,
640
+ platform: navigator.platform,
641
+ vendor: navigator.vendor,
642
+ language: navigator.language,
643
+ languages: navigator.languages,
644
+ cookieEnabled: navigator.cookieEnabled,
645
+ doNotTrack: navigator.doNotTrack,
646
+ onLine: navigator.onLine
647
+ },
648
+ screen: {
649
+ resolution: screen.width + 'x' + screen.height,
650
+ colorDepth: screen.colorDepth,
651
+ pixelRatio: window.devicePixelRatio
652
+ },
653
+ viewport: {
654
+ width: window.innerWidth,
655
+ height: window.innerHeight
656
+ },
657
+ hardware: {
658
+ cpuCores: navigator.hardwareConcurrency || 'Unknown',
659
+ deviceMemory: navigator.deviceMemory ? navigator.deviceMemory + 'GB' : 'Unknown'
660
+ },
661
+ location: {
662
+ href: window.location.href,
663
+ hostname: window.location.hostname,
664
+ protocol: window.location.protocol,
665
+ pathname: window.location.pathname
666
+ },
667
+ time: {
668
+ timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
669
+ offset: new Date().getTimezoneOffset(),
670
+ locale: new Date().toLocaleString()
671
+ }
672
+ };
673
+
674
+ document.getElementById('browserDetails').textContent = JSON.stringify(details, null, 2);
675
+ document.getElementById('detailsSection').style.display = 'block';
676
+
677
+ // 상세 정보 보기도 추적
678
+ window._tracker.trackEvent('view_details', { action: 'show_browser_info' });
679
+ }
680
+
681
+ // 서버 확인
682
+ function checkServer() {
683
+ console.log('🔍 서버 구조 확인 중...');
684
+ window._tracker.debug.checkConfig();
685
+ alert('서버 구조를 확인합니다.\n개발자 도구(F12) Console 탭을 확인하세요.');
686
+ }
687
+
688
+ // 콘솔에 추적 정보 출력
689
+ console.log('%c🔍 EV Cook 추적 시스템 활성화됨', 'color: #4CAF50; font-size: 16px; font-weight: bold;');
690
+ console.log('%c추적 서버:', 'font-weight: bold;', 'https://seawolf2357-evcook.hf.space');
691
+ console.log('%c사이트 ID:', 'font-weight: bold;', '38e0a883');
692
+ console.log('%cDevice ID:', 'font-weight: bold;', localStorage.getItem('_tracker_device_id') || '생성 중...');
693
+ console.log('\n%c💡 Tip: window._tracker.trackEvent(name, data)로 커스텀 이벤트를 전송할 수 있습니다.', 'color: #666;');
694
+ </script>
695
+ </body>
696
+ </html>