- 添加 pnpm workspace 和 Turborepo 配置 - 创建 packages/shared 共享类型和工具 - 创建 packages/core-sdk 核心 SDK - 创建 packages/vscode-extension VSCode 插件 - 创建 packages/jetbrains-plugin JetBrains 插件基础结构 - 添加 README 文档
227 lines
5.6 KiB
TypeScript
227 lines
5.6 KiB
TypeScript
import type { BaseEvent, CollectorConfig, UserInfo, CodeContext } from '@ide-collector/shared';
|
|
import { DEFAULT_COLLECTOR_CONFIG, generateAnonymousUserId } from '@ide-collector/shared';
|
|
import { EventQueue } from './storage/event-queue';
|
|
import { HttpReporter } from './reporters/http-reporter';
|
|
import { PrivacyHandler } from './privacy/privacy-handler';
|
|
import { ConfigManager } from './config/config-manager';
|
|
|
|
/**
|
|
* IDE 数据采集器主类
|
|
*/
|
|
export class Collector {
|
|
private static instance: Collector | null = null;
|
|
|
|
private config: CollectorConfig;
|
|
private userInfo: UserInfo | null = null;
|
|
private eventQueue: EventQueue;
|
|
private reporter: HttpReporter;
|
|
private privacyHandler: PrivacyHandler;
|
|
private configManager: ConfigManager;
|
|
private flushTimer: ReturnType<typeof setInterval> | null = null;
|
|
private isInitialized = false;
|
|
|
|
private constructor(config: Partial<CollectorConfig> = {}) {
|
|
this.config = { ...DEFAULT_COLLECTOR_CONFIG, ...config };
|
|
this.eventQueue = new EventQueue(this.config.batchSize);
|
|
this.reporter = new HttpReporter(this.config.apiEndpoint);
|
|
this.privacyHandler = new PrivacyHandler(this.config.privacy);
|
|
this.configManager = new ConfigManager();
|
|
}
|
|
|
|
/**
|
|
* 获取单例实例
|
|
*/
|
|
public static getInstance(config?: Partial<CollectorConfig>): Collector {
|
|
if (!Collector.instance) {
|
|
Collector.instance = new Collector(config);
|
|
}
|
|
return Collector.instance;
|
|
}
|
|
|
|
/**
|
|
* 初始化采集器
|
|
*/
|
|
public async initialize(userInfo: Omit<UserInfo, 'userId'>): Promise<void> {
|
|
if (this.isInitialized) {
|
|
return;
|
|
}
|
|
|
|
// 加载保存的配置
|
|
const savedConfig = await this.configManager.loadConfig();
|
|
if (savedConfig) {
|
|
this.config = { ...this.config, ...savedConfig };
|
|
}
|
|
|
|
// 生成或恢复用户ID
|
|
const savedUserId = await this.configManager.getUserId();
|
|
this.userInfo = {
|
|
...userInfo,
|
|
userId: savedUserId || generateAnonymousUserId(),
|
|
};
|
|
|
|
// 保存用户ID
|
|
if (!savedUserId) {
|
|
await this.configManager.saveUserId(this.userInfo.userId);
|
|
}
|
|
|
|
// 恢复未发送的事件
|
|
const pendingEvents = await this.eventQueue.loadFromStorage();
|
|
if (pendingEvents.length > 0) {
|
|
console.log(`[Collector] Restored ${pendingEvents.length} pending events`);
|
|
}
|
|
|
|
// 启动定时刷新
|
|
this.startFlushTimer();
|
|
|
|
this.isInitialized = true;
|
|
console.log('[Collector] Initialized successfully');
|
|
}
|
|
|
|
/**
|
|
* 记录事件
|
|
*/
|
|
public async trackEvent(event: Omit<BaseEvent, 'userInfo'>): Promise<void> {
|
|
if (!this.isInitialized || !this.config.enabled) {
|
|
return;
|
|
}
|
|
|
|
// 检查采样率
|
|
if (Math.random() > this.config.samplingRate) {
|
|
return;
|
|
}
|
|
|
|
// 检查事件类型是否需要采集
|
|
if (!this.config.eventsToCapture.includes(event.eventType)) {
|
|
return;
|
|
}
|
|
|
|
// 应用隐私处理
|
|
const sanitizedEvent = this.privacyHandler.sanitizeEvent({
|
|
...event,
|
|
userInfo: this.userInfo!,
|
|
});
|
|
|
|
// 添加到队列
|
|
await this.eventQueue.enqueue(sanitizedEvent);
|
|
|
|
// 如果开启实时上报,立即发送
|
|
if (this.config.realtimeEnabled) {
|
|
await this.flush();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 强制刷新队列
|
|
*/
|
|
public async flush(): Promise<void> {
|
|
if (!this.isInitialized) {
|
|
return;
|
|
}
|
|
|
|
const events = await this.eventQueue.dequeueAll();
|
|
if (events.length === 0) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
await this.reporter.sendBatch(events);
|
|
console.log(`[Collector] Successfully sent ${events.length} events`);
|
|
} catch (error) {
|
|
console.error('[Collector] Failed to send events:', error);
|
|
// 重新入队失败的事件
|
|
for (const event of events) {
|
|
await this.eventQueue.enqueue(event);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 更新配置
|
|
*/
|
|
public async updateConfig(config: Partial<CollectorConfig>): Promise<void> {
|
|
this.config = { ...this.config, ...config };
|
|
this.privacyHandler = new PrivacyHandler(this.config.privacy);
|
|
this.reporter = new HttpReporter(this.config.apiEndpoint);
|
|
this.eventQueue.setMaxSize(this.config.batchSize);
|
|
|
|
// 重启定时器
|
|
this.stopFlushTimer();
|
|
this.startFlushTimer();
|
|
|
|
// 保存配置
|
|
await this.configManager.saveConfig(this.config);
|
|
}
|
|
|
|
/**
|
|
* 获取当前配置
|
|
*/
|
|
public getConfig(): CollectorConfig {
|
|
return { ...this.config };
|
|
}
|
|
|
|
/**
|
|
* 获取队列状态
|
|
*/
|
|
public getQueueStatus(): { size: number; maxSize: number } {
|
|
return {
|
|
size: this.eventQueue.size(),
|
|
maxSize: this.config.batchSize,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* 暂停采集
|
|
*/
|
|
public pause(): void {
|
|
this.config.enabled = false;
|
|
this.stopFlushTimer();
|
|
}
|
|
|
|
/**
|
|
* 恢复采集
|
|
*/
|
|
public resume(): void {
|
|
this.config.enabled = true;
|
|
this.startFlushTimer();
|
|
}
|
|
|
|
/**
|
|
* 销毁采集器
|
|
*/
|
|
public async destroy(): Promise<void> {
|
|
this.stopFlushTimer();
|
|
await this.flush();
|
|
this.isInitialized = false;
|
|
Collector.instance = null;
|
|
}
|
|
|
|
/**
|
|
* 创建代码上下文
|
|
*/
|
|
public createCodeContext(context: Partial<CodeContext>): CodeContext {
|
|
return this.privacyHandler.sanitizeContext({
|
|
filePath: context.filePath || '',
|
|
language: context.language || 'unknown',
|
|
...context,
|
|
});
|
|
}
|
|
|
|
private startFlushTimer(): void {
|
|
if (this.flushTimer) {
|
|
return;
|
|
}
|
|
this.flushTimer = setInterval(
|
|
() => this.flush(),
|
|
this.config.flushIntervalSeconds * 1000
|
|
);
|
|
}
|
|
|
|
private stopFlushTimer(): void {
|
|
if (this.flushTimer) {
|
|
clearInterval(this.flushTimer);
|
|
this.flushTimer = null;
|
|
}
|
|
}
|
|
}
|
|
|