import * as vscode from "vscode"; import * as http from "http"; import * as path from "path"; import * as fs from "fs"; /** * IC Coder Authentication Provider * 集成到 VSCode 账户系统 */ export class ICCoderAuthenticationProvider implements vscode.AuthenticationProvider { private static readonly AUTH_TYPE = "iccoder"; private static readonly AUTH_NAME = "IC Coder"; private static readonly LOGIN_URL = "http://192.168.1.108:2005/login"; private static loginServer: http.Server | null = null; private static currentPort: number | null = null; private _onDidChangeSessions = new vscode.EventEmitter(); public readonly onDidChangeSessions = this._onDidChangeSessions.event; private _sessions: vscode.AuthenticationSession[] = []; constructor(private readonly context: vscode.ExtensionContext) { // 从存储中恢复会话 this.loadSessions(); } /** * 从存储中加载会话 */ private async loadSessions(): Promise { const storedSessions = this.context.globalState.get< vscode.AuthenticationSession[] >("icCoderSessions", []); this._sessions = storedSessions; } /** * 保存会话到存储 */ private async saveSessions(): Promise { await this.context.globalState.update("icCoderSessions", this._sessions); } /** * 获取会话列表 */ async getSessions( scopes?: readonly string[] ): Promise { return [...this._sessions]; } /** * 创建会话(登录) */ async createSession( scopes: readonly string[] ): Promise { try { const token = await this.login(); // 创建会话 const session: vscode.AuthenticationSession = { id: this.generateSessionId(), accessToken: token, account: { id: "iccoder-user", label: "IC Coder 用户", }, scopes: [...scopes], }; this._sessions.push(session); await this.saveSessions(); // 触发会话变化事件 this._onDidChangeSessions.fire({ added: [session], removed: [], changed: [], }); vscode.window.showInformationMessage("登录成功!窗口将自动刷新..."); // 延迟 1 秒后重新加载窗口,让用户看到成功消息 setTimeout(() => { vscode.commands.executeCommand("workbench.action.reloadWindow"); }, 1000); return session; } catch (error) { vscode.window.showErrorMessage( `登录失败: ${error instanceof Error ? error.message : String(error)}` ); throw error; } } /** * 删除会话(登出) */ async removeSession(sessionId: string): Promise { const sessionIndex = this._sessions.findIndex((s) => s.id === sessionId); if (sessionIndex > -1) { const session = this._sessions[sessionIndex]; this._sessions.splice(sessionIndex, 1); await this.saveSessions(); // 触发会话变化事件 this._onDidChangeSessions.fire({ added: [], removed: [session], changed: [], }); vscode.window.showInformationMessage("已退出登录!窗口将自动刷新..."); // 延迟 1 秒后重新加载窗口,让用户看到成功消息 setTimeout(() => { vscode.commands.executeCommand("workbench.action.reloadWindow"); }, 1000); } } /** * 生成会话 ID */ private generateSessionId(): string { return `iccoder-${Date.now()}-${Math.random().toString(36).substring(7)}`; } /** * 登录逻辑(打开浏览器并等待回调) */ private async login(): Promise { // 如果已有服务器在运行,先关闭 if (ICCoderAuthenticationProvider.loginServer) { ICCoderAuthenticationProvider.loginServer.close(); ICCoderAuthenticationProvider.loginServer = null; } // 创建本地服务器监听回调 const { server, port } = await this.createCallbackServer(); ICCoderAuthenticationProvider.loginServer = server; ICCoderAuthenticationProvider.currentPort = port; // 构建登录 URL const callbackUrl = `http://localhost:${port}/callback`; const loginUrl = `${ ICCoderAuthenticationProvider.LOGIN_URL }?redirect_uri=${encodeURIComponent(callbackUrl)}`; console.log("🔐 登录服务器已启动,监听端口:", port); console.log("🌐 登录 URL:", loginUrl); // 打开浏览器登录 await vscode.env.openExternal(vscode.Uri.parse(loginUrl)); vscode.window.showInformationMessage( "请在浏览器中完成登录,登录成功后将自动返回..." ); // 等待 token(通过 Promise) return new Promise((resolve, reject) => { const timeout = setTimeout(() => { if (ICCoderAuthenticationProvider.loginServer) { ICCoderAuthenticationProvider.loginServer.close(); ICCoderAuthenticationProvider.loginServer = null; reject(new Error("登录超时")); } }, 5 * 60 * 1000); // 将 resolve 和 reject 保存到服务器上下文 (server as any)._loginResolve = resolve; (server as any)._loginReject = reject; (server as any)._loginTimeout = timeout; }); } /** * 创建本地回调服务器 */ private createCallbackServer(): Promise<{ server: http.Server; port: number; }> { return new Promise((resolve, reject) => { // 读取 icon.png 并转换为 Base64 const iconPath = path.join( this.context.extensionPath, "media", "icon.png" ); let iconBase64 = ""; try { const iconBuffer = fs.readFileSync(iconPath); iconBase64 = `data:image/png;base64,${iconBuffer.toString("base64")}`; } catch (error) { console.warn("无法读取 icon.png:", error); } const server = http.createServer(async (req, res) => { try { console.log("📥 收到回调请求:", req.url); const url = new URL( req.url!, `http://localhost:${ICCoderAuthenticationProvider.currentPort}` ); console.log("📍 路径:", url.pathname); console.log("📋 所有参数:", Object.fromEntries(url.searchParams)); if (url.pathname === "/callback") { const token = url.searchParams.get("token"); console.log("🔑 Token:", token ? "已获取" : "未找到"); if (token) { // 返回成功页面 res.writeHead(200, { "Content-Type": "text/html; charset=utf-8", }); res.end(this.getSuccessPage(iconBase64)); // 关闭服务器 server.close(); ICCoderAuthenticationProvider.loginServer = null; // 清除超时 if ((server as any)._loginTimeout) { clearTimeout((server as any)._loginTimeout); } // 返回 token if ((server as any)._loginResolve) { (server as any)._loginResolve(token); } } else { res.writeHead(400, { "Content-Type": "text/html; charset=utf-8", }); res.end(` 登录失败

❌ 登录失败

未获取到有效的 Token

`); if ((server as any)._loginReject) { (server as any)._loginReject(new Error("未获取到有效的 Token")); } } } else { res.writeHead(404); res.end("Not Found"); } } catch (error) { res.writeHead(500); res.end("Internal Server Error"); if ((server as any)._loginReject) { (server as any)._loginReject(error); } } }); // 监听端口(使用 0 表示自动分配可用端口) server.listen(0, () => { const address = server.address(); const port = typeof address === "object" && address ? address.port : 3000; resolve({ server, port }); }); // 处理错误 server.on("error", (error: NodeJS.ErrnoException) => { reject(error); }); }); } /** * 获取登录成功页面 HTML */ private getSuccessPage(iconBase64: string): string { return ` 登录成功 - IC Coder

登录成功!

您已成功登录 IC Coder
现在可以返回 VSCode 继续使用

IC Coder
`; } }