@intranefr/superbackend 1.6.7 → 1.7.8
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 +9 -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,443 @@
|
|
|
1
|
+
const mongoose = require("mongoose");
|
|
2
|
+
|
|
3
|
+
const changelogSchema = new mongoose.Schema(
|
|
4
|
+
{
|
|
5
|
+
projectId: {
|
|
6
|
+
type: mongoose.Schema.Types.ObjectId,
|
|
7
|
+
ref: "Project",
|
|
8
|
+
required: true,
|
|
9
|
+
},
|
|
10
|
+
repositoryId: {
|
|
11
|
+
type: mongoose.Schema.Types.ObjectId,
|
|
12
|
+
ref: "Repository",
|
|
13
|
+
required: true,
|
|
14
|
+
},
|
|
15
|
+
|
|
16
|
+
// Changelog Information
|
|
17
|
+
title: {
|
|
18
|
+
type: String,
|
|
19
|
+
required: true,
|
|
20
|
+
trim: true,
|
|
21
|
+
maxlength: [200, "Title cannot exceed 200 characters"],
|
|
22
|
+
},
|
|
23
|
+
description: {
|
|
24
|
+
type: String,
|
|
25
|
+
trim: true,
|
|
26
|
+
maxlength: [500, "Description cannot exceed 500 characters"],
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
// Time Period
|
|
30
|
+
month: {
|
|
31
|
+
type: String,
|
|
32
|
+
required: true,
|
|
33
|
+
enum: [
|
|
34
|
+
"01",
|
|
35
|
+
"02",
|
|
36
|
+
"03",
|
|
37
|
+
"04",
|
|
38
|
+
"05",
|
|
39
|
+
"06",
|
|
40
|
+
"07",
|
|
41
|
+
"08",
|
|
42
|
+
"09",
|
|
43
|
+
"10",
|
|
44
|
+
"11",
|
|
45
|
+
"12",
|
|
46
|
+
],
|
|
47
|
+
},
|
|
48
|
+
year: {
|
|
49
|
+
type: Number,
|
|
50
|
+
required: true,
|
|
51
|
+
min: [2000, "Year must be 2000 or later"],
|
|
52
|
+
max: [2100, "Year must be 2100 or earlier"],
|
|
53
|
+
index: true,
|
|
54
|
+
},
|
|
55
|
+
|
|
56
|
+
// Content
|
|
57
|
+
content: {
|
|
58
|
+
type: String,
|
|
59
|
+
required: true,
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
// Format and Storage
|
|
63
|
+
format: {
|
|
64
|
+
type: String,
|
|
65
|
+
enum: ["markdown", "html", "json"],
|
|
66
|
+
default: "markdown",
|
|
67
|
+
},
|
|
68
|
+
downloadUrl: {
|
|
69
|
+
type: String,
|
|
70
|
+
trim: true,
|
|
71
|
+
},
|
|
72
|
+
fileSize: {
|
|
73
|
+
type: Number,
|
|
74
|
+
default: 0,
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
// Generation Status
|
|
78
|
+
status: {
|
|
79
|
+
type: String,
|
|
80
|
+
enum: ["pending", "generating", "completed", "failed"],
|
|
81
|
+
default: "pending",
|
|
82
|
+
},
|
|
83
|
+
error: {
|
|
84
|
+
type: String,
|
|
85
|
+
maxlength: [1000, "Error message cannot exceed 1000 characters"],
|
|
86
|
+
},
|
|
87
|
+
|
|
88
|
+
// Generation Metadata
|
|
89
|
+
generatedAt: {
|
|
90
|
+
type: Date,
|
|
91
|
+
},
|
|
92
|
+
generatedBy: {
|
|
93
|
+
type: mongoose.Schema.Types.ObjectId,
|
|
94
|
+
ref: "users", // SuperBackend User model
|
|
95
|
+
},
|
|
96
|
+
generationTime: {
|
|
97
|
+
type: Number, // in milliseconds
|
|
98
|
+
},
|
|
99
|
+
|
|
100
|
+
// Content Metadata
|
|
101
|
+
metadata: {
|
|
102
|
+
totalCommits: {
|
|
103
|
+
type: Number,
|
|
104
|
+
default: 0,
|
|
105
|
+
},
|
|
106
|
+
totalPRs: {
|
|
107
|
+
type: Number,
|
|
108
|
+
default: 0,
|
|
109
|
+
},
|
|
110
|
+
totalIssues: {
|
|
111
|
+
type: Number,
|
|
112
|
+
default: 0,
|
|
113
|
+
},
|
|
114
|
+
categories: [
|
|
115
|
+
{
|
|
116
|
+
type: String,
|
|
117
|
+
enum: [
|
|
118
|
+
"feat",
|
|
119
|
+
"feature",
|
|
120
|
+
"enhancement",
|
|
121
|
+
"fix",
|
|
122
|
+
"bugfix",
|
|
123
|
+
"bug",
|
|
124
|
+
"docs",
|
|
125
|
+
"documentation",
|
|
126
|
+
"style",
|
|
127
|
+
"formatting",
|
|
128
|
+
"refactor",
|
|
129
|
+
"refactoring",
|
|
130
|
+
"test",
|
|
131
|
+
"testing",
|
|
132
|
+
"chore",
|
|
133
|
+
"maintenance",
|
|
134
|
+
"perf",
|
|
135
|
+
"performance",
|
|
136
|
+
"ci",
|
|
137
|
+
"continuous-integration",
|
|
138
|
+
"deps",
|
|
139
|
+
"dependencies",
|
|
140
|
+
],
|
|
141
|
+
},
|
|
142
|
+
],
|
|
143
|
+
authors: [
|
|
144
|
+
{
|
|
145
|
+
name: String,
|
|
146
|
+
email: String,
|
|
147
|
+
commits: Number,
|
|
148
|
+
},
|
|
149
|
+
],
|
|
150
|
+
dateRange: {
|
|
151
|
+
start: Date,
|
|
152
|
+
end: Date,
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
|
|
156
|
+
// GitHub Integration
|
|
157
|
+
github: {
|
|
158
|
+
releaseUrl: {
|
|
159
|
+
type: String,
|
|
160
|
+
trim: true,
|
|
161
|
+
},
|
|
162
|
+
releaseTag: {
|
|
163
|
+
type: String,
|
|
164
|
+
trim: true,
|
|
165
|
+
},
|
|
166
|
+
releaseNotes: {
|
|
167
|
+
type: String,
|
|
168
|
+
},
|
|
169
|
+
autoCreatedRelease: {
|
|
170
|
+
type: Boolean,
|
|
171
|
+
default: false,
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
|
|
175
|
+
// Sharing and Privacy
|
|
176
|
+
isPublic: {
|
|
177
|
+
type: Boolean,
|
|
178
|
+
default: false,
|
|
179
|
+
},
|
|
180
|
+
shareToken: {
|
|
181
|
+
type: String,
|
|
182
|
+
unique: true,
|
|
183
|
+
sparse: true,
|
|
184
|
+
},
|
|
185
|
+
viewCount: {
|
|
186
|
+
type: Number,
|
|
187
|
+
default: 0,
|
|
188
|
+
},
|
|
189
|
+
lastViewedAt: {
|
|
190
|
+
type: Date,
|
|
191
|
+
},
|
|
192
|
+
|
|
193
|
+
// Template and Customization
|
|
194
|
+
template: {
|
|
195
|
+
type: String,
|
|
196
|
+
enum: ["default", "angular", "conventional", "custom"],
|
|
197
|
+
default: "default",
|
|
198
|
+
},
|
|
199
|
+
customTemplate: {
|
|
200
|
+
type: String,
|
|
201
|
+
},
|
|
202
|
+
|
|
203
|
+
// Version Information
|
|
204
|
+
version: {
|
|
205
|
+
major: {
|
|
206
|
+
type: Number,
|
|
207
|
+
default: 0,
|
|
208
|
+
},
|
|
209
|
+
minor: {
|
|
210
|
+
type: Number,
|
|
211
|
+
default: 0,
|
|
212
|
+
},
|
|
213
|
+
patch: {
|
|
214
|
+
type: Number,
|
|
215
|
+
default: 0,
|
|
216
|
+
},
|
|
217
|
+
preRelease: {
|
|
218
|
+
type: String,
|
|
219
|
+
},
|
|
220
|
+
build: {
|
|
221
|
+
type: String,
|
|
222
|
+
},
|
|
223
|
+
},
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
timestamps: true,
|
|
227
|
+
},
|
|
228
|
+
);
|
|
229
|
+
|
|
230
|
+
// Compound indexes
|
|
231
|
+
changelogSchema.index({ projectId: 1, month: 1, year: 1 }, { unique: true });
|
|
232
|
+
changelogSchema.index({ repositoryId: 1, month: 1, year: 1 }, { unique: true });
|
|
233
|
+
changelogSchema.index({ status: 1, generatedAt: 1 });
|
|
234
|
+
changelogSchema.index({ generatedBy: 1, createdAt: -1 });
|
|
235
|
+
|
|
236
|
+
// Pre-save middleware to generate share token
|
|
237
|
+
changelogSchema.pre("save", function (next) {
|
|
238
|
+
if (this.isNew && this.isPublic && !this.shareToken) {
|
|
239
|
+
const crypto = require("crypto");
|
|
240
|
+
this.shareToken = crypto.randomBytes(16).toString("hex");
|
|
241
|
+
}
|
|
242
|
+
next();
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
// Static method to find changelogs by project
|
|
246
|
+
changelogSchema.statics.findByProject = function (projectId, options = {}) {
|
|
247
|
+
const {
|
|
248
|
+
status = ["completed"],
|
|
249
|
+
limit = 20,
|
|
250
|
+
skip = 0,
|
|
251
|
+
sortBy = "createdAt",
|
|
252
|
+
sortOrder = -1,
|
|
253
|
+
} = options;
|
|
254
|
+
|
|
255
|
+
return this.find({
|
|
256
|
+
projectId,
|
|
257
|
+
status: { $in: Array.isArray(status) ? status : [status] },
|
|
258
|
+
})
|
|
259
|
+
.sort({ [sortBy]: sortOrder })
|
|
260
|
+
.limit(limit)
|
|
261
|
+
.skip(skip)
|
|
262
|
+
.populate("repositoryId", "name githubRepoName")
|
|
263
|
+
.populate("generatedBy", "name email");
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
// Static method to find changelogs by repository
|
|
267
|
+
changelogSchema.statics.findByRepository = function (
|
|
268
|
+
repositoryId,
|
|
269
|
+
options = {},
|
|
270
|
+
) {
|
|
271
|
+
const {
|
|
272
|
+
status = ["completed"],
|
|
273
|
+
limit = 20,
|
|
274
|
+
skip = 0,
|
|
275
|
+
sortBy = "createdAt",
|
|
276
|
+
sortOrder = -1,
|
|
277
|
+
} = options;
|
|
278
|
+
|
|
279
|
+
return this.find({
|
|
280
|
+
repositoryId,
|
|
281
|
+
status: { $in: Array.isArray(status) ? status : [status] },
|
|
282
|
+
})
|
|
283
|
+
.sort({ [sortBy]: sortOrder })
|
|
284
|
+
.limit(limit)
|
|
285
|
+
.skip(skip)
|
|
286
|
+
.populate("projectId", "name slug")
|
|
287
|
+
.populate("generatedBy", "name email");
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
// Static method to get available months for a repository
|
|
291
|
+
changelogSchema.statics.getAvailableMonths = async function (repositoryId) {
|
|
292
|
+
const repository = await mongoose.model("Repository").findById(repositoryId);
|
|
293
|
+
if (!repository) {
|
|
294
|
+
throw new Error("Repository not found");
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// Get commit history from repository
|
|
298
|
+
const commits = await repository.fetchCommitHistory();
|
|
299
|
+
|
|
300
|
+
// Get already generated changelogs
|
|
301
|
+
const generatedChangelogs = await this.find({
|
|
302
|
+
repositoryId,
|
|
303
|
+
status: "completed",
|
|
304
|
+
})
|
|
305
|
+
.select("month year")
|
|
306
|
+
.lean();
|
|
307
|
+
|
|
308
|
+
const generatedMonths = new Set(
|
|
309
|
+
generatedChangelogs.map((c) => `${c.year}-${c.month}`),
|
|
310
|
+
);
|
|
311
|
+
|
|
312
|
+
// Extract unique months from commits
|
|
313
|
+
const availableMonths = new Set();
|
|
314
|
+
|
|
315
|
+
commits.forEach((commit) => {
|
|
316
|
+
const date = new Date(commit.date);
|
|
317
|
+
const year = date.getFullYear();
|
|
318
|
+
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
319
|
+
const key = `${year}-${month}`;
|
|
320
|
+
availableMonths.add(key);
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
// Return months that have commits but no generated changelog
|
|
324
|
+
const result = Array.from(availableMonths)
|
|
325
|
+
.filter((month) => !generatedMonths.has(month))
|
|
326
|
+
.sort()
|
|
327
|
+
.reverse();
|
|
328
|
+
|
|
329
|
+
return result;
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
// Static method to check if changelog exists for a specific month/year
|
|
333
|
+
changelogSchema.statics.existsForPeriod = function (repositoryId, month, year) {
|
|
334
|
+
return this.exists({
|
|
335
|
+
repositoryId,
|
|
336
|
+
month,
|
|
337
|
+
year,
|
|
338
|
+
status: { $ne: "deleted" },
|
|
339
|
+
});
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
// Instance method to update generation status
|
|
343
|
+
changelogSchema.methods.updateStatus = async function (status, error = null) {
|
|
344
|
+
const updates = {
|
|
345
|
+
status,
|
|
346
|
+
generatedAt: status === "completed" ? new Date() : this.generatedAt,
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
if (error) {
|
|
350
|
+
updates.error = error;
|
|
351
|
+
updates.status = "failed";
|
|
352
|
+
} else {
|
|
353
|
+
updates.error = null;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
return this.updateOne(updates);
|
|
357
|
+
};
|
|
358
|
+
|
|
359
|
+
// Instance method to update content and metadata
|
|
360
|
+
changelogSchema.methods.updateContent = async function (
|
|
361
|
+
content,
|
|
362
|
+
metadata = {},
|
|
363
|
+
) {
|
|
364
|
+
const updates = {
|
|
365
|
+
content,
|
|
366
|
+
"metadata.totalCommits":
|
|
367
|
+
metadata.totalCommits || this.metadata.totalCommits,
|
|
368
|
+
"metadata.totalPRs": metadata.totalPRs || this.metadata.totalPRs,
|
|
369
|
+
"metadata.totalIssues": metadata.totalIssues || this.metadata.totalIssues,
|
|
370
|
+
"metadata.categories": metadata.categories || this.metadata.categories,
|
|
371
|
+
"metadata.authors": metadata.authors || this.metadata.authors,
|
|
372
|
+
"metadata.dateRange": metadata.dateRange || this.metadata.dateRange,
|
|
373
|
+
};
|
|
374
|
+
|
|
375
|
+
return this.updateOne(updates);
|
|
376
|
+
};
|
|
377
|
+
|
|
378
|
+
// Instance method to increment view count
|
|
379
|
+
changelogSchema.methods.incrementViewCount = async function () {
|
|
380
|
+
return this.updateOne({
|
|
381
|
+
$inc: { viewCount: 1 },
|
|
382
|
+
$set: { lastViewedAt: new Date() },
|
|
383
|
+
});
|
|
384
|
+
};
|
|
385
|
+
|
|
386
|
+
// Instance method to get changelog statistics
|
|
387
|
+
changelogSchema.methods.getStatistics = async function () {
|
|
388
|
+
const repository = await mongoose
|
|
389
|
+
.model("Repository")
|
|
390
|
+
.findById(this.repositoryId);
|
|
391
|
+
const project = await mongoose.model("Project").findById(this.projectId);
|
|
392
|
+
|
|
393
|
+
return {
|
|
394
|
+
project: {
|
|
395
|
+
name: project?.name,
|
|
396
|
+
slug: project?.slug,
|
|
397
|
+
},
|
|
398
|
+
repository: {
|
|
399
|
+
name: repository?.name,
|
|
400
|
+
githubRepoName: repository?.githubRepoName,
|
|
401
|
+
},
|
|
402
|
+
changelog: {
|
|
403
|
+
title: this.title,
|
|
404
|
+
month: this.month,
|
|
405
|
+
year: this.year,
|
|
406
|
+
status: this.status,
|
|
407
|
+
format: this.format,
|
|
408
|
+
viewCount: this.viewCount,
|
|
409
|
+
generatedAt: this.generatedAt,
|
|
410
|
+
generationTime: this.generationTime,
|
|
411
|
+
},
|
|
412
|
+
content: {
|
|
413
|
+
totalCommits: this.metadata.totalCommits,
|
|
414
|
+
totalPRs: this.metadata.totalPRs,
|
|
415
|
+
totalIssues: this.metadata.totalIssues,
|
|
416
|
+
categories: this.metadata.categories,
|
|
417
|
+
authors: this.metadata.authors,
|
|
418
|
+
dateRange: this.metadata.dateRange,
|
|
419
|
+
},
|
|
420
|
+
};
|
|
421
|
+
};
|
|
422
|
+
|
|
423
|
+
// Instance method to generate version number
|
|
424
|
+
changelogSchema.methods.generateVersion = function () {
|
|
425
|
+
const major = this.metadata.categories.includes("feat") ? 1 : 0;
|
|
426
|
+
const minor = this.metadata.categories.includes("fix") ? 1 : 0;
|
|
427
|
+
const patch = this.metadata.categories.includes("docs") ? 1 : 0;
|
|
428
|
+
|
|
429
|
+
return {
|
|
430
|
+
major: this.version.major + major,
|
|
431
|
+
minor: this.version.minor + minor,
|
|
432
|
+
patch: this.version.patch + patch,
|
|
433
|
+
};
|
|
434
|
+
};
|
|
435
|
+
|
|
436
|
+
// Clean up response
|
|
437
|
+
changelogSchema.methods.toJSON = function () {
|
|
438
|
+
const obj = this.toObject();
|
|
439
|
+
delete obj.__v;
|
|
440
|
+
return obj;
|
|
441
|
+
};
|
|
442
|
+
|
|
443
|
+
module.exports = mongoose.model("Changelog", changelogSchema);
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
const mongoose = require("mongoose");
|
|
2
|
+
|
|
3
|
+
const projectSchema = new mongoose.Schema(
|
|
4
|
+
{
|
|
5
|
+
name: {
|
|
6
|
+
type: String,
|
|
7
|
+
required: [true, "Project name is required"],
|
|
8
|
+
trim: true,
|
|
9
|
+
maxlength: [100, "Project name cannot exceed 100 characters"],
|
|
10
|
+
},
|
|
11
|
+
description: {
|
|
12
|
+
type: String,
|
|
13
|
+
trim: true,
|
|
14
|
+
maxlength: [500, "Description cannot exceed 500 characters"],
|
|
15
|
+
},
|
|
16
|
+
slug: {
|
|
17
|
+
type: String,
|
|
18
|
+
unique: true,
|
|
19
|
+
lowercase: true,
|
|
20
|
+
trim: true,
|
|
21
|
+
},
|
|
22
|
+
|
|
23
|
+
// Ownership - uses SuperBackend's User and Organization models
|
|
24
|
+
ownerId: {
|
|
25
|
+
type: mongoose.Schema.Types.ObjectId,
|
|
26
|
+
ref: "users", // SuperBackend User model
|
|
27
|
+
required: true,
|
|
28
|
+
},
|
|
29
|
+
organizationId: {
|
|
30
|
+
type: mongoose.Schema.Types.ObjectId,
|
|
31
|
+
ref: "organizations", // SuperBackend Organization model
|
|
32
|
+
},
|
|
33
|
+
|
|
34
|
+
// Status
|
|
35
|
+
status: {
|
|
36
|
+
type: String,
|
|
37
|
+
enum: ["active", "archived", "deleted"],
|
|
38
|
+
default: "active",
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
// Settings
|
|
42
|
+
settings: {
|
|
43
|
+
defaultBranch: {
|
|
44
|
+
type: String,
|
|
45
|
+
default: "main",
|
|
46
|
+
},
|
|
47
|
+
changelogTemplate: {
|
|
48
|
+
type: String,
|
|
49
|
+
enum: ["default", "angular", "conventional"],
|
|
50
|
+
default: "default",
|
|
51
|
+
},
|
|
52
|
+
includeCommitsWithoutPR: {
|
|
53
|
+
type: Boolean,
|
|
54
|
+
default: true,
|
|
55
|
+
},
|
|
56
|
+
includeMergeCommits: {
|
|
57
|
+
type: Boolean,
|
|
58
|
+
default: false,
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
// Metadata
|
|
63
|
+
metadata: {
|
|
64
|
+
createdAt: {
|
|
65
|
+
type: Date,
|
|
66
|
+
default: Date.now,
|
|
67
|
+
},
|
|
68
|
+
lastChangelogGenerated: {
|
|
69
|
+
type: Date,
|
|
70
|
+
},
|
|
71
|
+
totalRepositories: {
|
|
72
|
+
type: Number,
|
|
73
|
+
default: 0,
|
|
74
|
+
},
|
|
75
|
+
totalChangelogs: {
|
|
76
|
+
type: Number,
|
|
77
|
+
default: 0,
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
|
|
81
|
+
// Tags/Categories
|
|
82
|
+
tags: [
|
|
83
|
+
{
|
|
84
|
+
type: String,
|
|
85
|
+
trim: true,
|
|
86
|
+
lowercase: true,
|
|
87
|
+
},
|
|
88
|
+
],
|
|
89
|
+
|
|
90
|
+
// Privacy
|
|
91
|
+
isPublic: {
|
|
92
|
+
type: Boolean,
|
|
93
|
+
default: false,
|
|
94
|
+
},
|
|
95
|
+
|
|
96
|
+
// GitHub Integration
|
|
97
|
+
githubIntegration: {
|
|
98
|
+
enabled: {
|
|
99
|
+
type: Boolean,
|
|
100
|
+
default: false,
|
|
101
|
+
},
|
|
102
|
+
webhookSecret: {
|
|
103
|
+
type: String,
|
|
104
|
+
},
|
|
105
|
+
autoGenerateOnPush: {
|
|
106
|
+
type: Boolean,
|
|
107
|
+
default: false,
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
timestamps: true,
|
|
113
|
+
},
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
// Indexes
|
|
117
|
+
projectSchema.index({ ownerId: 1, status: 1 });
|
|
118
|
+
projectSchema.index({ organizationId: 1, status: 1 });
|
|
119
|
+
projectSchema.index({ tags: 1 });
|
|
120
|
+
|
|
121
|
+
// Pre-save middleware to generate slug
|
|
122
|
+
projectSchema.pre("save", async function (next) {
|
|
123
|
+
if (this.isModified("name")) {
|
|
124
|
+
let baseSlug = this.name
|
|
125
|
+
.toLowerCase()
|
|
126
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
127
|
+
.replace(/^-|-$/g, "");
|
|
128
|
+
|
|
129
|
+
// Check if slug already exists
|
|
130
|
+
const existingProject = await this.constructor.findOne({
|
|
131
|
+
slug: new RegExp(`^${baseSlug}(-\d+)?$`, "i"),
|
|
132
|
+
});
|
|
133
|
+
if (
|
|
134
|
+
existingProject &&
|
|
135
|
+
existingProject._id.toString() !== this._id.toString()
|
|
136
|
+
) {
|
|
137
|
+
// Add timestamp or incrementing number to make unique
|
|
138
|
+
this.slug = `${baseSlug}-${Date.now().toString(36)}`;
|
|
139
|
+
} else {
|
|
140
|
+
this.slug = baseSlug;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
next();
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
// Static method to find projects by user
|
|
147
|
+
projectSchema.statics.findByUser = function (userId, options = {}) {
|
|
148
|
+
const { status = "active", limit = 10, skip = 0 } = options;
|
|
149
|
+
|
|
150
|
+
return this.find({
|
|
151
|
+
ownerId: userId,
|
|
152
|
+
status,
|
|
153
|
+
})
|
|
154
|
+
.sort({ updatedAt: -1 })
|
|
155
|
+
.limit(limit)
|
|
156
|
+
.skip(skip)
|
|
157
|
+
.populate("ownerId", "name email avatar");
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
// Static method to check if user can create more projects
|
|
161
|
+
projectSchema.statics.canCreateProject = async function (userId) {
|
|
162
|
+
const user = await mongoose.model("users").findById(userId);
|
|
163
|
+
if (!user) return false;
|
|
164
|
+
|
|
165
|
+
const currentProjects = await this.countDocuments({
|
|
166
|
+
ownerId: userId,
|
|
167
|
+
status: "active",
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
return currentProjects < user.subscription.projectsLimit;
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
// Instance method to update repository count
|
|
174
|
+
projectSchema.methods.updateRepositoryCount = async function () {
|
|
175
|
+
const Repository = mongoose.model("Repository");
|
|
176
|
+
const count = await Repository.countDocuments({
|
|
177
|
+
projectId: this._id,
|
|
178
|
+
status: "active",
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
return this.updateOne({ "metadata.totalRepositories": count });
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
// Instance method to update changelog count
|
|
185
|
+
projectSchema.methods.updateChangelogCount = async function () {
|
|
186
|
+
const Changelog = mongoose.model("Changelog");
|
|
187
|
+
const count = await Changelog.countDocuments({
|
|
188
|
+
projectId: this._id,
|
|
189
|
+
status: { $in: ["completed", "failed"] },
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
return this.updateOne({ "metadata.totalChangelogs": count });
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
// Instance method to get project statistics
|
|
196
|
+
projectSchema.methods.getStatistics = async function () {
|
|
197
|
+
const Repository = mongoose.model("Repository");
|
|
198
|
+
const Changelog = mongoose.model("Changelog");
|
|
199
|
+
|
|
200
|
+
const [repositories, changelogs] = await Promise.all([
|
|
201
|
+
Repository.find({ projectId: this._id, status: "active" })
|
|
202
|
+
.select("name githubRepoName lastSyncedAt")
|
|
203
|
+
.lean(),
|
|
204
|
+
Changelog.find({ projectId: this._id })
|
|
205
|
+
.sort({ createdAt: -1 })
|
|
206
|
+
.limit(5)
|
|
207
|
+
.select("month year status createdAt")
|
|
208
|
+
.lean(),
|
|
209
|
+
]);
|
|
210
|
+
|
|
211
|
+
return {
|
|
212
|
+
repositories,
|
|
213
|
+
recentChangelogs: changelogs,
|
|
214
|
+
totalRepositories: repositories.length,
|
|
215
|
+
totalChangelogs: await Changelog.countDocuments({ projectId: this._id }),
|
|
216
|
+
};
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
// Clean up response
|
|
220
|
+
projectSchema.methods.toJSON = function () {
|
|
221
|
+
const obj = this.toObject();
|
|
222
|
+
delete obj.__v;
|
|
223
|
+
return obj;
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
module.exports = mongoose.model("Project", projectSchema);
|