| 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, "0.0.0.0", () => {
|
| 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;
|
| });
|
| }
|
|
|