@intranefr/superbackend 1.7.7 → 1.7.9
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.
- package/.beads/.br_history/issues.20260314_212352_900045509.jsonl +0 -0
- package/.beads/.br_history/issues.20260314_212352_900045509.jsonl.meta.json +1 -0
- package/.beads/.br_history/issues.20260314_212353_087140743.jsonl +1 -0
- package/.beads/.br_history/issues.20260314_212353_087140743.jsonl.meta.json +1 -0
- package/.beads/.br_history/issues.20260314_212353_285881504.jsonl +2 -0
- package/.beads/.br_history/issues.20260314_212353_285881504.jsonl.meta.json +1 -0
- package/.beads/.br_history/issues.20260314_212353_473915419.jsonl +3 -0
- package/.beads/.br_history/issues.20260314_212353_473915419.jsonl.meta.json +1 -0
- package/.beads/.br_history/issues.20260314_212353_659476307.jsonl +4 -0
- package/.beads/.br_history/issues.20260314_212353_659476307.jsonl.meta.json +1 -0
- package/.beads/.br_history/issues.20260314_212353_869998925.jsonl +5 -0
- package/.beads/.br_history/issues.20260314_212353_869998925.jsonl.meta.json +1 -0
- package/.beads/.br_history/issues.20260314_212354_054785029.jsonl +6 -0
- package/.beads/.br_history/issues.20260314_212354_054785029.jsonl.meta.json +1 -0
- package/.beads/.br_history/issues.20260314_213336_175893691.jsonl +7 -0
- package/.beads/.br_history/issues.20260314_213336_175893691.jsonl.meta.json +1 -0
- package/.beads/.br_history/issues.20260314_213336_338509797.jsonl +7 -0
- package/.beads/.br_history/issues.20260314_213336_338509797.jsonl.meta.json +1 -0
- package/.beads/.br_history/issues.20260314_213336_515443192.jsonl +7 -0
- package/.beads/.br_history/issues.20260314_213336_515443192.jsonl.meta.json +1 -0
- package/.beads/.br_history/issues.20260314_213336_676417592.jsonl +7 -0
- package/.beads/.br_history/issues.20260314_213336_676417592.jsonl.meta.json +1 -0
- package/.beads/.br_history/issues.20260314_213336_839182422.jsonl +7 -0
- package/.beads/.br_history/issues.20260314_213336_839182422.jsonl.meta.json +1 -0
- package/.beads/.br_history/issues.20260314_213337_004349113.jsonl +7 -0
- package/.beads/.br_history/issues.20260314_213337_004349113.jsonl.meta.json +1 -0
- package/.beads/.br_history/issues.20260314_213337_179824080.jsonl +7 -0
- package/.beads/.br_history/issues.20260314_213337_179824080.jsonl.meta.json +1 -0
- package/.beads/.br_history/issues.20260314_213701_705075332.jsonl +7 -0
- package/.beads/.br_history/issues.20260314_213701_705075332.jsonl.meta.json +1 -0
- package/.beads/.br_history/issues.20260314_213706_783128702.jsonl +8 -0
- package/.beads/.br_history/issues.20260314_213706_783128702.jsonl.meta.json +1 -0
- package/.beads/config.yaml +4 -0
- package/.beads/issues.jsonl +8 -0
- package/.beads/metadata.json +4 -0
- package/.env.example +8 -0
- package/autochangelog/.env.example +36 -0
- package/autochangelog/README.md +412 -0
- package/autochangelog/config/database.js +27 -0
- package/autochangelog/package.json +47 -0
- package/autochangelog/public/landing.html +581 -0
- package/autochangelog/server.js +104 -0
- package/autochangelog/src/app.js +181 -0
- package/autochangelog/src/config/database.js +26 -0
- package/autochangelog/src/controllers/auth.js +488 -0
- package/autochangelog/src/controllers/changelog.js +682 -0
- package/autochangelog/src/controllers/project.js +580 -0
- package/autochangelog/src/controllers/repository.js +780 -0
- package/autochangelog/src/middleware/auth.js +386 -0
- package/autochangelog/src/models/Changelog.js +443 -0
- package/autochangelog/src/models/Project.js +226 -0
- package/autochangelog/src/models/Repository.js +366 -0
- package/autochangelog/src/models/User.js +223 -0
- package/autochangelog/src/routes/auth.routes.js +32 -0
- package/autochangelog/src/routes/changelog.routes.js +42 -0
- package/autochangelog/src/routes/github-auth.routes.js +102 -0
- package/autochangelog/src/routes/project.routes.js +50 -0
- package/autochangelog/src/routes/repository.routes.js +54 -0
- package/autochangelog/src/services/changelog.js +722 -0
- package/autochangelog/src/services/github.js +243 -0
- package/autochangelog/utils/logger.js +77 -0
- package/autochangelog/views/404.ejs +18 -0
- package/autochangelog/views/dashboard.ejs +596 -0
- package/autochangelog/views/index.ejs +231 -0
- package/autochangelog/views/layouts/main.ejs +44 -0
- package/autochangelog/views/login.ejs +104 -0
- package/autochangelog/views/partials/footer.ejs +20 -0
- package/autochangelog/views/partials/navbar.ejs +51 -0
- package/autochangelog/views/register.ejs +109 -0
- package/autochangelog-cli/README.md +266 -0
- package/autochangelog-cli/bin/autochangelog +120 -0
- package/autochangelog-cli/package.json +46 -0
- package/autochangelog-cli/src/cli/commands/auth.js +291 -0
- package/autochangelog-cli/src/cli/commands/changelog.js +619 -0
- package/autochangelog-cli/src/cli/commands/project.js +427 -0
- package/autochangelog-cli/src/cli/commands/repo.js +557 -0
- package/autochangelog-cli/src/cli/commands/stats.js +706 -0
- package/autochangelog-cli/src/cli/utils/config.js +277 -0
- package/autochangelog-cli/src/cli/utils/errors.js +307 -0
- package/autochangelog-cli/src/cli/utils/logger.js +75 -0
- package/autochangelog-cli/src/cli/utils/output.js +357 -0
- package/package.json +8 -3
- package/plugins/supercli/README.md +108 -0
- package/plugins/supercli/plugin.json +123 -0
- package/server.js +1 -1
- package/src/cli/api.js +380 -0
- package/src/cli/direct/agent-utils.js +61 -0
- package/src/cli/direct/cli-utils.js +112 -0
- package/src/cli/direct/data-seeding.js +307 -0
- package/src/cli/direct/db-admin.js +84 -0
- package/src/cli/direct/db-advanced.js +372 -0
- package/src/cli/direct/db-utils.js +558 -0
- package/src/cli/direct/help.js +195 -0
- package/src/cli/direct/migration.js +107 -0
- package/src/cli/direct/rbac-advanced.js +132 -0
- package/src/cli/direct/resources-additional.js +400 -0
- package/src/cli/direct/resources-cms-advanced.js +173 -0
- package/src/cli/direct/resources-cms.js +247 -0
- package/src/cli/direct/resources-core.js +253 -0
- package/src/cli/direct/resources-execution.js +367 -0
- package/src/cli/direct/resources-health.js +152 -0
- package/src/cli/direct/resources-integrations.js +182 -0
- package/src/cli/direct/resources-logs.js +204 -0
- package/src/cli/direct/resources-org-rbac.js +187 -0
- package/src/cli/direct/resources-system.js +236 -0
- package/src/cli/direct.js +556 -0
- package/src/controllers/admin.controller.js +4 -0
- package/src/controllers/auth.controller.js +148 -1
- package/src/controllers/waitingList.controller.js +130 -1
- package/src/models/RbacRole.js +1 -1
- package/src/models/User.js +39 -5
- package/src/routes/auth.routes.js +6 -0
- package/src/routes/waitingList.routes.js +12 -2
- package/src/routes/waitingListAdmin.routes.js +3 -0
- package/src/services/email.service.js +1 -0
- package/src/services/github.service.js +255 -0
- package/src/services/rateLimiter.service.js +29 -1
- package/src/services/waitingListJson.service.js +32 -3
- package/views/admin-waiting-list.ejs +386 -3
|
@@ -0,0 +1,706 @@
|
|
|
1
|
+
const { Command } = require('commander');
|
|
2
|
+
const chalk = require('chalk');
|
|
3
|
+
const { output } = require('../utils/output');
|
|
4
|
+
const { handleCliError, validateRequiredArgs, ERROR_CODES } = require('../utils/errors');
|
|
5
|
+
const { loadConfig, getAuthToken } = require('../utils/config');
|
|
6
|
+
const { setupLogger } = require('../utils/logger');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Statistics and utility commands for AutoChangelog CLI
|
|
10
|
+
* Handles usage statistics, subscription info, and system utilities
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
function setupStatsCommands(program) {
|
|
14
|
+
const stats = program
|
|
15
|
+
.command('stats')
|
|
16
|
+
.description('Show project statistics and usage');
|
|
17
|
+
|
|
18
|
+
// Project statistics command
|
|
19
|
+
stats
|
|
20
|
+
.command('project [project-id]')
|
|
21
|
+
.description('Show project statistics')
|
|
22
|
+
.option('--detailed', 'Show detailed statistics')
|
|
23
|
+
.action(async (projectId, options) => {
|
|
24
|
+
const logger = setupLogger();
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
const token = getAuthToken();
|
|
28
|
+
if (!token) {
|
|
29
|
+
output.error('Not authenticated. Run "autochangelog auth login" first.');
|
|
30
|
+
process.exit(ERROR_CODES.AUTHENTICATION_FAILED);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (!projectId) {
|
|
34
|
+
output.error('Project ID is required');
|
|
35
|
+
process.exit(ERROR_CODES.INVALID_ARGUMENT);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const result = await getProjectStats(projectId, options.detailed, token, logger);
|
|
39
|
+
|
|
40
|
+
if (output.isJsonMode()) {
|
|
41
|
+
output.output({
|
|
42
|
+
success: true,
|
|
43
|
+
data: result,
|
|
44
|
+
metadata: {
|
|
45
|
+
projectId,
|
|
46
|
+
detailed: options.detailed,
|
|
47
|
+
timestamp: new Date().toISOString(),
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
} else {
|
|
51
|
+
displayProjectStats(result, options.detailed);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
} catch (error) {
|
|
55
|
+
handleCliError(error, output);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// Usage command
|
|
60
|
+
stats
|
|
61
|
+
.command('usage')
|
|
62
|
+
.description('Show subscription usage')
|
|
63
|
+
.action(async () => {
|
|
64
|
+
const logger = setupLogger();
|
|
65
|
+
|
|
66
|
+
try {
|
|
67
|
+
const token = getAuthToken();
|
|
68
|
+
if (!token) {
|
|
69
|
+
output.error('Not authenticated. Run "autochangelog auth login" first.');
|
|
70
|
+
process.exit(ERROR_CODES.AUTHENTICATION_FAILED);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const result = await getUsageStats(token, logger);
|
|
74
|
+
|
|
75
|
+
if (output.isJsonMode()) {
|
|
76
|
+
output.output({
|
|
77
|
+
success: true,
|
|
78
|
+
data: result,
|
|
79
|
+
metadata: {
|
|
80
|
+
timestamp: new Date().toISOString(),
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
} else {
|
|
84
|
+
displayUsageStats(result);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
} catch (error) {
|
|
88
|
+
handleCliError(error, output);
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// System info command
|
|
93
|
+
stats
|
|
94
|
+
.command('system')
|
|
95
|
+
.description('Show system information')
|
|
96
|
+
.action(async () => {
|
|
97
|
+
const logger = setupLogger();
|
|
98
|
+
|
|
99
|
+
try {
|
|
100
|
+
const config = loadConfig();
|
|
101
|
+
const token = getAuthToken();
|
|
102
|
+
|
|
103
|
+
const result = await getSystemInfo(config, token, logger);
|
|
104
|
+
|
|
105
|
+
if (output.isJsonMode()) {
|
|
106
|
+
output.output({
|
|
107
|
+
success: true,
|
|
108
|
+
data: result,
|
|
109
|
+
metadata: {
|
|
110
|
+
timestamp: new Date().toISOString(),
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
} else {
|
|
114
|
+
displaySystemInfo(result);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
} catch (error) {
|
|
118
|
+
handleCliError(error, output);
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// Health check command
|
|
123
|
+
stats
|
|
124
|
+
.command('health')
|
|
125
|
+
.description('Check system health')
|
|
126
|
+
.action(async () => {
|
|
127
|
+
const logger = setupLogger();
|
|
128
|
+
|
|
129
|
+
try {
|
|
130
|
+
const config = loadConfig();
|
|
131
|
+
const token = getAuthToken();
|
|
132
|
+
|
|
133
|
+
const result = await checkHealth(config, token, logger);
|
|
134
|
+
|
|
135
|
+
if (output.isJsonMode()) {
|
|
136
|
+
output.output({
|
|
137
|
+
success: true,
|
|
138
|
+
data: result,
|
|
139
|
+
metadata: {
|
|
140
|
+
timestamp: new Date().toISOString(),
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
} else {
|
|
144
|
+
displayHealthCheck(result);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
} catch (error) {
|
|
148
|
+
handleCliError(error, output);
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Get project statistics
|
|
155
|
+
* @param {string} projectId - Project ID
|
|
156
|
+
* @param {boolean} detailed - Show detailed stats
|
|
157
|
+
* @param {string} token - Authentication token
|
|
158
|
+
* @param {Object} logger - Logger instance
|
|
159
|
+
* @returns {Promise<Object>} Project statistics
|
|
160
|
+
*/
|
|
161
|
+
async function getProjectStats(projectId, detailed, token, logger) {
|
|
162
|
+
try {
|
|
163
|
+
const mongoose = require('mongoose');
|
|
164
|
+
const Project = require('../../autochangelog/src/models/Project');
|
|
165
|
+
const Repository = require('../../autochangelog/src/models/Repository');
|
|
166
|
+
const Changelog = require('../../autochangelog/src/models/Changelog');
|
|
167
|
+
|
|
168
|
+
await mongoose.connect(loadConfig().mongodb_uri);
|
|
169
|
+
|
|
170
|
+
// Get project info
|
|
171
|
+
const project = await Project.findById(projectId).lean();
|
|
172
|
+
if (!project) {
|
|
173
|
+
const error = new Error('Project not found');
|
|
174
|
+
error.code = ERROR_CODES.RESOURCE_NOT_FOUND;
|
|
175
|
+
error.type = 'resource_not_found';
|
|
176
|
+
throw error;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Get repository count
|
|
180
|
+
const repositoryCount = await Repository.countDocuments({ projectId });
|
|
181
|
+
|
|
182
|
+
// Get changelog stats
|
|
183
|
+
const changelogStats = await Changelog.aggregate([
|
|
184
|
+
{ $match: { projectId } },
|
|
185
|
+
{
|
|
186
|
+
$group: {
|
|
187
|
+
_id: null,
|
|
188
|
+
total: { $sum: 1 },
|
|
189
|
+
generated: { $sum: { $cond: [{ $eq: ['$status', 'generated'] }, 1, 0] } },
|
|
190
|
+
public: { $sum: { $cond: ['$public', 1, 0] } },
|
|
191
|
+
totalSize: { $sum: { $ifNull: [{ $strLenBytes: '$content' }, 0] } },
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
]);
|
|
195
|
+
|
|
196
|
+
const changelogData = changelogStats[0] || { total: 0, generated: 0, public: 0, totalSize: 0 };
|
|
197
|
+
|
|
198
|
+
// Get recent activity
|
|
199
|
+
const recentChangelogs = await Changelog.find({ projectId })
|
|
200
|
+
.sort({ createdAt: -1 })
|
|
201
|
+
.limit(5)
|
|
202
|
+
.lean();
|
|
203
|
+
|
|
204
|
+
const stats = {
|
|
205
|
+
project: {
|
|
206
|
+
id: project._id,
|
|
207
|
+
name: project.name,
|
|
208
|
+
plan: project.plan,
|
|
209
|
+
createdAt: project.createdAt,
|
|
210
|
+
},
|
|
211
|
+
repositories: {
|
|
212
|
+
total: repositoryCount,
|
|
213
|
+
},
|
|
214
|
+
changelogs: {
|
|
215
|
+
total: changelogData.total,
|
|
216
|
+
generated: changelogData.generated,
|
|
217
|
+
public: changelogData.public,
|
|
218
|
+
totalSize: changelogData.totalSize,
|
|
219
|
+
averageSize: changelogData.total > 0 ? Math.round(changelogData.totalSize / changelogData.total) : 0,
|
|
220
|
+
},
|
|
221
|
+
recentActivity: recentChangelogs.map(changelog => ({
|
|
222
|
+
id: changelog._id,
|
|
223
|
+
title: changelog.title,
|
|
224
|
+
format: changelog.format,
|
|
225
|
+
createdAt: changelog.createdAt,
|
|
226
|
+
})),
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
if (detailed) {
|
|
230
|
+
// Add detailed repository stats
|
|
231
|
+
const repositories = await Repository.find({ projectId }).lean();
|
|
232
|
+
stats.repositories.details = repositories.map(repo => ({
|
|
233
|
+
id: repo._id,
|
|
234
|
+
fullName: repo.fullName,
|
|
235
|
+
status: repo.status,
|
|
236
|
+
lastSync: repo.lastSync,
|
|
237
|
+
private: repo.private,
|
|
238
|
+
}));
|
|
239
|
+
|
|
240
|
+
// Add monthly breakdown
|
|
241
|
+
const monthlyStats = await Changelog.aggregate([
|
|
242
|
+
{ $match: { projectId } },
|
|
243
|
+
{
|
|
244
|
+
$group: {
|
|
245
|
+
_id: { year: '$year', month: '$month' },
|
|
246
|
+
count: { $sum: 1 },
|
|
247
|
+
size: { $sum: { $ifNull: [{ $strLenBytes: '$content' }, 0] } },
|
|
248
|
+
}
|
|
249
|
+
},
|
|
250
|
+
{ $sort: { '_id.year': -1, '_id.month': -1 } }
|
|
251
|
+
]);
|
|
252
|
+
|
|
253
|
+
stats.monthlyBreakdown = monthlyStats;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
logger.info(`Project stats retrieved: ${project.name}`);
|
|
257
|
+
|
|
258
|
+
return stats;
|
|
259
|
+
|
|
260
|
+
} catch (error) {
|
|
261
|
+
throw error;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Get usage statistics
|
|
267
|
+
* @param {string} token - Authentication token
|
|
268
|
+
* @param {Object} logger - Logger instance
|
|
269
|
+
* @returns {Promise<Object>} Usage statistics
|
|
270
|
+
*/
|
|
271
|
+
async function getUsageStats(token, logger) {
|
|
272
|
+
try {
|
|
273
|
+
const mongoose = require('mongoose');
|
|
274
|
+
const Project = require('../../autochangelog/src/models/Project');
|
|
275
|
+
const Repository = require('../../autochangelog/src/models/Repository');
|
|
276
|
+
const Changelog = require('../../autochangelog/src/models/Changelog');
|
|
277
|
+
|
|
278
|
+
await mongoose.connect(loadConfig().mongodb_uri);
|
|
279
|
+
|
|
280
|
+
// Get all projects for the authenticated user
|
|
281
|
+
const projects = await Project.find().lean();
|
|
282
|
+
|
|
283
|
+
const usage = {
|
|
284
|
+
projects: {
|
|
285
|
+
total: projects.length,
|
|
286
|
+
byPlan: {},
|
|
287
|
+
},
|
|
288
|
+
repositories: {
|
|
289
|
+
total: 0,
|
|
290
|
+
},
|
|
291
|
+
changelogs: {
|
|
292
|
+
total: 0,
|
|
293
|
+
generated: 0,
|
|
294
|
+
public: 0,
|
|
295
|
+
totalSize: 0,
|
|
296
|
+
},
|
|
297
|
+
limits: {
|
|
298
|
+
projects: { current: projects.length, limit: 100 },
|
|
299
|
+
repositories: { current: 0, limit: 1000 },
|
|
300
|
+
changelogs: { current: 0, limit: 10000 },
|
|
301
|
+
}
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
// Count by plan
|
|
305
|
+
projects.forEach(project => {
|
|
306
|
+
usage.projects.byPlan[project.plan] = (usage.projects.byPlan[project.plan] || 0) + 1;
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
// Get repository count
|
|
310
|
+
usage.repositories.total = await Repository.countDocuments();
|
|
311
|
+
usage.limits.repositories.current = usage.repositories.total;
|
|
312
|
+
|
|
313
|
+
// Get changelog stats
|
|
314
|
+
const changelogStats = await Changelog.aggregate([
|
|
315
|
+
{
|
|
316
|
+
$group: {
|
|
317
|
+
_id: null,
|
|
318
|
+
total: { $sum: 1 },
|
|
319
|
+
generated: { $sum: { $cond: [{ $eq: ['$status', 'generated'] }, 1, 0] } },
|
|
320
|
+
public: { $sum: { $cond: ['$public', 1, 0] } },
|
|
321
|
+
totalSize: { $sum: { $ifNull: [{ $strLenBytes: '$content' }, 0] } },
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
]);
|
|
325
|
+
|
|
326
|
+
const changelogData = changelogStats[0] || { total: 0, generated: 0, public: 0, totalSize: 0 };
|
|
327
|
+
usage.changelogs = {
|
|
328
|
+
total: changelogData.total,
|
|
329
|
+
generated: changelogData.generated,
|
|
330
|
+
public: changelogData.public,
|
|
331
|
+
totalSize: changelogData.totalSize,
|
|
332
|
+
};
|
|
333
|
+
usage.limits.changelogs.current = changelogData.total;
|
|
334
|
+
|
|
335
|
+
logger.info('Usage stats retrieved');
|
|
336
|
+
|
|
337
|
+
return usage;
|
|
338
|
+
|
|
339
|
+
} catch (error) {
|
|
340
|
+
throw error;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Get system information
|
|
346
|
+
* @param {Object} config - Configuration object
|
|
347
|
+
* @param {string} token - Authentication token
|
|
348
|
+
* @param {Object} logger - Logger instance
|
|
349
|
+
* @returns {Promise<Object>} System information
|
|
350
|
+
*/
|
|
351
|
+
async function getSystemInfo(config, token, logger) {
|
|
352
|
+
try {
|
|
353
|
+
const os = require('os');
|
|
354
|
+
const mongoose = require('mongoose');
|
|
355
|
+
|
|
356
|
+
// Test database connection
|
|
357
|
+
let dbStatus = 'unknown';
|
|
358
|
+
try {
|
|
359
|
+
await mongoose.connect(config.mongodb_uri);
|
|
360
|
+
dbStatus = 'connected';
|
|
361
|
+
} catch (error) {
|
|
362
|
+
dbStatus = 'disconnected';
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
const systemInfo = {
|
|
366
|
+
cli: {
|
|
367
|
+
version: require('../../../package.json').version,
|
|
368
|
+
nodeVersion: process.version,
|
|
369
|
+
platform: process.platform,
|
|
370
|
+
arch: process.arch,
|
|
371
|
+
cwd: process.cwd(),
|
|
372
|
+
},
|
|
373
|
+
database: {
|
|
374
|
+
status: dbStatus,
|
|
375
|
+
uri: config.mongodb_uri ? config.mongodb_uri.replace(/\/\/.*@/, '//***:***@') : 'Not configured',
|
|
376
|
+
},
|
|
377
|
+
authentication: {
|
|
378
|
+
authenticated: !!token,
|
|
379
|
+
tokenPreview: token ? token.substring(0, 10) + '...' : 'Not set',
|
|
380
|
+
},
|
|
381
|
+
environment: {
|
|
382
|
+
nodeEnv: process.env.NODE_ENV || 'development',
|
|
383
|
+
homeDir: os.homedir(),
|
|
384
|
+
tempDir: os.tmpdir(),
|
|
385
|
+
uptime: process.uptime(),
|
|
386
|
+
},
|
|
387
|
+
paths: {
|
|
388
|
+
config: config.config_path,
|
|
389
|
+
cache: config.cache_path,
|
|
390
|
+
}
|
|
391
|
+
};
|
|
392
|
+
|
|
393
|
+
logger.info('System info retrieved');
|
|
394
|
+
|
|
395
|
+
return systemInfo;
|
|
396
|
+
|
|
397
|
+
} catch (error) {
|
|
398
|
+
throw error;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* Check system health
|
|
404
|
+
* @param {Object} config - Configuration object
|
|
405
|
+
* @param {string} token - Authentication token
|
|
406
|
+
* @param {Object} logger - Logger instance
|
|
407
|
+
* @returns {Promise<Object>} Health check results
|
|
408
|
+
*/
|
|
409
|
+
async function checkHealth(config, token, logger) {
|
|
410
|
+
try {
|
|
411
|
+
const mongoose = require('mongoose');
|
|
412
|
+
const fs = require('fs-extra');
|
|
413
|
+
|
|
414
|
+
const health = {
|
|
415
|
+
status: 'healthy',
|
|
416
|
+
checks: [],
|
|
417
|
+
timestamp: new Date().toISOString(),
|
|
418
|
+
};
|
|
419
|
+
|
|
420
|
+
// Check database connection
|
|
421
|
+
try {
|
|
422
|
+
await mongoose.connect(config.mongodb_uri);
|
|
423
|
+
health.checks.push({
|
|
424
|
+
name: 'database',
|
|
425
|
+
status: 'healthy',
|
|
426
|
+
message: 'Database connection successful',
|
|
427
|
+
});
|
|
428
|
+
} catch (error) {
|
|
429
|
+
health.status = 'unhealthy';
|
|
430
|
+
health.checks.push({
|
|
431
|
+
name: 'database',
|
|
432
|
+
status: 'unhealthy',
|
|
433
|
+
message: `Database connection failed: ${error.message}`,
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// Check authentication
|
|
438
|
+
if (token) {
|
|
439
|
+
try {
|
|
440
|
+
const { Octokit } = require('@octokit/rest');
|
|
441
|
+
const octokit = new Octokit({ auth: token });
|
|
442
|
+
await octokit.rest.users.getAuthenticated();
|
|
443
|
+
health.checks.push({
|
|
444
|
+
name: 'authentication',
|
|
445
|
+
status: 'healthy',
|
|
446
|
+
message: 'GitHub authentication valid',
|
|
447
|
+
});
|
|
448
|
+
} catch (error) {
|
|
449
|
+
health.status = 'unhealthy';
|
|
450
|
+
health.checks.push({
|
|
451
|
+
name: 'authentication',
|
|
452
|
+
status: 'unhealthy',
|
|
453
|
+
message: 'GitHub authentication failed',
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
} else {
|
|
457
|
+
health.checks.push({
|
|
458
|
+
name: 'authentication',
|
|
459
|
+
status: 'warning',
|
|
460
|
+
message: 'No authentication token set',
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// Check file system permissions
|
|
465
|
+
try {
|
|
466
|
+
const configDir = require('path').dirname(config.config_path);
|
|
467
|
+
await fs.ensureDir(configDir);
|
|
468
|
+
health.checks.push({
|
|
469
|
+
name: 'filesystem',
|
|
470
|
+
status: 'healthy',
|
|
471
|
+
message: 'Configuration directory accessible',
|
|
472
|
+
});
|
|
473
|
+
} catch (error) {
|
|
474
|
+
health.status = 'unhealthy';
|
|
475
|
+
health.checks.push({
|
|
476
|
+
name: 'filesystem',
|
|
477
|
+
status: 'unhealthy',
|
|
478
|
+
message: `File system access failed: ${error.message}`,
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
// Check GitHub API rate limit
|
|
483
|
+
if (token) {
|
|
484
|
+
try {
|
|
485
|
+
const { Octokit } = require('@octokit/rest');
|
|
486
|
+
const octokit = new Octokit({ auth: token });
|
|
487
|
+
const { data } = await octokit.rest.rateLimit.get();
|
|
488
|
+
|
|
489
|
+
const remaining = data.rate.remaining;
|
|
490
|
+
const limit = data.rate.limit;
|
|
491
|
+
const resetTime = new Date(data.rate.reset * 1000);
|
|
492
|
+
|
|
493
|
+
health.checks.push({
|
|
494
|
+
name: 'github_rate_limit',
|
|
495
|
+
status: remaining > 100 ? 'healthy' : 'warning',
|
|
496
|
+
message: `Rate limit: ${remaining}/${limit} remaining, resets at ${resetTime.toISOString()}`,
|
|
497
|
+
details: {
|
|
498
|
+
remaining,
|
|
499
|
+
limit,
|
|
500
|
+
resetTime: resetTime.toISOString(),
|
|
501
|
+
}
|
|
502
|
+
});
|
|
503
|
+
} catch (error) {
|
|
504
|
+
health.checks.push({
|
|
505
|
+
name: 'github_rate_limit',
|
|
506
|
+
status: 'unknown',
|
|
507
|
+
message: 'Could not check GitHub rate limit',
|
|
508
|
+
});
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
logger.info(`Health check completed: ${health.status}`);
|
|
513
|
+
|
|
514
|
+
return health;
|
|
515
|
+
|
|
516
|
+
} catch (error) {
|
|
517
|
+
throw error;
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
/**
|
|
522
|
+
* Display project statistics in human-readable format
|
|
523
|
+
* @param {Object} stats - Project statistics
|
|
524
|
+
* @param {boolean} detailed - Whether to show detailed stats
|
|
525
|
+
*/
|
|
526
|
+
function displayProjectStats(stats, detailed) {
|
|
527
|
+
console.log(chalk.cyan.bold(`📊 Project Statistics: ${stats.project.name}`));
|
|
528
|
+
console.log('');
|
|
529
|
+
|
|
530
|
+
// Basic info
|
|
531
|
+
console.log(chalk.blue('📋 Basic Information:'));
|
|
532
|
+
console.log(` Project ID: ${stats.project.id}`);
|
|
533
|
+
console.log(` Plan: ${chalk.yellow(stats.project.plan)}`);
|
|
534
|
+
console.log(` Created: ${new Date(stats.project.createdAt).toLocaleDateString()}`);
|
|
535
|
+
console.log('');
|
|
536
|
+
|
|
537
|
+
// Repositories
|
|
538
|
+
console.log(chalk.blue('📦 Repositories:'));
|
|
539
|
+
console.log(` Total: ${chalk.yellow(stats.repositories.total)}`);
|
|
540
|
+
console.log('');
|
|
541
|
+
|
|
542
|
+
// Changelogs
|
|
543
|
+
console.log(chalk.blue('📝 Changelogs:'));
|
|
544
|
+
console.log(` Total: ${chalk.yellow(stats.changelogs.total)}`);
|
|
545
|
+
console.log(` Generated: ${chalk.yellow(stats.changelogs.generated)}`);
|
|
546
|
+
console.log(` Public: ${chalk.yellow(stats.changelogs.public)}`);
|
|
547
|
+
console.log(` Average Size: ${chalk.yellow(formatBytes(stats.changelogs.averageSize))}`);
|
|
548
|
+
console.log(` Total Size: ${chalk.yellow(formatBytes(stats.changelogs.totalSize))}`);
|
|
549
|
+
console.log('');
|
|
550
|
+
|
|
551
|
+
// Recent activity
|
|
552
|
+
console.log(chalk.blue('🕐 Recent Activity:'));
|
|
553
|
+
if (stats.recentActivity.length === 0) {
|
|
554
|
+
console.log(' No recent changelogs');
|
|
555
|
+
} else {
|
|
556
|
+
stats.recentActivity.forEach((changelog, index) => {
|
|
557
|
+
console.log(` ${index + 1}. ${changelog.title} (${changelog.format}) - ${new Date(changelog.createdAt).toLocaleDateString()}`);
|
|
558
|
+
});
|
|
559
|
+
}
|
|
560
|
+
console.log('');
|
|
561
|
+
|
|
562
|
+
// Detailed stats
|
|
563
|
+
if (detailed) {
|
|
564
|
+
if (stats.repositories.details && stats.repositories.details.length > 0) {
|
|
565
|
+
console.log(chalk.blue('🔍 Repository Details:'));
|
|
566
|
+
stats.repositories.details.forEach(repo => {
|
|
567
|
+
const statusColor = repo.status === 'active' ? chalk.green : chalk.yellow;
|
|
568
|
+
console.log(` • ${repo.fullName} ${statusColor(`[${repo.status}]`)} ${repo.private ? chalk.red('[PRIVATE]') : ''}`);
|
|
569
|
+
});
|
|
570
|
+
console.log('');
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
if (stats.monthlyBreakdown && stats.monthlyBreakdown.length > 0) {
|
|
574
|
+
console.log(chalk.blue('📅 Monthly Breakdown:'));
|
|
575
|
+
stats.monthlyBreakdown.forEach(month => {
|
|
576
|
+
console.log(` ${month._id.year}-${month._id.month}: ${month.count} changelogs, ${formatBytes(month.size)} total`);
|
|
577
|
+
});
|
|
578
|
+
console.log('');
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
/**
|
|
584
|
+
* Display usage statistics in human-readable format
|
|
585
|
+
* @param {Object} usage - Usage statistics
|
|
586
|
+
*/
|
|
587
|
+
function displayUsageStats(usage) {
|
|
588
|
+
console.log(chalk.cyan.bold('📈 Subscription Usage'));
|
|
589
|
+
console.log('');
|
|
590
|
+
|
|
591
|
+
// Projects
|
|
592
|
+
console.log(chalk.blue('🏗️ Projects:'));
|
|
593
|
+
console.log(` Total: ${chalk.yellow(usage.projects.total)} / ${usage.limits.projects.limit}`);
|
|
594
|
+
console.log(` By Plan:`);
|
|
595
|
+
Object.entries(usage.projects.byPlan).forEach(([plan, count]) => {
|
|
596
|
+
console.log(` ${plan}: ${chalk.yellow(count)}`);
|
|
597
|
+
});
|
|
598
|
+
console.log('');
|
|
599
|
+
|
|
600
|
+
// Repositories
|
|
601
|
+
console.log(chalk.blue('📦 Repositories:'));
|
|
602
|
+
console.log(` Total: ${chalk.yellow(usage.repositories.total)} / ${usage.limits.repositories.limit}`);
|
|
603
|
+
console.log('');
|
|
604
|
+
|
|
605
|
+
// Changelogs
|
|
606
|
+
console.log(chalk.blue('📝 Changelogs:'));
|
|
607
|
+
console.log(` Total: ${chalk.yellow(usage.changelogs.total)} / ${usage.limits.changelogs.limit}`);
|
|
608
|
+
console.log(` Generated: ${chalk.yellow(usage.changelogs.generated)}`);
|
|
609
|
+
console.log(` Public: ${chalk.yellow(usage.changelogs.public)}`);
|
|
610
|
+
console.log(` Total Size: ${chalk.yellow(formatBytes(usage.changelogs.totalSize))}`);
|
|
611
|
+
console.log('');
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
/**
|
|
615
|
+
* Display system information in human-readable format
|
|
616
|
+
* @param {Object} systemInfo - System information
|
|
617
|
+
*/
|
|
618
|
+
function displaySystemInfo(systemInfo) {
|
|
619
|
+
console.log(chalk.cyan.bold('🖥️ System Information'));
|
|
620
|
+
console.log('');
|
|
621
|
+
|
|
622
|
+
// CLI Info
|
|
623
|
+
console.log(chalk.blue('🔧 CLI:'));
|
|
624
|
+
console.log(` Version: ${chalk.yellow(systemInfo.cli.version)}`);
|
|
625
|
+
console.log(` Node.js: ${chalk.yellow(systemInfo.cli.nodeVersion)}`);
|
|
626
|
+
console.log(` Platform: ${chalk.yellow(systemInfo.cli.platform)} ${chalk.yellow(systemInfo.cli.arch)}`);
|
|
627
|
+
console.log(` Working Directory: ${chalk.yellow(systemInfo.cli.cwd)}`);
|
|
628
|
+
console.log('');
|
|
629
|
+
|
|
630
|
+
// Database
|
|
631
|
+
console.log(chalk.blue('🗄️ Database:'));
|
|
632
|
+
const dbStatusColor = systemInfo.database.status === 'connected' ? chalk.green : chalk.red;
|
|
633
|
+
console.log(` Status: ${dbStatusColor(systemInfo.database.status)}`);
|
|
634
|
+
console.log(` URI: ${chalk.yellow(systemInfo.database.uri)}`);
|
|
635
|
+
console.log('');
|
|
636
|
+
|
|
637
|
+
// Authentication
|
|
638
|
+
console.log(chalk.blue('🔐 Authentication:'));
|
|
639
|
+
const authStatusColor = systemInfo.authentication.authenticated ? chalk.green : chalk.red;
|
|
640
|
+
console.log(` Status: ${authStatusColor(systemInfo.authentication.authenticated ? 'Authenticated' : 'Not Authenticated')}`);
|
|
641
|
+
console.log(` Token: ${chalk.yellow(systemInfo.authentication.tokenPreview)}`);
|
|
642
|
+
console.log('');
|
|
643
|
+
|
|
644
|
+
// Environment
|
|
645
|
+
console.log(chalk.blue('🌍 Environment:'));
|
|
646
|
+
console.log(` Node Environment: ${chalk.yellow(systemInfo.environment.nodeEnv)}`);
|
|
647
|
+
console.log(` Home Directory: ${chalk.yellow(systemInfo.environment.homeDir)}`);
|
|
648
|
+
console.log(` Temp Directory: ${chalk.yellow(systemInfo.environment.tempDir)}`);
|
|
649
|
+
console.log(` Uptime: ${chalk.yellow(formatUptime(systemInfo.environment.uptime))}`);
|
|
650
|
+
console.log('');
|
|
651
|
+
|
|
652
|
+
// Paths
|
|
653
|
+
console.log(chalk.blue('📁 Paths:'));
|
|
654
|
+
console.log(` Config: ${chalk.yellow(systemInfo.paths.config)}`);
|
|
655
|
+
console.log(` Cache: ${chalk.yellow(systemInfo.paths.cache)}`);
|
|
656
|
+
console.log('');
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
/**
|
|
660
|
+
* Display health check results in human-readable format
|
|
661
|
+
* @param {Object} health - Health check results
|
|
662
|
+
*/
|
|
663
|
+
function displayHealthCheck(health) {
|
|
664
|
+
const statusColor = health.status === 'healthy' ? chalk.green : chalk.red;
|
|
665
|
+
console.log(chalk.cyan.bold(`🏥 System Health: ${statusColor(health.status.toUpperCase())}`));
|
|
666
|
+
console.log('');
|
|
667
|
+
|
|
668
|
+
health.checks.forEach(check => {
|
|
669
|
+
const statusColor = check.status === 'healthy' ? chalk.green : check.status === 'warning' ? chalk.yellow : chalk.red;
|
|
670
|
+
console.log(`${statusColor(`• ${check.name}: ${check.status.toUpperCase()}`)}`);
|
|
671
|
+
console.log(` ${chalk.gray(check.message)}`);
|
|
672
|
+
console.log('');
|
|
673
|
+
});
|
|
674
|
+
|
|
675
|
+
console.log(chalk.gray(`Last checked: ${new Date(health.timestamp).toLocaleString()}`));
|
|
676
|
+
console.log('');
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
/**
|
|
680
|
+
* Format bytes to human-readable format
|
|
681
|
+
* @param {number} bytes - Number of bytes
|
|
682
|
+
* @returns {string} Formatted string
|
|
683
|
+
*/
|
|
684
|
+
function formatBytes(bytes) {
|
|
685
|
+
if (bytes === 0) return '0 B';
|
|
686
|
+
const k = 1024;
|
|
687
|
+
const sizes = ['B', 'KB', 'MB', 'GB'];
|
|
688
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
689
|
+
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
/**
|
|
693
|
+
* Format uptime to human-readable format
|
|
694
|
+
* @param {number} seconds - Uptime in seconds
|
|
695
|
+
* @returns {string} Formatted string
|
|
696
|
+
*/
|
|
697
|
+
function formatUptime(seconds) {
|
|
698
|
+
const days = Math.floor(seconds / 86400);
|
|
699
|
+
const hours = Math.floor((seconds % 86400) / 3600);
|
|
700
|
+
const minutes = Math.floor((seconds % 3600) / 60);
|
|
701
|
+
const secs = Math.floor(seconds % 60);
|
|
702
|
+
|
|
703
|
+
return `${days}d ${hours}h ${minutes}m ${secs}s`;
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
module.exports = setupStatsCommands;
|