From 31e882856c2b955ee97b1c8b84ab2ec32d274b28 Mon Sep 17 00:00:00 2001 From: Antigravity Date: Sat, 20 Jun 2026 17:11:32 +0000 Subject: [PATCH] =?UTF-8?q?fix(audit):=20prototypes=20Gemini=20server-side?= =?UTF-8?q?=20+=20retire=20metrics-token=20du=20d=C3=A9p=C3=B4t?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Proxy Gemini côté serveur (plus de clé dans le bundle Vite), port prototype 4000, et suppression du token métriques placeholder versionné. Co-authored-by: Cursor --- architectural-grid/server.ts | 32 +++++++++++ .../src/services/geminiService.ts | 53 +++++++++++++++---- architectural-grid1/server.ts | 32 +++++++++++ .../src/services/geminiService.ts | 53 +++++++++++++++---- monitoring/metrics-token | 1 - 5 files changed, 152 insertions(+), 19 deletions(-) delete mode 100644 monitoring/metrics-token diff --git a/architectural-grid/server.ts b/architectural-grid/server.ts index 2c6cb42..ff34fbd 100644 --- a/architectural-grid/server.ts +++ b/architectural-grid/server.ts @@ -170,6 +170,38 @@ async function startServer() { res.json(ideas[index]); }); + app.post('/api/gemini/embed', async (req, res) => { + const key = process.env.GEMINI_API_KEY; + if (!key) { + return res.status(503).json({ error: 'GEMINI_API_KEY not configured on server' }); + } + try { + const { GoogleGenAI } = await import('@google/genai'); + const ai = new GoogleGenAI({ apiKey: key }); + const response = await ai.models.embedContent(req.body); + res.json(response); + } catch (error) { + console.error('Gemini embed error:', error); + res.status(500).json({ error: 'Gemini embed failed' }); + } + }); + + app.post('/api/gemini/generate', async (req, res) => { + const key = process.env.GEMINI_API_KEY; + if (!key) { + return res.status(503).json({ error: 'GEMINI_API_KEY not configured on server' }); + } + try { + const { GoogleGenAI } = await import('@google/genai'); + const ai = new GoogleGenAI({ apiKey: key }); + const response = await ai.models.generateContent(req.body); + res.json({ text: response.text ?? '' }); + } catch (error) { + console.error('Gemini proxy error:', error); + res.status(500).json({ error: 'Gemini request failed' }); + } + }); + // Vite middleware for development if (process.env.NODE_ENV !== "production") { const vite = await createViteServer({ diff --git a/architectural-grid/src/services/geminiService.ts b/architectural-grid/src/services/geminiService.ts index 01e2451..47302ec 100644 --- a/architectural-grid/src/services/geminiService.ts +++ b/architectural-grid/src/services/geminiService.ts @@ -2,7 +2,24 @@ import { GoogleGenAI, Type } from "@google/genai"; import { BrainstormIdea } from "../types"; -const ai = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY }); +async function generateContent( + request: Parameters['models']['generateContent']>[0], +) { + if (typeof window !== 'undefined') { + const res = await fetch('/api/gemini/generate', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(request), + }); + if (!res.ok) throw new Error('Gemini request failed'); + const data = await res.json(); + return { text: data.text ?? '' }; + } + const key = process.env.GEMINI_API_KEY; + if (!key) throw new Error('GEMINI_API_KEY required on server'); + const ai = new GoogleGenAI({ apiKey: key }); + return ai.models.generateContent(request); +} const BRAINSTORM_SCHEMA = { type: Type.OBJECT, @@ -63,7 +80,7 @@ export async function generateBrainstormWave( `; try { - const response = await ai.models.generateContent({ + const response = await generateContent({ model: "gemini-3-flash-preview", contents: [{ role: "user", parts: [{ text: prompt }] }], config: { @@ -101,7 +118,7 @@ export async function generateExpansion(parentIdeaTitle: string, parentIdeaDescr `; try { - const response = await ai.models.generateContent({ + const response = await generateContent({ model: "gemini-3-flash-preview", contents: [{ role: "user", parts: [{ text: prompt }] }], config: { @@ -130,9 +147,27 @@ export async function generateExpansion(parentIdeaTitle: string, parentIdeaDescr } } +async function embedContent( + request: Parameters['models']['embedContent']>[0], +) { + if (typeof window !== 'undefined') { + const res = await fetch('/api/gemini/embed', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(request), + }); + if (!res.ok) throw new Error('Gemini embed failed'); + return res.json(); + } + const key = process.env.GEMINI_API_KEY; + if (!key) throw new Error('GEMINI_API_KEY required on server'); + const ai = new GoogleGenAI({ apiKey: key }); + return ai.models.embedContent(request); +} + export async function getEmbedding(text: string): Promise { try { - const result = await ai.models.embedContent({ + const result = await embedContent({ model: 'gemini-embedding-2-preview', contents: [text], }); @@ -155,7 +190,7 @@ export function cosineSimilarity(a: number[], b: number[]): number { export async function nameCluster(noteSummaries: string[]): Promise { const prompt = `Quel thème commun relie ces notes ? Donne un nom court (2-4 mots).\nNotes :\n${noteSummaries.join('\n- ')}`; try { - const result = await ai.models.generateContent({ + const result = await generateContent({ model: "gemini-3-flash-preview", contents: prompt }); @@ -184,7 +219,7 @@ export async function suggestBridgeIdeas( `; try { - const response = await ai.models.generateContent({ + const response = await generateContent({ model: "gemini-3-flash-preview", contents: prompt, config: { @@ -207,7 +242,7 @@ export async function parseDocument(fileUrl: string, fileName: string): Promise< try { // In a real scenario, we would use media upload. // Here we simulate the extraction. - const response = await ai.models.generateContent({ + const response = await generateContent({ model: "gemini-3-flash-preview", contents: [{ role: "user", parts: [{ text: prompt }] }], config: { @@ -236,7 +271,7 @@ export async function extractActionItems(notes: { title: string; content: string `; try { - const response = await ai.models.generateContent({ + const response = await generateContent({ model: "gemini-3-flash-preview", contents: prompt, config: { @@ -289,7 +324,7 @@ export async function generateFlashcardsForNote( `; try { - const response = await ai.models.generateContent({ + const response = await generateContent({ model: "gemini-3.5-flash", contents: [{ role: "user", parts: [{ text: prompt }] }], config: { diff --git a/architectural-grid1/server.ts b/architectural-grid1/server.ts index 2c6cb42..ff34fbd 100644 --- a/architectural-grid1/server.ts +++ b/architectural-grid1/server.ts @@ -170,6 +170,38 @@ async function startServer() { res.json(ideas[index]); }); + app.post('/api/gemini/embed', async (req, res) => { + const key = process.env.GEMINI_API_KEY; + if (!key) { + return res.status(503).json({ error: 'GEMINI_API_KEY not configured on server' }); + } + try { + const { GoogleGenAI } = await import('@google/genai'); + const ai = new GoogleGenAI({ apiKey: key }); + const response = await ai.models.embedContent(req.body); + res.json(response); + } catch (error) { + console.error('Gemini embed error:', error); + res.status(500).json({ error: 'Gemini embed failed' }); + } + }); + + app.post('/api/gemini/generate', async (req, res) => { + const key = process.env.GEMINI_API_KEY; + if (!key) { + return res.status(503).json({ error: 'GEMINI_API_KEY not configured on server' }); + } + try { + const { GoogleGenAI } = await import('@google/genai'); + const ai = new GoogleGenAI({ apiKey: key }); + const response = await ai.models.generateContent(req.body); + res.json({ text: response.text ?? '' }); + } catch (error) { + console.error('Gemini proxy error:', error); + res.status(500).json({ error: 'Gemini request failed' }); + } + }); + // Vite middleware for development if (process.env.NODE_ENV !== "production") { const vite = await createViteServer({ diff --git a/architectural-grid1/src/services/geminiService.ts b/architectural-grid1/src/services/geminiService.ts index 01e2451..47302ec 100644 --- a/architectural-grid1/src/services/geminiService.ts +++ b/architectural-grid1/src/services/geminiService.ts @@ -2,7 +2,24 @@ import { GoogleGenAI, Type } from "@google/genai"; import { BrainstormIdea } from "../types"; -const ai = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY }); +async function generateContent( + request: Parameters['models']['generateContent']>[0], +) { + if (typeof window !== 'undefined') { + const res = await fetch('/api/gemini/generate', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(request), + }); + if (!res.ok) throw new Error('Gemini request failed'); + const data = await res.json(); + return { text: data.text ?? '' }; + } + const key = process.env.GEMINI_API_KEY; + if (!key) throw new Error('GEMINI_API_KEY required on server'); + const ai = new GoogleGenAI({ apiKey: key }); + return ai.models.generateContent(request); +} const BRAINSTORM_SCHEMA = { type: Type.OBJECT, @@ -63,7 +80,7 @@ export async function generateBrainstormWave( `; try { - const response = await ai.models.generateContent({ + const response = await generateContent({ model: "gemini-3-flash-preview", contents: [{ role: "user", parts: [{ text: prompt }] }], config: { @@ -101,7 +118,7 @@ export async function generateExpansion(parentIdeaTitle: string, parentIdeaDescr `; try { - const response = await ai.models.generateContent({ + const response = await generateContent({ model: "gemini-3-flash-preview", contents: [{ role: "user", parts: [{ text: prompt }] }], config: { @@ -130,9 +147,27 @@ export async function generateExpansion(parentIdeaTitle: string, parentIdeaDescr } } +async function embedContent( + request: Parameters['models']['embedContent']>[0], +) { + if (typeof window !== 'undefined') { + const res = await fetch('/api/gemini/embed', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(request), + }); + if (!res.ok) throw new Error('Gemini embed failed'); + return res.json(); + } + const key = process.env.GEMINI_API_KEY; + if (!key) throw new Error('GEMINI_API_KEY required on server'); + const ai = new GoogleGenAI({ apiKey: key }); + return ai.models.embedContent(request); +} + export async function getEmbedding(text: string): Promise { try { - const result = await ai.models.embedContent({ + const result = await embedContent({ model: 'gemini-embedding-2-preview', contents: [text], }); @@ -155,7 +190,7 @@ export function cosineSimilarity(a: number[], b: number[]): number { export async function nameCluster(noteSummaries: string[]): Promise { const prompt = `Quel thème commun relie ces notes ? Donne un nom court (2-4 mots).\nNotes :\n${noteSummaries.join('\n- ')}`; try { - const result = await ai.models.generateContent({ + const result = await generateContent({ model: "gemini-3-flash-preview", contents: prompt }); @@ -184,7 +219,7 @@ export async function suggestBridgeIdeas( `; try { - const response = await ai.models.generateContent({ + const response = await generateContent({ model: "gemini-3-flash-preview", contents: prompt, config: { @@ -207,7 +242,7 @@ export async function parseDocument(fileUrl: string, fileName: string): Promise< try { // In a real scenario, we would use media upload. // Here we simulate the extraction. - const response = await ai.models.generateContent({ + const response = await generateContent({ model: "gemini-3-flash-preview", contents: [{ role: "user", parts: [{ text: prompt }] }], config: { @@ -236,7 +271,7 @@ export async function extractActionItems(notes: { title: string; content: string `; try { - const response = await ai.models.generateContent({ + const response = await generateContent({ model: "gemini-3-flash-preview", contents: prompt, config: { @@ -289,7 +324,7 @@ export async function generateFlashcardsForNote( `; try { - const response = await ai.models.generateContent({ + const response = await generateContent({ model: "gemini-3.5-flash", contents: [{ role: "user", parts: [{ text: prompt }] }], config: { diff --git a/monitoring/metrics-token b/monitoring/metrics-token deleted file mode 100644 index 9972f9a..0000000 --- a/monitoring/metrics-token +++ /dev/null @@ -1 +0,0 @@ -changeme-replace-with-real-token