Add files via upload
This commit is contained in:
53
services/geminiService.ts
Normal file
53
services/geminiService.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import { GoogleGenAI } from "@google/genai";
|
||||
|
||||
const getClient = () => {
|
||||
const apiKey = process.env.API_KEY;
|
||||
if (!apiKey) return null;
|
||||
return new GoogleGenAI({ apiKey });
|
||||
};
|
||||
|
||||
export const generateEmailContent = async (
|
||||
prompt: string,
|
||||
section: 'header' | 'body' | 'footer'
|
||||
): Promise<string> => {
|
||||
const client = getClient();
|
||||
if (!client) {
|
||||
throw new Error("API Key not found");
|
||||
}
|
||||
|
||||
// Model selection based on text task complexity
|
||||
const modelId = 'gemini-2.5-flash';
|
||||
|
||||
const systemInstruction = `
|
||||
You are an expert email marketing developer.
|
||||
You are generating HTML content for an email template ${section}.
|
||||
|
||||
RULES:
|
||||
1. Return ONLY the HTML code. Do not include markdown ticks, 'html' labels, or explanations.
|
||||
2. Use inline CSS styles for compatibility.
|
||||
3. Use the Handlebars syntax for placeholders: {{variable_name}}.
|
||||
4. If the user asks for a specific variable, format it correctly.
|
||||
5. For Header: Include logo placeholders or standard menu links if asked.
|
||||
6. For Footer: Include unsubscribe links and copyright info if asked.
|
||||
7. For Body: Focus on clean, readable content.
|
||||
`;
|
||||
|
||||
try {
|
||||
const response = await client.models.generateContent({
|
||||
model: modelId,
|
||||
contents: prompt,
|
||||
config: {
|
||||
systemInstruction: systemInstruction,
|
||||
temperature: 0.7,
|
||||
}
|
||||
});
|
||||
|
||||
let text = response.text || "";
|
||||
// Cleanup if model adds markdown despite instructions
|
||||
text = text.replace(/```html/g, '').replace(/```/g, '').trim();
|
||||
return text;
|
||||
} catch (error) {
|
||||
console.error("Gemini API Error:", error);
|
||||
throw new Error("Failed to generate content");
|
||||
}
|
||||
};
|
||||
106
services/storage.ts
Normal file
106
services/storage.ts
Normal file
@@ -0,0 +1,106 @@
|
||||
import { EmailTemplate } from '../types';
|
||||
|
||||
// Helper functions remain synchronous as they are utility functions
|
||||
export const generateTemplateKey = (name: string): string => {
|
||||
return name.trim().toLowerCase().replace(/\s+/g, '_').replace(/[^a-z0-9_]/g, '');
|
||||
};
|
||||
|
||||
export const generateSQL = (template: EmailTemplate): string => {
|
||||
const fullHtml = `${template.header}${template.body}${template.footer}`.replace(/'/g, "''");
|
||||
const header = template.header.replace(/'/g, "''");
|
||||
const body = template.body.replace(/'/g, "''");
|
||||
const footer = template.footer.replace(/'/g, "''");
|
||||
const subject = template.subject.replace(/'/g, "''");
|
||||
const vars = JSON.stringify(template.variables).replace(/'/g, "''");
|
||||
const key = generateTemplateKey(template.name);
|
||||
|
||||
return `INSERT INTO email_templates (id, template_key, name, description, subject, header_html, body_html, footer_html, full_html, required_variables)
|
||||
VALUES ('${template.id}', '${key}', '${template.name.replace(/'/g, "''")}', '${template.description?.replace(/'/g, "''") || ''}', '${subject}', '${header}', '${body}', '${footer}', '${fullHtml}', '${vars}')
|
||||
ON DUPLICATE KEY UPDATE
|
||||
subject = VALUES(subject),
|
||||
header_html = VALUES(header_html),
|
||||
body_html = VALUES(body_html),
|
||||
footer_html = VALUES(footer_html),
|
||||
full_html = VALUES(full_html),
|
||||
required_variables = VALUES(required_variables);`;
|
||||
};
|
||||
|
||||
export const generateSelectSQL = (template: EmailTemplate): string => {
|
||||
const key = generateTemplateKey(template.name);
|
||||
return `SELECT * FROM email_templates WHERE template_key = '${key}';`;
|
||||
};
|
||||
|
||||
export const generateN8nCode = (template: EmailTemplate): string => {
|
||||
const varsMap = template.variables.map(v => ` "${v}": "REPLACE_WITH_VALUE"`).join(',\n');
|
||||
const hasVars = template.variables.length > 0;
|
||||
|
||||
return `// n8n Code Node - Template Populator
|
||||
// 1. Ensure the previous node (SQL) returns the template with 'full_html' column.
|
||||
// 2. Adjust the 'item.json.full_html' path if your SQL node output is different.
|
||||
|
||||
for (const item of items) {
|
||||
const templateHtml = item.json.full_html;
|
||||
|
||||
// Define your dynamic data here
|
||||
const replacements = {
|
||||
${hasVars ? varsMap : ' // No variables detected in this template'}
|
||||
};
|
||||
|
||||
let finalHtml = templateHtml;
|
||||
|
||||
// Perform replacement
|
||||
for (const [key, value] of Object.entries(replacements)) {
|
||||
// Replaces {{key}} globally
|
||||
finalHtml = finalHtml.replace(new RegExp('{{' + key + '}}', 'g'), value);
|
||||
}
|
||||
|
||||
// Output the processed HTML
|
||||
item.json.processed_html = finalHtml;
|
||||
}
|
||||
|
||||
return items;`;
|
||||
};
|
||||
|
||||
// Async API calls to replace synchronous localStorage
|
||||
export const getTemplates = async (): Promise<EmailTemplate[]> => {
|
||||
try {
|
||||
const response = await fetch('/api/templates');
|
||||
if (!response.ok) throw new Error('Failed to fetch');
|
||||
return await response.json();
|
||||
} catch (e) {
|
||||
console.error("Failed to load templates", e);
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
export const saveTemplate = async (template: EmailTemplate): Promise<void> => {
|
||||
try {
|
||||
const response = await fetch('/api/templates', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(template),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const error = await response.json();
|
||||
throw new Error(error.message || 'Failed to save');
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Failed to save template", e);
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
|
||||
export const deleteTemplate = async (id: string): Promise<void> => {
|
||||
try {
|
||||
const response = await fetch(`/api/templates/${id}`, {
|
||||
method: 'DELETE',
|
||||
});
|
||||
if (!response.ok) throw new Error('Failed to delete');
|
||||
} catch (e) {
|
||||
console.error("Failed to delete template", e);
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user