| import http from "http"; |
| import { URL } from "url"; |
|
|
| |
| |
| |
| |
| |
| |
| export function startLocalServer( |
| onCallback: (params: Record<string, string>) => void, |
| fixedPort: number | null = null |
| ): Promise<{ server: any; port: number; close: () => void }> { |
| return new Promise((resolve, reject) => { |
| const server = http.createServer((req, res) => { |
| const url = new URL(req.url || "/", `http://localhost`); |
|
|
| if (url.pathname === "/callback" || url.pathname === "/auth/callback") { |
| const params = Object.fromEntries(url.searchParams); |
|
|
| |
| res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" }); |
| res.end(`<!DOCTYPE html> |
| <html> |
| <head> |
| <meta charset="utf-8"> |
| <title>Authentication Successful</title> |
| <style> |
| body { font-family: system-ui; display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; background: #f5f5f5; } |
| .container { text-align: center; padding: 2rem; background: white; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); } |
| .success { color: #22c55e; font-size: 3rem; } |
| h1 { margin: 1rem 0; } |
| p { color: #666; } |
| #countdown { font-weight: bold; } |
| </style> |
| </head> |
| <body> |
| <div class="container"> |
| <div class="success">✓</div> |
| <h1>Authentication Successful</h1> |
| <p id="message">Closing in <span id="countdown">3</span> seconds...</p> |
| </div> |
| <script> |
| let count = 3; |
| const countdown = document.getElementById("countdown"); |
| const message = document.getElementById("message"); |
| const timer = setInterval(() => { |
| count--; |
| countdown.textContent = count; |
| if (count <= 0) { |
| clearInterval(timer); |
| window.close(); |
| setTimeout(() => { |
| message.textContent = "Please close this tab manually."; |
| }, 500); |
| } |
| }, 1000); |
| </script> |
| </body> |
| </html>`); |
|
|
| |
| onCallback(params); |
| } else { |
| res.writeHead(404); |
| res.end("Not found"); |
| } |
| }); |
|
|
| |
| const portToUse = fixedPort || 0; |
| server.listen(portToUse, "127.0.0.1", () => { |
| const addr = server.address() as { port: number }; |
| resolve({ |
| server, |
| port: addr.port, |
| close: () => server.close(), |
| }); |
| }); |
|
|
| server.on("error", (err: any) => { |
| if (err.code === "EADDRINUSE" && fixedPort) { |
| reject( |
| new Error( |
| `Port ${fixedPort} is already in use. Please close other applications using this port.` |
| ) |
| ); |
| } else { |
| reject(err); |
| } |
| }); |
| }); |
| } |
|
|
| |
| |
| |
| |
| |
| export function waitForCallback(timeoutMs = 300000) { |
| return new Promise((resolve, reject) => { |
| let resolved = false; |
|
|
| const timeout = setTimeout(() => { |
| if (!resolved) { |
| resolved = true; |
| reject(new Error("Authentication timeout")); |
| } |
| }, timeoutMs); |
|
|
| const onCallback = (params: Record<string, string>) => { |
| if (!resolved) { |
| resolved = true; |
| clearTimeout(timeout); |
| resolve(params); |
| } |
| }; |
|
|
| |
| (resolve as any).__onCallback = onCallback; |
| }); |
| } |
|
|