@manojkmfsi/monodog 1.1.28 → 1.1.30
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/CHANGELOG.md +64 -0
- package/check-db.js +58 -0
- package/create-test-session.js +50 -0
- package/dev-server.pid +1 -0
- package/dist/controllers/pipeline-controller.js +329 -0
- package/dist/controllers/publish-controller.js +119 -7
- package/dist/middleware/auth-middleware.js +1 -0
- package/dist/middleware/server-startup.js +19 -0
- package/dist/routes/auth-routes.js +2 -0
- package/dist/routes/pipeline-routes.js +76 -0
- package/dist/services/changeset-service.js +2 -13
- package/dist/services/github-actions-service.js +552 -0
- package/dist/services/pipeline-service.js +356 -0
- package/dist/types/github-actions.js +6 -0
- package/monodog-dashboard/dist/assets/index-6NrFUGfK.js +15 -0
- package/monodog-dashboard/dist/assets/index-6NrFUGfK.js.map +1 -0
- package/monodog-dashboard/dist/assets/index-DcvKt8qx.css +1 -0
- package/monodog-dashboard/dist/index.html +2 -2
- package/package.json +2 -2
- package/prisma/migrations/{20260118042615_init_database → 20260224091033_init_db}/migration.sql +58 -0
- package/prisma/schema/pipeline-audit-log.prisma +22 -0
- package/prisma/schema/release-pipeline.prisma +25 -0
- package/monodog-dashboard/dist/assets/index-7G3SCgcz.css +0 -1
- package/monodog-dashboard/dist/assets/index-BMoE990p.js +0 -13
- package/monodog-dashboard/dist/assets/index-BMoE990p.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,69 @@
|
|
|
1
1
|
# @manojkmfsi/monoapp
|
|
2
2
|
|
|
3
|
+
## 1.1.30
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [`f27db43`](https://github.com/manojkmfsi/monodog/commit/f27db43277895381dbf239ffd57ebb17e7fd6e13) - sasasasasasasas
|
|
8
|
+
|
|
9
|
+
## 1.1.29
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- [`8600e0c`](https://github.com/manojkmfsi/monodog/commit/8600e0c636f76c8b9dcc8fbf7a2617d355f2d0e5) - fsfyfhfhfjjh,m
|
|
14
|
+
|
|
15
|
+
- [`188bcf2`](https://github.com/manojkmfsi/monodog/commit/188bcf2ab06b5fe58b2cea5915a7751cba3a68d2) - cxzcxvxcbvcbnb,m
|
|
16
|
+
|
|
17
|
+
- [`cfb03da`](https://github.com/manojkmfsi/monodog/commit/cfb03da50efd0b829bf4a8b4c5c2ae68c4d1a4ce) - mkjmnmnmnmnnmnm
|
|
18
|
+
|
|
19
|
+
- [`d95b183`](https://github.com/manojkmfsi/monodog/commit/d95b183ea3aa3a60f153d40d1db4049565ecaffa) - m nm nm nm nm mn nm mmm nm mn mm
|
|
20
|
+
|
|
21
|
+
- [`8abe3fb`](https://github.com/manojkmfsi/monodog/commit/8abe3fb09b2608696e6960ac0fdb8e0a9112dfa7) - xzdfdguiulmn cxdtyj
|
|
22
|
+
|
|
23
|
+
- [`9add854`](https://github.com/manojkmfsi/monodog/commit/9add854171041d38161916c2ce5ed34af471614a) - hbmjnbmm,n,,m,
|
|
24
|
+
|
|
25
|
+
- [`5f9b61e`](https://github.com/manojkmfsi/monodog/commit/5f9b61e73c8d1c460dac445aa9c5ae52ed053883) - cxgfhgjmnmnm,k,m
|
|
26
|
+
|
|
27
|
+
- [`bde7bfb`](https://github.com/manojkmfsi/monodog/commit/bde7bfb74376917ab590c8f0d831c3aa0a9e0482) - vnmn,nkjlkn,mnm,
|
|
28
|
+
|
|
29
|
+
- [`e67b026`](https://github.com/manojkmfsi/monodog/commit/e67b02689d82fb4cae0ca917fdb829c3d23d3b9d) - drkjk,,kjudscxvxvvvvnm
|
|
30
|
+
|
|
31
|
+
- [`6b78e09`](https://github.com/manojkmfsi/monodog/commit/6b78e09e6e090ece45485b0e43b1b6e5a537e4eb) - fcdgcbvnkmkllml.m.
|
|
32
|
+
|
|
33
|
+
- [`1a74552`](https://github.com/manojkmfsi/monodog/commit/1a74552277f8deaef831649ad424ea41c77fd467) - nhuyfgxbvnbmklm, .,nmkn,
|
|
34
|
+
|
|
35
|
+
- [`fe566fd`](https://github.com/manojkmfsi/monodog/commit/fe566fdb7041ce079e8afe78ebdb4b477f0cae44) - fghjvvnvnbn
|
|
36
|
+
|
|
37
|
+
- [`79566ae`](https://github.com/manojkmfsi/monodog/commit/79566ae23e3f30a03482134f35b5491e289d2775) - adssdxvcvbcbvcxvcbvcb
|
|
38
|
+
|
|
39
|
+
- [`804ddf2`](https://github.com/manojkmfsi/monodog/commit/804ddf22cf845aa2c980bff9488a04e251be119a) - fxcbcvcxcbvnbvnb
|
|
40
|
+
|
|
41
|
+
- [`07e1878`](https://github.com/manojkmfsi/monodog/commit/07e18786e6f3d8dad57f90822af93bd578c9c3ae) - bvcghmjnm,nmnmhkjhn,mn
|
|
42
|
+
|
|
43
|
+
- [`faa4e0c`](https://github.com/manojkmfsi/monodog/commit/faa4e0cb48e0b1f8eae6aa93f104924452076b94) - mnmmmmnmnmn,mn,mnmn,
|
|
44
|
+
|
|
45
|
+
- [`4ed3483`](https://github.com/manojkmfsi/monodog/commit/4ed34834edea9c6789ffbf93c1354f9752b59b8b) - n,mnmnmnmn,mnm,,m
|
|
46
|
+
|
|
47
|
+
- [`dd47e0b`](https://github.com/manojkmfsi/monodog/commit/dd47e0b8480f14a78b958f3ff68d3b41082f3465) - mnn,mn,mn,n,mn,,
|
|
48
|
+
|
|
49
|
+
- [`621bee1`](https://github.com/manojkmfsi/monodog/commit/621bee11b6194ee56fd90c130a4b3c83b386831a) - kmmnmmn,n,m,m, n,m
|
|
50
|
+
|
|
51
|
+
- [`ec7e1b0`](https://github.com/manojkmfsi/monodog/commit/ec7e1b0817ae540ece67576fbceb7486dcadc48f) - mnm,n,n,mn,mn,m,m,m,
|
|
52
|
+
|
|
53
|
+
- [`411523b`](https://github.com/manojkmfsi/monodog/commit/411523b813a96fcfd46c3e459bcd887c255fd2e7) - mnmn,mn,mn,m.,m.,m.,m,m.,
|
|
54
|
+
|
|
55
|
+
- [`ec19a7c`](https://github.com/manojkmfsi/monodog/commit/ec19a7ccff425f7319c24d0506a147fcfdd0e9ca) - vbcbvbnvnmbnmbnmnm
|
|
56
|
+
|
|
57
|
+
- [`6818670`](https://github.com/manojkmfsi/monodog/commit/6818670424be6bdb98e1fd2e04f1fe985638e67e) - cxxvcvcbvbnvnbvnmnm
|
|
58
|
+
|
|
59
|
+
- [`10eac2e`](https://github.com/manojkmfsi/monodog/commit/10eac2ee72753474d4ec0d9ba2ba3ebdd4639c96) - sdhgjhkjhkjjkkkjn
|
|
60
|
+
|
|
61
|
+
- [`4c924c9`](https://github.com/manojkmfsi/monodog/commit/4c924c918f93cbd54c1c752060a6bdaab45e987d) - wdassdsfddfsdffdgfdsgfsgfg
|
|
62
|
+
|
|
63
|
+
- [`a9e5e26`](https://github.com/manojkmfsi/monodog/commit/a9e5e2680f49c448c49e3a1c414d5c2533a5e9cf) - nnknknnnknkn,,mnm,
|
|
64
|
+
|
|
65
|
+
- [`d559595`](https://github.com/manojkmfsi/monodog/commit/d559595e3b56371a102a21244d0d6c5696541bd3) - dsdsmnd,mns,mnd,msnd,msn,md
|
|
66
|
+
|
|
3
67
|
## 1.1.28
|
|
4
68
|
|
|
5
69
|
### Patch Changes
|
package/check-db.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
const { PrismaClient } = require('@prisma/client');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
|
|
5
|
+
// Load config similar to the app
|
|
6
|
+
let databasePath = 'file:./monodog.db'; // default
|
|
7
|
+
|
|
8
|
+
// Try to read monodog-config.json
|
|
9
|
+
try {
|
|
10
|
+
const configPath = path.join(__dirname, 'monodog-config.json');
|
|
11
|
+
if (fs.existsSync(configPath)) {
|
|
12
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
13
|
+
databasePath = config.database?.path || databasePath;
|
|
14
|
+
}
|
|
15
|
+
} catch (e) {
|
|
16
|
+
console.warn('Could not read config:', e.message);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
console.log(`Database URL: ${databasePath}\n`);
|
|
20
|
+
|
|
21
|
+
async function checkDatabase() {
|
|
22
|
+
const prisma = new PrismaClient({
|
|
23
|
+
datasources: {
|
|
24
|
+
db: {
|
|
25
|
+
url: databasePath,
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
console.log('=== Database Status ===\n');
|
|
32
|
+
|
|
33
|
+
// Check ReleasePipeline count
|
|
34
|
+
const pipelineCount = await prisma.releasePipeline.count();
|
|
35
|
+
console.log(`Total pipelines in database: ${pipelineCount}\n`);
|
|
36
|
+
|
|
37
|
+
if (pipelineCount > 0) {
|
|
38
|
+
const pipelines = await prisma.releasePipeline.findMany({
|
|
39
|
+
take: 5,
|
|
40
|
+
orderBy: { createdAt: 'desc' },
|
|
41
|
+
});
|
|
42
|
+
console.log('Latest pipelines:');
|
|
43
|
+
pipelines.forEach(p => {
|
|
44
|
+
console.log(` - ${p.packageName} v${p.releaseVersion} (${p.currentStatus})`);
|
|
45
|
+
console.log(` Triggered by: ${p.triggeredBy} at ${p.triggeredAt}`);
|
|
46
|
+
console.log(` Owner/Repo: ${p.owner}/${p.repo}`);
|
|
47
|
+
console.log('');
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
process.exit(0);
|
|
52
|
+
} catch (error) {
|
|
53
|
+
console.error('Error:', error.message);
|
|
54
|
+
process.exit(1);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
checkDatabase();
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* This script creates a test session by directly calling the auth middleware's storeSession function
|
|
5
|
+
* It simulates what happens after a successful GitHub OAuth login
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { storeSession } = require('./dist/middleware/auth-middleware');
|
|
9
|
+
|
|
10
|
+
// Create a mock session
|
|
11
|
+
const mockSession = {
|
|
12
|
+
accessToken: 'gho_test_token_' + Math.random().toString(36).substr(2, 9),
|
|
13
|
+
expiresIn: 3600,
|
|
14
|
+
expiresAt: Date.now() + 24 * 60 * 60 * 1000,
|
|
15
|
+
user: {
|
|
16
|
+
id: 12345,
|
|
17
|
+
login: 'testuser',
|
|
18
|
+
name: 'Test User',
|
|
19
|
+
email: 'test@example.com',
|
|
20
|
+
avatar_url: 'https://avatars.githubusercontent.com/u/12345?v=4',
|
|
21
|
+
public_repos: 5,
|
|
22
|
+
followers: 10,
|
|
23
|
+
following: 5,
|
|
24
|
+
},
|
|
25
|
+
scopes: ['repo', 'read:user'],
|
|
26
|
+
permission: {
|
|
27
|
+
permission: 'maintain',
|
|
28
|
+
role: 'Maintainer',
|
|
29
|
+
userId: 12345,
|
|
30
|
+
username: 'testuser',
|
|
31
|
+
owner: 'manojkmfsi',
|
|
32
|
+
repo: 'MonoDog',
|
|
33
|
+
cachedAt: Date.now(),
|
|
34
|
+
ttl: 3600000,
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
const token = storeSession(mockSession);
|
|
40
|
+
console.log('✅ Test session created successfully!');
|
|
41
|
+
console.log(`\nSession Token: ${token}`);
|
|
42
|
+
console.log(`User: ${mockSession.user.login}`);
|
|
43
|
+
console.log(`Permission: ${mockSession.permission.permission}`);
|
|
44
|
+
console.log(`Expires at: ${new Date(mockSession.expiresAt).toISOString()}`);
|
|
45
|
+
console.log(`\nYou can use this token in API requests:`);
|
|
46
|
+
console.log(`curl -H "Authorization: Bearer ${token}" http://localhost:8999/api/publish/trigger`);
|
|
47
|
+
} catch (error) {
|
|
48
|
+
console.error('❌ Failed to create session:', error.message);
|
|
49
|
+
process.exit(1);
|
|
50
|
+
}
|
package/dev-server.pid
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
22401
|
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Pipeline Controller
|
|
4
|
+
* Handles HTTP requests and responses for pipeline operations
|
|
5
|
+
* Delegates business logic to pipeline service
|
|
6
|
+
*/
|
|
7
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
8
|
+
if (k2 === undefined) k2 = k;
|
|
9
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
10
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
11
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
12
|
+
}
|
|
13
|
+
Object.defineProperty(o, k2, desc);
|
|
14
|
+
}) : (function(o, m, k, k2) {
|
|
15
|
+
if (k2 === undefined) k2 = k;
|
|
16
|
+
o[k2] = m[k];
|
|
17
|
+
}));
|
|
18
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
19
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
20
|
+
}) : function(o, v) {
|
|
21
|
+
o["default"] = v;
|
|
22
|
+
});
|
|
23
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
24
|
+
var ownKeys = function(o) {
|
|
25
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
26
|
+
var ar = [];
|
|
27
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
28
|
+
return ar;
|
|
29
|
+
};
|
|
30
|
+
return ownKeys(o);
|
|
31
|
+
};
|
|
32
|
+
return function (mod) {
|
|
33
|
+
if (mod && mod.__esModule) return mod;
|
|
34
|
+
var result = {};
|
|
35
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
36
|
+
__setModuleDefault(result, mod);
|
|
37
|
+
return result;
|
|
38
|
+
};
|
|
39
|
+
})();
|
|
40
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
41
|
+
exports.getRecentPipelines = getRecentPipelines;
|
|
42
|
+
exports.updatePipelineStatus = updatePipelineStatus;
|
|
43
|
+
exports.listAvailableWorkflows = listAvailableWorkflows;
|
|
44
|
+
exports.getWorkflowRuns = getWorkflowRuns;
|
|
45
|
+
exports.getWorkflowRunWithJobs = getWorkflowRunWithJobs;
|
|
46
|
+
exports.getJobLogs = getJobLogs;
|
|
47
|
+
exports.triggerWorkflow = triggerWorkflow;
|
|
48
|
+
exports.getPipelineAuditLogs = getPipelineAuditLogs;
|
|
49
|
+
exports.cancelWorkflowRun = cancelWorkflowRun;
|
|
50
|
+
exports.rerunWorkflow = rerunWorkflow;
|
|
51
|
+
const logger_1 = require("../middleware/logger");
|
|
52
|
+
const pipelineService = __importStar(require("../services/pipeline-service"));
|
|
53
|
+
const githubActionsService = __importStar(require("../services/github-actions-service"));
|
|
54
|
+
/**
|
|
55
|
+
* Get recent pipelines for the dashboard
|
|
56
|
+
* GET /api/pipelines
|
|
57
|
+
*/
|
|
58
|
+
async function getRecentPipelines(req, res) {
|
|
59
|
+
try {
|
|
60
|
+
if (!req.user) {
|
|
61
|
+
return res.status(401).json({ error: 'Unauthorized' });
|
|
62
|
+
}
|
|
63
|
+
const limit = Math.min(parseInt(req.query.limit) || 20, 100);
|
|
64
|
+
const offset = parseInt(req.query.offset) || 0;
|
|
65
|
+
const pipelines = await pipelineService.getRecentPipelines(limit, offset);
|
|
66
|
+
res.json(pipelines);
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
logger_1.AppLogger.error(`Error getting pipelines: ${error}`);
|
|
70
|
+
res.status(500).json({ error: 'Failed to get pipelines' });
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Update pipeline status based on latest workflow run
|
|
75
|
+
* PUT /api/pipelines/:pipelineId/status
|
|
76
|
+
*/
|
|
77
|
+
async function updatePipelineStatus(req, res) {
|
|
78
|
+
try {
|
|
79
|
+
if (!req.user) {
|
|
80
|
+
return res.status(401).json({ error: 'Unauthorized' });
|
|
81
|
+
}
|
|
82
|
+
const { pipelineId } = req.params;
|
|
83
|
+
const { currentStatus, currentConclusion, lastRunId } = req.body;
|
|
84
|
+
if (!currentStatus) {
|
|
85
|
+
return res.status(400).json({ error: 'currentStatus is required' });
|
|
86
|
+
}
|
|
87
|
+
const updatedPipeline = await pipelineService.updatePipelineStatus(pipelineId, currentStatus, currentConclusion || null, lastRunId ? String(lastRunId) : undefined);
|
|
88
|
+
res.json({
|
|
89
|
+
success: true,
|
|
90
|
+
pipeline: updatedPipeline,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
logger_1.AppLogger.error(`Error updating pipeline status: ${error}`);
|
|
95
|
+
res.status(500).json({ error: 'Failed to update pipeline status' });
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Get available workflows in a repository
|
|
100
|
+
* GET /api/workflows/:owner/:repo/available
|
|
101
|
+
*/
|
|
102
|
+
async function listAvailableWorkflows(req, res) {
|
|
103
|
+
try {
|
|
104
|
+
if (!req.user || !req.accessToken) {
|
|
105
|
+
return res.status(401).json({ error: 'Unauthorized' });
|
|
106
|
+
}
|
|
107
|
+
const { owner, repo } = req.params;
|
|
108
|
+
const result = await githubActionsService.listWorkflows(owner, repo, req.accessToken);
|
|
109
|
+
res.json(result);
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
logger_1.AppLogger.error(`Error listing workflows: ${error}`);
|
|
113
|
+
res.status(500).json({ error: 'Failed to list workflows' });
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Get workflow runs for a repository
|
|
118
|
+
* GET /api/workflows/:owner/:repo
|
|
119
|
+
*/
|
|
120
|
+
async function getWorkflowRuns(req, res) {
|
|
121
|
+
try {
|
|
122
|
+
if (!req.user || !req.accessToken) {
|
|
123
|
+
return res.status(401).json({ error: 'Unauthorized' });
|
|
124
|
+
}
|
|
125
|
+
const { owner, repo } = req.params;
|
|
126
|
+
const workflowId = req.query.workflow_id;
|
|
127
|
+
const workflowPath = req.query.workflow_path;
|
|
128
|
+
const status = req.query.status;
|
|
129
|
+
const page = parseInt(req.query.page) || 1;
|
|
130
|
+
const per_page = parseInt(req.query.per_page) || 30;
|
|
131
|
+
logger_1.AppLogger.info(`GET /workflows/${owner}/${repo}: workflowId=${workflowId}, workflowPath=${workflowPath}, status=${status}, page=${page}, per_page=${per_page}`);
|
|
132
|
+
const result = await githubActionsService.getWorkflowRuns(owner, repo, req.accessToken, {
|
|
133
|
+
workflowId,
|
|
134
|
+
workflowPath,
|
|
135
|
+
status,
|
|
136
|
+
page,
|
|
137
|
+
per_page,
|
|
138
|
+
});
|
|
139
|
+
logger_1.AppLogger.info(`GET /workflows/${owner}/${repo}: Returned ${result.runs.length} runs (total: ${result.totalCount})`);
|
|
140
|
+
res.json(result);
|
|
141
|
+
}
|
|
142
|
+
catch (error) {
|
|
143
|
+
logger_1.AppLogger.error(`Error getting workflows: ${error}`);
|
|
144
|
+
res.status(500).json({ error: 'Failed to get workflows' });
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Get specific workflow run with jobs
|
|
149
|
+
* GET /api/workflows/:owner/:repo/runs/:runId
|
|
150
|
+
*/
|
|
151
|
+
async function getWorkflowRunWithJobs(req, res) {
|
|
152
|
+
try {
|
|
153
|
+
if (!req.user || !req.accessToken) {
|
|
154
|
+
return res.status(401).json({ error: 'Unauthorized' });
|
|
155
|
+
}
|
|
156
|
+
const { owner, repo, runId } = req.params;
|
|
157
|
+
const page = parseInt(req.query.page) || 1;
|
|
158
|
+
// Get run details
|
|
159
|
+
const { run, rateLimit: runRateLimit } = await githubActionsService.getWorkflowRun(owner, repo, parseInt(runId), req.accessToken);
|
|
160
|
+
// Get jobs
|
|
161
|
+
const { jobs, totalCount, rateLimit: jobsRateLimit } = await githubActionsService.getWorkflowRunJobs(owner, repo, parseInt(runId), req.accessToken, page);
|
|
162
|
+
// Transform jobs to match frontend expectations
|
|
163
|
+
const transformedJobs = jobs.map((job) => ({
|
|
164
|
+
id: job.id,
|
|
165
|
+
gitHubJobId: job.id,
|
|
166
|
+
name: job.name,
|
|
167
|
+
status: job.status,
|
|
168
|
+
conclusion: job.conclusion || null,
|
|
169
|
+
htmlUrl: job.html_url,
|
|
170
|
+
startedAt: job.started_at,
|
|
171
|
+
completedAt: job.completed_at,
|
|
172
|
+
}));
|
|
173
|
+
res.json({
|
|
174
|
+
run,
|
|
175
|
+
jobs: transformedJobs,
|
|
176
|
+
pagination: {
|
|
177
|
+
page,
|
|
178
|
+
totalCount,
|
|
179
|
+
pageSize: transformedJobs.length,
|
|
180
|
+
},
|
|
181
|
+
rateLimit: {
|
|
182
|
+
limit: runRateLimit?.limit,
|
|
183
|
+
remaining: runRateLimit?.remaining,
|
|
184
|
+
reset: runRateLimit?.reset,
|
|
185
|
+
},
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
catch (error) {
|
|
189
|
+
logger_1.AppLogger.error(`Error getting workflow run: ${error}`);
|
|
190
|
+
res.status(500).json({ error: 'Failed to get workflow run' });
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Get job logs
|
|
195
|
+
* GET /api/workflows/:owner/:repo/jobs/:jobId/logs
|
|
196
|
+
*/
|
|
197
|
+
async function getJobLogs(req, res) {
|
|
198
|
+
try {
|
|
199
|
+
if (!req.user || !req.accessToken) {
|
|
200
|
+
return res.status(401).json({ error: 'Unauthorized' });
|
|
201
|
+
}
|
|
202
|
+
const { owner, repo, jobId } = req.params;
|
|
203
|
+
logger_1.AppLogger.info(`[LOGS] Fetching job logs: owner=${owner}, repo=${repo}, jobId=${jobId}, user=${req.user?.login}`);
|
|
204
|
+
const { logs, rateLimit } = await githubActionsService.getJobLogs(owner, repo, parseInt(jobId), req.accessToken);
|
|
205
|
+
logger_1.AppLogger.info(`[LOGS] Successfully fetched ${logs.length} characters of logs`);
|
|
206
|
+
res.json({
|
|
207
|
+
logs: logs || '',
|
|
208
|
+
rateLimit,
|
|
209
|
+
meta: {
|
|
210
|
+
size: logs.length,
|
|
211
|
+
isEmpty: !logs || logs.trim().length === 0,
|
|
212
|
+
},
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
catch (error) {
|
|
216
|
+
const errorMsg = error instanceof Error ? error.message : 'Unknown error';
|
|
217
|
+
logger_1.AppLogger.error(`[LOGS ERROR] Failed to fetch job logs: ${errorMsg}`);
|
|
218
|
+
res.status(500).json({
|
|
219
|
+
error: 'Failed to get job logs',
|
|
220
|
+
details: errorMsg,
|
|
221
|
+
jobId: req.params.jobId,
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Trigger a workflow run
|
|
227
|
+
* POST /api/workflows/:owner/:repo/trigger
|
|
228
|
+
*/
|
|
229
|
+
async function triggerWorkflow(req, res) {
|
|
230
|
+
try {
|
|
231
|
+
if (!req.user || !req.accessToken) {
|
|
232
|
+
return res.status(401).json({ error: 'Unauthorized' });
|
|
233
|
+
}
|
|
234
|
+
const { owner, repo } = req.params;
|
|
235
|
+
const { pipelineId, workflowId, ref, inputs } = req.body;
|
|
236
|
+
if (!workflowId || !ref) {
|
|
237
|
+
return res.status(400).json({
|
|
238
|
+
error: 'Missing required fields: workflowId, ref',
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
const result = await githubActionsService.triggerWorkflow(req.accessToken, {
|
|
242
|
+
owner,
|
|
243
|
+
repo,
|
|
244
|
+
workflow: workflowId,
|
|
245
|
+
ref,
|
|
246
|
+
inputs,
|
|
247
|
+
});
|
|
248
|
+
if (pipelineId) {
|
|
249
|
+
await pipelineService.createAuditLog(pipelineId, req.user.id, req.user.login, 'trigger', 'workflow', String(workflowId), `Trigger workflow`, {}, result.response.success ? 'success' : 'failure');
|
|
250
|
+
}
|
|
251
|
+
res.json({
|
|
252
|
+
success: result.response.success,
|
|
253
|
+
message: result.response.message,
|
|
254
|
+
rateLimit: result.rateLimit,
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
catch (error) {
|
|
258
|
+
logger_1.AppLogger.error(`Error triggering workflow: ${error}`);
|
|
259
|
+
res.status(500).json({ error: 'Failed to trigger workflow' });
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Get pipeline audit logs
|
|
264
|
+
* GET /api/pipelines/:pipelineId/audit
|
|
265
|
+
*/
|
|
266
|
+
async function getPipelineAuditLogs(req, res) {
|
|
267
|
+
try {
|
|
268
|
+
if (!req.user) {
|
|
269
|
+
return res.status(401).json({ error: 'Unauthorized' });
|
|
270
|
+
}
|
|
271
|
+
const { pipelineId } = req.params;
|
|
272
|
+
const limit = Math.min(parseInt(req.query.limit) || 50, 500);
|
|
273
|
+
const offset = parseInt(req.query.offset) || 0;
|
|
274
|
+
const logs = await pipelineService.getPipelineAuditLogs(pipelineId, limit, offset);
|
|
275
|
+
res.json({
|
|
276
|
+
success: true,
|
|
277
|
+
logs,
|
|
278
|
+
count: logs.length,
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
catch (error) {
|
|
282
|
+
logger_1.AppLogger.error(`Error getting audit logs: ${error}`);
|
|
283
|
+
res.status(500).json({ error: 'Failed to get audit logs' });
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Cancel a workflow run
|
|
288
|
+
* POST /api/workflows/:owner/:repo/runs/:runId/cancel
|
|
289
|
+
*/
|
|
290
|
+
async function cancelWorkflowRun(req, res) {
|
|
291
|
+
try {
|
|
292
|
+
if (!req.user || !req.accessToken) {
|
|
293
|
+
return res.status(401).json({ error: 'Unauthorized' });
|
|
294
|
+
}
|
|
295
|
+
const { owner, repo, runId } = req.params;
|
|
296
|
+
const { pipelineId } = req.body;
|
|
297
|
+
const { success, rateLimit } = await githubActionsService.cancelWorkflowRun(owner, repo, parseInt(runId), req.accessToken);
|
|
298
|
+
if (pipelineId) {
|
|
299
|
+
await pipelineService.createAuditLog(pipelineId, req.user.id, req.user.login, 'cancel', 'workflow_run', String(runId), `Cancel workflow run ${runId}`, {}, success ? 'success' : 'failure');
|
|
300
|
+
}
|
|
301
|
+
res.json({ success, rateLimit });
|
|
302
|
+
}
|
|
303
|
+
catch (error) {
|
|
304
|
+
logger_1.AppLogger.error(`Error cancelling workflow: ${error}`);
|
|
305
|
+
res.status(500).json({ error: 'Failed to cancel workflow' });
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Rerun a workflow
|
|
310
|
+
* POST /api/workflows/:owner/:repo/runs/:runId/rerun
|
|
311
|
+
*/
|
|
312
|
+
async function rerunWorkflow(req, res) {
|
|
313
|
+
try {
|
|
314
|
+
if (!req.user || !req.accessToken) {
|
|
315
|
+
return res.status(401).json({ error: 'Unauthorized' });
|
|
316
|
+
}
|
|
317
|
+
const { owner, repo, runId } = req.params;
|
|
318
|
+
const { failedOnly = false, pipelineId } = req.body;
|
|
319
|
+
const { success, rateLimit } = await githubActionsService.rerunWorkflow(owner, repo, parseInt(runId), req.accessToken, failedOnly);
|
|
320
|
+
if (pipelineId) {
|
|
321
|
+
await pipelineService.createAuditLog(pipelineId, req.user.id, req.user.login, 'rerun', 'workflow_run', String(runId), `Rerun workflow ${failedOnly ? '(failed jobs only)' : ''}`, { failedOnly }, success ? 'success' : 'failure');
|
|
322
|
+
}
|
|
323
|
+
res.json({ success, rateLimit });
|
|
324
|
+
}
|
|
325
|
+
catch (error) {
|
|
326
|
+
logger_1.AppLogger.error(`Error rerunning workflow: ${error}`);
|
|
327
|
+
res.status(500).json({ error: 'Failed to rerun workflow' });
|
|
328
|
+
}
|
|
329
|
+
}
|