archicore 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. package/README.md +530 -0
  2. package/dist/analyzers/dead-code.d.ts +95 -0
  3. package/dist/analyzers/dead-code.js +327 -0
  4. package/dist/analyzers/duplication.d.ts +90 -0
  5. package/dist/analyzers/duplication.js +344 -0
  6. package/dist/analyzers/security.d.ts +79 -0
  7. package/dist/analyzers/security.js +484 -0
  8. package/dist/architecture/index.d.ts +35 -0
  9. package/dist/architecture/index.js +249 -0
  10. package/dist/cli/commands/analyzers.d.ts +6 -0
  11. package/dist/cli/commands/analyzers.js +431 -0
  12. package/dist/cli/commands/export.d.ts +6 -0
  13. package/dist/cli/commands/export.js +78 -0
  14. package/dist/cli/commands/index.d.ts +8 -0
  15. package/dist/cli/commands/index.js +8 -0
  16. package/dist/cli/commands/init.d.ts +26 -0
  17. package/dist/cli/commands/init.js +140 -0
  18. package/dist/cli/commands/interactive.d.ts +7 -0
  19. package/dist/cli/commands/interactive.js +522 -0
  20. package/dist/cli/commands/projects.d.ts +6 -0
  21. package/dist/cli/commands/projects.js +249 -0
  22. package/dist/cli/index.d.ts +7 -0
  23. package/dist/cli/index.js +7 -0
  24. package/dist/cli/ui/box.d.ts +17 -0
  25. package/dist/cli/ui/box.js +62 -0
  26. package/dist/cli/ui/colors.d.ts +49 -0
  27. package/dist/cli/ui/colors.js +86 -0
  28. package/dist/cli/ui/index.d.ts +9 -0
  29. package/dist/cli/ui/index.js +9 -0
  30. package/dist/cli/ui/prompt.d.ts +34 -0
  31. package/dist/cli/ui/prompt.js +122 -0
  32. package/dist/cli/ui/spinner.d.ts +29 -0
  33. package/dist/cli/ui/spinner.js +80 -0
  34. package/dist/cli/ui/table.d.ts +33 -0
  35. package/dist/cli/ui/table.js +84 -0
  36. package/dist/cli/utils/config.d.ts +23 -0
  37. package/dist/cli/utils/config.js +73 -0
  38. package/dist/cli/utils/index.d.ts +6 -0
  39. package/dist/cli/utils/index.js +6 -0
  40. package/dist/cli/utils/session.d.ts +27 -0
  41. package/dist/cli/utils/session.js +117 -0
  42. package/dist/cli.d.ts +8 -0
  43. package/dist/cli.js +295 -0
  44. package/dist/code-index/ast-parser.d.ts +16 -0
  45. package/dist/code-index/ast-parser.js +330 -0
  46. package/dist/code-index/dependency-graph.d.ts +16 -0
  47. package/dist/code-index/dependency-graph.js +161 -0
  48. package/dist/code-index/index.d.ts +44 -0
  49. package/dist/code-index/index.js +124 -0
  50. package/dist/code-index/symbol-extractor.d.ts +13 -0
  51. package/dist/code-index/symbol-extractor.js +150 -0
  52. package/dist/export/index.d.ts +92 -0
  53. package/dist/export/index.js +676 -0
  54. package/dist/github/github-service.d.ts +146 -0
  55. package/dist/github/github-service.js +609 -0
  56. package/dist/impact-engine/index.d.ts +25 -0
  57. package/dist/impact-engine/index.js +284 -0
  58. package/dist/index.d.ts +60 -0
  59. package/dist/index.js +149 -0
  60. package/dist/metrics/index.d.ts +136 -0
  61. package/dist/metrics/index.js +525 -0
  62. package/dist/orchestrator/deepseek-optimizer.d.ts +67 -0
  63. package/dist/orchestrator/deepseek-optimizer.js +320 -0
  64. package/dist/orchestrator/index.d.ts +34 -0
  65. package/dist/orchestrator/index.js +305 -0
  66. package/dist/pr-guardian/index.d.ts +143 -0
  67. package/dist/pr-guardian/index.js +553 -0
  68. package/dist/refactoring/index.d.ts +108 -0
  69. package/dist/refactoring/index.js +580 -0
  70. package/dist/rules-engine/index.d.ts +129 -0
  71. package/dist/rules-engine/index.js +482 -0
  72. package/dist/semantic-memory/embedding-service.d.ts +24 -0
  73. package/dist/semantic-memory/embedding-service.js +120 -0
  74. package/dist/semantic-memory/index.d.ts +45 -0
  75. package/dist/semantic-memory/index.js +206 -0
  76. package/dist/semantic-memory/vector-store.d.ts +27 -0
  77. package/dist/semantic-memory/vector-store.js +166 -0
  78. package/dist/server/index.d.ts +28 -0
  79. package/dist/server/index.js +141 -0
  80. package/dist/server/middleware/api-auth.d.ts +43 -0
  81. package/dist/server/middleware/api-auth.js +256 -0
  82. package/dist/server/routes/admin.d.ts +5 -0
  83. package/dist/server/routes/admin.js +123 -0
  84. package/dist/server/routes/api.d.ts +7 -0
  85. package/dist/server/routes/api.js +362 -0
  86. package/dist/server/routes/auth.d.ts +16 -0
  87. package/dist/server/routes/auth.js +191 -0
  88. package/dist/server/routes/developer.d.ts +8 -0
  89. package/dist/server/routes/developer.js +439 -0
  90. package/dist/server/routes/github.d.ts +7 -0
  91. package/dist/server/routes/github.js +495 -0
  92. package/dist/server/routes/upload.d.ts +7 -0
  93. package/dist/server/routes/upload.js +196 -0
  94. package/dist/server/services/api-key-service.d.ts +81 -0
  95. package/dist/server/services/api-key-service.js +281 -0
  96. package/dist/server/services/auth-service.d.ts +40 -0
  97. package/dist/server/services/auth-service.js +315 -0
  98. package/dist/server/services/project-service.d.ts +123 -0
  99. package/dist/server/services/project-service.js +533 -0
  100. package/dist/server/services/token-service.d.ts +107 -0
  101. package/dist/server/services/token-service.js +416 -0
  102. package/dist/server/services/upload-service.d.ts +93 -0
  103. package/dist/server/services/upload-service.js +464 -0
  104. package/dist/types/api.d.ts +188 -0
  105. package/dist/types/api.js +86 -0
  106. package/dist/types/github.d.ts +335 -0
  107. package/dist/types/github.js +5 -0
  108. package/dist/types/index.d.ts +265 -0
  109. package/dist/types/index.js +32 -0
  110. package/dist/types/user.d.ts +69 -0
  111. package/dist/types/user.js +42 -0
  112. package/dist/utils/file-utils.d.ts +20 -0
  113. package/dist/utils/file-utils.js +163 -0
  114. package/dist/utils/logger.d.ts +17 -0
  115. package/dist/utils/logger.js +41 -0
  116. package/dist/watcher/index.d.ts +125 -0
  117. package/dist/watcher/index.js +397 -0
  118. package/package.json +71 -0
@@ -0,0 +1,416 @@
1
+ /**
2
+ * Token Service for ArchiCore Developer API
3
+ *
4
+ * Подсчёт токенов, биллинг и rate limiting
5
+ */
6
+ import { randomBytes } from 'crypto';
7
+ import { readFile, writeFile, mkdir } from 'fs/promises';
8
+ import { join } from 'path';
9
+ import { PRICING_TIERS, OPERATION_TOKEN_COSTS } from '../../types/api.js';
10
+ import { Logger } from '../../utils/logger.js';
11
+ const DATA_DIR = '.archicore';
12
+ const USAGE_FILE = 'token-usage.json';
13
+ const BILLING_FILE = 'billing.json';
14
+ const RATE_LIMITS_FILE = 'rate-limits.json';
15
+ export class TokenService {
16
+ dataDir;
17
+ usageRecords = [];
18
+ billingAccounts = [];
19
+ rateLimitStates = [];
20
+ initialized = false;
21
+ constructor(dataDir = DATA_DIR) {
22
+ this.dataDir = dataDir;
23
+ }
24
+ async ensureInitialized() {
25
+ if (this.initialized)
26
+ return;
27
+ try {
28
+ await mkdir(this.dataDir, { recursive: true });
29
+ }
30
+ catch { }
31
+ // Load usage records
32
+ try {
33
+ const path = join(this.dataDir, USAGE_FILE);
34
+ const data = await readFile(path, 'utf-8');
35
+ const parsed = JSON.parse(data);
36
+ this.usageRecords = parsed.records || [];
37
+ }
38
+ catch {
39
+ this.usageRecords = [];
40
+ }
41
+ // Load billing accounts
42
+ try {
43
+ const path = join(this.dataDir, BILLING_FILE);
44
+ const data = await readFile(path, 'utf-8');
45
+ const parsed = JSON.parse(data);
46
+ this.billingAccounts = parsed.accounts || [];
47
+ }
48
+ catch {
49
+ this.billingAccounts = [];
50
+ }
51
+ // Load rate limit states
52
+ try {
53
+ const path = join(this.dataDir, RATE_LIMITS_FILE);
54
+ const data = await readFile(path, 'utf-8');
55
+ const parsed = JSON.parse(data);
56
+ this.rateLimitStates = parsed.states || [];
57
+ }
58
+ catch {
59
+ this.rateLimitStates = [];
60
+ }
61
+ this.initialized = true;
62
+ }
63
+ async saveUsage() {
64
+ const path = join(this.dataDir, USAGE_FILE);
65
+ await writeFile(path, JSON.stringify({ records: this.usageRecords }, null, 2));
66
+ }
67
+ async saveBilling() {
68
+ const path = join(this.dataDir, BILLING_FILE);
69
+ await writeFile(path, JSON.stringify({ accounts: this.billingAccounts }, null, 2));
70
+ }
71
+ async saveRateLimits() {
72
+ const path = join(this.dataDir, RATE_LIMITS_FILE);
73
+ await writeFile(path, JSON.stringify({ states: this.rateLimitStates }, null, 2));
74
+ }
75
+ // ===== TOKEN CALCULATION =====
76
+ /**
77
+ * Подсчёт токенов для операции
78
+ */
79
+ calculateTokens(operation, params) {
80
+ const costs = OPERATION_TOKEN_COSTS[operation];
81
+ const fileCount = params.fileCount || 0;
82
+ const contentSizeKb = params.contentSizeKb || 0;
83
+ // Базовый расчёт токенов
84
+ let inputTokens = costs.base + (fileCount * costs.perFile) + (contentSizeKb * costs.perKb);
85
+ // Добавляем токены за текст (примерно 1 токен = 4 символа)
86
+ if (params.inputText) {
87
+ inputTokens += Math.ceil(params.inputText.length / 4);
88
+ }
89
+ let outputTokens = 0;
90
+ if (params.outputText) {
91
+ outputTokens = Math.ceil(params.outputText.length / 4);
92
+ }
93
+ return {
94
+ inputTokens,
95
+ outputTokens,
96
+ totalTokens: inputTokens + outputTokens
97
+ };
98
+ }
99
+ /**
100
+ * Расчёт стоимости в центах
101
+ */
102
+ calculateCost(totalTokens, pricingTier) {
103
+ const tier = PRICING_TIERS[pricingTier] || PRICING_TIERS.starter;
104
+ // Цена за 1M токенов → цена за токен → умножаем на количество
105
+ const pricePerToken = tier.pricePerMillionTokens / 1_000_000;
106
+ return Math.ceil(totalTokens * pricePerToken * 100); // в центах
107
+ }
108
+ // ===== USAGE TRACKING =====
109
+ /**
110
+ * Запись использования токенов
111
+ */
112
+ async recordUsage(apiKeyId, userId, operation, tokens, projectId, metadata) {
113
+ await this.ensureInitialized();
114
+ // Получаем billing account для расчёта стоимости
115
+ const billing = await this.getBillingAccount(userId);
116
+ const cost = this.calculateCost(tokens.totalTokens, billing.pricingTier);
117
+ const usage = {
118
+ id: 'usage_' + randomBytes(12).toString('hex'),
119
+ apiKeyId,
120
+ userId,
121
+ operation,
122
+ inputTokens: tokens.inputTokens,
123
+ outputTokens: tokens.outputTokens,
124
+ totalTokens: tokens.totalTokens,
125
+ cost,
126
+ timestamp: new Date(),
127
+ projectId,
128
+ metadata
129
+ };
130
+ this.usageRecords.push(usage);
131
+ await this.saveUsage();
132
+ // Обновляем billing account
133
+ await this.updateBillingUsage(userId, tokens.totalTokens, cost);
134
+ Logger.debug(`Token usage recorded: ${tokens.totalTokens} tokens, $${(cost / 100).toFixed(4)}`);
135
+ return usage;
136
+ }
137
+ /**
138
+ * Получение истории использования
139
+ */
140
+ async getUsageHistory(userId, options) {
141
+ await this.ensureInitialized();
142
+ let records = this.usageRecords.filter(r => r.userId === userId);
143
+ if (options.apiKeyId) {
144
+ records = records.filter(r => r.apiKeyId === options.apiKeyId);
145
+ }
146
+ if (options.operation) {
147
+ records = records.filter(r => r.operation === options.operation);
148
+ }
149
+ // Сортируем по дате (новые первые)
150
+ records.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());
151
+ const offset = options.offset || 0;
152
+ const limit = options.limit || 100;
153
+ return records.slice(offset, offset + limit);
154
+ }
155
+ /**
156
+ * Получение статистики использования
157
+ */
158
+ async getUsageStats(userId, period) {
159
+ await this.ensureInitialized();
160
+ const now = new Date();
161
+ let startDate;
162
+ switch (period) {
163
+ case 'hour':
164
+ startDate = new Date(now.getTime() - 60 * 60 * 1000);
165
+ break;
166
+ case 'day':
167
+ startDate = new Date(now.getTime() - 24 * 60 * 60 * 1000);
168
+ break;
169
+ case 'week':
170
+ startDate = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);
171
+ break;
172
+ case 'month':
173
+ startDate = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);
174
+ break;
175
+ }
176
+ const records = this.usageRecords.filter(r => r.userId === userId && new Date(r.timestamp) >= startDate);
177
+ // Агрегация по операциям
178
+ const byOperation = {};
179
+ for (const op of Object.keys(OPERATION_TOKEN_COSTS)) {
180
+ const opRecords = records.filter(r => r.operation === op);
181
+ byOperation[op] = {
182
+ requests: opRecords.length,
183
+ tokens: opRecords.reduce((sum, r) => sum + r.totalTokens, 0),
184
+ cost: opRecords.reduce((sum, r) => sum + r.cost, 0)
185
+ };
186
+ }
187
+ // Агрегация по дням (для week и month)
188
+ let byDay;
189
+ if (period === 'week' || period === 'month') {
190
+ const dayMap = new Map();
191
+ for (const r of records) {
192
+ const date = new Date(r.timestamp).toISOString().split('T')[0];
193
+ const existing = dayMap.get(date) || { requests: 0, tokens: 0, cost: 0 };
194
+ existing.requests++;
195
+ existing.tokens += r.totalTokens;
196
+ existing.cost += r.cost;
197
+ dayMap.set(date, existing);
198
+ }
199
+ byDay = Array.from(dayMap.entries())
200
+ .map(([date, stats]) => ({ date, ...stats }))
201
+ .sort((a, b) => a.date.localeCompare(b.date));
202
+ }
203
+ return {
204
+ period,
205
+ startDate,
206
+ endDate: now,
207
+ totalRequests: records.length,
208
+ totalTokens: records.reduce((sum, r) => sum + r.totalTokens, 0),
209
+ totalCost: records.reduce((sum, r) => sum + r.cost, 0),
210
+ byOperation,
211
+ byDay
212
+ };
213
+ }
214
+ // ===== BILLING =====
215
+ /**
216
+ * Получение или создание billing account
217
+ */
218
+ async getBillingAccount(userId) {
219
+ await this.ensureInitialized();
220
+ let account = this.billingAccounts.find(a => a.userId === userId);
221
+ if (!account) {
222
+ // Создаём новый account
223
+ const now = new Date();
224
+ const endOfMonth = new Date(now.getFullYear(), now.getMonth() + 1, 0);
225
+ account = {
226
+ id: 'billing_' + randomBytes(12).toString('hex'),
227
+ userId,
228
+ pricingTier: 'free',
229
+ balance: 0,
230
+ tokensUsedThisMonth: 0,
231
+ tokensIncludedThisMonth: PRICING_TIERS.free.includedTokens,
232
+ billingCycleStart: now,
233
+ billingCycleEnd: endOfMonth,
234
+ autoRecharge: false,
235
+ autoRechargeThreshold: 500, // $5
236
+ autoRechargeAmount: 2000, // $20
237
+ paymentMethods: [],
238
+ invoices: []
239
+ };
240
+ this.billingAccounts.push(account);
241
+ await this.saveBilling();
242
+ }
243
+ return account;
244
+ }
245
+ /**
246
+ * Обновление использования в billing account
247
+ */
248
+ async updateBillingUsage(userId, tokens, cost) {
249
+ const account = await this.getBillingAccount(userId);
250
+ account.tokensUsedThisMonth += tokens;
251
+ // Списываем с баланса если превышены included tokens
252
+ const tier = PRICING_TIERS[account.pricingTier] || PRICING_TIERS.free;
253
+ if (account.tokensUsedThisMonth > tier.includedTokens) {
254
+ account.balance -= cost;
255
+ }
256
+ await this.saveBilling();
257
+ }
258
+ /**
259
+ * Пополнение баланса
260
+ */
261
+ async addCredits(userId, amount) {
262
+ await this.ensureInitialized();
263
+ const account = await this.getBillingAccount(userId);
264
+ account.balance += amount;
265
+ await this.saveBilling();
266
+ Logger.info(`Added ${amount} cents to user ${userId} balance`);
267
+ return account;
268
+ }
269
+ /**
270
+ * Изменение pricing tier
271
+ */
272
+ async updatePricingTier(userId, tier) {
273
+ await this.ensureInitialized();
274
+ const account = await this.getBillingAccount(userId);
275
+ const newTier = PRICING_TIERS[tier];
276
+ if (!newTier) {
277
+ throw new Error(`Invalid pricing tier: ${tier}`);
278
+ }
279
+ account.pricingTier = tier;
280
+ account.tokensIncludedThisMonth = newTier.includedTokens;
281
+ await this.saveBilling();
282
+ Logger.info(`Updated user ${userId} to tier ${tier}`);
283
+ return account;
284
+ }
285
+ /**
286
+ * Проверка достаточности баланса
287
+ */
288
+ async checkBalance(userId, estimatedTokens) {
289
+ const account = await this.getBillingAccount(userId);
290
+ const tier = PRICING_TIERS[account.pricingTier] || PRICING_TIERS.free;
291
+ const tokensRemaining = Math.max(0, tier.includedTokens - account.tokensUsedThisMonth);
292
+ const tokensToCharge = Math.max(0, estimatedTokens - tokensRemaining);
293
+ const estimatedCost = this.calculateCost(tokensToCharge, account.pricingTier);
294
+ const allowed = account.balance >= estimatedCost || tokensRemaining >= estimatedTokens;
295
+ return {
296
+ allowed,
297
+ balance: account.balance,
298
+ estimatedCost,
299
+ tokensRemaining
300
+ };
301
+ }
302
+ // ===== RATE LIMITING =====
303
+ /**
304
+ * Проверка rate limit
305
+ */
306
+ async checkRateLimit(apiKeyId, limits) {
307
+ await this.ensureInitialized();
308
+ const now = new Date();
309
+ let state = this.rateLimitStates.find(s => s.apiKeyId === apiKeyId);
310
+ if (!state) {
311
+ state = {
312
+ apiKeyId,
313
+ requestsThisMinute: 0,
314
+ requestsToday: 0,
315
+ tokensThisMinute: 0,
316
+ tokensToday: 0,
317
+ minuteResetAt: new Date(now.getTime() + 60 * 1000),
318
+ dayResetAt: new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1)
319
+ };
320
+ this.rateLimitStates.push(state);
321
+ }
322
+ // Reset counters if needed
323
+ if (new Date(state.minuteResetAt) <= now) {
324
+ state.requestsThisMinute = 0;
325
+ state.tokensThisMinute = 0;
326
+ state.minuteResetAt = new Date(now.getTime() + 60 * 1000);
327
+ }
328
+ if (new Date(state.dayResetAt) <= now) {
329
+ state.requestsToday = 0;
330
+ state.tokensToday = 0;
331
+ state.dayResetAt = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1);
332
+ }
333
+ // Check limits
334
+ const allowed = state.requestsThisMinute < limits.requestsPerMinute &&
335
+ state.requestsToday < limits.requestsPerDay;
336
+ let retryAfter;
337
+ if (!allowed) {
338
+ if (state.requestsThisMinute >= limits.requestsPerMinute) {
339
+ retryAfter = Math.ceil((new Date(state.minuteResetAt).getTime() - now.getTime()) / 1000);
340
+ }
341
+ else {
342
+ retryAfter = Math.ceil((new Date(state.dayResetAt).getTime() - now.getTime()) / 1000);
343
+ }
344
+ }
345
+ return {
346
+ allowed,
347
+ remaining: {
348
+ requestsPerMinute: Math.max(0, limits.requestsPerMinute - state.requestsThisMinute),
349
+ requestsPerDay: Math.max(0, limits.requestsPerDay - state.requestsToday),
350
+ tokensPerMinute: Math.max(0, limits.tokensPerMinute - state.tokensThisMinute),
351
+ tokensPerDay: Math.max(0, limits.tokensPerDay - state.tokensToday)
352
+ },
353
+ resetAt: {
354
+ minute: new Date(state.minuteResetAt),
355
+ day: new Date(state.dayResetAt)
356
+ },
357
+ retryAfter
358
+ };
359
+ }
360
+ /**
361
+ * Инкремент rate limit счётчиков
362
+ */
363
+ async incrementRateLimit(apiKeyId, tokens) {
364
+ await this.ensureInitialized();
365
+ const state = this.rateLimitStates.find(s => s.apiKeyId === apiKeyId);
366
+ if (state) {
367
+ state.requestsThisMinute++;
368
+ state.requestsToday++;
369
+ state.tokensThisMinute += tokens;
370
+ state.tokensToday += tokens;
371
+ await this.saveRateLimits();
372
+ }
373
+ }
374
+ /**
375
+ * Получение текущего состояния rate limit
376
+ */
377
+ async getRateLimitState(apiKeyId) {
378
+ await this.ensureInitialized();
379
+ return this.rateLimitStates.find(s => s.apiKeyId === apiKeyId) || null;
380
+ }
381
+ // ===== CLEANUP =====
382
+ /**
383
+ * Очистка старых записей usage (старше 90 дней)
384
+ */
385
+ async cleanupOldUsageRecords() {
386
+ await this.ensureInitialized();
387
+ const cutoffDate = new Date(Date.now() - 90 * 24 * 60 * 60 * 1000);
388
+ const initialLength = this.usageRecords.length;
389
+ this.usageRecords = this.usageRecords.filter(r => new Date(r.timestamp) > cutoffDate);
390
+ const removedCount = initialLength - this.usageRecords.length;
391
+ if (removedCount > 0) {
392
+ await this.saveUsage();
393
+ Logger.info(`Cleaned up ${removedCount} old usage records`);
394
+ }
395
+ return removedCount;
396
+ }
397
+ /**
398
+ * Сброс месячных лимитов (вызывается в начале месяца)
399
+ */
400
+ async resetMonthlyLimits() {
401
+ await this.ensureInitialized();
402
+ const now = new Date();
403
+ for (const account of this.billingAccounts) {
404
+ if (new Date(account.billingCycleEnd) <= now) {
405
+ account.tokensUsedThisMonth = 0;
406
+ account.billingCycleStart = now;
407
+ account.billingCycleEnd = new Date(now.getFullYear(), now.getMonth() + 1, 0);
408
+ const tier = PRICING_TIERS[account.pricingTier] || PRICING_TIERS.free;
409
+ account.tokensIncludedThisMonth = tier.includedTokens;
410
+ }
411
+ }
412
+ await this.saveBilling();
413
+ Logger.info('Monthly limits reset completed');
414
+ }
415
+ }
416
+ //# sourceMappingURL=token-service.js.map
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Upload Service
3
+ *
4
+ * Безопасная загрузка и распаковка ZIP/RAR архивов.
5
+ *
6
+ * Защита от:
7
+ * - ZIP-бомб (проверка соотношения сжатия)
8
+ * - Path traversal атак (../../../etc/passwd)
9
+ * - Опасных файлов (исполняемые, скрипты и т.д.)
10
+ * - Слишком больших файлов
11
+ */
12
+ export interface UploadConfig {
13
+ maxFileSize: number;
14
+ maxExtractedSize: number;
15
+ maxCompressionRatio: number;
16
+ uploadsDir: string;
17
+ extractDir: string;
18
+ }
19
+ export interface UploadResult {
20
+ success: boolean;
21
+ uploadId: string;
22
+ projectPath: string;
23
+ error?: string;
24
+ warnings: string[];
25
+ stats: {
26
+ originalSize: number;
27
+ extractedSize: number;
28
+ fileCount: number;
29
+ skippedFiles: string[];
30
+ };
31
+ }
32
+ export interface SecurityScanResult {
33
+ safe: boolean;
34
+ threats: SecurityThreat[];
35
+ warnings: string[];
36
+ }
37
+ export interface SecurityThreat {
38
+ type: 'zip_bomb' | 'path_traversal' | 'dangerous_file' | 'symlink' | 'oversized';
39
+ description: string;
40
+ file?: string;
41
+ severity: 'critical' | 'high' | 'medium' | 'low';
42
+ }
43
+ export declare class UploadService {
44
+ private config;
45
+ constructor(config?: Partial<UploadConfig>);
46
+ /**
47
+ * Проверка безопасности архива БЕЗ распаковки
48
+ */
49
+ scanArchive(filePath: string): Promise<SecurityScanResult>;
50
+ /**
51
+ * Безопасная распаковка архива
52
+ */
53
+ extractArchive(filePath: string, uploadId: string): Promise<UploadResult>;
54
+ /**
55
+ * Полный процесс загрузки: сохранение + сканирование + распаковка
56
+ */
57
+ processUpload(buffer: Buffer, originalName: string): Promise<UploadResult>;
58
+ /**
59
+ * Удаление проекта
60
+ */
61
+ deleteProject(uploadId: string): Promise<void>;
62
+ /**
63
+ * Проверка на path traversal
64
+ */
65
+ private isPathTraversal;
66
+ /**
67
+ * Очистка пути от опасных элементов
68
+ */
69
+ private sanitizePath;
70
+ /**
71
+ * Форматирование размера в читаемый вид
72
+ */
73
+ private formatBytes;
74
+ /**
75
+ * Получить информацию о загруженных проектах
76
+ */
77
+ listUploadedProjects(): Promise<Array<{
78
+ id: string;
79
+ path: string;
80
+ size: number;
81
+ files: number;
82
+ createdAt: Date;
83
+ }>>;
84
+ /**
85
+ * Подсчёт файлов в директории рекурсивно
86
+ */
87
+ private countFiles;
88
+ /**
89
+ * Размер директории рекурсивно
90
+ */
91
+ private getDirSize;
92
+ }
93
+ //# sourceMappingURL=upload-service.d.ts.map