/callback
+ // Session is managed server-side via Passport.js + express-session (not Supabase)
} catch (error) {
console.error("Social Login Error", error);
setMessage(`Error logging in with ${provider}: ${error.message}`, "error");
diff --git a/client/JS/codeWorkspace.js b/client/JS/codeWorkspace.js
index 4813175..4fd7e06 100644
--- a/client/JS/codeWorkspace.js
+++ b/client/JS/codeWorkspace.js
@@ -1,4 +1,4 @@
-import config from './config.js';
+// config.js removed - AI key is now server-side in .env
import { generateTextWithGemini } from './geminiAPI.js';
import { wireThemeToggle, setThemeStorageKey } from './themeManager.js';
import { CODE_THEME_KEY } from './constants.js';
@@ -120,26 +120,38 @@ class CodeWorkspace {
}
}
- checkAPIKey() {
- const apiKey = config.GROQ_API_KEY;
- if (!apiKey || apiKey === "YOUR_GROQ_API_KEY" || apiKey.includes("YOUR")) {
- document.getElementById('api-warning').classList.remove('hidden');
- ['ai-explain-btn', 'ai-docs-btn', 'ai-improve-btn', 'ai-analyze-btn', 'ai-debug-btn', 'ai-visualize-flowchart-btn', 'ai-chat-btn'].forEach(id => {
- const btn = document.getElementById(id);
- if (btn) {
- btn.disabled = true;
- btn.classList.add('is-locked');
- btn.title = "Add Groq API key to enable AI features";
-
- // Add a small lock badge
- if (!btn.querySelector('.lock-badge')) {
- const badge = document.createElement('div');
- badge.className = 'lock-badge';
- badge.innerHTML = '🔒';
- btn.appendChild(badge);
- }
- }
+ async checkAPIKey() {
+ try {
+ const res = await fetch('/api/ai/generate', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ prompt: 'ping' })
});
+ // 200 = works, 401 = auth needed (key exists), 429 = rate limited (key exists)
+ if (res.ok || res.status === 401 || res.status === 429) {
+ return; // AI is available
+ }
+ const data = await res.json().catch(() => ({}));
+ const msg = (data.error || '').toLowerCase();
+ if (msg.includes('api key') || msg.includes('groq') || res.status === 503) {
+ document.getElementById('api-warning').classList.remove('hidden');
+ ['ai-explain-btn', 'ai-docs-btn', 'ai-improve-btn', 'ai-analyze-btn', 'ai-debug-btn', 'ai-visualize-flowchart-btn', 'ai-chat-btn'].forEach(id => {
+ const btn = document.getElementById(id);
+ if (btn) {
+ btn.disabled = true;
+ btn.classList.add('is-locked');
+ btn.title = "AI proxy key not configured on server";
+ if (!btn.querySelector('.lock-badge')) {
+ const badge = document.createElement('div');
+ badge.className = 'lock-badge';
+ badge.innerHTML = '🔒';
+ btn.appendChild(badge);
+ }
+ }
+ });
+ }
+ } catch (e) {
+ console.warn('[codeWorkspace] Could not reach AI proxy:', e.message);
}
}
diff --git a/client/JS/exportImport.js b/client/JS/exportImport.js
index 3f79678..9776c46 100644
--- a/client/JS/exportImport.js
+++ b/client/JS/exportImport.js
@@ -11,7 +11,7 @@ export function formatNotesAsText(notes) {
return notes
.map((note, index) => {
const title = note.title || "Untitled note";
- const tags = (note.tags || []).join(", ") || "none";
+ const tags = (Array.isArray(note.tags) ? note.tags : []).join(", ") || "none";
const created = note.createdAt || "";
const updated = note.updatedAt || "";
const content = stripHtml(note.content || "");
@@ -78,7 +78,7 @@ export function formatNotesAsMarkdown(notes) {
return notes.map((note) => {
const title = note.title || "Untitled";
const created = note.createdAt ? `*Created: ${note.createdAt}*` : "";
- const tags = (note.tags || []).length ? `**Tags:** ${note.tags.join(", ")}` : "";
+ const tags = (Array.isArray(note.tags) ? note.tags : []).length ? `**Tags:** ${note.tags.join(", ")}` : "";
const content = htmlToMarkdown(note.content);
return `# ${title}\n${created}\n${tags}\n\n${content}\n\n---\n`;
@@ -133,7 +133,7 @@ function printNotes(notes) {
Tags
- ${escapeHtml((note.tags || []).join(", ") || "No specific tags")}
+ ${escapeHtml((Array.isArray(note.tags) ? note.tags : []).join(", ") || "No specific tags")}
${note.folderId ? `