File size: 6,050 Bytes
9f207c1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
<!DOCTYPE html>
<html lang="id">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
  <title>GPT-5</title>

  <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
  <meta http-equiv="Pragma" content="no-cache">
  <meta http-equiv="Expires" content="0">

  <style>
    html, body { margin: 0; padding: 0; width: 100%; height: 100%; }
    #loading {
      position: fixed; inset: 0; background: rgba(255,255,255,0.9);
      display: flex; justify-content: center; align-items: center;
      font-size: 18px; z-index: 1000;
    }
    iframe { width: 100%; height: 100vh; border: none; }
  </style>
</head>
<body>
  <div id="loading">Please wait... Memilih server optimal...</div>
  <iframe id="streamlit-frame" referrerpolicy="no-referrer"></iframe>

  <script>
    const VERSION = "v1";
    const SERVERS = [
      "https://gepatest-hwak3xkal5nltlveztf5nn.streamlit.app/?embed=true"
    ];

    // Timeout definisi
    const pingTimeoutMs = 5000;          // timeout ping img
    const iframeLoadTimeoutMs = 12000;   // waktu nunggu iframe dianggap "siap"
    const overallTryTimeoutMs = 20000;   // batas keras setiap percobaan server

    // Ping ringan via <img> untuk menghindari no-cors opaque fetch
    function ping(url, timeout = pingTimeoutMs) {
      return new Promise((resolve) => {
        const start = performance.now();
        const img = new Image();
        let done = false;

        const timer = setTimeout(() => {
          if (done) return;
          done = true;
          // Timeout dianggap gagal (Infinity)
          resolve({ url, ok: false, latency: Infinity });
          img.src = ""; // abort
        }, timeout);

        const onComplete = (ok) => {
          if (done) return;
          done = true;
          clearTimeout(timer);
          const latency = performance.now() - start;
          resolve({ url, ok, latency });
        };

        img.onload = () => onComplete(true);
        img.onerror = () => onComplete(false);

        // Gunakan endpoint root dengan cache buster (Streamlit akan render HTML, img akan gagal, tapi kita tetap dapat latency)
        // Trik: walau img gagal (onerror), kita catat latency untuk RTT awal.
        // Tambah cb agar tidak cached.
        const u = new URL(url);
        u.searchParams.set("cb", Date.now().toString());
        img.src = u.toString();
      });
    }

    async function rankServersByPing(urls) {
      const results = await Promise.all(urls.map(u => ping(u)));
      // Urutkan berdasarkan latency terkecil
      return results
        .sort((a,b) => a.latency - b.latency)
        .map(r => r.url);
    }

    function setIframeSrc(iframe, baseUrl) {
      const u = new URL(baseUrl);
      u.searchParams.set("cb", Date.now().toString());
      iframe.src = u.toString();
    }

    // Coba load satu server dengan mekanisme timeout. Kembalikan true jika dianggap siap, false jika gagal.
    function tryLoadServer(iframe, url) {
      return new Promise((resolve) => {
        let settled = false;

        // Hard timeout untuk satu percobaan
        const hardTimer = setTimeout(() => {
          if (settled) return;
          settled = true;
          resolve(false);
        }, overallTryTimeoutMs);

        // Timer "app-ready" (karena onload bisa sukses walau app error)
        const readyTimer = setTimeout(() => {
          if (settled) return;
          // Lewati: tidak ada sinyal ready; anggap gagal
          settled = true;
          clearTimeout(hardTimer);
          resolve(false);
        }, iframeLoadTimeoutMs);

        // onload: dokumen termuat. Namun belum tentu app sehat.
        iframe.onload = () => {
          // Tetap menunggu sampai readyTimer. Jika Anda punya postMessage dari app,
          // di sini seharusnya menunggu pesan "ready" untuk resolve(true).
          // Karena tidak ada, kita pakai heuristik: beri sedikit grace period lalu anggap OK.
          // Grace singkat agar tidak menunggu lama kalau memang error.
          setTimeout(() => {
            if (settled) return;
            settled = true;
            clearTimeout(readyTimer);
            clearTimeout(hardTimer);
            resolve(true);
          }, 1500); // grace 1.5s setelah onload
        };

        // onerror hanya terjadi untuk kegagalan jaringan keras
        iframe.onerror = () => {
          if (settled) return;
          settled = true;
          clearTimeout(readyTimer);
          clearTimeout(hardTimer);
          resolve(false);
        };

        setIframeSrc(iframe, url);
      });
    }

    async function loadWithFallback() {
      const iframe = document.getElementById("streamlit-frame");
      const loading = document.getElementById("loading");
      const storageKey = `streamlitUrl_${VERSION}`;

      // Coba baca cache sebelumnya
      const cached = sessionStorage.getItem(storageKey);

      // Urutkan server berdasarkan ping (kasih prioritas yang lebih cepat).
      let prioritized = await rankServersByPing(SERVERS);

      // Jika ada cache, letakkan di depan daftar supaya dicoba dulu
      if (cached && prioritized.includes(cached)) {
        prioritized = [cached, ...prioritized.filter(u => u !== cached)];
      }

      for (const url of prioritized) {
        const ok = await tryLoadServer(iframe, url);
        if (ok) {
          // Anggap server siap. Simpan ke sessionStorage.
          sessionStorage.setItem(storageKey, url);
          loading.style.display = "none";
          // Pasang watchdog: jika nanti ada kegagalan reload (jarang), bersihkan cache.
          iframe.addEventListener('error', () => {
            sessionStorage.removeItem(storageKey);
          });
          return;
        }
      }

      // Semua gagal: bersihkan cache dan tampilkan pesan sederhana
      sessionStorage.removeItem(storageKey);
      loading.textContent = "Semua server gagal dimuat. Coba refresh halaman.";
    }

    window.addEventListener("load", loadWithFallback);
  </script>
</body>
</html>