Skip to content

Commit 8832f31

Browse files
Merge pull request #38 from StructZ/develop
feat/fix: new feature and fixes added and updated from head of develop branch
2 parents e44198f + fabe5ca commit 8832f31

File tree

23 files changed

+1260
-270
lines changed

23 files changed

+1260
-270
lines changed
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
---
2+
name: Bug report
3+
about: Create a report to help us improve
4+
title: "[BUG]"
5+
labels: bug, review
6+
assignees: ''
7+
8+
---
9+
10+
**Describe the bug**
11+
A clear and concise description of what the bug is.
12+
13+
**To Reproduce**
14+
Steps to reproduce the behavior:
15+
1. Go to '...'
16+
2. Click on '....'
17+
3. Scroll down to '....'
18+
4. See error
19+
20+
**Expected behavior**
21+
A clear and concise description of what you expected to happen.
22+
23+
**Screenshots**
24+
If applicable, add screenshots to help explain your problem.
25+
26+
**Desktop (please complete the following information):**
27+
- OS: [e.g. iOS]
28+
- Browser [e.g. chrome, safari]
29+
- Version [e.g. 22]
30+
31+
**Smartphone (please complete the following information):**
32+
- Device: [e.g. iPhone6]
33+
- OS: [e.g. iOS8.1]
34+
- Browser [e.g. stock browser, safari]
35+
- Version [e.g. 22]
36+
37+
**Additional context**
38+
Add any other context about the problem here.
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
---
2+
name: Feature request
3+
about: Suggest an idea for this project
4+
title: "[ISSUE]"
5+
labels: feature, review
6+
assignees: ''
7+
8+
---
9+
10+
**Is your feature request related to a problem? Please describe.**
11+
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12+
13+
**Describe the solution you'd like**
14+
A clear and concise description of what you want to happen.
15+
16+
**Describe alternatives you've considered**
17+
A clear and concise description of any alternative solutions or features you've considered.
18+
19+
**Additional context**
20+
Add any other context or screenshots about the feature request here.

.github/workflows/welcome.yml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
name: Welcome First-Time Contributors
2+
3+
on:
4+
pull_request_target:
5+
types: [opened]
6+
issues:
7+
types: [opened]
8+
9+
jobs:
10+
greet:
11+
runs-on: ubuntu-latest
12+
permissions:
13+
pull-requests: write
14+
issues: write
15+
steps:
16+
- uses: actions/first-interaction@v1
17+
with:
18+
repo-token: ${{ secrets.GITHUB_TOKEN }}
19+
issue-message: |
20+
Hello @${{ github.actor }}! 👋
21+
Thank you so much for taking the time to open your first issue with CodeTranslateAI. We appreciate you helping us make the project better!
22+
We'll take a look and get back to you soon.
23+
pr-message: |
24+
Hi @${{ github.actor }}! 🎉
25+
Welcome, and thank you for submitting your first pull request to CodeTranslateAI! We're thrilled to have your contribution.
26+
Please make sure you've read our CONTRIBUTING.md guide. We'll review your changes shortly.

backend/src/index.ts

Lines changed: 111 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,137 @@
11
import { GoogleGenerativeAI } from '@google/generative-ai';
22

33
export interface Env {
4+
RATE_LIMIT: KVNamespace;
45
GEMINI_API_KEY: string;
56
}
67

7-
export default {
8-
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
9-
const corsHeaders = {
10-
'Access-Control-Allow-Origin': '*',
11-
'Access-Control-Allow-Methods': 'POST, OPTIONS',
12-
'Access-Control-Allow-Headers': 'Content-Type',
13-
};
8+
const corsHeaders = {
9+
'Access-Control-Allow-Origin': '*',
10+
'Access-Control-Allow-Methods': 'POST, OPTIONS',
11+
'Access-Control-Allow-Headers': 'Content-Type',
12+
};
1413

15-
if (request.method === 'OPTIONS') {
16-
return new Response(null, { headers: corsHeaders });
17-
}
14+
const MAX_REQUESTS_ALLOWED = 10;
15+
const DURATION = 60_000;
16+
17+
async function checkRateLimit(ip: string, env: Env) {
18+
const key = `ip_key:${ip}`;
19+
const now = Date.now();
20+
let value = await env.RATE_LIMIT.get(key);
21+
let data = { count: 0, time: now };
1822

23+
if (value) {
1924
try {
20-
const { code, targetLanguage } = await request.json<{ code: string; targetLanguage: string }>();
25+
data = JSON.parse(value);
26+
} catch {
27+
data = { count: 0, time: now };
28+
}
29+
}
2130

22-
if (!code || !targetLanguage) {
23-
return new Response(JSON.stringify({ error: "Missing 'code' or 'targetLanguage' in request body." }), {
24-
status: 400,
25-
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
26-
});
27-
}
31+
if (now - data.time > DURATION) {
32+
data.count = 0;
33+
data.time = now;
34+
}
2835

29-
const genAI = new GoogleGenerativeAI(env.GEMINI_API_KEY);
36+
data.count += 1;
37+
await env.RATE_LIMIT.put(key, JSON.stringify(data), { expirationTtl: 65 });
3038

31-
const model = genAI.getGenerativeModel({ model: 'gemini-2.0-flash' });
39+
return data.count <= MAX_REQUESTS_ALLOWED;
40+
}
41+
async function handleTranslate(request: Request, model: ReturnType<GoogleGenerativeAI['getGenerativeModel']>) {
42+
const { code, targetLanguage } = await request.json<{ code: string; targetLanguage: string }>();
43+
44+
if (!code || !targetLanguage) {
45+
return new Response(JSON.stringify({ error: "Missing 'code' or 'targetLanguage' in request body." }), {
46+
status: 400,
47+
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
48+
});
49+
}
3250

33-
const prompt = `Translate the following code snippet to ${targetLanguage}.
51+
const prompt = `Translate the following code snippet to ${targetLanguage}.
3452
Do not add any explanation, commentary, or markdown formatting like \`\`\` around the code.
35-
**IMPORTANT: Preserve all original comments and their exact placement in the translated code. do not add extra spaces in between.**
53+
**IMPORTANT: Preserve all original comments and their exact placement in the translated code. Do not add extra spaces in between.**
3654
Only provide the raw, translated code itself.
3755
3856
Original Code:
3957
${code}`;
4058

41-
const result = await model.generateContent(prompt);
42-
const geminiResponse = result.response;
43-
const translatedCode = geminiResponse.text();
59+
const result = await model.generateContent(prompt);
60+
const translatedCode = result.response.text();
61+
62+
return new Response(JSON.stringify({ translation: translatedCode }), {
63+
status: 200,
64+
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
65+
});
66+
}
67+
68+
async function handleExplain(request: Request, model: ReturnType<GoogleGenerativeAI['getGenerativeModel']>) {
69+
const { code } = await request.json<{ code: string }>();
70+
71+
if (!code) {
72+
return new Response(JSON.stringify({ error: "Missing 'code' in request body." }), {
73+
status: 400,
74+
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
75+
});
76+
}
77+
78+
const prompt = `Explain the following code snippet in detail:
79+
1. Provide a clear breakdown of what each part (functions, variables, logic blocks) does.
80+
2. If applicable, describe the overall purpose or intent of the code.
81+
3. Offer a step-by-step explanation of how the code executes.
82+
4. If the code is executable, show a sample input and the corresponding output.
83+
5. Keep the explanation beginner-friendly but technically accurate.
84+
85+
Code:
86+
${code}`;
87+
88+
const result = await model.generateContent(prompt);
89+
const explanation = result.response.text();
90+
91+
return new Response(JSON.stringify({ explanation }), {
92+
status: 200,
93+
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
94+
});
95+
}
96+
97+
export default {
98+
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
99+
if (request.method === 'OPTIONS') {
100+
return new Response(null, { headers: corsHeaders });
101+
}
102+
103+
try {
104+
const ip = request.headers.get('CF-Connecting-IP') || 'unknown';
105+
const allowed = await checkRateLimit(ip, env);
106+
if (!allowed) {
107+
return new Response(JSON.stringify({ error: "Too many requests. Try again later." }), {
108+
status: 429,
109+
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
110+
});
111+
}
112+
const url = new URL(request.url);
113+
const path = url.pathname;
114+
const genAI = new GoogleGenerativeAI(env.GEMINI_API_KEY);
115+
const model = genAI.getGenerativeModel({ model: 'gemini-2.0-flash' });
116+
117+
if(path==="/test-rate-limit"){
118+
return new Response(JSON.stringify("Proceed !"))
119+
}
120+
if (path === '/' || path === '/v1/translate') {
121+
return await handleTranslate(request, model);
122+
}
123+
124+
if (path === '/v1/explain') {
125+
return await handleExplain(request, model);
126+
}
44127

45-
return new Response(JSON.stringify({ translation: translatedCode }), {
46-
status: 200,
128+
return new Response(JSON.stringify({ error: 'Route not found.' }), {
129+
status: 404,
47130
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
48131
});
49132
} catch (error) {
50-
console.error('Error during translation:', error);
51-
return new Response(JSON.stringify({ error: 'An error occurred while translating the code.' }), {
133+
console.error('Error during request:', error);
134+
return new Response(JSON.stringify({ error: 'An internal error occurred.' }), {
52135
status: 500,
53136
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
54137
});

backend/worker-configuration.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// Runtime types generated with workerd@1.20250712.0 2025-07-15
44
declare namespace Cloudflare {
55
interface Env {
6+
RATE_LIMIT: KVNamespace;
67
}
78
}
89
interface Env extends Cloudflare.Env {}

backend/wrangler.jsonc

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,14 @@
99
"compatibility_date": "2025-07-15",
1010
"observability": {
1111
"enabled": true
12-
}
12+
},
13+
"kv_namespaces": [
14+
{
15+
"binding": "RATE_LIMIT",
16+
"id": "<your_kv_id>"
17+
}
18+
]
19+
1320
/**
1421
* Smart Placement
1522
* Docs: https://developers.cloudflare.com/workers/configuration/smart-placement/#smart-placement

frontend/.husky/pre-commit

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#!/usr/bin/env sh
2+
. "$(dirname -- "$0")/_/husky.sh"
3+
4+
npx lint-staged

frontend/.prettierignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
dist
2+
node_modules
3+
packages
4+
package-lock.json

frontend/.prettierrc.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"semi": true,
3+
"singleQuote": false,
4+
"trailingComma": "es5"
5+
}

frontend/background.js

Lines changed: 48 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,52 @@
1-
21
chrome.runtime.onMessage.addListener((request, _, sendResponse) => {
3-
if (request.type === "TRANSLATE_CODE") {
4-
const BACKEND_URL = process.env.BACKEND_URL;
5-
6-
chrome.storage.sync.get(['targetLanguage'], (result) => {
7-
const targetLanguage = result.targetLanguage || 'Java';
8-
fetch(BACKEND_URL, {
9-
method: 'POST',
10-
headers: {
11-
'Content-Type': 'application/json',
12-
},
13-
body: JSON.stringify({
14-
code: request.code,
15-
targetLanguage: targetLanguage,
16-
}),
17-
})
18-
.then(response => {
19-
if (!response.ok) {
20-
throw new Error(`Network response was not ok: ${response.statusText}`);
21-
}
22-
return response.json();
23-
})
24-
.then(data => {
25-
26-
if (data.error) {
27-
sendResponse({ error: data.error });
28-
} else {
29-
sendResponse({ translation: data.translation });
30-
}
31-
})
32-
.catch(error => {
2+
if (request.type === "TRANSLATE_CODE") {
3+
const BACKEND_URL = process.env.BACKEND_URL;
334

34-
console.error("Error calling backend:", error);
35-
sendResponse({ error: `Failed to connect to the translation service: ${error.message}` });
36-
});
5+
chrome.storage.sync.get(["targetLanguage"], (result) => {
6+
const targetLanguage = result.targetLanguage || "Java";
7+
fetch(BACKEND_URL, {
8+
method: "POST",
9+
headers: {
10+
"Content-Type": "application/json",
11+
},
12+
body: JSON.stringify({
13+
code: request.code,
14+
targetLanguage: targetLanguage,
15+
}),
16+
})
17+
.then((response) => {
18+
if (!response.ok) {
19+
throw new Error(
20+
`Network response was not ok: ${response.statusText}`
21+
);
22+
}
23+
return response.json();
24+
})
25+
.then((data) => {
26+
if (data.error) {
27+
sendResponse({ error: data.error });
28+
} else {
29+
sendResponse({ translation: data.translation });
30+
}
31+
})
32+
.catch((error) => {
33+
console.error("Error calling backend:", error);
34+
sendResponse({
35+
error: `Failed to connect to the translation service: ${error.message}`,
36+
});
3737
});
38+
});
3839

39-
return true;
40-
}
41-
});
40+
return true;
41+
}
42+
});
43+
//Default commmand = Alt+T , Mac = Option+T
44+
chrome.commands.onCommand.addListener((command) => {
45+
if (command === "translate") {
46+
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
47+
if (tabs.length > 0) {
48+
chrome.tabs.sendMessage(tabs[0].id, { type: "ENABLE_PICKER" });
49+
}
50+
});
51+
}
52+
});

0 commit comments

Comments
 (0)