@programinglive/dev-workflow-mcp-server 1.8.1 → 1.10.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/.env.example +21 -1
- package/AGENTS.md +25 -0
- package/README.md +372 -649
- package/factory-client.js +554 -0
- package/git-facade.js +35 -0
- package/index.js +9 -34
- package/package.json +21 -20
- package/scripts/bootstrap-unix.sh +55 -0
- package/scripts/bootstrap-windows.ps1 +75 -0
- package/scripts/install-husky.js +43 -0
- package/scripts/setup-stack.js +436 -0
- package/scripts/setup-test-dbs.sh +61 -0
- package/scripts/test-db-adapters.sh +71 -0
- package/scripts/verify-bundle.mjs +92 -0
- package/tools/definitions.js +143 -0
- package/tools/handler-modules/commit-handlers.js +2 -1
- package/tools/handler-modules/factory-handlers.js +276 -0
- package/tools/handler-modules/index.js +24 -7
- package/tools/handler-modules/shared.js +7 -41
- package/tools/handler-modules/status-handlers.js +25 -3
- package/tools/handler-modules/workflow-handlers.js +57 -10
- package/vite.config.js +6 -0
- package/workflow-state.js +176 -2
- package/.agent/workflows/dev-workflow-bugfix.md +0 -250
- package/.agent/workflows/dev-workflow-feature.md +0 -201
- package/.agent/workflows/dev-workflow-full-cycle.md +0 -400
- package/.agent/workflows/dev-workflow-refactor.md +0 -238
- package/.env.production +0 -16
- package/.github/ISSUE_TEMPLATE/bug_report.md +0 -34
- package/.github/ISSUE_TEMPLATE/feature_request.md +0 -25
- package/.github/pull_request_template.md +0 -22
- package/.github/workflows/github-release.yml +0 -131
- package/.husky/commit-msg +0 -3
- package/.husky/pre-commit +0 -2
- package/CHANGELOG.md +0 -1145
- package/docs/GCP_DEPLOYMENT.md +0 -298
- package/docs/NETLIFY_DEPLOYMENT.md +0 -125
- package/docs/PLESK_DEPLOYMENT.md +0 -170
- package/docs/PRD.md +0 -86
- package/docs/WORKFLOW-BEST-PRACTICES.md +0 -58
- package/docs/antigravity/EXAMPLES.md +0 -535
- package/docs/antigravity/GETTING-STARTED.md +0 -292
- package/docs/antigravity/TROUBLESHOOTING.md +0 -742
- package/docs/antigravity/WALKTHROUGH.md +0 -25
- package/docs/antigravity/WORKFLOW-TEMPLATES.md +0 -670
- package/docs/release-notes/RELEASE_NOTES.md +0 -524
- package/docs/web-dashboard.md +0 -175
- package/scripts/test-auth-flow.js +0 -75
- package/tailwind.config.js +0 -8
- package/tests/build.test.js +0 -74
- package/tests/db-adapters.test.js +0 -108
- package/tests/env-support.test.js +0 -46
- package/tests/exec.test.js +0 -35
- package/tests/handlers-commit.test.js +0 -174
- package/tests/handlers-release.test.js +0 -212
- package/tests/handlers-workflow.test.js +0 -68
- package/tests/handlers.test.js +0 -1142
- package/tests/hello-world.test.js +0 -13
- package/tests/mcpRegistry.test.js +0 -65
- package/tests/patch-release.test.js +0 -7
- package/tests/release-wrapper.test.js +0 -107
- package/tests/startup-resilience.test.js +0 -73
- package/tests/test-helpers.js +0 -50
- package/tests/workflowState.test.js +0 -520
- package/web/Dockerfile.api +0 -25
- package/web/README.md +0 -133
- package/web/__tests__/api/workflow-history.test.js +0 -63
- package/web/api-server.js +0 -211
- package/web/app/docs/page.tsx +0 -202
- package/web/app/favicon.ico +0 -0
- package/web/app/globals.css +0 -91
- package/web/app/history/page.tsx +0 -220
- package/web/app/layout.tsx +0 -39
- package/web/app/login/page.tsx +0 -142
- package/web/app/page.tsx +0 -13
- package/web/components/ApiReference.tsx +0 -20
- package/web/components/Features.tsx +0 -61
- package/web/components/Footer.tsx +0 -9
- package/web/components/Hero.tsx +0 -96
- package/web/components/History.tsx +0 -175
- package/web/components/Navbar.tsx +0 -208
- package/web/components/Terminal.tsx +0 -114
- package/web/components/ThemeProvider.tsx +0 -58
- package/web/eslint.config.mjs +0 -18
- package/web/lib/api-client.ts +0 -123
- package/web/lib/auth.js +0 -75
- package/web/lib/db.js +0 -131
- package/web/lib/docs.js +0 -73
- package/web/middleware/auth.js +0 -26
- package/web/netlify.toml +0 -8
- package/web/next.config.ts +0 -19
- package/web/package-lock.json +0 -10911
- package/web/package.json +0 -42
- package/web/postcss.config.mjs +0 -9
- package/web/public/assets/android-chrome-192x192.png +0 -0
- package/web/public/assets/android-chrome-512x512.png +0 -0
- package/web/public/assets/apple-touch-icon.png +0 -0
- package/web/public/assets/favicon-16x16.png +0 -0
- package/web/public/assets/favicon-32x32.png +0 -0
- package/web/public/assets/favicon.ico +0 -0
- package/web/public/assets/favicon.png +0 -0
- package/web/public/assets/logo.png +0 -0
- package/web/public/assets/og-preview.png +0 -0
- package/web/public/assets/site.webmanifest +0 -1
- package/web/public/file.svg +0 -1
- package/web/public/globe.svg +0 -1
- package/web/public/next.svg +0 -1
- package/web/public/vercel.svg +0 -1
- package/web/public/window.svg +0 -1
- package/web/scripts/db-query-history.js +0 -120
- package/web/scripts/smoke-test.js +0 -30
- package/web/server.js +0 -184
- package/web/tailwind.config.ts +0 -20
- package/web/tsconfig.json +0 -42
package/web/package.json
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "web",
|
|
3
|
-
"version": "1.7.15",
|
|
4
|
-
"private": true,
|
|
5
|
-
"type": "module",
|
|
6
|
-
"scripts": {
|
|
7
|
-
"dev": "node server.js",
|
|
8
|
-
"build": "next build",
|
|
9
|
-
"start": "cross-env NODE_ENV=production node server.js",
|
|
10
|
-
"lint": "next lint"
|
|
11
|
-
},
|
|
12
|
-
"dependencies": {
|
|
13
|
-
"bcrypt": "^6.0.0",
|
|
14
|
-
"connect-pg-simple": "^10.0.0",
|
|
15
|
-
"cors": "^2.8.5",
|
|
16
|
-
"dotenv": "^17.2.3",
|
|
17
|
-
"express": "^5.2.1",
|
|
18
|
-
"express-session": "^1.18.2",
|
|
19
|
-
"jsonwebtoken": "^9.0.3",
|
|
20
|
-
"lucide-react": "^0.555.0",
|
|
21
|
-
"next": "^16.0.10",
|
|
22
|
-
"pg": "^8.16.3",
|
|
23
|
-
"react": "19.2.0",
|
|
24
|
-
"react-dom": "19.2.0"
|
|
25
|
-
},
|
|
26
|
-
"devDependencies": {
|
|
27
|
-
"@netlify/plugin-nextjs": "^5.15.2",
|
|
28
|
-
"@types/node": "^20",
|
|
29
|
-
"@types/pg": "^8.16.0",
|
|
30
|
-
"@types/react": "^19",
|
|
31
|
-
"@types/react-dom": "^19",
|
|
32
|
-
"autoprefixer": "^10.4.22",
|
|
33
|
-
"cross-env": "^10.1.0",
|
|
34
|
-
"eslint": "^9",
|
|
35
|
-
"eslint-config-next": "16.0.4",
|
|
36
|
-
"jest": "^30.2.0",
|
|
37
|
-
"postcss": "^8.5.6",
|
|
38
|
-
"supertest": "^7.1.4",
|
|
39
|
-
"tailwindcss": "^3.4.18",
|
|
40
|
-
"typescript": "^5"
|
|
41
|
-
}
|
|
42
|
-
}
|
package/web/postcss.config.mjs
DELETED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}
|
package/web/public/file.svg
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
<svg fill="none" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M14.5 13.5V5.41a1 1 0 0 0-.3-.7L9.8.29A1 1 0 0 0 9.08 0H1.5v13.5A2.5 2.5 0 0 0 4 16h8a2.5 2.5 0 0 0 2.5-2.5m-1.5 0v-7H8v-5H3v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1M9.5 5V2.12L12.38 5zM5.13 5h-.62v1.25h2.12V5zm-.62 3h7.12v1.25H4.5zm.62 3h-.62v1.25h7.12V11z" clip-rule="evenodd" fill="#666" fill-rule="evenodd"/></svg>
|
package/web/public/globe.svg
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g clip-path="url(#a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.27 14.1a6.5 6.5 0 0 0 3.67-3.45q-1.24.21-2.7.34-.31 1.83-.97 3.1M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m.48-1.52a7 7 0 0 1-.96 0H7.5a4 4 0 0 1-.84-1.32q-.38-.89-.63-2.08a40 40 0 0 0 3.92 0q-.25 1.2-.63 2.08a4 4 0 0 1-.84 1.31zm2.94-4.76q1.66-.15 2.95-.43a7 7 0 0 0 0-2.58q-1.3-.27-2.95-.43a18 18 0 0 1 0 3.44m-1.27-3.54a17 17 0 0 1 0 3.64 39 39 0 0 1-4.3 0 17 17 0 0 1 0-3.64 39 39 0 0 1 4.3 0m1.1-1.17q1.45.13 2.69.34a6.5 6.5 0 0 0-3.67-3.44q.65 1.26.98 3.1M8.48 1.5l.01.02q.41.37.84 1.31.38.89.63 2.08a40 40 0 0 0-3.92 0q.25-1.2.63-2.08a4 4 0 0 1 .85-1.32 7 7 0 0 1 .96 0m-2.75.4a6.5 6.5 0 0 0-3.67 3.44 29 29 0 0 1 2.7-.34q.31-1.83.97-3.1M4.58 6.28q-1.66.16-2.95.43a7 7 0 0 0 0 2.58q1.3.27 2.95.43a18 18 0 0 1 0-3.44m.17 4.71q-1.45-.12-2.69-.34a6.5 6.5 0 0 0 3.67 3.44q-.65-1.27-.98-3.1" fill="#666"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></svg>
|
package/web/public/next.svg
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>
|
package/web/public/vercel.svg
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1155 1000"><path d="m577.3 0 577.4 1000H0z" fill="#fff"/></svg>
|
package/web/public/window.svg
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 2.5h13v10a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1zM0 1h16v11.5a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5zm3.75 4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5M7 4.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0m1.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5" fill="#666"/></svg>
|
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
import pg from 'pg';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import fs from 'fs';
|
|
4
|
-
import { fileURLToPath } from 'url';
|
|
5
|
-
import dotenv from 'dotenv';
|
|
6
|
-
import { createRequire } from 'module';
|
|
7
|
-
|
|
8
|
-
const require = createRequire(import.meta.url);
|
|
9
|
-
const { Pool } = pg;
|
|
10
|
-
|
|
11
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
12
|
-
const __dirname = path.dirname(__filename);
|
|
13
|
-
|
|
14
|
-
// Silence stdout during dotenv loading to prevent noise in API output
|
|
15
|
-
const originalWrite = process.stdout.write;
|
|
16
|
-
process.stdout.write = () => true; // No-op
|
|
17
|
-
|
|
18
|
-
try {
|
|
19
|
-
// Try web/.env
|
|
20
|
-
dotenv.config({ path: path.join(__dirname, '../.env') });
|
|
21
|
-
// Try root .env
|
|
22
|
-
dotenv.config({ path: path.join(__dirname, '../../.env') });
|
|
23
|
-
} catch (e) {
|
|
24
|
-
// dotenv might not be available
|
|
25
|
-
} finally {
|
|
26
|
-
// Restore stdout
|
|
27
|
-
process.stdout.write = originalWrite;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const defaultUrl = 'postgres://devworkflow:devworkflow_secure_password_change_me@localhost:5432/devworkflow';
|
|
31
|
-
const connectionString = process.env.DEV_WORKFLOW_DB_URL || defaultUrl;
|
|
32
|
-
const rawSid = process.argv[2]; // Passed from API
|
|
33
|
-
|
|
34
|
-
// DEBUG: output connection details to stderr (so it doesn't break JSON on stdout)
|
|
35
|
-
try {
|
|
36
|
-
const urlStr = connectionString.startsWith('postgres') ? connectionString : `postgres://${connectionString}`; // handle simpler strings if needed
|
|
37
|
-
// Simple heuristic parsing or URL object
|
|
38
|
-
const u = new URL(urlStr);
|
|
39
|
-
console.error(`DEBUG: Connecting to Host: ${u.hostname}, Port: ${u.port}, User: ${u.username}`);
|
|
40
|
-
} catch (err) {
|
|
41
|
-
console.error('DEBUG: Could not parse connection string for logging');
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
if (!connectionString) {
|
|
45
|
-
// Should be unreachable with defaultUrl, but good for safety
|
|
46
|
-
console.error(JSON.stringify({ error: 'Database configuration missing' }));
|
|
47
|
-
process.exit(1);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const pool = new Pool({ connectionString });
|
|
51
|
-
|
|
52
|
-
async function run() {
|
|
53
|
-
try {
|
|
54
|
-
let userId = null;
|
|
55
|
-
|
|
56
|
-
// 1. Validate Session if provided
|
|
57
|
-
if (rawSid) {
|
|
58
|
-
// Parse 's:session_id.signature' -> 'session_id'
|
|
59
|
-
// Simplified logic: between 's:' and '.'
|
|
60
|
-
let sid = rawSid;
|
|
61
|
-
if (rawSid.startsWith('s:')) {
|
|
62
|
-
sid = rawSid.slice(2, rawSid.lastIndexOf('.'));
|
|
63
|
-
} else if (rawSid.includes('.')) {
|
|
64
|
-
// heuristic for signed cookie without s: prefix?
|
|
65
|
-
sid = rawSid.slice(0, rawSid.lastIndexOf('.'));
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// Try lookup
|
|
69
|
-
const sessionRes = await pool.query("SELECT sess FROM session WHERE sid = $1", [sid]);
|
|
70
|
-
if (sessionRes.rows.length > 0) {
|
|
71
|
-
const sessionData = sessionRes.rows[0].sess;
|
|
72
|
-
if (sessionData && sessionData.userId) {
|
|
73
|
-
userId = sessionData.userId;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// 2. Fallback if no valid session found (for dev/recovery comfort)
|
|
79
|
-
if (!userId) {
|
|
80
|
-
// Try admin
|
|
81
|
-
const userRes = await pool.query("SELECT id FROM users WHERE LOWER(username) = 'admin'");
|
|
82
|
-
if (userRes.rows.length > 0) {
|
|
83
|
-
userId = userRes.rows[0].id;
|
|
84
|
-
} else {
|
|
85
|
-
const anyUser = await pool.query("SELECT id FROM users LIMIT 1");
|
|
86
|
-
if (anyUser.rows.length > 0) {
|
|
87
|
-
userId = anyUser.rows[0].id;
|
|
88
|
-
} else {
|
|
89
|
-
throw new Error('No user found');
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// 3. (Self-healing removed as it was causing schema errors)
|
|
95
|
-
|
|
96
|
-
const result = await pool.query(`
|
|
97
|
-
SELECT
|
|
98
|
-
id,
|
|
99
|
-
task_type,
|
|
100
|
-
description,
|
|
101
|
-
commit_message,
|
|
102
|
-
completed_at,
|
|
103
|
-
tests_passed,
|
|
104
|
-
documentation_type
|
|
105
|
-
FROM workflow_history
|
|
106
|
-
WHERE user_id = $1
|
|
107
|
-
ORDER BY completed_at DESC LIMIT 100
|
|
108
|
-
`, [userId]);
|
|
109
|
-
|
|
110
|
-
console.log(JSON.stringify({ history: result.rows }));
|
|
111
|
-
|
|
112
|
-
} catch (error) {
|
|
113
|
-
console.error(JSON.stringify({ error: error.message, stack: error.stack }));
|
|
114
|
-
process.exit(1);
|
|
115
|
-
} finally {
|
|
116
|
-
await pool.end();
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
run();
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
|
-
import { execSync } from 'child_process';
|
|
3
|
-
import path from 'path';
|
|
4
|
-
import { fileURLToPath } from 'url';
|
|
5
|
-
|
|
6
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
7
|
-
const rootDir = path.join(__dirname, '..');
|
|
8
|
-
|
|
9
|
-
console.log('Running smoke tests...');
|
|
10
|
-
|
|
11
|
-
try {
|
|
12
|
-
// 1. Verify package.json type: module
|
|
13
|
-
const packageJsonPath = path.join(rootDir, 'package.json');
|
|
14
|
-
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
15
|
-
if (packageJson.type !== 'module') {
|
|
16
|
-
throw new Error('FAIL: package.json must have "type": "module"');
|
|
17
|
-
}
|
|
18
|
-
console.log('✅ package.json has "type": "module"');
|
|
19
|
-
|
|
20
|
-
// 2. Syntax check server.js
|
|
21
|
-
const serverJsPath = path.join(rootDir, 'server.js');
|
|
22
|
-
execSync(`node -c "${serverJsPath}"`, { stdio: 'inherit' });
|
|
23
|
-
console.log('✅ server.js syntax check passed');
|
|
24
|
-
|
|
25
|
-
console.log('All smoke tests passed!');
|
|
26
|
-
process.exit(0);
|
|
27
|
-
} catch (error) {
|
|
28
|
-
console.error('❌ Smoke test failed:', error.message);
|
|
29
|
-
process.exit(1);
|
|
30
|
-
}
|
package/web/server.js
DELETED
|
@@ -1,184 +0,0 @@
|
|
|
1
|
-
import { createServer } from 'http';
|
|
2
|
-
import { parse } from 'url';
|
|
3
|
-
import next from 'next';
|
|
4
|
-
import express from 'express';
|
|
5
|
-
import session from 'express-session';
|
|
6
|
-
import connectPgSimple from 'connect-pg-simple';
|
|
7
|
-
import pg from 'pg';
|
|
8
|
-
import dotenv from 'dotenv';
|
|
9
|
-
import path from 'path';
|
|
10
|
-
import { fileURLToPath } from 'url';
|
|
11
|
-
|
|
12
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
13
|
-
const __dirname = path.dirname(__filename);
|
|
14
|
-
|
|
15
|
-
// Load env from root
|
|
16
|
-
dotenv.config({ path: path.resolve(__dirname, '../.env') });
|
|
17
|
-
|
|
18
|
-
const pgSession = connectPgSimple(session);
|
|
19
|
-
const { Pool } = pg;
|
|
20
|
-
|
|
21
|
-
const dev = process.env.NODE_ENV !== 'production';
|
|
22
|
-
const hostname = 'localhost';
|
|
23
|
-
const port = process.env.PORT || 3000;
|
|
24
|
-
|
|
25
|
-
// Initialize PostgreSQL pool for sessions
|
|
26
|
-
const dbUrl = process.env.DEV_WORKFLOW_DB_URL;
|
|
27
|
-
const pool = dbUrl ? new Pool({ connectionString: dbUrl }) : null;
|
|
28
|
-
|
|
29
|
-
// Initialize Next.js app
|
|
30
|
-
const app = next({ dev, hostname, port });
|
|
31
|
-
const handle = app.getRequestHandler();
|
|
32
|
-
|
|
33
|
-
// Import auth utilities
|
|
34
|
-
let authenticateUser, getUserById;
|
|
35
|
-
if (pool) {
|
|
36
|
-
import('./lib/auth.js').then(module => {
|
|
37
|
-
authenticateUser = module.authenticateUser;
|
|
38
|
-
getUserById = module.getUserById;
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
app.prepare().then(() => {
|
|
43
|
-
const server = express();
|
|
44
|
-
|
|
45
|
-
server.use(express.json());
|
|
46
|
-
server.use(express.urlencoded({ extended: true }));
|
|
47
|
-
|
|
48
|
-
// Session middleware
|
|
49
|
-
if (pool) {
|
|
50
|
-
server.use(
|
|
51
|
-
session({
|
|
52
|
-
store: new pgSession({
|
|
53
|
-
pool,
|
|
54
|
-
tableName: 'session'
|
|
55
|
-
}),
|
|
56
|
-
secret: process.env.SESSION_SECRET || 'dev-workflow-secret-change-in-production',
|
|
57
|
-
resave: false,
|
|
58
|
-
saveUninitialized: false,
|
|
59
|
-
cookie: {
|
|
60
|
-
maxAge: 30 * 24 * 60 * 60 * 1000, // 30 days
|
|
61
|
-
httpOnly: true,
|
|
62
|
-
secure: process.env.NODE_ENV === 'production'
|
|
63
|
-
}
|
|
64
|
-
})
|
|
65
|
-
);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// Auth API routes
|
|
69
|
-
server.post('/api/auth/login', async (req, res) => {
|
|
70
|
-
if (!pool || !authenticateUser) {
|
|
71
|
-
return res.status(500).json({ error: 'Database not configured' });
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const { username, password } = req.body;
|
|
75
|
-
|
|
76
|
-
if (!username || !password) {
|
|
77
|
-
return res.status(400).json({ error: 'Username and password required' });
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
try {
|
|
81
|
-
const user = await authenticateUser(pool, username, password);
|
|
82
|
-
|
|
83
|
-
if (!user) {
|
|
84
|
-
return res.status(401).json({ error: 'Invalid credentials' });
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
req.session.userId = user.id;
|
|
88
|
-
req.session.username = user.username;
|
|
89
|
-
|
|
90
|
-
res.json({ success: true, user: { username: user.username, email: user.email } });
|
|
91
|
-
} catch (error) {
|
|
92
|
-
console.error('Login error:', error);
|
|
93
|
-
res.status(500).json({ error: 'Internal server error' });
|
|
94
|
-
}
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
server.post('/api/auth/logout', (req, res) => {
|
|
98
|
-
req.session.destroy((err) => {
|
|
99
|
-
if (err) {
|
|
100
|
-
return res.status(500).json({ error: 'Logout failed' });
|
|
101
|
-
}
|
|
102
|
-
res.json({ success: true });
|
|
103
|
-
});
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
server.get('/api/auth/me', async (req, res) => {
|
|
107
|
-
if (!req.session || !req.session.userId) {
|
|
108
|
-
return res.status(401).json({ error: 'Not authenticated' });
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
if (!pool || !getUserById) {
|
|
112
|
-
return res.status(500).json({ error: 'Database not configured' });
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
try {
|
|
116
|
-
const user = await getUserById(pool, req.session.userId);
|
|
117
|
-
|
|
118
|
-
if (!user) {
|
|
119
|
-
return res.status(404).json({ error: 'User not found' });
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
res.json({ user: { username: user.username, email: user.email } });
|
|
123
|
-
} catch (error) {
|
|
124
|
-
console.error('Get user error:', error);
|
|
125
|
-
res.status(500).json({ error: 'Internal server error' });
|
|
126
|
-
}
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
// Get workflow history
|
|
130
|
-
server.get('/api/workflow/history', async (req, res) => {
|
|
131
|
-
if (!req.session || !req.session.userId) {
|
|
132
|
-
return res.status(401).json({ error: 'Not authenticated' });
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
if (!pool) {
|
|
136
|
-
return res.status(500).json({ error: 'Database not configured' });
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
try {
|
|
140
|
-
// Get user details to filter history
|
|
141
|
-
// We assume workflow_history.user_id stores the username (based on schema)
|
|
142
|
-
// We use userId (integer) to query the foreign key column
|
|
143
|
-
const userId = req.session.userId;
|
|
144
|
-
|
|
145
|
-
let query = `
|
|
146
|
-
SELECT
|
|
147
|
-
id,
|
|
148
|
-
task_type,
|
|
149
|
-
task_description as description,
|
|
150
|
-
commit_message,
|
|
151
|
-
timestamp as completed_at,
|
|
152
|
-
tests_passed,
|
|
153
|
-
documentation_type
|
|
154
|
-
FROM workflow_history
|
|
155
|
-
`;
|
|
156
|
-
|
|
157
|
-
const params = [];
|
|
158
|
-
|
|
159
|
-
if (userId) {
|
|
160
|
-
query += ` WHERE user_id = $1 `;
|
|
161
|
-
params.push(userId);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
query += ` ORDER BY timestamp DESC LIMIT 100`;
|
|
165
|
-
|
|
166
|
-
const result = await pool.query(query, params);
|
|
167
|
-
|
|
168
|
-
res.json({ history: result.rows });
|
|
169
|
-
} catch (error) {
|
|
170
|
-
console.error('Get history error:', error);
|
|
171
|
-
res.status(500).json({ error: 'Internal server error' });
|
|
172
|
-
}
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
// Next.js handler for all other routes
|
|
176
|
-
server.all('*', (req, res) => {
|
|
177
|
-
return handle(req, res);
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
server.listen(port, (err) => {
|
|
181
|
-
if (err) throw err;
|
|
182
|
-
console.log(`> Ready on http://${hostname}:${port}`);
|
|
183
|
-
});
|
|
184
|
-
});
|
package/web/tailwind.config.ts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import type { Config } from "tailwindcss";
|
|
2
|
-
|
|
3
|
-
const config: Config = {
|
|
4
|
-
content: [
|
|
5
|
-
"./pages/**/*.{js,ts,jsx,tsx,mdx}",
|
|
6
|
-
"./components/**/*.{js,ts,jsx,tsx,mdx}",
|
|
7
|
-
"./app/**/*.{js,ts,jsx,tsx,mdx}",
|
|
8
|
-
],
|
|
9
|
-
darkMode: "class",
|
|
10
|
-
theme: {
|
|
11
|
-
extend: {
|
|
12
|
-
colors: {
|
|
13
|
-
background: "var(--background)",
|
|
14
|
-
foreground: "var(--foreground)",
|
|
15
|
-
},
|
|
16
|
-
},
|
|
17
|
-
},
|
|
18
|
-
plugins: [],
|
|
19
|
-
};
|
|
20
|
-
export default config;
|
package/web/tsconfig.json
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ES2017",
|
|
4
|
-
"lib": [
|
|
5
|
-
"dom",
|
|
6
|
-
"dom.iterable",
|
|
7
|
-
"esnext"
|
|
8
|
-
],
|
|
9
|
-
"allowJs": true,
|
|
10
|
-
"skipLibCheck": true,
|
|
11
|
-
"strict": true,
|
|
12
|
-
"noEmit": true,
|
|
13
|
-
"esModuleInterop": true,
|
|
14
|
-
"module": "esnext",
|
|
15
|
-
"moduleResolution": "bundler",
|
|
16
|
-
"resolveJsonModule": true,
|
|
17
|
-
"isolatedModules": true,
|
|
18
|
-
"jsx": "react-jsx",
|
|
19
|
-
"incremental": true,
|
|
20
|
-
"plugins": [
|
|
21
|
-
{
|
|
22
|
-
"name": "next"
|
|
23
|
-
}
|
|
24
|
-
],
|
|
25
|
-
"paths": {
|
|
26
|
-
"@/*": [
|
|
27
|
-
"./*"
|
|
28
|
-
]
|
|
29
|
-
}
|
|
30
|
-
},
|
|
31
|
-
"include": [
|
|
32
|
-
"next-env.d.ts",
|
|
33
|
-
"**/*.ts",
|
|
34
|
-
"**/*.tsx",
|
|
35
|
-
".next/types/**/*.ts",
|
|
36
|
-
".next/dev/types/**/*.ts",
|
|
37
|
-
"**/*.mts"
|
|
38
|
-
],
|
|
39
|
-
"exclude": [
|
|
40
|
-
"node_modules"
|
|
41
|
-
]
|
|
42
|
-
}
|