Skip to content

Commit 7951e73

Browse files
added github integration
1 parent 0431fdf commit 7951e73

File tree

12 files changed

+917
-348
lines changed

12 files changed

+917
-348
lines changed

Codespace_Service/src/controllers/codespaceController.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,4 +201,38 @@ export class CodespaceController {
201201
next(error);
202202
}
203203
}
204+
205+
static async updateGitHubDetails(req, res, next) {
206+
try {
207+
const { id } = req.params;
208+
const { githubRepo, githubAccessToken } = req.body;
209+
210+
if (!githubRepo || !githubRepo.trim()) {
211+
return res.status(400).json({
212+
error: "GitHub repository path is required",
213+
code: "MISSING_GITHUB_REPO",
214+
});
215+
}
216+
217+
const result = await CodespaceService.updateGitHubDetails(
218+
id,
219+
req.user.id,
220+
githubRepo,
221+
githubAccessToken
222+
);
223+
224+
res.json({
225+
message: "GitHub details updated successfully",
226+
workspace: result,
227+
});
228+
} catch (error) {
229+
if (error.statusCode) {
230+
return res.status(error.statusCode).json({
231+
error: error.message,
232+
code: error.code,
233+
});
234+
}
235+
next(error);
236+
}
237+
}
204238
}

Codespace_Service/src/routes/codespaceRoutes.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,9 @@ router.put(
3838
"/accept-invitation/:invitationId",
3939
CodespaceController.acceptInvitation
4040
);
41+
router.put(
42+
"/:id/github-details",
43+
validateCodespaceId,
44+
CodespaceController.updateGitHubDetails
45+
);
4146
export default router;

Codespace_Service/src/services/codespaceService.js

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,4 +400,65 @@ export class CodespaceService {
400400
throw err;
401401
}
402402
}
403+
404+
static async updateGitHubDetails(
405+
codespaceId,
406+
userId,
407+
githubRepo,
408+
githubAccessToken
409+
) {
410+
// Check permissions first - only allow admin or owner to update GitHub details
411+
await this.checkUserPermission(codespaceId, userId, ["admin", "owner"]);
412+
413+
try {
414+
// Update the workspaces table with GitHub details
415+
const { data, error } = await supabase
416+
.from("workspaces")
417+
.update({
418+
github_repo: githubRepo,
419+
github_access_token: githubAccessToken,
420+
})
421+
.eq("id", codespaceId)
422+
.select("id, name, created_at, github_repo")
423+
.single();
424+
425+
if (error) {
426+
console.error("Error updating GitHub details:", error);
427+
const updateError = new Error(
428+
`Failed to update GitHub details: ${error.message}`
429+
);
430+
updateError.statusCode = 500;
431+
updateError.code = "UPDATE_GITHUB_FAILED";
432+
throw updateError;
433+
}
434+
435+
return {
436+
id: data.id,
437+
name: data.name,
438+
lastModified: new Date(data.created_at).toLocaleString("en-US", {
439+
year: "numeric",
440+
month: "short",
441+
day: "numeric",
442+
hour: "2-digit",
443+
minute: "2-digit",
444+
}),
445+
githubRepo: data.github_repo,
446+
};
447+
} catch (err) {
448+
console.error("Error in updateGitHubDetails:", err);
449+
450+
// Re-throw the error if it's already structured
451+
if (err.statusCode && err.code) {
452+
throw err;
453+
}
454+
455+
// Otherwise create a structured error
456+
const serviceError = new Error(
457+
`Failed to update GitHub details: ${err.message}`
458+
);
459+
serviceError.statusCode = 500;
460+
serviceError.code = "GITHUB_UPDATE_FAILED";
461+
throw serviceError;
462+
}
463+
}
403464
}

Frontend/src/App/CodeEditor/CompilerPanel.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ const CompilerPanel = ({ selectedFile }: CompilerPanelProps) => {
4141
(compilationMode === "single" && selectedFile);
4242

4343
return (
44-
<div className={`h-full ${theme.surface} ${theme.text}`}>
44+
<div className={`h-full flex flex-col ${theme.surface} ${theme.text}`}>
4545
{/* Header */}
4646
<div
4747
className={`${theme.surfaceSecondary} px-4 py-3 border-b ${theme.border}`}
@@ -50,7 +50,7 @@ const CompilerPanel = ({ selectedFile }: CompilerPanelProps) => {
5050
</div>
5151

5252
{/* Content */}
53-
<div className="p-4 space-y-4">
53+
<div className="p-4 space-y-4 overflow-x-clip overflow-y-auto Simple-Scrollbar flex-1">
5454
{/* Compilation Mode Section */}
5555
<div className="space-y-3">
5656
<label
@@ -183,7 +183,7 @@ const CompilerPanel = ({ selectedFile }: CompilerPanelProps) => {
183183
Output
184184
</div>
185185
<div
186-
className={`${theme.surface} px-3 py-8 text-xs ${theme.textMuted} text-center`}
186+
className={`${theme.surface} px-3 py-3 text-xs ${theme.textMuted} text-center`}
187187
>
188188
{compilerLoading
189189
? "Running code..."

Frontend/src/App/CodeEditor/GitPanel/CommitHistory.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ const CommitItem = ({ commit, onRollback, isLoading }: CommitItemProps) => {
8888
<div
8989
className={`text-xs ${theme.textMuted} flex justify-between items-center`}
9090
>
91-
<span>Anuja Kalhara</span>
91+
<span>commit</span>
9292
<span className="flex items-center">
9393
<Clock className="w-3 h-3 mr-1" />
9494
{formatDateTime(commit.createdAt)}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import React from "react";
2+
import { useTheme } from "../../../Contexts/ThemeProvider";
3+
import { useEditorCollaboration } from "../../../Contexts/EditorContext";
4+
5+
interface GitHubIntegrationButtonProps {
6+
onOpenModal: () => void;
7+
}
8+
9+
const GitHubIntegrationButton: React.FC<GitHubIntegrationButtonProps> = ({
10+
onOpenModal,
11+
}) => {
12+
const { theme } = useTheme();
13+
const { codespace } = useEditorCollaboration();
14+
15+
return (
16+
<div className={`px-4 py-3 ${theme.border} border-b`}>
17+
<div className="flex justify-between items-center">
18+
<div>
19+
<h3 className="text-sm font-medium">GitHub Repository</h3>
20+
<p
21+
className={`text-xs ${theme.textMuted} max-w-44 truncate`}
22+
title={codespace?.gitHubRepo || "No repository linked"}
23+
>
24+
{codespace?.gitHubRepo
25+
? codespace.gitHubRepo
26+
: "No repository linked"}
27+
</p>
28+
</div>
29+
<button
30+
onClick={onOpenModal}
31+
className={`px-3 py-1 text-sm rounded-md ${theme.active}`}
32+
>
33+
{codespace?.gitHubRepo ? "Update" : "Link"}
34+
</button>
35+
</div>
36+
</div>
37+
);
38+
};
39+
40+
export default GitHubIntegrationButton;
Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
import React, { useState, useEffect } from "react";
2+
import { useTheme } from "../../../Contexts/ThemeProvider";
3+
import { useEditorCollaboration } from "../../../Contexts/EditorContext";
4+
5+
interface GitHubIntegrationModalProps {
6+
isOpen: boolean;
7+
onClose: () => void;
8+
}
9+
10+
const GitHubIntegrationModal: React.FC<GitHubIntegrationModalProps> = ({
11+
isOpen,
12+
onClose,
13+
}) => {
14+
const { theme } = useTheme();
15+
const { codespace, updateGitHubDetails } = useEditorCollaboration();
16+
17+
const [githubRepo, setGithubRepo] = useState("");
18+
const [githubToken, setGithubToken] = useState("");
19+
const [isSubmitting, setIsSubmitting] = useState(false);
20+
21+
// Initialize form with existing values when opened
22+
useEffect(() => {
23+
if (isOpen && codespace?.gitHubRepo) {
24+
setGithubRepo(codespace.gitHubRepo);
25+
}
26+
}, [isOpen, codespace]);
27+
28+
const handleGitHubIntegration = async (e: React.FormEvent) => {
29+
e.preventDefault();
30+
setIsSubmitting(true);
31+
32+
try {
33+
const success = await updateGitHubDetails(githubRepo, githubToken);
34+
if (success) {
35+
onClose();
36+
// Clear the token after successful integration for security
37+
setGithubToken("");
38+
}
39+
} catch (err) {
40+
console.error("Failed to update GitHub details:", err);
41+
} finally {
42+
setIsSubmitting(false);
43+
}
44+
};
45+
46+
if (!isOpen) return null;
47+
48+
return (
49+
<div className="fixed inset-0 z-50 overflow-y-auto">
50+
{/* Backdrop */}
51+
<div
52+
className="fixed inset-0 bg-black bg-opacity-50"
53+
onClick={onClose}
54+
></div>
55+
56+
{/* Modal Container */}
57+
<div className="flex items-center justify-center min-h-screen p-4">
58+
<div
59+
className={`relative w-full max-w-md p-6 ${theme.surface} ${theme.border} border rounded-md shadow-lg`}
60+
style={{ maxWidth: "500px" }}
61+
onClick={(e) => e.stopPropagation()}
62+
>
63+
{/* Modal Header */}
64+
<div className="mb-4 flex justify-between items-center">
65+
<h2 className={`text-lg font-medium ${theme.text}`}>
66+
GitHub Repository Integration
67+
</h2>
68+
<button
69+
onClick={onClose}
70+
className={`${theme.textMuted} hover:${theme.text}`}
71+
>
72+
<svg
73+
xmlns="http://www.w3.org/2000/svg"
74+
width="16"
75+
height="16"
76+
viewBox="0 0 24 24"
77+
fill="none"
78+
stroke="currentColor"
79+
strokeWidth="2"
80+
strokeLinecap="round"
81+
strokeLinejoin="round"
82+
>
83+
<line x1="18" y1="6" x2="6" y2="18"></line>
84+
<line x1="6" y1="6" x2="18" y2="18"></line>
85+
</svg>
86+
</button>
87+
</div>
88+
89+
<p className={`mb-4 text-sm ${theme.textMuted}`}>
90+
Only the owner and admins of this project can update GitHub details.
91+
</p>
92+
93+
{/* Modal Content */}
94+
<form onSubmit={handleGitHubIntegration}>
95+
<div className="mb-4">
96+
<label className={`block text-sm font-medium mb-1 ${theme.text}`}>
97+
Repository URL
98+
<span className="text-red-500 ml-1">*</span>
99+
</label>
100+
<input
101+
type="text"
102+
value={githubRepo}
103+
onChange={(e) => setGithubRepo(e.target.value)}
104+
placeholder="Github Repo Link"
105+
className={`w-full px-3 py-2 rounded-md outline-none ${theme.text} border-2 ${theme.border}`}
106+
required
107+
/>
108+
<p className={`text-xs mt-1 ${theme.textMuted}`}>
109+
{"Format: https://github.com/<<userName>>/<<repo>>.git"}
110+
</p>
111+
</div>
112+
113+
<div className="mb-6">
114+
<label className={`block text-sm font-medium mb-1 ${theme.text}`}>
115+
Personal Access Token
116+
<span className="text-red-500 ml-1">*</span>
117+
</label>
118+
<input
119+
type="password"
120+
value={githubToken}
121+
onChange={(e) => setGithubToken(e.target.value)}
122+
placeholder="github_pat_xxxxxx"
123+
className={`w-full px-3 py-2 rounded-md outline-none ${theme.text} border-2 ${theme.border}`}
124+
required
125+
/>
126+
<p className={`text-xs mt-1 ${theme.textMuted}`}>
127+
Token must have repo scope access for repository operations
128+
</p>
129+
</div>
130+
131+
{/* Modal Footer */}
132+
<div className="flex justify-end space-x-3">
133+
<button
134+
type="button"
135+
onClick={onClose}
136+
className={`px-4 py-2 rounded-md ${theme.active}`}
137+
disabled={isSubmitting}
138+
>
139+
Cancel
140+
</button>
141+
<button
142+
type="submit"
143+
disabled={
144+
isSubmitting || !githubRepo.trim() || !githubToken.trim()
145+
}
146+
className={`px-4 py-2 rounded-md ${theme.statusBar} ${
147+
isSubmitting || !githubRepo.trim() || !githubToken.trim()
148+
? "opacity-70 cursor-not-allowed"
149+
: ""
150+
}`}
151+
>
152+
{isSubmitting ? (
153+
<span className="flex items-center">
154+
<svg
155+
className="animate-spin -ml-1 mr-2 h-4 w-4"
156+
xmlns="http://www.w3.org/2000/svg"
157+
fill="none"
158+
viewBox="0 0 24 24"
159+
>
160+
<circle
161+
className="opacity-25"
162+
cx="12"
163+
cy="12"
164+
r="10"
165+
stroke="currentColor"
166+
strokeWidth="4"
167+
></circle>
168+
<path
169+
className="opacity-75"
170+
fill="currentColor"
171+
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
172+
></path>
173+
</svg>
174+
Saving...
175+
</span>
176+
) : (
177+
"Update"
178+
)}
179+
</button>
180+
</div>
181+
</form>
182+
</div>
183+
</div>
184+
</div>
185+
);
186+
};
187+
188+
export default GitHubIntegrationModal;

0 commit comments

Comments
 (0)