diff --git a/extension/background.js b/extension/background.js index 55f323c..46dca07 100644 --- a/extension/background.js +++ b/extension/background.js @@ -1,39 +1,45 @@ const API_BASE_URL = "https://leetcodeai-backend.onrender.com"; -//const API_BASE_URL = "http://localhost:10000"; -function getUserEmail() { - return new Promise(resolve => { - chrome.storage.local.get({ userEmail: null }, ({ userEmail }) => resolve(userEmail)); - }); + +async function parseApiResponse(response) { + const data = await response.json().catch(() => ({})); + if (!response.ok) { + throw new Error(data.detail || data.message || `Request failed with status ${response.status}`); + } + return data; } -function getUserEmail() { - return new Promise(resolve => { - chrome.storage.local.get({ userEmail: null }, ({ userEmail }) => resolve(userEmail)); - }); +function authHeaders(userEmail, sessionToken) { + return { + "Content-Type": "application/json", + "X-User-Email": userEmail, + "Authorization": `Bearer ${sessionToken}` + }; } + chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { if (request.type === 'GENERATE_BLOG') { - const { title, description, code, author, client_time, custom_prompt, difficulty, topics } = request.payload; + const { title, description, code, author, client_time, custom_prompt, difficulty, language, topics } = request.payload; chrome.storage.local.get({ publishingPlatforms: ['devto'], publishAsDraft: false, userEmail: null, - }, async ({ publishingPlatforms, publishAsDraft, userEmail }) => { - if (!userEmail) { - chrome.runtime.sendMessage({ type: 'STATUS_UPDATE', message: 'Please set your email in the extension settings before publishing.', status: 'error' }); + sessionToken: null, + }, async ({ publishingPlatforms, publishAsDraft, userEmail, sessionToken }) => { + if (!userEmail || !sessionToken) { + chrome.runtime.sendMessage({ type: 'STATUS_UPDATE', message: 'Please log in before publishing.', status: 'error' }); return; } fetch(`${API_BASE_URL}/generate-blog`, { method: "POST", - headers: { "Content-Type": "application/json", "X-User-Email": userEmail }, + headers: authHeaders(userEmail, sessionToken), body: JSON.stringify({ - title, description, code, author, client_time, custom_prompt, difficulty, + title, description, code, author, client_time, custom_prompt, difficulty, language, tags: (topics && topics.length > 0) ? topics : null, platforms: publishingPlatforms, publish_as_draft: publishAsDraft }) }) - .then(r => r.json()) + .then(parseApiResponse) .then(data => { if (data.status === 'success' || data.status === 'partial_success') { const generatedBlog = @@ -55,15 +61,14 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { const platforms = data.data?.platforms || []; const postedPlatforms = platforms .filter(result => result.status === 'success') - .map(result => result.platform) - .join(', '); + .map(result => result.platform); const devtoResult = platforms.find(r => r.platform === 'devto' && r.status === 'success'); chrome.storage.local.get({ publishHistory: [] }, (res) => { const entry = { title: title, url: devtoResult?.url || null, publishedAt: client_time || new Date().toISOString(), - platforms: postedPlatforms ? postedPlatforms.split(', ').filter(p => p) : [] + platforms: postedPlatforms }; const history = res.publishHistory; history.unshift(entry); @@ -90,7 +95,7 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { fetch(`${API_BASE_URL}/dashboard/record`, { method: "POST", - headers: { "Content-Type": "application/json", "X-User-Email": userEmail }, + headers: authHeaders(userEmail, sessionToken), body: JSON.stringify(entry) }).catch(() => { }); chrome.runtime.sendMessage({ @@ -127,17 +132,18 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { publishingPlatforms: ['devto'], publishAsDraft: false, userEmail: null, + sessionToken: null, generatedProblemTitle: 'leetcode-blog' - }, async ({ publishingPlatforms, publishAsDraft, userEmail, generatedProblemTitle }) => { - if (!userEmail) { - const errMsg = 'Please set your email in the extension settings before publishing.'; + }, async ({ publishingPlatforms, publishAsDraft, userEmail, sessionToken, generatedProblemTitle }) => { + if (!userEmail || !sessionToken) { + const errMsg = 'Please log in before publishing.'; chrome.runtime.sendMessage({ type: 'STATUS_UPDATE', message: errMsg, status: 'error' }); sendResponse({ success: false, error: errMsg }); return; } fetch(`${API_BASE_URL}/publish-blog`, { method: "POST", - headers: { "Content-Type": "application/json", "X-User-Email": userEmail }, + headers: authHeaders(userEmail, sessionToken), body: JSON.stringify({ title: generatedProblemTitle, content: blog, @@ -146,7 +152,7 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { author: "Anonymous Developer" }) }) - .then(r => r.json()) + .then(parseApiResponse) .then(data => { if (data.status === 'success' || data.status === 'partial_success') { const platforms = data.data?.platforms || []; @@ -173,7 +179,7 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { fetch(`${API_BASE_URL}/dashboard/record`, { method: "POST", - headers: { "Content-Type": "application/json", "X-User-Email": userEmail }, + headers: authHeaders(userEmail, sessionToken), body: JSON.stringify(entry) }).catch(() => { }); @@ -208,4 +214,4 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { }); return true; } -}); \ No newline at end of file +}); diff --git a/extension/content.js b/extension/content.js index eddc3f6..a7fdbca 100644 --- a/extension/content.js +++ b/extension/content.js @@ -66,6 +66,12 @@ document.querySelector('.ant-select-selection-item'); const language = langElement ? langElement.innerText.trim().toLowerCase() : "unknown"; + const difficultyElement = document.querySelector('[diff]') || + document.querySelector('div[class*="text-difficulty-"]') || + document.querySelector('span[class*="text-difficulty-"]'); + const difficulty = difficultyElement + ? (difficultyElement.getAttribute('diff') || difficultyElement.innerText || "").trim() + : null; // Extract topic tags (e.g. Array, Dynamic Programming, Hash Table) const tagElements = document.querySelectorAll('a[href*="/tag/"]'); const topics = Array.from(tagElements) diff --git a/extension/dashboard.js b/extension/dashboard.js index 253368e..6b73e6a 100644 --- a/extension/dashboard.js +++ b/extension/dashboard.js @@ -1,5 +1,4 @@ -//const API_BASE_URL = "https://leetcodeai-backend.onrender.com"; -const API_BASE_URL = "http://localhost:10000"; +const API_BASE_URL = "https://leetcodeai-backend.onrender.com"; //fixes timezone for IST and all non-UTC users function getLocalDateStr(date) { @@ -10,11 +9,11 @@ function getLocalDateStr(date) { return `${year}-${month}-${day}`; } -const colors = { devto:'#3b49df', hashnode:'#2962ff', medium:'#00ab6c', webhook:'#f7a01a' }; +const colors = { devto: '#3b49df', hashnode: '#2962ff', medium: '#00ab6c', webhook: '#f7a01a' }; function escapeHTML(str) { if (!str) return ''; - return String(str).replace(/[&<>'"]/g, + return String(str).replace(/[&<>'"]/g, tag => ({ '&': '&', '<': '<', @@ -26,13 +25,13 @@ function escapeHTML(str) { } document.addEventListener('DOMContentLoaded', () => { - chrome.storage.local.get({ userEmail: null }, ({ userEmail }) => { - if (!userEmail) { + chrome.storage.local.get({ userEmail: null, sessionToken: null }, ({ userEmail, sessionToken }) => { + if (!userEmail || !sessionToken) { showBanner('No email set — open the extension and enter your email first.'); return; } setLoading(true); - fetchStatsFromBackend(userEmail) + fetchStatsFromBackend(userEmail, sessionToken) .catch(() => { showBanner("Couldn't reach backend — showing local data."); return loadFromLocalStorage(userEmail); @@ -41,9 +40,12 @@ document.addEventListener('DOMContentLoaded', () => { }); }); -async function fetchStatsFromBackend(email) { +async function fetchStatsFromBackend(email, sessionToken) { const res = await fetch(`${API_BASE_URL}/dashboard/stats`, { - headers: { 'X-User-Email': email } + headers: { + 'X-User-Email': email, + 'Authorization': `Bearer ${sessionToken}` + } }); if (!res.ok) throw new Error('Bad response'); const { total_posts, platform_counts, week_activity, recent, current_streak } = await res.json(); @@ -60,10 +62,10 @@ async function fetchStatsFromBackend(email) { if (Array.isArray(platform_counts)) { platform_counts.forEach(item => countsMap[item.name] = item.value); } else { - countsMap = platform_counts || {}; + countsMap = platform_counts || {}; } renderPlatformBarsFromMap(countsMap); - + renderHistory(recent); } @@ -95,14 +97,14 @@ function calculateStreakFromWeekMap(weekMap) { d.setDate(d.getDate() - i); const key = getLocalDateStr(d); if (weekMap[key]) streak++; - else if (i > 0) break; + else if (i > 0) break; } return streak; } // --- Render from backend shapes --- function renderWeekGridFromMap(weekMap) { - const days = ['Sun','Mon','Tue','Wed','Thu','Fri','Sat']; + const days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; const grid = document.getElementById('weekGrid'); grid.innerHTML = Array.from({ length: 7 }, (_, i) => { const d = new Date(); @@ -128,7 +130,7 @@ function renderPlatformBarsFromMap(counts) {
`).join(''); @@ -151,8 +153,8 @@ function calculateStreak(history) { const uniqueDates = [...new Set(history.map(h => getDateStr(h.date)))].sort().reverse(); let streak = 0; for (const dateStr of uniqueDates) { - const diff = Math.floor((new Date().setHours(0,0,0,0) - new Date(dateStr)) / 86400000); - if (diff === streak) streak++; + const diff = Math.floor((new Date().setHours(0, 0, 0, 0) - new Date(dateStr)) / 86400000); + if (diff === streak) streak++; else break; } return streak; @@ -164,7 +166,7 @@ function countThisWeek(history) { } function renderWeekGrid(history) { - const days = ['Sun','Mon','Tue','Wed','Thu','Fri','Sat']; + const days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; const grid = document.getElementById('weekGrid'); grid.innerHTML = Array.from({ length: 7 }, (_, i) => { const d = new Date(); d.setDate(d.getDate() - (6 - i)); @@ -190,13 +192,13 @@ function renderHistory(history) { return; } container.innerHTML = history.slice(0, 10).map(h => { - const dateStr = new Date(h.date).toLocaleDateString('en-US', { month:'short', day:'numeric', year:'numeric' }); + const dateStr = new Date(h.date).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); return `Enter your email so your stats are saved separately from other users.
- -Stored locally and only used to identify your data on the backend.
- ++ Connect your account so generated blogs publish to the right user + profile. +
+ + + + ++ New here? + +
Generate & publish stunning LeetCode blogs instantly.
- ++ Generate & publish stunning LeetCode blogs instantly. +
+