@programinglive/dev-workflow-mcp-server 1.7.13 → 1.7.14
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 +41 -0
- package/cloudflared-config.yml +3 -0
- package/docker-compose.yml +17 -0
- package/docs/release-notes/RELEASE_NOTES.md +38 -0
- package/netlify.toml +7 -0
- package/nginx-api.conf +27 -0
- package/package.json +1 -1
- package/scripts/test-auth-flow.js +75 -0
- package/web/Dockerfile.api +25 -0
- package/web/api-server.js +211 -0
- package/web/app/history/page.tsx +25 -18
- package/web/app/login/page.tsx +12 -14
- package/web/components/Hero.tsx +1 -1
- package/web/components/History.tsx +4 -4
- package/web/components/Navbar.tsx +11 -12
- package/web/lib/api-client.ts +123 -0
- package/web/next.config.ts +1 -2
- package/web/package-lock.json +820 -23
- package/web/package.json +8 -1
- package/scripts/add-user.js +0 -52
- package/scripts/health-check.js +0 -50
- package/scripts/test-history.js +0 -56
- package/web/app/api/history-fallback/route.ts +0 -56
|
@@ -4,6 +4,7 @@ import { useState, useEffect } from "react";
|
|
|
4
4
|
import Link from "next/link";
|
|
5
5
|
import { Github, Menu, X, Moon, Sun, LogOut } from "lucide-react";
|
|
6
6
|
import Image from "next/image";
|
|
7
|
+
import { apiClient } from "@/lib/api-client";
|
|
7
8
|
|
|
8
9
|
export default function Navbar() {
|
|
9
10
|
const [isOpen, setIsOpen] = useState(false);
|
|
@@ -29,18 +30,16 @@ export default function Navbar() {
|
|
|
29
30
|
const mightBeLoggedIn = localStorage.getItem('isLoggedIn') === 'true';
|
|
30
31
|
|
|
31
32
|
if (mightBeLoggedIn) {
|
|
32
|
-
|
|
33
|
-
.then(res => {
|
|
34
|
-
if (res.ok) {
|
|
35
|
-
return res.json();
|
|
36
|
-
}
|
|
37
|
-
throw new Error('Not authenticated');
|
|
38
|
-
})
|
|
33
|
+
apiClient.getCurrentUser()
|
|
39
34
|
.then(data => {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
35
|
+
if (data) {
|
|
36
|
+
setIsAuthenticated(true);
|
|
37
|
+
setUsername(data.username || 'User');
|
|
38
|
+
// Ensure local storage is in sync
|
|
39
|
+
localStorage.setItem('isLoggedIn', 'true');
|
|
40
|
+
} else {
|
|
41
|
+
throw new Error('Not authenticated');
|
|
42
|
+
}
|
|
44
43
|
})
|
|
45
44
|
.catch(() => {
|
|
46
45
|
// Session invalid/expired
|
|
@@ -74,7 +73,7 @@ export default function Navbar() {
|
|
|
74
73
|
|
|
75
74
|
const handleLogout = async () => {
|
|
76
75
|
try {
|
|
77
|
-
await
|
|
76
|
+
await apiClient.logout();
|
|
78
77
|
localStorage.removeItem('isLoggedIn');
|
|
79
78
|
setIsAuthenticated(false);
|
|
80
79
|
setUsername(null);
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
// API configuration
|
|
2
|
+
const API_BASE_URL = 'https://hollywood-andrew-cycle-genre.trycloudflare.com';
|
|
3
|
+
|
|
4
|
+
export const apiClient = {
|
|
5
|
+
async login(username: string, password: string) {
|
|
6
|
+
const response = await fetch(`${API_BASE_URL}/api/auth/login`, {
|
|
7
|
+
method: 'POST',
|
|
8
|
+
headers: {
|
|
9
|
+
'Content-Type': 'application/json',
|
|
10
|
+
},
|
|
11
|
+
credentials: 'include',
|
|
12
|
+
body: JSON.stringify({ username, password }),
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
if (!response.ok) {
|
|
16
|
+
const error = await response.json();
|
|
17
|
+
throw new Error(error.error || 'Login failed');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const data = await response.json();
|
|
21
|
+
|
|
22
|
+
// Store JWT token if present
|
|
23
|
+
if (data.token) {
|
|
24
|
+
localStorage.setItem('token', data.token);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return data;
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
async logout() {
|
|
31
|
+
const response = await fetch(`${API_BASE_URL}/api/auth/logout`, {
|
|
32
|
+
method: 'POST',
|
|
33
|
+
credentials: 'include',
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
if (!response.ok) {
|
|
37
|
+
throw new Error('Logout failed');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Clear token
|
|
41
|
+
localStorage.removeItem('token');
|
|
42
|
+
|
|
43
|
+
return response.json();
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
async getCurrentUser() {
|
|
47
|
+
try {
|
|
48
|
+
const token = localStorage.getItem('token');
|
|
49
|
+
const headers: Record<string, string> = {};
|
|
50
|
+
|
|
51
|
+
if (token) {
|
|
52
|
+
headers['Authorization'] = `Bearer ${token}`;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const response = await fetch(`${API_BASE_URL}/api/auth/me`, {
|
|
56
|
+
headers,
|
|
57
|
+
credentials: 'include',
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
if (!response.ok) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return response.json();
|
|
65
|
+
} catch (error) {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
|
|
70
|
+
async getHistory() {
|
|
71
|
+
const token = localStorage.getItem('token');
|
|
72
|
+
const headers: Record<string, string> = {
|
|
73
|
+
'Content-Type': 'application/json',
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
if (token) {
|
|
77
|
+
headers['Authorization'] = `Bearer ${token}`;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const response = await fetch(`${API_BASE_URL}/api/history`, {
|
|
81
|
+
headers,
|
|
82
|
+
credentials: 'include',
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
if (!response.ok) {
|
|
86
|
+
const error = await response.json();
|
|
87
|
+
throw new Error(error.error || 'Failed to fetch history');
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return response.json();
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
async createHistory(data: {
|
|
94
|
+
task_type: string;
|
|
95
|
+
description: string;
|
|
96
|
+
commit_message?: string;
|
|
97
|
+
tests_passed?: boolean;
|
|
98
|
+
documentation_type?: string;
|
|
99
|
+
}) {
|
|
100
|
+
const token = localStorage.getItem('token');
|
|
101
|
+
const headers: Record<string, string> = {
|
|
102
|
+
'Content-Type': 'application/json',
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
if (token) {
|
|
106
|
+
headers['Authorization'] = `Bearer ${token}`;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const response = await fetch(`${API_BASE_URL}/api/history`, {
|
|
110
|
+
method: 'POST',
|
|
111
|
+
headers,
|
|
112
|
+
credentials: 'include',
|
|
113
|
+
body: JSON.stringify(data),
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
if (!response.ok) {
|
|
117
|
+
const error = await response.json();
|
|
118
|
+
throw new Error(error.error || 'Failed to create history entry');
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return response.json();
|
|
122
|
+
},
|
|
123
|
+
};
|
package/web/next.config.ts
CHANGED
|
@@ -2,8 +2,7 @@ import type { NextConfig } from "next";
|
|
|
2
2
|
|
|
3
3
|
const nextConfig: NextConfig = {
|
|
4
4
|
// Static export for Netlify deployment
|
|
5
|
-
|
|
6
|
-
// output: 'export',
|
|
5
|
+
output: 'export',
|
|
7
6
|
// Set turbopack root to silence multi-lockfile warning
|
|
8
7
|
turbopack: {
|
|
9
8
|
root: __dirname,
|