| |
|
|
| const express = require('express'); |
| const bodyParser = require('body-parser'); |
| const axios = require('axios'); |
| const path = require('path'); |
| const mysql = require('mysql2'); |
| const morgan = require('morgan'); |
| const helmet = require('helmet'); |
| const { body, validationResult } = require('express-validator'); |
| require('dotenv').config(); |
|
|
| const app = express(); |
| const port = process.env.PORT || 3000; |
|
|
| |
| app.use(bodyParser.urlencoded({ extended: true })); |
| app.use(bodyParser.json()); |
| app.use(helmet()); |
| app.use(morgan('combined')); |
| app.use(express.static('public')); |
|
|
| |
| const pool = mysql.createPool({ |
| host: process.env.DB_HOST, |
| port: process.env.DB_PORT || 3306, |
| user: process.env.DB_USER, |
| password: process.env.DB_PASSWORD, |
| database: process.env.DB_NAME, |
| waitForConnections: true, |
| connectionLimit: 10, |
| queueLimit: 0 |
| }); |
|
|
| |
| const promisePool = pool.promise(); |
|
|
| |
| async function initializeDatabase() { |
| try { |
| |
| const createRedemptionCodesTable = ` |
| CREATE TABLE IF NOT EXISTS redemption_codes ( |
| id INT AUTO_INCREMENT PRIMARY KEY, |
| code TEXT NOT NULL, |
| name VARCHAR(255), |
| quota INT, |
| count INT, |
| user_ip VARCHAR(45), |
| created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP |
| ) |
| `; |
|
|
| await promisePool.query(createRedemptionCodesTable); |
| console.log('Redemption codes table is ready.'); |
|
|
| |
| const createSessionCookiesTable = ` |
| CREATE TABLE IF NOT EXISTS session_cookies ( |
| id INT AUTO_INCREMENT PRIMARY KEY, |
| username VARCHAR(255) NOT NULL, |
| session_cookies TEXT NOT NULL, |
| created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, |
| updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP |
| ) |
| `; |
|
|
| await promisePool.query(createSessionCookiesTable); |
| console.log('Session_cookies table is ready.'); |
|
|
| |
| const createTokensTable = ` |
| CREATE TABLE IF NOT EXISTS tokens ( |
| id INT AUTO_INCREMENT PRIMARY KEY, |
| name VARCHAR(255) NOT NULL, |
| token TEXT NOT NULL, |
| token_key VARCHAR(255) NOT NULL, |
| remain_quota INT NOT NULL, |
| expired_time INT NOT NULL, |
| unlimited_quota BOOLEAN NOT NULL, |
| model_limits_enabled BOOLEAN NOT NULL, |
| model_limits TEXT, |
| allow_ips TEXT, |
| group_name VARCHAR(255), |
| user_ip VARCHAR(45), |
| created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP |
| ) |
| `; |
|
|
| await promisePool.query(createTokensTable); |
| console.log('Tokens table is ready.'); |
| } catch (err) { |
| console.error('Error initializing database tables:', err); |
| process.exit(1); |
| } |
| } |
|
|
| |
| initializeDatabase(); |
|
|
| |
| app.get('/', (req, res) => { |
| res.sendFile(path.join(__dirname, 'views', 'index.html')); |
| }); |
|
|
| |
| function getUserIP(req) { |
| const xForwardedFor = req.headers['x-forwarded-for']; |
| if (xForwardedFor) { |
| const ips = xForwardedFor.split(',').map(ip => ip.trim()); |
| return ips[0]; |
| } |
| return req.socket.remoteAddress; |
| } |
|
|
| |
| async function saveSessionCookies(username, sessionCookies) { |
| try { |
| const insertQuery = ` |
| INSERT INTO session_cookies (username, session_cookies) |
| VALUES (?, ?) |
| `; |
|
|
| await promisePool.query(insertQuery, [username, sessionCookies]); |
| console.log(`New session cookies for user '${username}' saved successfully.`); |
| } catch (err) { |
| console.error(`Error saving session cookies for user '${username}':`, err); |
| throw err; |
| } |
| } |
|
|
| |
| async function performLogin() { |
| const loginUrl = `${process.env.BASE_URL}/api/user/login?turnstile=`; |
|
|
| const headers = { |
| 'Accept': 'application/json, text/plain, */*', |
| 'Accept-Language': 'en-US,en;q=0.9', |
| 'Connection': 'keep-alive', |
| 'Content-Type': 'application/json', |
| 'Origin': process.env.BASE_URL, |
| 'Referer': `${process.env.BASE_URL}/login`, |
| 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36', |
| 'VoApi-User': '-1' |
| }; |
|
|
| const data = { |
| username: process.env.ADMIN_USERNAME, |
| password: process.env.ADMIN_PASSWORD |
| }; |
|
|
| try { |
| const response = await axios.post(loginUrl, data, { |
| headers: headers, |
| withCredentials: true, |
| httpsAgent: new (require('https').Agent)({ rejectUnauthorized: false }) |
| }); |
|
|
| |
| const setCookieHeader = response.headers['set-cookie']; |
| if (setCookieHeader) { |
| |
| const cookies = setCookieHeader.map(cookie => cookie.split(';')[0]); |
| const cookieString = cookies.join('; '); |
|
|
| if (cookieString) { |
| console.log('Session cookies obtained from login.'); |
| return cookieString; |
| } |
| } |
|
|
| throw new Error('No session cookies received from login response.'); |
| } catch (error) { |
| if (error.response) { |
| throw new Error(`Login API Error: ${JSON.stringify(error.response.data)}`); |
| } else if (error.request) { |
| throw new Error('Login API Error: No response received from the login service.'); |
| } else { |
| throw new Error(`Login Error: ${error.message}`); |
| } |
| } |
| } |
|
|
| |
| async function generateRedemptionCode(name, quota, count, sessionCookies) { |
| console.log(`Using session cookies: ${sessionCookies}`); |
|
|
| const url = `${process.env.BASE_URL}/api/redemption/`; |
|
|
| const headers = { |
| 'Accept': 'application/json, text/plain, */*', |
| 'Accept-Language': 'en-US,en;q=0.9', |
| 'Connection': 'keep-alive', |
| 'Content-Type': 'application/json', |
| 'Origin': process.env.BASE_URL, |
| 'Referer': `${process.env.BASE_URL}/redemption`, |
| 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36', |
| 'VoApi-User': '1', |
| 'Cookie': sessionCookies |
| }; |
|
|
| const data = { |
| name: name, |
| quota: parseInt(quota), |
| count: parseInt(count) |
| }; |
|
|
| try { |
| const response = await axios.post(url, data, { |
| headers: headers, |
| httpsAgent: new (require('https').Agent)({ rejectUnauthorized: false }) |
| }); |
|
|
| console.log('Redemption code generated successfully.'); |
| return response.data; |
| } catch (error) { |
| if (error.response) { |
| throw new Error(`API Error: ${JSON.stringify(error.response.data)}`); |
| } else if (error.request) { |
| throw new Error('API Error: No response received from the redemption service.'); |
| } else { |
| throw new Error(`Error: ${error.message}`); |
| } |
| } |
| } |
|
|
| |
| async function saveRedemptionCode(codeData, name, quota, count, userIP) { |
| try { |
| |
| const code = codeData.code || JSON.stringify(codeData); |
|
|
| const insertQuery = ` |
| INSERT INTO redemption_codes (code, name, quota, count, user_ip) |
| VALUES (?, ?, ?, ?, ?) |
| `; |
|
|
| await promisePool.query(insertQuery, [code, name, quota, count, userIP]); |
| console.log('Redemption code saved to database.'); |
| } catch (err) { |
| console.error('Error saving redemption code to database:', err); |
| throw err; |
| } |
| } |
|
|
| |
| async function createToken(name, remain_quota, expired_time, unlimited_quota, model_limits_enabled, model_limits, allow_ips, group_name, sessionCookies) { |
| console.log(`Using session cookies for token creation: ${sessionCookies}`); |
|
|
| const url = `${process.env.BASE_URL}/api/token/`; |
|
|
| const headers = { |
| 'Accept': 'application/json, text/plain, */*', |
| 'Accept-Language': 'en-US,en;q=0.9,ru;q=0.8', |
| 'Connection': 'keep-alive', |
| 'Content-Type': 'application/json', |
| 'Origin': process.env.BASE_URL, |
| 'Referer': `${process.env.BASE_URL}/token`, |
| 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36', |
| 'VoApi-User': '1', |
| 'Cookie': sessionCookies |
| }; |
|
|
| const data = { |
| name: name, |
| remain_quota: parseInt(remain_quota), |
| expired_time: parseInt(expired_time), |
| unlimited_quota: unlimited_quota, |
| model_limits_enabled: model_limits_enabled, |
| model_limits: model_limits || '', |
| allow_ips: allow_ips || '', |
| group: group_name || '' |
| }; |
|
|
| try { |
| const response = await axios.post(url, data, { |
| headers: headers, |
| httpsAgent: new (require('https').Agent)({ rejectUnauthorized: false }) |
| }); |
|
|
| console.log('Token creation API call successful.'); |
| return response.data; |
| } catch (error) { |
| if (error.response) { |
| throw new Error(`Token API Error: ${JSON.stringify(error.response.data)}`); |
| } else if (error.request) { |
| throw new Error('Token API Error: No response received from the token service.'); |
| } else { |
| throw new Error(`Error: ${error.message}`); |
| } |
| } |
| } |
|
|
| |
| async function listTokens(sessionCookies, p = 0, size = 100) { |
| console.log(`Listing tokens with session cookies: ${sessionCookies}`); |
|
|
| const url = `${process.env.BASE_URL}/api/token/?p=${p}&size=${size}`; |
|
|
| const headers = { |
| 'Accept': 'application/json, text/plain, */*', |
| 'Accept-Language': 'en-US,en;q=0.9,ru;q=0.8', |
| 'Connection': 'keep-alive', |
| 'Referer': `${process.env.BASE_URL}/token`, |
| 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36', |
| 'VoApi-User': '1', |
| 'Cookie': sessionCookies |
| }; |
|
|
| try { |
| const response = await axios.get(url, { |
| headers: headers, |
| httpsAgent: new (require('https').Agent)({ rejectUnauthorized: false }) |
| }); |
|
|
| console.log('Tokens list retrieved successfully.'); |
| return response.data; |
| } catch (error) { |
| if (error.response) { |
| throw new Error(`List Tokens API Error: ${JSON.stringify(error.response.data)}`); |
| } else if (error.request) { |
| throw new Error('List Tokens API Error: No response received from the token service.'); |
| } else { |
| throw new Error(`Error: ${error.message}`); |
| } |
| } |
| } |
|
|
| |
| async function saveToken(tokenData, name, remain_quota, expired_time, unlimited_quota, model_limits_enabled, model_limits, allow_ips, group_name, userIP) { |
| try { |
| |
| const key = tokenData.key ? `sk-${tokenData.key}` : null; |
|
|
| if (!key) { |
| throw new Error('Token key not found in the response.'); |
| } |
|
|
| |
| const insertQuery = ` |
| INSERT INTO tokens (name, token, token_key, remain_quota, expired_time, unlimited_quota, model_limits_enabled, model_limits, allow_ips, group_name, user_ip) |
| VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) |
| `; |
|
|
| const [result] = await promisePool.query(insertQuery, [ |
| name, |
| JSON.stringify(tokenData), |
| key, |
| parseInt(remain_quota), |
| parseInt(expired_time), |
| unlimited_quota, |
| model_limits_enabled, |
| model_limits || '', |
| allow_ips || '', |
| group_name || '', |
| userIP |
| ]); |
|
|
| console.log(`Token saved to database with ID: ${result.insertId} and key: ${key}`); |
| } catch (err) { |
| console.error('Error saving token to database:', err); |
| throw err; |
| } |
| } |
|
|
| |
| app.post('/api/create', [ |
| body('username').isString().trim().notEmpty(), |
| body('name').isString().trim().notEmpty(), |
| body('quota').isInt({ min: 1 }), |
| body('count').isInt({ min: 1 }) |
| ], async (req, res) => { |
| |
| const errors = validationResult(req); |
| if (!errors.isEmpty()) { |
| return res.status(400).json({ success: false, errors: errors.array() }); |
| } |
|
|
| const { username, name, quota, count } = req.body; |
| const userIP = getUserIP(req); |
| const apiKey = req.headers['x-api-key']; |
|
|
| |
| if (apiKey !== process.env.API_KEY) { |
| return res.status(401).json({ |
| success: false, |
| error: 'Unauthorized: Invalid API key' |
| }); |
| } |
|
|
| try { |
| |
| console.log('Performing login to obtain new session cookies...'); |
| const sessionCookies = await performLogin(); |
|
|
| |
| await saveSessionCookies(username, sessionCookies); |
| console.log('New session cookies saved to the database.'); |
|
|
| |
| const redemptionCode = await generateRedemptionCode(name, quota, count, sessionCookies); |
|
|
| |
| await saveRedemptionCode(redemptionCode, name, quota, count, userIP); |
|
|
| res.json({ |
| success: true, |
| data: redemptionCode |
| }); |
| } catch (error) { |
| console.error('Error:', error.message); |
| res.status(500).json({ |
| success: false, |
| error: error.message |
| }); |
| } |
| }); |
|
|
| |
| app.post('/api/token', [ |
| body('name').isString().trim().notEmpty(), |
| body('remain_quota').isInt({ min: 1 }), |
| body('expired_time').isInt(), |
| body('unlimited_quota').isBoolean(), |
| body('model_limits_enabled').isBoolean(), |
| body('model_limits').optional().isString(), |
| body('allow_ips').optional().isString(), |
| body('group').optional().isString() |
| ], async (req, res) => { |
| |
| const errors = validationResult(req); |
| if (!errors.isEmpty()) { |
| return res.status(400).json({ success: false, errors: errors.array() }); |
| } |
|
|
| const { |
| name, |
| remain_quota, |
| expired_time, |
| unlimited_quota, |
| model_limits_enabled, |
| model_limits, |
| allow_ips, |
| group |
| } = req.body; |
|
|
| const userIP = getUserIP(req); |
| const apiKey = req.headers['x-api-key']; |
|
|
| |
| if (apiKey !== process.env.API_KEY) { |
| return res.status(401).json({ |
| success: false, |
| error: 'Unauthorized: Invalid API key' |
| }); |
| } |
|
|
| try { |
| |
| console.log('Performing login to obtain new session cookies for token creation...'); |
| const sessionCookies = await performLogin(); |
|
|
| |
| await saveSessionCookies(process.env.ADMIN_USERNAME, sessionCookies); |
| console.log('New session cookies saved to the database.'); |
|
|
| |
| const tokenData = await createToken( |
| name, |
| remain_quota, |
| expired_time, |
| unlimited_quota, |
| model_limits_enabled, |
| model_limits, |
| allow_ips, |
| group, |
| sessionCookies |
| ); |
|
|
| |
| let tokenKey = tokenData.key; |
| if (!tokenKey) { |
| console.log('Token key not found in creation response. Fetching from tokens list...'); |
| const tokensList = await listTokens(sessionCookies, 0, 100); |
| |
| if (tokensList.success && Array.isArray(tokensList.data) && tokensList.data.length > 0) { |
| |
| const latestToken = tokensList.data[0]; |
| tokenKey = latestToken.key; |
|
|
| if (!tokenKey) { |
| throw new Error('Token key not found in the tokens list response.'); |
| } |
| } else { |
| throw new Error('Failed to retrieve tokens list or no tokens available.'); |
| } |
| } |
|
|
| |
| tokenData.key = `sk-${tokenKey}`; |
|
|
| |
| await saveToken( |
| tokenData, |
| name, |
| remain_quota, |
| expired_time, |
| unlimited_quota, |
| model_limits_enabled, |
| model_limits, |
| allow_ips, |
| group, |
| userIP |
| ); |
|
|
| res.json({ |
| success: true, |
| data: tokenData.key |
| }); |
| } catch (error) { |
| console.error('Error:', error.message); |
| res.status(500).json({ |
| success: false, |
| error: error.message |
| }); |
| } |
| }); |
|
|
| |
| app.get('/health', (req, res) => { |
| res.status(200).json({ status: 'OK' }); |
| }); |
|
|
| |
| app.listen(port, () => { |
| console.log(`App is running at http://localhost:${port}`); |
| }); |
|
|