UNPKG

@defikitdotnet/x-ai-combat

Version:

XCombatAI - Social Media Engagement Template for the Agent Framework

282 lines 12.1 kB
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; import { Controller, Get, Post } from "@defikitdotnet/agent-framework-backend"; import { v4 as uuidv4 } from "uuid"; import jwt from "jsonwebtoken"; import { UserResource } from "../models/User"; import { XAIAdapter } from "../db/XAIAdapter"; import { TwitterApi } from "twitter-api-v2"; import { log } from '../utils/logger'; /** * Authentication controller * Handles user authentication including Twitter OAuth, JWT management, and session control */ let XAICombatAuthController = class XAICombatAuthController { constructor(adapter) { this.JWT_SECRET = process.env.JWT_SECRET || "x-combat-ai-secret-key"; this.TWITTER_CONSUMER_KEY = process.env.TWITTER_CONSUMER_KEY || ""; this.TWITTER_CONSUMER_SECRET = process.env.TWITTER_CONSUMER_SECRET || ""; this.FRONTEND_URL = process.env.FRONTEND_URL || "http://localhost:3001"; this.BACKEND_URL = process.env.BACKEND_URL || "http://localhost:3005"; this.adapter = adapter; this.client = new TwitterApi({ appKey: this.TWITTER_CONSUMER_KEY, appSecret: this.TWITTER_CONSUMER_SECRET, }); } /** * Initiate Twitter OAuth login flow */ async initiateTwitterLogin(req, res) { try { // Create CSRF protection state const state = uuidv4(); res.cookie('oauth_state', state, { httpOnly: true, secure: process.env.NODE_ENV === 'production', maxAge: 3600000, }); // Get the agentId from query parameter const agentId = req.query.agentId; log("agentId", agentId); if (agentId) { res.cookie('agent_id', agentId, { httpOnly: true, secure: process.env.NODE_ENV === 'production', maxAge: 3600000, }); } // Get authentication URL and temporary token const { url, oauth_token, oauth_token_secret } = await this.client.generateAuthLink(`${this.BACKEND_URL}/xaicombat/api/auth/twitter/callback?state=${state}`); // Store token secret in cookie res.cookie('oauth_token_secret', oauth_token_secret, { httpOnly: true, secure: process.env.NODE_ENV === 'production', maxAge: 3600000, }); res.redirect(url); } catch (error) { console.error('Twitter login error:', error); res.status(500).json({ error: 'Failed to initiate Twitter login' }); } } /** * Handle Twitter OAuth callback */ async handleTwitterCallback(req, res) { try { const { oauth_token, oauth_verifier, state } = req.query; const storedState = req.cookies?.oauth_state; const oauthTokenSecret = req.cookies?.oauth_token_secret; const agentId = req.cookies?.agent_id; // Verify CSRF state if (!state || state !== storedState) { res.status(400).json({ error: 'Invalid state parameter' }); return; } // Clear cookies res.clearCookie('oauth_state'); res.clearCookie('oauth_token_secret'); res.clearCookie('agent_id'); if (!oauth_token || !oauth_verifier || !oauthTokenSecret) { res.status(400).json({ error: 'Missing OAuth parameters' }); return; } // Get access token const client = new TwitterApi({ appKey: this.TWITTER_CONSUMER_KEY, appSecret: this.TWITTER_CONSUMER_SECRET, accessToken: oauth_token, accessSecret: oauthTokenSecret, }); const { accessToken, accessSecret, userId, screenName } = await client.login(oauth_verifier); if (!accessToken || !accessSecret) { res.status(400).json({ error: 'Failed to get access token' }); return; } // Check if user already exists in the database // First, try to find user with both twitterId and agentId (if provided) let user = null; if (agentId) { user = await this.adapter.findUserByTwitterId(userId, agentId); } // If no user found with agentId + twitterId, check if user exists with just twitterId if (!user) { user = await this.adapter.findUserByTwitterId(userId); } if (!user) { // Create new user if they don't exist const newUser = new UserResource(); newUser.id = uuidv4(); newUser.twitterId = userId; newUser.username = screenName; newUser.linkedAt = new Date(); newUser.agentId = agentId || null; // Set agentId if provided // Save user to database await this.adapter.createUser(newUser); log(`Created new user for Twitter account: ${screenName} (${userId}), Agent: ${agentId || 'None'}`); user = newUser; } else { // Check if the user is already linked to a different agent if (user.agentId && agentId && user.agentId !== agentId) { // This Twitter user is already linked to a different agent // Create a new user for this agent const newUser = new UserResource(); newUser.id = uuidv4(); newUser.twitterId = userId; newUser.username = screenName; newUser.linkedAt = new Date(); newUser.agentId = agentId; // Save user to database await this.adapter.createUser(newUser); log(`Created new user for Twitter account: ${screenName} (${userId}), Agent: ${agentId}`); user = newUser; } else { // Update existing user information user.username = screenName; user.updatedAt = new Date(); // Only update agentId if provided and not already set if (agentId && !user.agentId) { user.agentId = agentId; } await this.adapter.updateUser(user); log(`Updated existing user for Twitter account: ${screenName} (${userId}), Agent: ${user.agentId || 'None'}`); } } // Create JWT containing user info including agentId const token = jwt.sign({ userId: user.id, twitterId: userId, username: screenName, agentId: user.agentId // Include agentId in JWT }, this.JWT_SECRET, { expiresIn: '7d' }); res.cookie('jwt', token, { httpOnly: true, secure: true, maxAge: 7 * 24 * 60 * 60 * 1000, // 7 days sameSite: 'none', }); res.redirect(`${this.FRONTEND_URL}/${agentId}/xaicombat/auth/callback`); } catch (error) { console.error('Twitter callback error:', error); res.status(500).json({ error: 'Failed to authenticate with Twitter' }); } } /** * Check current authentication status */ async checkAuth(req, res) { try { const jwt = req.cookies?.jwt; if (!jwt) { res.status(401).json({ error: 'No authentication token found' }); return; } res.json({ authenticated: true, jwt }); } catch (error) { console.error('Check auth error:', error); res.status(401).json({ error: 'Authentication failed' }); } } /** * Get current user from JWT token */ async getCurrentUser(req, res) { try { // Extract token from Authorization header const authHeader = req.headers.authorization; if (!authHeader || !authHeader.startsWith('Bearer ')) { res.status(401).json({ error: "No token provided" }); return; } const token = authHeader.split(' ')[1]; // Verify token const decoded = jwt.verify(token, this.JWT_SECRET); // Find user const user = await this.adapter.findUserById(decoded.userId); if (!user) { res.status(404).json({ error: "User not found" }); return; } res.status(200).json({ data: user }); } catch (error) { console.error("Error retrieving current user:", error); if (error.name === 'JsonWebTokenError') { res.status(401).json({ error: "Invalid token" }); return; } if (error.name === 'TokenExpiredError') { res.status(401).json({ error: "Token expired" }); return; } res.status(500).json({ error: "Failed to retrieve user" }); } } /** * Logout user */ async logout(req, res) { try { res.clearCookie('jwt', { httpOnly: true, secure: process.env.NODE_ENV === 'production', sameSite: 'lax', path: '/', // Đảm bảo cookie bị xóa ở tất cả path }); res.status(200).json({ message: "Logged out successfully" }); } catch (error) { console.error("Error during logout:", error); res.status(500).json({ error: "Failed to logout" }); } } }; __decorate([ Get('/twitter/login'), __metadata("design:type", Function), __metadata("design:paramtypes", [Object, Object]), __metadata("design:returntype", Promise) ], XAICombatAuthController.prototype, "initiateTwitterLogin", null); __decorate([ Get('/twitter/callback'), __metadata("design:type", Function), __metadata("design:paramtypes", [Object, Object]), __metadata("design:returntype", Promise) ], XAICombatAuthController.prototype, "handleTwitterCallback", null); __decorate([ Get('/check'), __metadata("design:type", Function), __metadata("design:paramtypes", [Object, Object]), __metadata("design:returntype", Promise) ], XAICombatAuthController.prototype, "checkAuth", null); __decorate([ Get('/me'), __metadata("design:type", Function), __metadata("design:paramtypes", [Object, Object]), __metadata("design:returntype", Promise) ], XAICombatAuthController.prototype, "getCurrentUser", null); __decorate([ Post('/logout'), __metadata("design:type", Function), __metadata("design:paramtypes", [Object, Object]), __metadata("design:returntype", Promise) ], XAICombatAuthController.prototype, "logout", null); XAICombatAuthController = __decorate([ Controller("/xaicombat/api/auth"), __metadata("design:paramtypes", [XAIAdapter]) ], XAICombatAuthController); export { XAICombatAuthController }; //# sourceMappingURL=AuthController.js.map