cerber-core 1.0.4 → 1.1.0
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 +158 -68
- package/README.md +75 -0
- package/USAGE_GUIDE.md +254 -0
- package/bin/cerber +121 -105
- package/dev/templates/cerber-guardian.mjs.tpl +44 -0
- package/dev/templates/cerber.yml.tpl +53 -0
- package/dev/templates/health-checks.ts.tpl +11 -0
- package/dev/templates/health-route.ts.tpl +50 -0
- package/dev/templates/pre-commit.tpl +4 -0
- package/dist/cli/contract-parser.d.ts +13 -0
- package/dist/cli/contract-parser.d.ts.map +1 -0
- package/dist/cli/contract-parser.js +241 -0
- package/dist/cli/contract-parser.js.map +1 -0
- package/dist/cli/init.d.ts +11 -0
- package/dist/cli/init.d.ts.map +1 -0
- package/dist/cli/init.js +241 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/cli/template-generator.d.ts +28 -0
- package/dist/cli/template-generator.d.ts.map +1 -0
- package/dist/cli/template-generator.js +227 -0
- package/dist/cli/template-generator.js.map +1 -0
- package/dist/cli/types.d.ts +70 -0
- package/dist/cli/types.d.ts.map +1 -0
- package/dist/cli/types.js +8 -0
- package/dist/cli/types.js.map +1 -0
- package/package.json +106 -104
- package/solo/templates/cerber-guardian.mjs.tpl +44 -0
- package/solo/templates/cerber.yml.tpl +53 -0
- package/solo/templates/health-checks.ts.tpl +29 -0
- package/solo/templates/health-route.ts.tpl +50 -0
- package/solo/templates/pre-commit.tpl +4 -0
- package/team/templates/CODEOWNERS.tpl +6 -0
- package/team/templates/cerber-guardian.mjs.tpl +44 -0
- package/team/templates/cerber.yml.tpl +53 -0
- package/team/templates/health-checks.ts.tpl +10 -0
- package/team/templates/health-route.ts.tpl +50 -0
- package/team/templates/pre-commit.tpl +4 -0
package/package.json
CHANGED
|
@@ -1,104 +1,106 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "cerber-core",
|
|
3
|
-
"version": "1.0
|
|
4
|
-
"description": "Your architecture roadmap becomes executable law. Guardian blocks commits that violate your plan. Pay architect once, Cerber enforces forever. Save $6k/month/dev.",
|
|
5
|
-
"main": "dist/index.js",
|
|
6
|
-
"types": "dist/index.d.ts",
|
|
7
|
-
"type": "module",
|
|
8
|
-
"bin": {
|
|
9
|
-
"cerber": "./bin/cerber",
|
|
10
|
-
"cerber-guardian": "./bin/cerber-guardian",
|
|
11
|
-
"cerber-health": "./bin/cerber-health",
|
|
12
|
-
"cerber-focus": "./bin/cerber-focus",
|
|
13
|
-
"cerber-morning": "./bin/cerber-morning",
|
|
14
|
-
"cerber-repair": "./bin/cerber-repair"
|
|
15
|
-
},
|
|
16
|
-
"exports": {
|
|
17
|
-
".": "./dist/index.js",
|
|
18
|
-
"./guardian": "./dist/guardian/index.js",
|
|
19
|
-
"./cerber": "./dist/cerber/index.js",
|
|
20
|
-
"./types": "./dist/types.js"
|
|
21
|
-
},
|
|
22
|
-
"scripts": {
|
|
23
|
-
"build": "tsc",
|
|
24
|
-
"test": "jest",
|
|
25
|
-
"lint": "eslint src/**/*.ts",
|
|
26
|
-
"format": "prettier --write \"src/**/*.ts\"",
|
|
27
|
-
"prepublishOnly": "npm run build",
|
|
28
|
-
"validate": "node bin/cerber-guardian",
|
|
29
|
-
"health-check": "node bin/cerber-health",
|
|
30
|
-
"morning": "node solo/scripts/cerber-daily-check.js",
|
|
31
|
-
"repair": "node solo/scripts/cerber-auto-repair.js",
|
|
32
|
-
"repair:dry": "node solo/scripts/cerber-auto-repair.js --dry-run",
|
|
33
|
-
"dashboard": "node solo/scripts/cerber-dashboard.js"
|
|
34
|
-
},
|
|
35
|
-
"repository": {
|
|
36
|
-
"type": "git",
|
|
37
|
-
"url": "git+https://github.com/Agaslez/cerber-core.git"
|
|
38
|
-
},
|
|
39
|
-
"keywords": [
|
|
40
|
-
"pre-commit",
|
|
41
|
-
"code-quality",
|
|
42
|
-
"architecture",
|
|
43
|
-
"validation",
|
|
44
|
-
"health-check",
|
|
45
|
-
"monitoring",
|
|
46
|
-
"cerber",
|
|
47
|
-
"guardian",
|
|
48
|
-
"typescript",
|
|
49
|
-
"ci-cd",
|
|
50
|
-
"automation",
|
|
51
|
-
"team-collaboration",
|
|
52
|
-
"solo-developer",
|
|
53
|
-
"focus-mode",
|
|
54
|
-
"module-system"
|
|
55
|
-
],
|
|
56
|
-
"author": {
|
|
57
|
-
"name": "Agata Sleziak",
|
|
58
|
-
"email": "agata@example.com",
|
|
59
|
-
"url": "https://github.com/Agaslez"
|
|
60
|
-
},
|
|
61
|
-
"contributors": [
|
|
62
|
-
{
|
|
63
|
-
"name": "Stefan Pitek",
|
|
64
|
-
"role": "Collaborator"
|
|
65
|
-
}
|
|
66
|
-
],
|
|
67
|
-
"license": "MIT",
|
|
68
|
-
"bugs": {
|
|
69
|
-
"url": "https://github.com/Agaslez/cerber-core/issues"
|
|
70
|
-
},
|
|
71
|
-
"homepage": "https://github.com/Agaslez/cerber-core#readme",
|
|
72
|
-
"peerDependencies": {
|
|
73
|
-
"typescript": ">=5.0.0"
|
|
74
|
-
},
|
|
75
|
-
"dependencies": {
|
|
76
|
-
"chalk": "^5.3.0",
|
|
77
|
-
"commander": "^12.0.0"
|
|
78
|
-
},
|
|
79
|
-
"devDependencies": {
|
|
80
|
-
"@types/jest": "^29.5.0",
|
|
81
|
-
"@types/node": "^20.0.0",
|
|
82
|
-
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
|
83
|
-
"@typescript-eslint/parser": "^6.0.0",
|
|
84
|
-
"eslint": "^8.50.0",
|
|
85
|
-
"jest": "^29.7.0",
|
|
86
|
-
"prettier": "^3.0.0",
|
|
87
|
-
"ts-jest": "^29.1.0",
|
|
88
|
-
"typescript": "^5.3.0"
|
|
89
|
-
},
|
|
90
|
-
"engines": {
|
|
91
|
-
"node": ">=16.0.0"
|
|
92
|
-
},
|
|
93
|
-
"files": [
|
|
94
|
-
"dist",
|
|
95
|
-
"bin",
|
|
96
|
-
"
|
|
97
|
-
"
|
|
98
|
-
"
|
|
99
|
-
"
|
|
100
|
-
"
|
|
101
|
-
"
|
|
102
|
-
"
|
|
103
|
-
|
|
104
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "cerber-core",
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "Your architecture roadmap becomes executable law. Guardian blocks commits that violate your plan. Pay architect once, Cerber enforces forever. Save $6k/month/dev.",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"bin": {
|
|
9
|
+
"cerber": "./bin/cerber",
|
|
10
|
+
"cerber-guardian": "./bin/cerber-guardian",
|
|
11
|
+
"cerber-health": "./bin/cerber-health",
|
|
12
|
+
"cerber-focus": "./bin/cerber-focus",
|
|
13
|
+
"cerber-morning": "./bin/cerber-morning",
|
|
14
|
+
"cerber-repair": "./bin/cerber-repair"
|
|
15
|
+
},
|
|
16
|
+
"exports": {
|
|
17
|
+
".": "./dist/index.js",
|
|
18
|
+
"./guardian": "./dist/guardian/index.js",
|
|
19
|
+
"./cerber": "./dist/cerber/index.js",
|
|
20
|
+
"./types": "./dist/types.js"
|
|
21
|
+
},
|
|
22
|
+
"scripts": {
|
|
23
|
+
"build": "tsc",
|
|
24
|
+
"test": "jest",
|
|
25
|
+
"lint": "eslint src/**/*.ts",
|
|
26
|
+
"format": "prettier --write \"src/**/*.ts\"",
|
|
27
|
+
"prepublishOnly": "npm run build",
|
|
28
|
+
"validate": "node bin/cerber-guardian",
|
|
29
|
+
"health-check": "node bin/cerber-health",
|
|
30
|
+
"morning": "node solo/scripts/cerber-daily-check.js",
|
|
31
|
+
"repair": "node solo/scripts/cerber-auto-repair.js",
|
|
32
|
+
"repair:dry": "node solo/scripts/cerber-auto-repair.js --dry-run",
|
|
33
|
+
"dashboard": "node solo/scripts/cerber-dashboard.js"
|
|
34
|
+
},
|
|
35
|
+
"repository": {
|
|
36
|
+
"type": "git",
|
|
37
|
+
"url": "git+https://github.com/Agaslez/cerber-core.git"
|
|
38
|
+
},
|
|
39
|
+
"keywords": [
|
|
40
|
+
"pre-commit",
|
|
41
|
+
"code-quality",
|
|
42
|
+
"architecture",
|
|
43
|
+
"validation",
|
|
44
|
+
"health-check",
|
|
45
|
+
"monitoring",
|
|
46
|
+
"cerber",
|
|
47
|
+
"guardian",
|
|
48
|
+
"typescript",
|
|
49
|
+
"ci-cd",
|
|
50
|
+
"automation",
|
|
51
|
+
"team-collaboration",
|
|
52
|
+
"solo-developer",
|
|
53
|
+
"focus-mode",
|
|
54
|
+
"module-system"
|
|
55
|
+
],
|
|
56
|
+
"author": {
|
|
57
|
+
"name": "Agata Sleziak",
|
|
58
|
+
"email": "agata@example.com",
|
|
59
|
+
"url": "https://github.com/Agaslez"
|
|
60
|
+
},
|
|
61
|
+
"contributors": [
|
|
62
|
+
{
|
|
63
|
+
"name": "Stefan Pitek",
|
|
64
|
+
"role": "Collaborator"
|
|
65
|
+
}
|
|
66
|
+
],
|
|
67
|
+
"license": "MIT",
|
|
68
|
+
"bugs": {
|
|
69
|
+
"url": "https://github.com/Agaslez/cerber-core/issues"
|
|
70
|
+
},
|
|
71
|
+
"homepage": "https://github.com/Agaslez/cerber-core#readme",
|
|
72
|
+
"peerDependencies": {
|
|
73
|
+
"typescript": ">=5.0.0"
|
|
74
|
+
},
|
|
75
|
+
"dependencies": {
|
|
76
|
+
"chalk": "^5.3.0",
|
|
77
|
+
"commander": "^12.0.0"
|
|
78
|
+
},
|
|
79
|
+
"devDependencies": {
|
|
80
|
+
"@types/jest": "^29.5.0",
|
|
81
|
+
"@types/node": "^20.0.0",
|
|
82
|
+
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
|
83
|
+
"@typescript-eslint/parser": "^6.0.0",
|
|
84
|
+
"eslint": "^8.50.0",
|
|
85
|
+
"jest": "^29.7.0",
|
|
86
|
+
"prettier": "^3.0.0",
|
|
87
|
+
"ts-jest": "^29.1.0",
|
|
88
|
+
"typescript": "^5.3.0"
|
|
89
|
+
},
|
|
90
|
+
"engines": {
|
|
91
|
+
"node": ">=16.0.0"
|
|
92
|
+
},
|
|
93
|
+
"files": [
|
|
94
|
+
"dist",
|
|
95
|
+
"bin",
|
|
96
|
+
"examples",
|
|
97
|
+
".cerber-example",
|
|
98
|
+
"solo",
|
|
99
|
+
"dev",
|
|
100
|
+
"team",
|
|
101
|
+
"LICENSE",
|
|
102
|
+
"README.md",
|
|
103
|
+
"CHANGELOG.md",
|
|
104
|
+
"USAGE_GUIDE.md"
|
|
105
|
+
]
|
|
106
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Generated by Cerber init - DO NOT EDIT MANUALLY
|
|
3
|
+
// To regenerate: npx cerber init --force
|
|
4
|
+
|
|
5
|
+
import { execSync } from 'child_process';
|
|
6
|
+
import fs from 'fs';
|
|
7
|
+
|
|
8
|
+
const SCHEMA_FILE = '{{SCHEMA_FILE}}';
|
|
9
|
+
const APPROVALS_TAG = '{{APPROVALS_TAG}}';
|
|
10
|
+
|
|
11
|
+
async function main() {
|
|
12
|
+
console.log('🛡️ Cerber Guardian: Validating staged files...');
|
|
13
|
+
|
|
14
|
+
if (!fs.existsSync(SCHEMA_FILE)) {
|
|
15
|
+
console.error(`❌ Schema file not found: ${SCHEMA_FILE}`);
|
|
16
|
+
console.error('Guardian MVP: schema missing. Add your rules to proceed.');
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
let stagedFiles;
|
|
21
|
+
try {
|
|
22
|
+
stagedFiles = execSync('git diff --cached --name-only', { encoding: 'utf-8' })
|
|
23
|
+
.trim()
|
|
24
|
+
.split('\n')
|
|
25
|
+
.filter(Boolean);
|
|
26
|
+
} catch (err) {
|
|
27
|
+
console.error('❌ Failed to get staged files');
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (stagedFiles.length === 0) {
|
|
32
|
+
console.log('✅ No files staged for commit');
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
console.log(`Checking ${stagedFiles.length} file(s)...`);
|
|
37
|
+
console.log('Guardian MVP: schema detected. Add validation rules to enforce imports/forbidden patterns.');
|
|
38
|
+
console.log('✅ All checks passed');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
main().catch(err => {
|
|
42
|
+
console.error('❌ Guardian check failed:', err?.message || err);
|
|
43
|
+
process.exit(1);
|
|
44
|
+
});
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Generated by Cerber init
|
|
2
|
+
name: Cerber CI
|
|
3
|
+
|
|
4
|
+
on:
|
|
5
|
+
push:
|
|
6
|
+
branches: {{CI_BRANCHES}}
|
|
7
|
+
pull_request:
|
|
8
|
+
branches: {{CI_BRANCHES}}
|
|
9
|
+
workflow_dispatch:
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
cerber-ci:
|
|
13
|
+
name: Cerber CI
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
|
|
16
|
+
steps:
|
|
17
|
+
- name: Checkout code
|
|
18
|
+
uses: actions/checkout@v4
|
|
19
|
+
|
|
20
|
+
- name: Setup Node.js
|
|
21
|
+
uses: actions/setup-node@v4
|
|
22
|
+
with:
|
|
23
|
+
node-version: '20'
|
|
24
|
+
cache: 'npm'
|
|
25
|
+
|
|
26
|
+
- name: Install dependencies
|
|
27
|
+
run: npm ci
|
|
28
|
+
|
|
29
|
+
- name: Build (if script exists)
|
|
30
|
+
run: |
|
|
31
|
+
if npm run | grep -q "build"; then
|
|
32
|
+
npm run build
|
|
33
|
+
else
|
|
34
|
+
echo "No build script defined; skipping"
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
- name: Test (if script exists)
|
|
38
|
+
run: |
|
|
39
|
+
if npm run | grep -q "test"; then
|
|
40
|
+
npm test
|
|
41
|
+
else
|
|
42
|
+
echo "No test script defined; skipping"
|
|
43
|
+
fi
|
|
44
|
+
|
|
45
|
+
- name: Run Guardian
|
|
46
|
+
run: |
|
|
47
|
+
if npm run | grep -q "cerber:guardian"; then
|
|
48
|
+
npm run cerber:guardian
|
|
49
|
+
else
|
|
50
|
+
node scripts/cerber-guardian.mjs
|
|
51
|
+
fi
|
|
52
|
+
|
|
53
|
+
{{POST_DEPLOY_BLOCK}}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// Generated by Cerber init - CUSTOMIZE THIS FILE
|
|
2
|
+
// To regenerate template: npx cerber init --force
|
|
3
|
+
|
|
4
|
+
import { CerberCheck, makeIssue } from 'cerber-core';
|
|
5
|
+
|
|
6
|
+
export const checks: Record<string, CerberCheck> = {
|
|
7
|
+
database: async () => {
|
|
8
|
+
// TODO: Implement your database health check
|
|
9
|
+
// Example:
|
|
10
|
+
// try {
|
|
11
|
+
// await db.query('SELECT 1');
|
|
12
|
+
// return [];
|
|
13
|
+
// } catch (err) {
|
|
14
|
+
// return [
|
|
15
|
+
// makeIssue({
|
|
16
|
+
// code: 'DB_CONNECTION_FAILED',
|
|
17
|
+
// component: 'database',
|
|
18
|
+
// diagnosis: 'Cannot connect to database',
|
|
19
|
+
// rootCause: 'Database server is down or unreachable',
|
|
20
|
+
// fix: 'Check DATABASE_URL and verify DB is running',
|
|
21
|
+
// severity: 'critical',
|
|
22
|
+
// durationMs: 0
|
|
23
|
+
// })
|
|
24
|
+
// ];
|
|
25
|
+
// }
|
|
26
|
+
|
|
27
|
+
return []; // Placeholder - implement your check
|
|
28
|
+
},
|
|
29
|
+
};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// Generated by Cerber init - CUSTOMIZE THIS FILE
|
|
2
|
+
|
|
3
|
+
import { checks } from './health-checks.js';
|
|
4
|
+
|
|
5
|
+
export async function healthHandler(req: any, res: any) {
|
|
6
|
+
const startTime = Date.now();
|
|
7
|
+
|
|
8
|
+
try {
|
|
9
|
+
const results = await Promise.all(
|
|
10
|
+
Object.entries(checks).map(async ([name, check]) => ({
|
|
11
|
+
name,
|
|
12
|
+
issues: await check()
|
|
13
|
+
}))
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
const allIssues = results.flatMap(r => r.issues);
|
|
17
|
+
const critical = allIssues.filter(i => i.severity === 'critical').length;
|
|
18
|
+
const errors = allIssues.filter(i => i.severity === 'error').length;
|
|
19
|
+
const warnings = allIssues.filter(i => i.severity === 'warning').length;
|
|
20
|
+
|
|
21
|
+
const status = critical > 0 ? 'unhealthy' :
|
|
22
|
+
errors > 0 ? 'degraded' : 'healthy';
|
|
23
|
+
|
|
24
|
+
const statusCode = status === 'healthy' ? 200 : 503;
|
|
25
|
+
|
|
26
|
+
res.status(statusCode).json({
|
|
27
|
+
status,
|
|
28
|
+
timestamp: new Date().toISOString(),
|
|
29
|
+
durationMs: Date.now() - startTime,
|
|
30
|
+
summary: {
|
|
31
|
+
totalChecks: results.length,
|
|
32
|
+
failedChecks: results.filter(r => r.issues.length > 0).length,
|
|
33
|
+
criticalIssues: critical,
|
|
34
|
+
errorIssues: errors,
|
|
35
|
+
warningIssues: warnings
|
|
36
|
+
},
|
|
37
|
+
components: allIssues
|
|
38
|
+
});
|
|
39
|
+
} catch (err: any) {
|
|
40
|
+
res.status(503).json({
|
|
41
|
+
status: 'error',
|
|
42
|
+
message: 'Health check failed',
|
|
43
|
+
error: err.message
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Usage in your server:
|
|
49
|
+
// import { healthHandler } from './cerber/health-route.js';
|
|
50
|
+
// app.get('{{HEALTH_ENDPOINT}}', healthHandler);
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Generated by Cerber init - DO NOT EDIT MANUALLY
|
|
3
|
+
// To regenerate: npx cerber init --force
|
|
4
|
+
|
|
5
|
+
import { execSync } from 'child_process';
|
|
6
|
+
import fs from 'fs';
|
|
7
|
+
|
|
8
|
+
const SCHEMA_FILE = '{{SCHEMA_FILE}}';
|
|
9
|
+
const APPROVALS_TAG = '{{APPROVALS_TAG}}';
|
|
10
|
+
|
|
11
|
+
async function main() {
|
|
12
|
+
console.log('🛡️ Cerber Guardian: Validating staged files...');
|
|
13
|
+
|
|
14
|
+
if (!fs.existsSync(SCHEMA_FILE)) {
|
|
15
|
+
console.error(`❌ Schema file not found: ${SCHEMA_FILE}`);
|
|
16
|
+
console.error('Guardian MVP: schema missing. Add your rules to proceed.');
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
let stagedFiles;
|
|
21
|
+
try {
|
|
22
|
+
stagedFiles = execSync('git diff --cached --name-only', { encoding: 'utf-8' })
|
|
23
|
+
.trim()
|
|
24
|
+
.split('\n')
|
|
25
|
+
.filter(Boolean);
|
|
26
|
+
} catch (err) {
|
|
27
|
+
console.error('❌ Failed to get staged files');
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (stagedFiles.length === 0) {
|
|
32
|
+
console.log('✅ No files staged for commit');
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
console.log(`Checking ${stagedFiles.length} file(s)...`);
|
|
37
|
+
console.log('Guardian MVP: schema detected. Add validation rules to enforce imports/forbidden patterns.');
|
|
38
|
+
console.log('✅ All checks passed');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
main().catch(err => {
|
|
42
|
+
console.error('❌ Guardian check failed:', err?.message || err);
|
|
43
|
+
process.exit(1);
|
|
44
|
+
});
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Generated by Cerber init
|
|
2
|
+
name: Cerber CI
|
|
3
|
+
|
|
4
|
+
on:
|
|
5
|
+
push:
|
|
6
|
+
branches: {{CI_BRANCHES}}
|
|
7
|
+
pull_request:
|
|
8
|
+
branches: {{CI_BRANCHES}}
|
|
9
|
+
workflow_dispatch:
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
cerber-ci:
|
|
13
|
+
name: Cerber CI
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
|
|
16
|
+
steps:
|
|
17
|
+
- name: Checkout code
|
|
18
|
+
uses: actions/checkout@v4
|
|
19
|
+
|
|
20
|
+
- name: Setup Node.js
|
|
21
|
+
uses: actions/setup-node@v4
|
|
22
|
+
with:
|
|
23
|
+
node-version: '20'
|
|
24
|
+
cache: 'npm'
|
|
25
|
+
|
|
26
|
+
- name: Install dependencies
|
|
27
|
+
run: npm ci
|
|
28
|
+
|
|
29
|
+
- name: Build (if script exists)
|
|
30
|
+
run: |
|
|
31
|
+
if npm run | grep -q "build"; then
|
|
32
|
+
npm run build
|
|
33
|
+
else
|
|
34
|
+
echo "No build script defined; skipping"
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
- name: Test (if script exists)
|
|
38
|
+
run: |
|
|
39
|
+
if npm run | grep -q "test"; then
|
|
40
|
+
npm test
|
|
41
|
+
else
|
|
42
|
+
echo "No test script defined; skipping"
|
|
43
|
+
fi
|
|
44
|
+
|
|
45
|
+
- name: Run Guardian
|
|
46
|
+
run: |
|
|
47
|
+
if npm run | grep -q "cerber:guardian"; then
|
|
48
|
+
npm run cerber:guardian
|
|
49
|
+
else
|
|
50
|
+
node scripts/cerber-guardian.mjs
|
|
51
|
+
fi
|
|
52
|
+
|
|
53
|
+
{{POST_DEPLOY_BLOCK}}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// Generated by Cerber init - CUSTOMIZE THIS FILE
|
|
2
|
+
// To regenerate template: npx cerber init --force
|
|
3
|
+
|
|
4
|
+
import { CerberCheck, makeIssue } from 'cerber-core';
|
|
5
|
+
|
|
6
|
+
export const checks: Record<string, CerberCheck> = {
|
|
7
|
+
database: async () => {
|
|
8
|
+
return [];
|
|
9
|
+
},
|
|
10
|
+
};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// Generated by Cerber init - CUSTOMIZE THIS FILE
|
|
2
|
+
|
|
3
|
+
import { checks } from './health-checks.js';
|
|
4
|
+
|
|
5
|
+
export async function healthHandler(req: any, res: any) {
|
|
6
|
+
const startTime = Date.now();
|
|
7
|
+
|
|
8
|
+
try {
|
|
9
|
+
const results = await Promise.all(
|
|
10
|
+
Object.entries(checks).map(async ([name, check]) => ({
|
|
11
|
+
name,
|
|
12
|
+
issues: await check()
|
|
13
|
+
}))
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
const allIssues = results.flatMap(r => r.issues);
|
|
17
|
+
const critical = allIssues.filter(i => i.severity === 'critical').length;
|
|
18
|
+
const errors = allIssues.filter(i => i.severity === 'error').length;
|
|
19
|
+
const warnings = allIssues.filter(i => i.severity === 'warning').length;
|
|
20
|
+
|
|
21
|
+
const status = critical > 0 ? 'unhealthy' :
|
|
22
|
+
errors > 0 ? 'degraded' : 'healthy';
|
|
23
|
+
|
|
24
|
+
const statusCode = status === 'healthy' ? 200 : 503;
|
|
25
|
+
|
|
26
|
+
res.status(statusCode).json({
|
|
27
|
+
status,
|
|
28
|
+
timestamp: new Date().toISOString(),
|
|
29
|
+
durationMs: Date.now() - startTime,
|
|
30
|
+
summary: {
|
|
31
|
+
totalChecks: results.length,
|
|
32
|
+
failedChecks: results.filter(r => r.issues.length > 0).length,
|
|
33
|
+
criticalIssues: critical,
|
|
34
|
+
errorIssues: errors,
|
|
35
|
+
warningIssues: warnings
|
|
36
|
+
},
|
|
37
|
+
components: allIssues
|
|
38
|
+
});
|
|
39
|
+
} catch (err: any) {
|
|
40
|
+
res.status(503).json({
|
|
41
|
+
status: 'error',
|
|
42
|
+
message: 'Health check failed',
|
|
43
|
+
error: err.message
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Usage in your server:
|
|
49
|
+
// import { healthHandler } from './cerber/health-route.js';
|
|
50
|
+
// app.get('{{HEALTH_ENDPOINT}}', healthHandler);
|