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,141 @@
1
+ /**
2
+ * ArchiCore Web Server
3
+ *
4
+ * HTTP API для Architecture Digital Twin:
5
+ * - Индексация проектов
6
+ * - Визуализация архитектуры
7
+ * - Анализ влияния изменений
8
+ * - Симуляция изменений
9
+ */
10
+ import 'dotenv/config';
11
+ import express from 'express';
12
+ import cors from 'cors';
13
+ import { createServer } from 'http';
14
+ import path from 'path';
15
+ import { fileURLToPath } from 'url';
16
+ import { Logger } from '../utils/logger.js';
17
+ import { apiRouter } from './routes/api.js';
18
+ import { uploadRouter } from './routes/upload.js';
19
+ import { authRouter } from './routes/auth.js';
20
+ import { adminRouter } from './routes/admin.js';
21
+ import { developerRouter } from './routes/developer.js';
22
+ import { githubRouter } from './routes/github.js';
23
+ const __filename = fileURLToPath(import.meta.url);
24
+ const __dirname = path.dirname(__filename);
25
+ export class ArchiCoreServer {
26
+ app;
27
+ server = null;
28
+ config;
29
+ constructor(config) {
30
+ this.config = config;
31
+ this.app = express();
32
+ this.setupMiddleware();
33
+ this.setupRoutes();
34
+ this.setupErrorHandling();
35
+ }
36
+ setupMiddleware() {
37
+ // CORS для фронтенда
38
+ this.app.use(cors({
39
+ origin: '*',
40
+ methods: ['GET', 'POST', 'PUT', 'DELETE'],
41
+ allowedHeaders: ['Content-Type', 'Authorization']
42
+ }));
43
+ // JSON парсинг
44
+ this.app.use(express.json({ limit: '50mb' }));
45
+ this.app.use(express.urlencoded({ extended: true }));
46
+ // Статические файлы (фронтенд)
47
+ const publicPath = path.join(__dirname, '../../public');
48
+ this.app.use(express.static(publicPath));
49
+ // Логирование запросов
50
+ this.app.use((req, _res, next) => {
51
+ Logger.debug(`${req.method} ${req.path}`);
52
+ next();
53
+ });
54
+ }
55
+ setupRoutes() {
56
+ // Auth routes
57
+ this.app.use('/api/auth', authRouter);
58
+ // Admin routes
59
+ this.app.use('/api/admin', adminRouter);
60
+ // Developer API routes (for API key management and public API)
61
+ this.app.use('/api/developer', developerRouter);
62
+ // GitHub integration routes
63
+ this.app.use('/api/github', githubRouter);
64
+ // API маршруты
65
+ this.app.use('/api', apiRouter);
66
+ // Upload маршруты
67
+ this.app.use('/api/upload', uploadRouter);
68
+ // Health check
69
+ this.app.get('/health', (_req, res) => {
70
+ res.json({
71
+ status: 'ok',
72
+ version: '0.1.0',
73
+ name: 'ArchiCore',
74
+ timestamp: new Date().toISOString()
75
+ });
76
+ });
77
+ // SPA fallback - все остальные маршруты отдают index.html
78
+ this.app.get('/*splat', (_req, res) => {
79
+ const indexPath = path.join(__dirname, '../../public/index.html');
80
+ res.sendFile(indexPath);
81
+ });
82
+ }
83
+ setupErrorHandling() {
84
+ // 404 handler
85
+ this.app.use((_req, res) => {
86
+ res.status(404).json({ error: 'Not found' });
87
+ });
88
+ // Error handler
89
+ this.app.use((err, _req, res, _next) => {
90
+ Logger.error('Server error:', err);
91
+ res.status(500).json({
92
+ error: 'Internal server error',
93
+ message: err.message
94
+ });
95
+ });
96
+ }
97
+ async start() {
98
+ return new Promise((resolve) => {
99
+ this.server = createServer(this.app);
100
+ this.server.listen(this.config.port, this.config.host || '0.0.0.0', () => {
101
+ Logger.success(`ArchiCore server running at http://localhost:${this.config.port}`);
102
+ Logger.info(`API available at http://localhost:${this.config.port}/api`);
103
+ Logger.info(`Dashboard at http://localhost:${this.config.port}`);
104
+ resolve();
105
+ });
106
+ });
107
+ }
108
+ async stop() {
109
+ return new Promise((resolve) => {
110
+ if (this.server) {
111
+ this.server.close(() => {
112
+ Logger.info('Server stopped');
113
+ resolve();
114
+ });
115
+ }
116
+ else {
117
+ resolve();
118
+ }
119
+ });
120
+ }
121
+ getApp() {
122
+ return this.app;
123
+ }
124
+ }
125
+ // Запуск сервера при прямом вызове
126
+ const isMainModule = process.argv[1]?.includes('server');
127
+ if (isMainModule) {
128
+ const port = parseInt(process.env.PORT || '3000', 10);
129
+ const server = new ArchiCoreServer({ port });
130
+ server.start().catch((err) => {
131
+ Logger.error('Failed to start server:', err);
132
+ process.exit(1);
133
+ });
134
+ // Graceful shutdown
135
+ process.on('SIGINT', async () => {
136
+ Logger.info('Shutting down...');
137
+ await server.stop();
138
+ process.exit(0);
139
+ });
140
+ }
141
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,43 @@
1
+ /**
2
+ * API Authentication Middleware for ArchiCore Developer API
3
+ *
4
+ * Аутентификация через API ключи, rate limiting, проверка permissions
5
+ */
6
+ import { Request, Response, NextFunction } from 'express';
7
+ import { ApiPermission, ApiRequestContext } from '../../types/api.js';
8
+ declare global {
9
+ namespace Express {
10
+ interface Request {
11
+ apiContext?: ApiRequestContext;
12
+ }
13
+ }
14
+ }
15
+ /**
16
+ * Middleware для аутентификации через API ключ
17
+ * Поддерживает:
18
+ * - Header: Authorization: Bearer arc_xxxxx
19
+ * - Header: X-API-Key: arc_xxxxx
20
+ * - Query: ?api_key=arc_xxxxx
21
+ */
22
+ export declare function apiKeyAuth(req: Request, res: Response, next: NextFunction): Promise<void>;
23
+ /**
24
+ * Middleware для проверки permission
25
+ */
26
+ export declare function requirePermission(permission: ApiPermission): (req: Request, res: Response, next: NextFunction) => void;
27
+ /**
28
+ * Middleware для проверки баланса перед операцией
29
+ */
30
+ export declare function checkBalance(estimatedTokens: number): (req: Request, res: Response, next: NextFunction) => Promise<void>;
31
+ /**
32
+ * Wrapper для автоматического трекинга использования токенов
33
+ */
34
+ export declare function trackUsage(operation: ApiPermission extends `${infer Op}:${string}` ? Op : never): (req: Request, res: Response, next: NextFunction) => Promise<void>;
35
+ /**
36
+ * Error handler для API ошибок
37
+ */
38
+ export declare function apiErrorHandler(err: Error, _req: Request, res: Response, _next: NextFunction): void;
39
+ /**
40
+ * Optional API key auth (не требует ключ, но использует если есть)
41
+ */
42
+ export declare function optionalApiKeyAuth(req: Request, _res: Response, next: NextFunction): Promise<void>;
43
+ //# sourceMappingURL=api-auth.d.ts.map
@@ -0,0 +1,256 @@
1
+ /**
2
+ * API Authentication Middleware for ArchiCore Developer API
3
+ *
4
+ * Аутентификация через API ключи, rate limiting, проверка permissions
5
+ */
6
+ import { ApiKeyService } from '../services/api-key-service.js';
7
+ import { TokenService } from '../services/token-service.js';
8
+ import { Logger } from '../../utils/logger.js';
9
+ // Singleton instances
10
+ const apiKeyService = new ApiKeyService();
11
+ const tokenService = new TokenService();
12
+ /**
13
+ * Middleware для аутентификации через API ключ
14
+ * Поддерживает:
15
+ * - Header: Authorization: Bearer arc_xxxxx
16
+ * - Header: X-API-Key: arc_xxxxx
17
+ * - Query: ?api_key=arc_xxxxx
18
+ */
19
+ export async function apiKeyAuth(req, res, next) {
20
+ try {
21
+ // Извлекаем API ключ из разных источников
22
+ let apiKey;
23
+ // 1. Authorization header (Bearer token)
24
+ const authHeader = req.headers.authorization;
25
+ if (authHeader?.startsWith('Bearer ')) {
26
+ apiKey = authHeader.substring(7);
27
+ }
28
+ // 2. X-API-Key header
29
+ if (!apiKey && req.headers['x-api-key']) {
30
+ apiKey = req.headers['x-api-key'];
31
+ }
32
+ // 3. Query parameter (не рекомендуется, но поддерживаем)
33
+ if (!apiKey && req.query.api_key) {
34
+ apiKey = req.query.api_key;
35
+ }
36
+ if (!apiKey) {
37
+ res.status(401).json({
38
+ error: {
39
+ type: 'authentication_error',
40
+ message: 'API key is required. Provide it via Authorization header, X-API-Key header, or api_key query parameter.'
41
+ }
42
+ });
43
+ return;
44
+ }
45
+ // Валидация ключа
46
+ const validatedKey = await apiKeyService.validateKey(apiKey);
47
+ if (!validatedKey) {
48
+ res.status(401).json({
49
+ error: {
50
+ type: 'authentication_error',
51
+ message: 'Invalid or expired API key.'
52
+ }
53
+ });
54
+ return;
55
+ }
56
+ // Проверка rate limit
57
+ const rateLimitResult = await tokenService.checkRateLimit(validatedKey.id, validatedKey.rateLimit);
58
+ if (!rateLimitResult.allowed) {
59
+ res.status(429).json({
60
+ error: {
61
+ type: 'rate_limit_error',
62
+ message: 'Rate limit exceeded. Please retry later.',
63
+ retryAfter: rateLimitResult.retryAfter
64
+ },
65
+ limits: {
66
+ remaining: rateLimitResult.remaining,
67
+ resetAt: rateLimitResult.resetAt
68
+ }
69
+ });
70
+ return;
71
+ }
72
+ // Получаем billing account
73
+ const billingAccount = await tokenService.getBillingAccount(validatedKey.userId);
74
+ // Добавляем контекст в request
75
+ req.apiContext = {
76
+ apiKey: validatedKey,
77
+ userId: validatedKey.userId,
78
+ rateLimitState: (await tokenService.getRateLimitState(validatedKey.id)),
79
+ billingAccount
80
+ };
81
+ // Добавляем rate limit headers
82
+ res.setHeader('X-RateLimit-Limit-Requests', validatedKey.rateLimit.requestsPerMinute.toString());
83
+ res.setHeader('X-RateLimit-Remaining-Requests', rateLimitResult.remaining.requestsPerMinute.toString());
84
+ res.setHeader('X-RateLimit-Reset', rateLimitResult.resetAt.minute.toISOString());
85
+ next();
86
+ }
87
+ catch (error) {
88
+ Logger.error('API auth error:', error);
89
+ res.status(500).json({
90
+ error: {
91
+ type: 'server_error',
92
+ message: 'Authentication failed due to server error.'
93
+ }
94
+ });
95
+ }
96
+ }
97
+ /**
98
+ * Middleware для проверки permission
99
+ */
100
+ export function requirePermission(permission) {
101
+ return (req, res, next) => {
102
+ if (!req.apiContext) {
103
+ res.status(401).json({
104
+ error: {
105
+ type: 'authentication_error',
106
+ message: 'Not authenticated.'
107
+ }
108
+ });
109
+ return;
110
+ }
111
+ if (!apiKeyService.hasPermission(req.apiContext.apiKey, permission)) {
112
+ res.status(403).json({
113
+ error: {
114
+ type: 'permission_error',
115
+ message: `Permission '${permission}' is required for this operation.`,
116
+ required: permission,
117
+ available: req.apiContext.apiKey.permissions
118
+ }
119
+ });
120
+ return;
121
+ }
122
+ next();
123
+ };
124
+ }
125
+ /**
126
+ * Middleware для проверки баланса перед операцией
127
+ */
128
+ export function checkBalance(estimatedTokens) {
129
+ return async (req, res, next) => {
130
+ if (!req.apiContext) {
131
+ res.status(401).json({
132
+ error: {
133
+ type: 'authentication_error',
134
+ message: 'Not authenticated.'
135
+ }
136
+ });
137
+ return;
138
+ }
139
+ const balanceCheck = await tokenService.checkBalance(req.apiContext.userId, estimatedTokens);
140
+ if (!balanceCheck.allowed) {
141
+ res.status(402).json({
142
+ error: {
143
+ type: 'insufficient_funds',
144
+ message: 'Insufficient balance for this operation.',
145
+ balance: balanceCheck.balance / 100, // в долларах
146
+ estimatedCost: balanceCheck.estimatedCost / 100,
147
+ tokensRemaining: balanceCheck.tokensRemaining
148
+ }
149
+ });
150
+ return;
151
+ }
152
+ next();
153
+ };
154
+ }
155
+ /**
156
+ * Wrapper для автоматического трекинга использования токенов
157
+ */
158
+ export function trackUsage(operation) {
159
+ return async (req, res, next) => {
160
+ if (!req.apiContext) {
161
+ next();
162
+ return;
163
+ }
164
+ // Сохраняем оригинальный json метод
165
+ const originalJson = res.json.bind(res);
166
+ const startTime = Date.now();
167
+ // Перехватываем ответ
168
+ res.json = function (body) {
169
+ // Подсчитываем токены на основе размера ответа
170
+ const responseSize = JSON.stringify(body).length;
171
+ const inputSize = JSON.stringify(req.body || {}).length;
172
+ const tokens = tokenService.calculateTokens(operation, {
173
+ inputText: JSON.stringify(req.body || {}),
174
+ outputText: JSON.stringify(body),
175
+ contentSizeKb: Math.ceil((inputSize + responseSize) / 1024)
176
+ });
177
+ // Записываем использование асинхронно (не блокируем ответ)
178
+ tokenService.recordUsage(req.apiContext.apiKey.id, req.apiContext.userId, operation, tokens, req.params.id, // projectId if present
179
+ {
180
+ endpoint: req.path,
181
+ method: req.method,
182
+ duration: Date.now() - startTime
183
+ }).then(() => {
184
+ // Инкрементим rate limit
185
+ return tokenService.incrementRateLimit(req.apiContext.apiKey.id, tokens.totalTokens);
186
+ }).catch(err => {
187
+ Logger.error('Failed to record usage:', err);
188
+ });
189
+ // Добавляем usage info в ответ
190
+ if (body && typeof body === 'object' && !Array.isArray(body)) {
191
+ body.usage = {
192
+ inputTokens: tokens.inputTokens,
193
+ outputTokens: tokens.outputTokens,
194
+ totalTokens: tokens.totalTokens
195
+ };
196
+ }
197
+ return originalJson(body);
198
+ };
199
+ next();
200
+ };
201
+ }
202
+ /**
203
+ * Error handler для API ошибок
204
+ */
205
+ export function apiErrorHandler(err, _req, res, _next) {
206
+ Logger.error('API Error:', err);
207
+ // Определяем тип ошибки
208
+ let statusCode = 500;
209
+ let errorType = 'server_error';
210
+ if (err.message.includes('not found')) {
211
+ statusCode = 404;
212
+ errorType = 'not_found';
213
+ }
214
+ else if (err.message.includes('permission') || err.message.includes('forbidden')) {
215
+ statusCode = 403;
216
+ errorType = 'permission_error';
217
+ }
218
+ else if (err.message.includes('invalid') || err.message.includes('validation')) {
219
+ statusCode = 400;
220
+ errorType = 'validation_error';
221
+ }
222
+ res.status(statusCode).json({
223
+ error: {
224
+ type: errorType,
225
+ message: err.message
226
+ }
227
+ });
228
+ }
229
+ /**
230
+ * Optional API key auth (не требует ключ, но использует если есть)
231
+ */
232
+ export async function optionalApiKeyAuth(req, _res, next) {
233
+ // Пытаемся аутентифицировать, но не отклоняем если нет ключа
234
+ let apiKey;
235
+ const authHeader = req.headers.authorization;
236
+ if (authHeader?.startsWith('Bearer ')) {
237
+ apiKey = authHeader.substring(7);
238
+ }
239
+ if (!apiKey && req.headers['x-api-key']) {
240
+ apiKey = req.headers['x-api-key'];
241
+ }
242
+ if (apiKey) {
243
+ const validatedKey = await apiKeyService.validateKey(apiKey);
244
+ if (validatedKey) {
245
+ const billingAccount = await tokenService.getBillingAccount(validatedKey.userId);
246
+ req.apiContext = {
247
+ apiKey: validatedKey,
248
+ userId: validatedKey.userId,
249
+ rateLimitState: (await tokenService.getRateLimitState(validatedKey.id)),
250
+ billingAccount
251
+ };
252
+ }
253
+ }
254
+ next();
255
+ }
256
+ //# sourceMappingURL=api-auth.js.map
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Admin API Routes for ArchiCore
3
+ */
4
+ export declare const adminRouter: import("express-serve-static-core").Router;
5
+ //# sourceMappingURL=admin.d.ts.map
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Admin API Routes for ArchiCore
3
+ */
4
+ import { Router } from 'express';
5
+ import { AuthService } from '../services/auth-service.js';
6
+ import { authMiddleware, adminMiddleware } from './auth.js';
7
+ import { Logger } from '../../utils/logger.js';
8
+ export const adminRouter = Router();
9
+ const authService = new AuthService();
10
+ // All admin routes require authentication and admin role
11
+ adminRouter.use(authMiddleware);
12
+ adminRouter.use(adminMiddleware);
13
+ /**
14
+ * GET /api/admin/users
15
+ * Get all users
16
+ */
17
+ adminRouter.get('/users', async (_req, res) => {
18
+ try {
19
+ const users = await authService.getAllUsers();
20
+ res.json({ users });
21
+ }
22
+ catch (error) {
23
+ Logger.error('Failed to get users:', error);
24
+ res.status(500).json({ error: 'Failed to get users' });
25
+ }
26
+ });
27
+ /**
28
+ * GET /api/admin/users/:id
29
+ * Get specific user
30
+ */
31
+ adminRouter.get('/users/:id', async (req, res) => {
32
+ try {
33
+ const { id } = req.params;
34
+ const user = await authService.getUser(id);
35
+ if (!user) {
36
+ res.status(404).json({ error: 'User not found' });
37
+ return;
38
+ }
39
+ const { passwordHash, ...sanitized } = user;
40
+ res.json({ user: sanitized });
41
+ }
42
+ catch (error) {
43
+ Logger.error('Failed to get user:', error);
44
+ res.status(500).json({ error: 'Failed to get user' });
45
+ }
46
+ });
47
+ /**
48
+ * PUT /api/admin/users/:id/tier
49
+ * Update user's subscription tier
50
+ */
51
+ adminRouter.put('/users/:id/tier', async (req, res) => {
52
+ try {
53
+ const { id } = req.params;
54
+ const { tier } = req.body;
55
+ const validTiers = ['free', 'mid', 'pro', 'admin'];
56
+ if (!tier || !validTiers.includes(tier)) {
57
+ res.status(400).json({ error: 'Invalid tier' });
58
+ return;
59
+ }
60
+ const updated = await authService.updateUserTier(id, tier);
61
+ if (!updated) {
62
+ res.status(404).json({ error: 'User not found' });
63
+ return;
64
+ }
65
+ res.json({ success: true });
66
+ }
67
+ catch (error) {
68
+ Logger.error('Failed to update user tier:', error);
69
+ res.status(500).json({ error: 'Failed to update tier' });
70
+ }
71
+ });
72
+ /**
73
+ * DELETE /api/admin/users/:id
74
+ * Delete user
75
+ */
76
+ adminRouter.delete('/users/:id', async (req, res) => {
77
+ try {
78
+ const { id } = req.params;
79
+ const deleted = await authService.deleteUser(id);
80
+ if (!deleted) {
81
+ res.status(400).json({ error: 'Cannot delete user (admin or not found)' });
82
+ return;
83
+ }
84
+ res.json({ success: true });
85
+ }
86
+ catch (error) {
87
+ Logger.error('Failed to delete user:', error);
88
+ res.status(500).json({ error: 'Failed to delete user' });
89
+ }
90
+ });
91
+ /**
92
+ * GET /api/admin/stats
93
+ * Get system statistics
94
+ */
95
+ adminRouter.get('/stats', async (_req, res) => {
96
+ try {
97
+ const users = await authService.getAllUsers();
98
+ const stats = {
99
+ totalUsers: users.length,
100
+ byTier: {
101
+ free: users.filter(u => u.tier === 'free').length,
102
+ mid: users.filter(u => u.tier === 'mid').length,
103
+ pro: users.filter(u => u.tier === 'pro').length,
104
+ admin: users.filter(u => u.tier === 'admin').length
105
+ },
106
+ byProvider: {
107
+ email: users.filter(u => u.provider === 'email').length,
108
+ github: users.filter(u => u.provider === 'github').length,
109
+ google: users.filter(u => u.provider === 'google').length
110
+ },
111
+ recentUsers: users
112
+ .sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime())
113
+ .slice(0, 5)
114
+ .map(u => ({ id: u.id, username: u.username, tier: u.tier, createdAt: u.createdAt }))
115
+ };
116
+ res.json(stats);
117
+ }
118
+ catch (error) {
119
+ Logger.error('Failed to get stats:', error);
120
+ res.status(500).json({ error: 'Failed to get statistics' });
121
+ }
122
+ });
123
+ //# sourceMappingURL=admin.js.map
@@ -0,0 +1,7 @@
1
+ /**
2
+ * ArchiCore API Routes
3
+ *
4
+ * REST API для Architecture Digital Twin
5
+ */
6
+ export declare const apiRouter: import("express-serve-static-core").Router;
7
+ //# sourceMappingURL=api.d.ts.map