@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.
Files changed (119) hide show
  1. package/.beads/.br_history/issues.20260314_212352_900045509.jsonl +0 -0
  2. package/.beads/.br_history/issues.20260314_212352_900045509.jsonl.meta.json +1 -0
  3. package/.beads/.br_history/issues.20260314_212353_087140743.jsonl +1 -0
  4. package/.beads/.br_history/issues.20260314_212353_087140743.jsonl.meta.json +1 -0
  5. package/.beads/.br_history/issues.20260314_212353_285881504.jsonl +2 -0
  6. package/.beads/.br_history/issues.20260314_212353_285881504.jsonl.meta.json +1 -0
  7. package/.beads/.br_history/issues.20260314_212353_473915419.jsonl +3 -0
  8. package/.beads/.br_history/issues.20260314_212353_473915419.jsonl.meta.json +1 -0
  9. package/.beads/.br_history/issues.20260314_212353_659476307.jsonl +4 -0
  10. package/.beads/.br_history/issues.20260314_212353_659476307.jsonl.meta.json +1 -0
  11. package/.beads/.br_history/issues.20260314_212353_869998925.jsonl +5 -0
  12. package/.beads/.br_history/issues.20260314_212353_869998925.jsonl.meta.json +1 -0
  13. package/.beads/.br_history/issues.20260314_212354_054785029.jsonl +6 -0
  14. package/.beads/.br_history/issues.20260314_212354_054785029.jsonl.meta.json +1 -0
  15. package/.beads/.br_history/issues.20260314_213336_175893691.jsonl +7 -0
  16. package/.beads/.br_history/issues.20260314_213336_175893691.jsonl.meta.json +1 -0
  17. package/.beads/.br_history/issues.20260314_213336_338509797.jsonl +7 -0
  18. package/.beads/.br_history/issues.20260314_213336_338509797.jsonl.meta.json +1 -0
  19. package/.beads/.br_history/issues.20260314_213336_515443192.jsonl +7 -0
  20. package/.beads/.br_history/issues.20260314_213336_515443192.jsonl.meta.json +1 -0
  21. package/.beads/.br_history/issues.20260314_213336_676417592.jsonl +7 -0
  22. package/.beads/.br_history/issues.20260314_213336_676417592.jsonl.meta.json +1 -0
  23. package/.beads/.br_history/issues.20260314_213336_839182422.jsonl +7 -0
  24. package/.beads/.br_history/issues.20260314_213336_839182422.jsonl.meta.json +1 -0
  25. package/.beads/.br_history/issues.20260314_213337_004349113.jsonl +7 -0
  26. package/.beads/.br_history/issues.20260314_213337_004349113.jsonl.meta.json +1 -0
  27. package/.beads/.br_history/issues.20260314_213337_179824080.jsonl +7 -0
  28. package/.beads/.br_history/issues.20260314_213337_179824080.jsonl.meta.json +1 -0
  29. package/.beads/.br_history/issues.20260314_213701_705075332.jsonl +7 -0
  30. package/.beads/.br_history/issues.20260314_213701_705075332.jsonl.meta.json +1 -0
  31. package/.beads/.br_history/issues.20260314_213706_783128702.jsonl +8 -0
  32. package/.beads/.br_history/issues.20260314_213706_783128702.jsonl.meta.json +1 -0
  33. package/.beads/config.yaml +4 -0
  34. package/.beads/issues.jsonl +8 -0
  35. package/.beads/metadata.json +4 -0
  36. package/.env.example +8 -0
  37. package/autochangelog/.env.example +36 -0
  38. package/autochangelog/README.md +412 -0
  39. package/autochangelog/config/database.js +27 -0
  40. package/autochangelog/package.json +47 -0
  41. package/autochangelog/public/landing.html +581 -0
  42. package/autochangelog/server.js +104 -0
  43. package/autochangelog/src/app.js +181 -0
  44. package/autochangelog/src/config/database.js +26 -0
  45. package/autochangelog/src/controllers/auth.js +488 -0
  46. package/autochangelog/src/controllers/changelog.js +682 -0
  47. package/autochangelog/src/controllers/project.js +580 -0
  48. package/autochangelog/src/controllers/repository.js +780 -0
  49. package/autochangelog/src/middleware/auth.js +386 -0
  50. package/autochangelog/src/models/Changelog.js +443 -0
  51. package/autochangelog/src/models/Project.js +226 -0
  52. package/autochangelog/src/models/Repository.js +366 -0
  53. package/autochangelog/src/models/User.js +223 -0
  54. package/autochangelog/src/routes/auth.routes.js +32 -0
  55. package/autochangelog/src/routes/changelog.routes.js +42 -0
  56. package/autochangelog/src/routes/github-auth.routes.js +102 -0
  57. package/autochangelog/src/routes/project.routes.js +50 -0
  58. package/autochangelog/src/routes/repository.routes.js +54 -0
  59. package/autochangelog/src/services/changelog.js +722 -0
  60. package/autochangelog/src/services/github.js +243 -0
  61. package/autochangelog/utils/logger.js +77 -0
  62. package/autochangelog/views/404.ejs +18 -0
  63. package/autochangelog/views/dashboard.ejs +596 -0
  64. package/autochangelog/views/index.ejs +231 -0
  65. package/autochangelog/views/layouts/main.ejs +44 -0
  66. package/autochangelog/views/login.ejs +104 -0
  67. package/autochangelog/views/partials/footer.ejs +20 -0
  68. package/autochangelog/views/partials/navbar.ejs +51 -0
  69. package/autochangelog/views/register.ejs +109 -0
  70. package/autochangelog-cli/README.md +266 -0
  71. package/autochangelog-cli/bin/autochangelog +120 -0
  72. package/autochangelog-cli/package.json +46 -0
  73. package/autochangelog-cli/src/cli/commands/auth.js +291 -0
  74. package/autochangelog-cli/src/cli/commands/changelog.js +619 -0
  75. package/autochangelog-cli/src/cli/commands/project.js +427 -0
  76. package/autochangelog-cli/src/cli/commands/repo.js +557 -0
  77. package/autochangelog-cli/src/cli/commands/stats.js +706 -0
  78. package/autochangelog-cli/src/cli/utils/config.js +277 -0
  79. package/autochangelog-cli/src/cli/utils/errors.js +307 -0
  80. package/autochangelog-cli/src/cli/utils/logger.js +75 -0
  81. package/autochangelog-cli/src/cli/utils/output.js +357 -0
  82. package/package.json +8 -3
  83. package/plugins/supercli/README.md +108 -0
  84. package/plugins/supercli/plugin.json +123 -0
  85. package/server.js +1 -1
  86. package/src/cli/api.js +380 -0
  87. package/src/cli/direct/agent-utils.js +61 -0
  88. package/src/cli/direct/cli-utils.js +112 -0
  89. package/src/cli/direct/data-seeding.js +307 -0
  90. package/src/cli/direct/db-admin.js +84 -0
  91. package/src/cli/direct/db-advanced.js +372 -0
  92. package/src/cli/direct/db-utils.js +558 -0
  93. package/src/cli/direct/help.js +195 -0
  94. package/src/cli/direct/migration.js +107 -0
  95. package/src/cli/direct/rbac-advanced.js +132 -0
  96. package/src/cli/direct/resources-additional.js +400 -0
  97. package/src/cli/direct/resources-cms-advanced.js +173 -0
  98. package/src/cli/direct/resources-cms.js +247 -0
  99. package/src/cli/direct/resources-core.js +253 -0
  100. package/src/cli/direct/resources-execution.js +367 -0
  101. package/src/cli/direct/resources-health.js +152 -0
  102. package/src/cli/direct/resources-integrations.js +182 -0
  103. package/src/cli/direct/resources-logs.js +204 -0
  104. package/src/cli/direct/resources-org-rbac.js +187 -0
  105. package/src/cli/direct/resources-system.js +236 -0
  106. package/src/cli/direct.js +556 -0
  107. package/src/controllers/admin.controller.js +4 -0
  108. package/src/controllers/auth.controller.js +148 -1
  109. package/src/controllers/waitingList.controller.js +130 -1
  110. package/src/models/RbacRole.js +1 -1
  111. package/src/models/User.js +39 -5
  112. package/src/routes/auth.routes.js +6 -0
  113. package/src/routes/waitingList.routes.js +12 -2
  114. package/src/routes/waitingListAdmin.routes.js +3 -0
  115. package/src/services/email.service.js +1 -0
  116. package/src/services/github.service.js +255 -0
  117. package/src/services/rateLimiter.service.js +29 -1
  118. package/src/services/waitingListJson.service.js +32 -3
  119. package/views/admin-waiting-list.ejs +386 -3
@@ -0,0 +1,580 @@
1
+ const Project = require("../models/Project");
2
+ const Repository = require("../models/Repository");
3
+ const Changelog = require("../models/Changelog");
4
+ const User = require("../models/User");
5
+ const { checkSubscription, checkUsageLimits } = require("../middleware/auth");
6
+ const logger = require("../../utils/logger");
7
+
8
+ // Get user's overall statistics (for dashboard stats cards)
9
+ const getUserStatistics = async (req, res) => {
10
+ try {
11
+ // Get all active projects for the user
12
+ const projects = await Project.findByUser(req.user._id, {
13
+ status: "active",
14
+ limit: 1000, // Get all projects
15
+ skip: 0,
16
+ });
17
+
18
+ // Count total repositories and changelogs
19
+ const projectIds = projects.map((p) => p._id);
20
+
21
+ const [repositoriesCount, changelogsCount] = await Promise.all([
22
+ Repository.countDocuments({
23
+ projectId: { $in: projectIds },
24
+ status: "active",
25
+ }),
26
+ Changelog.countDocuments({
27
+ projectId: { $in: projectIds },
28
+ status: "completed",
29
+ createdAt: { $gte: new Date(new Date().setDate(1)) }, // Current month
30
+ }),
31
+ ]);
32
+
33
+ // Get user's plan (simplified - would integrate with SuperBackend billing in production)
34
+ const user = await User.findById(req.user._id);
35
+ const plan = user?.plan || "free";
36
+
37
+ res.json({
38
+ success: true,
39
+ data: {
40
+ projects: projects.length,
41
+ repositories: repositoriesCount,
42
+ changelogs: changelogsCount,
43
+ plan: plan.charAt(0).toUpperCase() + plan.slice(1),
44
+ },
45
+ });
46
+ } catch (error) {
47
+ logger.error("Get user statistics error:", error);
48
+ res.status(500).json({
49
+ success: false,
50
+ message: "Internal server error.",
51
+ });
52
+ }
53
+ };
54
+
55
+ // Create a new project
56
+ const createProject = async (req, res) => {
57
+ try {
58
+ const { name, description, tags, settings } = req.body;
59
+
60
+ // Validation
61
+ if (!name || name.trim().length === 0) {
62
+ return res.status(400).json({
63
+ success: false,
64
+ message: "Project name is required.",
65
+ });
66
+ }
67
+
68
+ if (name.length > 100) {
69
+ return res.status(400).json({
70
+ success: false,
71
+ message: "Project name cannot exceed 100 characters.",
72
+ });
73
+ }
74
+
75
+ // Check if user can create more projects
76
+ const canCreate = await Project.canCreateProject(req.user._id);
77
+ if (!canCreate) {
78
+ return res.status(403).json({
79
+ success: false,
80
+ message:
81
+ "You have reached your project limit. Upgrade your plan to create more projects.",
82
+ });
83
+ }
84
+
85
+ // Check subscription level for features
86
+ if (
87
+ settings &&
88
+ settings.githubIntegration &&
89
+ settings.githubIntegration.enabled
90
+ ) {
91
+ const subscriptionCheck = checkSubscription("pro");
92
+ if (!subscriptionCheck) {
93
+ return res.status(403).json({
94
+ success: false,
95
+ message: "GitHub integration requires a Pro subscription.",
96
+ });
97
+ }
98
+ }
99
+
100
+ // Create project
101
+ const project = new Project({
102
+ name: name.trim(),
103
+ description: description ? description.trim() : "",
104
+ tags: tags ? tags.map((tag) => tag.toLowerCase().trim()) : [],
105
+ settings: {
106
+ defaultBranch: "main",
107
+ changelogTemplate: "default",
108
+ includeCommitsWithoutPR: true,
109
+ includeMergeCommits: false,
110
+ ...settings,
111
+ },
112
+ ownerId: req.user._id,
113
+ });
114
+
115
+ await project.save();
116
+
117
+ // Update user's usage count (using local User model)
118
+ await User.findByIdAndUpdate(req.user._id, {
119
+ $inc: { "usage.projectsCount": 1 },
120
+ });
121
+
122
+ logger.info(`Project created: ${project.name} by user ${req.user.email}`);
123
+
124
+ res.status(201).json({
125
+ success: true,
126
+ message: "Project created successfully.",
127
+ data: {
128
+ project: project.toJSON(),
129
+ },
130
+ });
131
+ } catch (error) {
132
+ logger.error("Create project error:", error);
133
+ res.status(500).json({
134
+ success: false,
135
+ message: "Internal server error.",
136
+ });
137
+ }
138
+ };
139
+
140
+ // Get all projects for the authenticated user
141
+ const getProjects = async (req, res) => {
142
+ try {
143
+ const { limit = 10, skip = 0, status = "active" } = req.query;
144
+
145
+ const projects = await Project.findByUser(req.user._id, {
146
+ status,
147
+ limit: parseInt(limit),
148
+ skip: parseInt(skip),
149
+ });
150
+
151
+ // Get statistics for each project
152
+ const projectsWithStats = await Promise.all(
153
+ projects.map(async (project) => {
154
+ const stats = await project.getStatistics();
155
+ return {
156
+ ...project.toJSON(),
157
+ statistics: stats,
158
+ };
159
+ }),
160
+ );
161
+
162
+ res.json({
163
+ success: true,
164
+ data: {
165
+ projects: projectsWithStats,
166
+ pagination: {
167
+ limit: parseInt(limit),
168
+ skip: parseInt(skip),
169
+ total: projects.length,
170
+ },
171
+ },
172
+ });
173
+ } catch (error) {
174
+ logger.error("Get projects error:", error);
175
+ res.status(500).json({
176
+ success: false,
177
+ message: "Internal server error.",
178
+ });
179
+ }
180
+ };
181
+
182
+ // Get a specific project by ID
183
+ const getProject = async (req, res) => {
184
+ try {
185
+ const { id } = req.params;
186
+
187
+ const project = await Project.findById(id).populate(
188
+ "ownerId",
189
+ "name email avatar",
190
+ );
191
+
192
+ if (!project) {
193
+ return res.status(404).json({
194
+ success: false,
195
+ message: "Project not found.",
196
+ });
197
+ }
198
+
199
+ // Check ownership
200
+ if (project.ownerId._id.toString() !== req.user._id.toString()) {
201
+ return res.status(403).json({
202
+ success: false,
203
+ message: "Access denied. You do not own this project.",
204
+ });
205
+ }
206
+
207
+ const stats = await project.getStatistics();
208
+
209
+ res.json({
210
+ success: true,
211
+ data: {
212
+ project: {
213
+ ...project.toJSON(),
214
+ statistics: stats,
215
+ },
216
+ },
217
+ });
218
+ } catch (error) {
219
+ logger.error("Get project error:", error);
220
+ res.status(500).json({
221
+ success: false,
222
+ message: "Internal server error.",
223
+ });
224
+ }
225
+ };
226
+
227
+ // Update a project
228
+ const updateProject = async (req, res) => {
229
+ try {
230
+ const { id } = req.params;
231
+ const { name, description, tags, settings, isPublic } = req.body;
232
+
233
+ const project = await Project.findById(id);
234
+ if (!project) {
235
+ return res.status(404).json({
236
+ success: false,
237
+ message: "Project not found.",
238
+ });
239
+ }
240
+
241
+ // Check ownership
242
+ if (project.ownerId.toString() !== req.user._id.toString()) {
243
+ return res.status(403).json({
244
+ success: false,
245
+ message: "Access denied. You do not own this project.",
246
+ });
247
+ }
248
+
249
+ // Update fields
250
+ if (name) {
251
+ if (name.trim().length === 0) {
252
+ return res.status(400).json({
253
+ success: false,
254
+ message: "Project name cannot be empty.",
255
+ });
256
+ }
257
+ if (name.length > 100) {
258
+ return res.status(400).json({
259
+ success: false,
260
+ message: "Project name cannot exceed 100 characters.",
261
+ });
262
+ }
263
+ project.name = name.trim();
264
+ }
265
+
266
+ if (description !== undefined) {
267
+ project.description = description ? description.trim() : "";
268
+ }
269
+
270
+ if (tags !== undefined) {
271
+ project.tags = tags.map((tag) => tag.toLowerCase().trim());
272
+ }
273
+
274
+ if (settings) {
275
+ project.settings = { ...project.settings, ...settings };
276
+ }
277
+
278
+ if (isPublic !== undefined) {
279
+ project.isPublic = isPublic;
280
+ }
281
+
282
+ await project.save();
283
+
284
+ logger.info(`Project updated: ${project.name} by user ${req.user.email}`);
285
+
286
+ res.json({
287
+ success: true,
288
+ message: "Project updated successfully.",
289
+ data: {
290
+ project: project.toJSON(),
291
+ },
292
+ });
293
+ } catch (error) {
294
+ logger.error("Update project error:", error);
295
+ res.status(500).json({
296
+ success: false,
297
+ message: "Internal server error.",
298
+ });
299
+ }
300
+ };
301
+
302
+ // Delete a project (soft delete)
303
+ const deleteProject = async (req, res) => {
304
+ try {
305
+ const { id } = req.params;
306
+
307
+ const project = await Project.findById(id);
308
+ if (!project) {
309
+ return res.status(404).json({
310
+ success: false,
311
+ message: "Project not found.",
312
+ });
313
+ }
314
+
315
+ // Check ownership
316
+ if (project.ownerId.toString() !== req.user._id.toString()) {
317
+ return res.status(403).json({
318
+ success: false,
319
+ message: "Access denied. You do not own this project.",
320
+ });
321
+ }
322
+
323
+ // Soft delete - set status to 'deleted'
324
+ project.status = "deleted";
325
+ await project.save();
326
+
327
+ // Update user's usage count
328
+ await User.findByIdAndUpdate(req.user._id, {
329
+ $inc: { "usage.projectsCount": -1 },
330
+ });
331
+
332
+ logger.info(`Project deleted: ${project.name} by user ${req.user.email}`);
333
+
334
+ res.json({
335
+ success: true,
336
+ message: "Project deleted successfully.",
337
+ });
338
+ } catch (error) {
339
+ logger.error("Delete project error:", error);
340
+ res.status(500).json({
341
+ success: false,
342
+ message: "Internal server error.",
343
+ });
344
+ }
345
+ };
346
+
347
+ // Archive a project
348
+ const archiveProject = async (req, res) => {
349
+ try {
350
+ const { id } = req.params;
351
+
352
+ const project = await Project.findById(id);
353
+ if (!project) {
354
+ return res.status(404).json({
355
+ success: false,
356
+ message: "Project not found.",
357
+ });
358
+ }
359
+
360
+ // Check ownership
361
+ if (project.ownerId.toString() !== req.user._id.toString()) {
362
+ return res.status(403).json({
363
+ success: false,
364
+ message: "Access denied. You do not own this project.",
365
+ });
366
+ }
367
+
368
+ // Archive project
369
+ project.status = "archived";
370
+ await project.save();
371
+
372
+ logger.info(`Project archived: ${project.name} by user ${req.user.email}`);
373
+
374
+ res.json({
375
+ success: true,
376
+ message: "Project archived successfully.",
377
+ });
378
+ } catch (error) {
379
+ logger.error("Archive project error:", error);
380
+ res.status(500).json({
381
+ success: false,
382
+ message: "Internal server error.",
383
+ });
384
+ }
385
+ };
386
+
387
+ // Get project statistics
388
+ const getProjectStatistics = async (req, res) => {
389
+ try {
390
+ const { id } = req.params;
391
+
392
+ const project = await Project.findById(id);
393
+ if (!project) {
394
+ return res.status(404).json({
395
+ success: false,
396
+ message: "Project not found.",
397
+ });
398
+ }
399
+
400
+ // Check ownership
401
+ if (project.ownerId.toString() !== req.user._id.toString()) {
402
+ return res.status(403).json({
403
+ success: false,
404
+ message: "Access denied. You do not own this project.",
405
+ });
406
+ }
407
+
408
+ const stats = await project.getStatistics();
409
+
410
+ res.json({
411
+ success: true,
412
+ data: {
413
+ statistics: stats,
414
+ },
415
+ });
416
+ } catch (error) {
417
+ logger.error("Get project statistics error:", error);
418
+ res.status(500).json({
419
+ success: false,
420
+ message: "Internal server error.",
421
+ });
422
+ }
423
+ };
424
+
425
+ // Search projects
426
+ const searchProjects = async (req, res) => {
427
+ try {
428
+ const { q, tags, status = "active" } = req.query;
429
+
430
+ if (!q && !tags) {
431
+ return res.status(400).json({
432
+ success: false,
433
+ message: "Search query or tags are required.",
434
+ });
435
+ }
436
+
437
+ const query = {
438
+ ownerId: req.user._id,
439
+ status,
440
+ };
441
+
442
+ if (q) {
443
+ query.$or = [
444
+ { name: { $regex: q, $options: "i" } },
445
+ { description: { $regex: q, $options: "i" } },
446
+ ];
447
+ }
448
+
449
+ if (tags) {
450
+ const tagArray = Array.isArray(tags) ? tags : [tags];
451
+ query.tags = { $in: tagArray.map((tag) => tag.toLowerCase()) };
452
+ }
453
+
454
+ const projects = await Project.find(query)
455
+ .populate("ownerId", "name email avatar")
456
+ .sort({ updatedAt: -1 });
457
+
458
+ res.json({
459
+ success: true,
460
+ data: {
461
+ projects: projects.map((p) => p.toJSON()),
462
+ },
463
+ });
464
+ } catch (error) {
465
+ logger.error("Search projects error:", error);
466
+ res.status(500).json({
467
+ success: false,
468
+ message: "Internal server error.",
469
+ });
470
+ }
471
+ };
472
+
473
+ // Get project repositories
474
+ const getProjectRepositories = async (req, res) => {
475
+ try {
476
+ const { id } = req.params;
477
+ const { limit = 20, skip = 0 } = req.query;
478
+
479
+ const project = await Project.findById(id);
480
+ if (!project) {
481
+ return res.status(404).json({
482
+ success: false,
483
+ message: "Project not found.",
484
+ });
485
+ }
486
+
487
+ // Check ownership
488
+ if (project.ownerId.toString() !== req.user._id.toString()) {
489
+ return res.status(403).json({
490
+ success: false,
491
+ message: "Access denied. You do not own this project.",
492
+ });
493
+ }
494
+
495
+ const repositories = await Repository.findByProject(id, {
496
+ limit: parseInt(limit),
497
+ skip: parseInt(skip),
498
+ });
499
+
500
+ res.json({
501
+ success: true,
502
+ data: {
503
+ repositories: repositories.map((r) => r.toJSON()),
504
+ pagination: {
505
+ limit: parseInt(limit),
506
+ skip: parseInt(skip),
507
+ total: repositories.length,
508
+ },
509
+ },
510
+ });
511
+ } catch (error) {
512
+ logger.error("Get project repositories error:", error);
513
+ res.status(500).json({
514
+ success: false,
515
+ message: "Internal server error.",
516
+ });
517
+ }
518
+ };
519
+
520
+ // Get project changelogs
521
+ const getProjectChangelogs = async (req, res) => {
522
+ try {
523
+ const { id } = req.params;
524
+ const { limit = 20, skip = 0, status = ["completed"] } = req.query;
525
+
526
+ const project = await Project.findById(id);
527
+ if (!project) {
528
+ return res.status(404).json({
529
+ success: false,
530
+ message: "Project not found.",
531
+ });
532
+ }
533
+
534
+ // Check ownership
535
+ if (project.ownerId.toString() !== req.user._id.toString()) {
536
+ return res.status(403).json({
537
+ success: false,
538
+ message: "Access denied. You do not own this project.",
539
+ });
540
+ }
541
+
542
+ const changelogs = await Changelog.findByProject(id, {
543
+ status,
544
+ limit: parseInt(limit),
545
+ skip: parseInt(skip),
546
+ });
547
+
548
+ res.json({
549
+ success: true,
550
+ data: {
551
+ changelogs: changelogs.map((c) => c.toJSON()),
552
+ pagination: {
553
+ limit: parseInt(limit),
554
+ skip: parseInt(skip),
555
+ total: changelogs.length,
556
+ },
557
+ },
558
+ });
559
+ } catch (error) {
560
+ logger.error("Get project changelogs error:", error);
561
+ res.status(500).json({
562
+ success: false,
563
+ message: "Internal server error.",
564
+ });
565
+ }
566
+ };
567
+
568
+ module.exports = {
569
+ createProject,
570
+ getProjects,
571
+ getProject,
572
+ updateProject,
573
+ deleteProject,
574
+ archiveProject,
575
+ getProjectStatistics,
576
+ searchProjects,
577
+ getProjectRepositories,
578
+ getProjectChangelogs,
579
+ getUserStatistics,
580
+ };