@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.
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 +9 -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,366 @@
1
+ const mongoose = require("mongoose");
2
+
3
+ const repositorySchema = new mongoose.Schema(
4
+ {
5
+ projectId: {
6
+ type: mongoose.Schema.Types.ObjectId,
7
+ ref: "Project",
8
+ required: true,
9
+ },
10
+
11
+ // GitHub Information
12
+ githubRepoId: {
13
+ type: String,
14
+ required: true,
15
+ },
16
+ githubRepoName: {
17
+ type: String,
18
+ required: true,
19
+ trim: true,
20
+ },
21
+ githubRepoUrl: {
22
+ type: String,
23
+ required: true,
24
+ trim: true,
25
+ },
26
+ githubRepoOwner: {
27
+ type: String,
28
+ required: true,
29
+ trim: true,
30
+ },
31
+ githubRepoFullName: {
32
+ type: String,
33
+ required: true,
34
+ trim: true,
35
+ },
36
+
37
+ // Repository Settings
38
+ name: {
39
+ type: String,
40
+ required: true,
41
+ trim: true,
42
+ maxlength: [100, "Repository name cannot exceed 100 characters"],
43
+ },
44
+ description: {
45
+ type: String,
46
+ trim: true,
47
+ maxlength: [500, "Description cannot exceed 500 characters"],
48
+ },
49
+
50
+ // Authentication
51
+ githubAccessToken: {
52
+ type: String,
53
+ required: true,
54
+ },
55
+ githubRefreshToken: {
56
+ type: String,
57
+ },
58
+
59
+ // Configuration
60
+ defaultBranch: {
61
+ type: String,
62
+ default: "main",
63
+ trim: true,
64
+ },
65
+ includeCommitsWithoutPR: {
66
+ type: Boolean,
67
+ default: true,
68
+ },
69
+ includeMergeCommits: {
70
+ type: Boolean,
71
+ default: false,
72
+ },
73
+ includeDraftPRs: {
74
+ type: Boolean,
75
+ default: false,
76
+ },
77
+ includeClosedPRs: {
78
+ type: Boolean,
79
+ default: true,
80
+ },
81
+
82
+ // Status
83
+ status: {
84
+ type: String,
85
+ enum: ["active", "inactive", "error", "deleted"],
86
+ default: "active",
87
+ },
88
+
89
+ // Sync Information
90
+ lastSyncedAt: {
91
+ type: Date,
92
+ },
93
+ lastSyncStatus: {
94
+ type: String,
95
+ enum: ["success", "failed", "partial"],
96
+ default: "success",
97
+ },
98
+ lastSyncError: {
99
+ type: String,
100
+ },
101
+
102
+ // Metadata
103
+ metadata: {
104
+ totalCommits: {
105
+ type: Number,
106
+ default: 0,
107
+ },
108
+ totalPRs: {
109
+ type: Number,
110
+ default: 0,
111
+ },
112
+ totalIssues: {
113
+ type: Number,
114
+ default: 0,
115
+ },
116
+ lastCommitSha: {
117
+ type: String,
118
+ },
119
+ lastCommitDate: {
120
+ type: Date,
121
+ },
122
+ branches: [
123
+ {
124
+ name: String,
125
+ lastCommitSha: String,
126
+ lastCommitDate: Date,
127
+ },
128
+ ],
129
+ },
130
+
131
+ // Webhook
132
+ webhook: {
133
+ enabled: {
134
+ type: Boolean,
135
+ default: false,
136
+ },
137
+ webhookId: {
138
+ type: String,
139
+ },
140
+ webhookUrl: {
141
+ type: String,
142
+ },
143
+ secret: {
144
+ type: String,
145
+ },
146
+ },
147
+
148
+ // Tags
149
+ tags: [
150
+ {
151
+ type: String,
152
+ trim: true,
153
+ lowercase: true,
154
+ },
155
+ ],
156
+
157
+ // Privacy
158
+ isPrivate: {
159
+ type: Boolean,
160
+ default: false,
161
+ },
162
+
163
+ // GitHub Features
164
+ features: {
165
+ issues: {
166
+ type: Boolean,
167
+ default: true,
168
+ },
169
+ pullRequests: {
170
+ type: Boolean,
171
+ default: true,
172
+ },
173
+ releases: {
174
+ type: Boolean,
175
+ default: true,
176
+ },
177
+ discussions: {
178
+ type: Boolean,
179
+ default: false,
180
+ },
181
+ },
182
+ },
183
+ {
184
+ timestamps: true,
185
+ },
186
+ );
187
+
188
+ // Indexes
189
+ repositorySchema.index({ projectId: 1, status: 1 });
190
+ repositorySchema.index({ githubRepoId: 1 });
191
+ repositorySchema.index({ githubRepoFullName: 1 });
192
+ repositorySchema.index({ "metadata.lastCommitDate": 1 });
193
+
194
+ // Pre-save middleware to validate GitHub URL
195
+ repositorySchema.pre("save", function (next) {
196
+ if (this.isModified("githubRepoUrl")) {
197
+ try {
198
+ new URL(this.githubRepoUrl);
199
+ } catch (error) {
200
+ const err = new Error("Invalid GitHub repository URL");
201
+ return next(err);
202
+ }
203
+ }
204
+ next();
205
+ });
206
+
207
+ // Static method to find repositories by project
208
+ repositorySchema.statics.findByProject = function (projectId, options = {}) {
209
+ const { status = "active", limit = 20, skip = 0 } = options;
210
+
211
+ return this.find({
212
+ projectId,
213
+ status,
214
+ })
215
+ .sort({ updatedAt: -1 })
216
+ .limit(limit)
217
+ .skip(skip)
218
+ .populate("projectId", "name slug");
219
+ };
220
+
221
+ // Static method to check if repository exists for project
222
+ repositorySchema.statics.existsForProject = function (projectId, githubRepoId) {
223
+ return this.exists({
224
+ projectId,
225
+ githubRepoId,
226
+ status: { $ne: "deleted" },
227
+ });
228
+ };
229
+
230
+ // Static method to get repository by GitHub ID
231
+ repositorySchema.statics.findByGithubId = function (githubRepoId) {
232
+ return this.findOne({
233
+ githubRepoId,
234
+ status: { $ne: "deleted" },
235
+ }).populate("projectId", "name slug ownerId");
236
+ };
237
+
238
+ // Instance method to update sync status
239
+ repositorySchema.methods.updateSyncStatus = async function (
240
+ status,
241
+ error = null,
242
+ ) {
243
+ const updates = {
244
+ lastSyncedAt: new Date(),
245
+ lastSyncStatus: status,
246
+ };
247
+
248
+ if (error) {
249
+ updates.lastSyncError = error;
250
+ } else {
251
+ updates.lastSyncError = null;
252
+ }
253
+
254
+ return this.updateOne(updates);
255
+ };
256
+
257
+ // Instance method to update metadata
258
+ repositorySchema.methods.updateMetadata = async function (metadata) {
259
+ return this.updateOne({
260
+ $set: {
261
+ "metadata.totalCommits":
262
+ metadata.totalCommits || this.metadata.totalCommits,
263
+ "metadata.totalPRs": metadata.totalPRs || this.metadata.totalPRs,
264
+ "metadata.totalIssues": metadata.totalIssues || this.metadata.totalIssues,
265
+ "metadata.lastCommitSha":
266
+ metadata.lastCommitSha || this.metadata.lastCommitSha,
267
+ "metadata.lastCommitDate":
268
+ metadata.lastCommitDate || this.metadata.lastCommitDate,
269
+ },
270
+ });
271
+ };
272
+
273
+ // Instance method to get available months for changelog generation
274
+ repositorySchema.methods.getAvailableMonths = async function () {
275
+ const Changelog = mongoose.model("Changelog");
276
+
277
+ // Get all generated changelogs for this repository
278
+ const generatedChangelogs = await Changelog.find({
279
+ repositoryId: this._id,
280
+ status: "completed",
281
+ })
282
+ .select("month year")
283
+ .lean();
284
+
285
+ const generatedMonths = new Set(
286
+ generatedChangelogs.map((c) => `${c.year}-${c.month}`),
287
+ );
288
+
289
+ // Get commit history from metadata or fetch from GitHub
290
+ const commits = await this.fetchCommitHistory();
291
+
292
+ const availableMonths = new Set();
293
+
294
+ commits.forEach((commit) => {
295
+ const date = new Date(commit.date);
296
+ const year = date.getFullYear();
297
+ const month = String(date.getMonth() + 1).padStart(2, "0");
298
+ const key = `${year}-${month}`;
299
+ availableMonths.add(key);
300
+ });
301
+
302
+ // Return months that have commits but no generated changelog
303
+ const result = Array.from(availableMonths)
304
+ .filter((month) => !generatedMonths.has(month))
305
+ .sort()
306
+ .reverse();
307
+
308
+ return result;
309
+ };
310
+
311
+ // Instance method to fetch commit history (placeholder for GitHub API integration)
312
+ repositorySchema.methods.fetchCommitHistory = async function () {
313
+ // This would integrate with GitHub API or local git repository
314
+ // For now, return empty array
315
+ return [];
316
+ };
317
+
318
+ // Instance method to validate repository access
319
+ repositorySchema.methods.validateAccess = async function () {
320
+ try {
321
+ // This would make a test API call to GitHub to validate access
322
+ // For now, return true
323
+ return { valid: true, error: null };
324
+ } catch (error) {
325
+ return { valid: false, error: error.message };
326
+ }
327
+ };
328
+
329
+ // Instance method to get repository statistics
330
+ repositorySchema.methods.getStatistics = async function () {
331
+ const Changelog = mongoose.model("Changelog");
332
+
333
+ const [totalChangelogs, recentChangelogs] = await Promise.all([
334
+ Changelog.countDocuments({
335
+ repositoryId: this._id,
336
+ status: { $in: ["completed", "failed"] },
337
+ }),
338
+ Changelog.find({
339
+ repositoryId: this._id,
340
+ })
341
+ .sort({ createdAt: -1 })
342
+ .limit(5)
343
+ .select("month year status createdAt")
344
+ .lean(),
345
+ ]);
346
+
347
+ return {
348
+ totalChangelogs,
349
+ recentChangelogs,
350
+ lastSyncedAt: this.lastSyncedAt,
351
+ lastSyncStatus: this.lastSyncStatus,
352
+ metadata: this.metadata,
353
+ };
354
+ };
355
+
356
+ // Clean up response
357
+ repositorySchema.methods.toJSON = function () {
358
+ const obj = this.toObject();
359
+ delete obj.githubAccessToken;
360
+ delete obj.githubRefreshToken;
361
+ delete obj.webhook.secret;
362
+ delete obj.__v;
363
+ return obj;
364
+ };
365
+
366
+ module.exports = mongoose.model("Repository", repositorySchema);
@@ -0,0 +1,223 @@
1
+ const mongoose = require("mongoose");
2
+ const bcrypt = require("bcryptjs");
3
+
4
+ const userSchema = new mongoose.Schema(
5
+ {
6
+ email: {
7
+ type: String,
8
+ required: [true, "Email is required"],
9
+ unique: true,
10
+ lowercase: true,
11
+ trim: true,
12
+ match: [
13
+ /^\w+([.-]?\w+)*@\w+([.-]?\w+)*(\.\w{2,3})+$/,
14
+ "Please enter a valid email",
15
+ ],
16
+ },
17
+ password: {
18
+ type: String,
19
+ minlength: [6, "Password must be at least 6 characters long"],
20
+ // Password is optional for OAuth users (GitHub, etc.)
21
+ validate: {
22
+ validator: function (v) {
23
+ // If user has a githubId, password is optional
24
+ if (this.githubId) return true;
25
+ // Otherwise, password is required
26
+ return v && v.length >= 6;
27
+ },
28
+ message: "Password is required (min 6 characters)",
29
+ },
30
+ },
31
+ name: {
32
+ type: String,
33
+ trim: true,
34
+ },
35
+
36
+ // GitHub Integration
37
+ githubId: {
38
+ type: String,
39
+ sparse: true,
40
+ },
41
+ githubUsername: {
42
+ type: String,
43
+ sparse: true,
44
+ },
45
+ githubAccessToken: {
46
+ type: String,
47
+ },
48
+ githubRefreshToken: {
49
+ type: String,
50
+ },
51
+ githubEmail: {
52
+ type: String,
53
+ sparse: true,
54
+ },
55
+
56
+ // Profile
57
+ avatar: {
58
+ type: String,
59
+ },
60
+ isActive: {
61
+ type: Boolean,
62
+ default: true,
63
+ },
64
+ emailVerified: {
65
+ type: Boolean,
66
+ default: false,
67
+ },
68
+
69
+ // Subscription
70
+ subscription: {
71
+ plan: {
72
+ type: String,
73
+ enum: ["free", "pro", "enterprise"],
74
+ default: "free",
75
+ },
76
+ projectsLimit: {
77
+ type: Number,
78
+ default: 3,
79
+ },
80
+ repositoriesLimit: {
81
+ type: Number,
82
+ default: 5,
83
+ },
84
+ changelogsLimit: {
85
+ type: Number,
86
+ default: 10,
87
+ },
88
+ lastResetDate: {
89
+ type: Date,
90
+ default: Date.now,
91
+ },
92
+ },
93
+
94
+ // Usage tracking
95
+ usage: {
96
+ projectsCount: {
97
+ type: Number,
98
+ default: 0,
99
+ },
100
+ repositoriesCount: {
101
+ type: Number,
102
+ default: 0,
103
+ },
104
+ changelogsCount: {
105
+ type: Number,
106
+ default: 0,
107
+ },
108
+ lastActivity: {
109
+ type: Date,
110
+ default: Date.now,
111
+ },
112
+ },
113
+
114
+ // Security
115
+ lastLogin: {
116
+ type: Date,
117
+ },
118
+ loginAttempts: {
119
+ type: Number,
120
+ default: 0,
121
+ },
122
+ lockUntil: {
123
+ type: Date,
124
+ },
125
+
126
+ // Preferences
127
+ preferences: {
128
+ defaultChangelogFormat: {
129
+ type: String,
130
+ enum: ["markdown", "html", "json"],
131
+ default: "markdown",
132
+ },
133
+ notifications: {
134
+ email: {
135
+ type: Boolean,
136
+ default: true,
137
+ },
138
+ changelogGenerated: {
139
+ type: Boolean,
140
+ default: true,
141
+ },
142
+ },
143
+ },
144
+ },
145
+ {
146
+ timestamps: true,
147
+ },
148
+ );
149
+
150
+ // Indexes
151
+ userSchema.index({ githubId: 1 });
152
+ userSchema.index({ email: 1 });
153
+
154
+ // Virtual for account lockout
155
+ userSchema.virtual("isLocked").get(function () {
156
+ return !!(this.lockUntil && this.lockUntil > Date.now());
157
+ });
158
+
159
+ // Pre-save middleware to hash password
160
+ userSchema.pre("save", async function (next) {
161
+ // Only hash the password if it has been modified (or is new) AND exists
162
+ if (!this.isModified("password") || !this.password) return next();
163
+
164
+ try {
165
+ // Hash the password with cost of 12
166
+ const salt = await bcrypt.genSalt(12);
167
+ this.password = await bcrypt.hash(this.password, salt);
168
+ next();
169
+ } catch (error) {
170
+ next(error);
171
+ }
172
+ });
173
+
174
+ // Instance method to check password
175
+ userSchema.methods.comparePassword = async function (candidatePassword) {
176
+ return bcrypt.compare(candidatePassword, this.password);
177
+ };
178
+
179
+ // Instance method to increment login attempts
180
+ userSchema.methods.incLoginAttempts = function () {
181
+ // If we have a previous lock that has expired, restart at 1
182
+ if (this.lockUntil && this.lockUntil < Date.now()) {
183
+ return this.updateOne({
184
+ $unset: { lockUntil: 1 },
185
+ $set: { loginAttempts: 1 },
186
+ });
187
+ }
188
+
189
+ const updates = { $inc: { loginAttempts: 1 } };
190
+
191
+ // If we're at max attempts and not locked, lock the account
192
+ if (this.loginAttempts + 1 >= 5 && !this.isLocked) {
193
+ updates.$set = { lockUntil: Date.now() + 2 * 60 * 60 * 1000 }; // Lock for 2 hours
194
+ }
195
+
196
+ return this.updateOne(updates);
197
+ };
198
+
199
+ // Instance method to reset login attempts
200
+ userSchema.methods.resetLoginAttempts = function () {
201
+ return this.updateOne({
202
+ $unset: { loginAttempts: 1, lockUntil: 1 },
203
+ });
204
+ };
205
+
206
+ // Instance method to update last activity
207
+ userSchema.methods.updateLastActivity = function () {
208
+ return this.updateOne({ lastActivity: new Date() });
209
+ };
210
+
211
+ // Clean up response
212
+ userSchema.methods.toJSON = function () {
213
+ const obj = this.toObject();
214
+ delete obj.password;
215
+ delete obj.githubAccessToken;
216
+ delete obj.githubRefreshToken;
217
+ delete obj.loginAttempts;
218
+ delete obj.lockUntil;
219
+ delete obj.__v;
220
+ return obj;
221
+ };
222
+
223
+ module.exports = mongoose.model("users", userSchema);
@@ -0,0 +1,32 @@
1
+ const express = require('express');
2
+ const {
3
+ register,
4
+ login,
5
+ githubLogin,
6
+ githubCallback,
7
+ getProfile,
8
+ updateProfile,
9
+ changePassword,
10
+ logout,
11
+ refreshToken,
12
+ deleteAccount
13
+ } = require('../controllers/auth');
14
+ const { authenticate, authRateLimit } = require('../middleware/auth');
15
+
16
+ const router = express.Router();
17
+
18
+ // Public routes
19
+ router.post('/register', authRateLimit, register);
20
+ router.post('/login', authRateLimit, login);
21
+ router.get('/github/login', githubLogin);
22
+ router.get('/github/callback', githubCallback);
23
+ router.post('/refresh', refreshToken);
24
+
25
+ // Protected routes
26
+ router.get('/profile', authenticate, getProfile);
27
+ router.put('/profile', authenticate, updateProfile);
28
+ router.post('/change-password', authenticate, changePassword);
29
+ router.post('/logout', authenticate, logout);
30
+ router.delete('/account', authenticate, deleteAccount);
31
+
32
+ module.exports = router;
@@ -0,0 +1,42 @@
1
+ const express = require('express');
2
+ const {
3
+ generateChangelog,
4
+ getChangelogs,
5
+ getRepositoryChangelogs,
6
+ getChangelog,
7
+ updateChangelog,
8
+ deleteChangelog,
9
+ getChangelogStatistics,
10
+ getAvailableMonths,
11
+ shareChangelog,
12
+ unshareChangelog,
13
+ getPublicChangelog,
14
+ downloadChangelog
15
+ } = require('../controllers/changelog');
16
+ const { authenticate, checkOwnership, checkSubscription, checkUsageLimits, apiRateLimit } = require('../middleware/auth');
17
+
18
+ const router = express.Router();
19
+
20
+ // Protected routes - require authentication
21
+ router.use(authenticate);
22
+ router.use(apiRateLimit);
23
+
24
+ // Changelog CRUD operations
25
+ router.post('/', checkUsageLimits, generateChangelog);
26
+ router.get('/project/:projectId', getChangelogs);
27
+ router.get('/repository/:repositoryId', getRepositoryChangelogs);
28
+ router.get('/:id', getChangelog);
29
+ router.put('/:id', checkOwnership('Changelog'), updateChangelog);
30
+ router.delete('/:id', checkOwnership('Changelog'), deleteChangelog);
31
+
32
+ // Changelog management
33
+ router.get('/:id/statistics', checkOwnership('Changelog'), getChangelogStatistics);
34
+ router.get('/:repositoryId/available-months', getAvailableMonths);
35
+ router.post('/:id/share', checkOwnership('Changelog'), shareChangelog);
36
+ router.post('/:id/unshare', checkOwnership('Changelog'), unshareChangelog);
37
+ router.get('/download/:id', checkOwnership('Changelog'), downloadChangelog);
38
+
39
+ // Public routes - for shared changelogs
40
+ router.get('/share/:shareToken', getPublicChangelog);
41
+
42
+ module.exports = router;