- Apply Stitch design system to leadspeicher, suche, TopBar, globals.css - Add Energieversorger queue campaign (Netzbetreiber, Fernwärme, Industriepark) with BW + Bayern priority, tracks usage per term+location combo - Remove TopBar right-side actions (Leads finden, bell, settings) - Remove mode tabs from manual search, rename KI button - Fix Google Fonts @import order (move to <link> in layout.tsx) - Add cursor-pointer globally via globals.css - Responsive fixes for campaign buttons and KI button - Fix .dockerignore to exclude .env from image build - Add stadtwerke-cities API + city data (50 cities per Bundesland) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
85 lines
3.2 KiB
TypeScript
85 lines
3.2 KiB
TypeScript
import { NextRequest, NextResponse } from "next/server";
|
|
import { prisma } from "@/lib/db";
|
|
import { ALL_CITIES } from "@/lib/data/stadtwerke-cities";
|
|
|
|
const OPENROUTER_API_KEY = process.env.OPENROUTER_API_KEY || "";
|
|
const OPENROUTER_BASE = "https://openrouter.ai/api/v1";
|
|
|
|
export async function GET(req: NextRequest) {
|
|
try {
|
|
const { searchParams } = new URL(req.url);
|
|
const count = Math.min(parseInt(searchParams.get("count") || "20", 10), 100);
|
|
|
|
// Get all regions already used for stadtwerke mode
|
|
const used = await prisma.searchHistory.findMany({
|
|
where: { searchMode: "stadtwerke" },
|
|
select: { region: true },
|
|
});
|
|
const usedRegions = new Set(used.map(u => u.region.toLowerCase().trim()));
|
|
|
|
// Filter static list — cities not yet searched
|
|
const remaining = ALL_CITIES.filter(city => !usedRegions.has(city.toLowerCase().trim()));
|
|
|
|
if (remaining.length === 0) {
|
|
// All static cities exhausted — ask AI for new suggestions
|
|
const newCities = await generateNewCities(usedRegions);
|
|
return NextResponse.json({ cities: newCities.slice(0, count), exhausted: true, totalRemaining: newCities.length });
|
|
}
|
|
|
|
return NextResponse.json({
|
|
cities: remaining.slice(0, count),
|
|
exhausted: false,
|
|
totalRemaining: remaining.length,
|
|
});
|
|
} catch (err) {
|
|
console.error("GET /api/stadtwerke-cities error:", err);
|
|
return NextResponse.json({ error: "Failed to fetch cities" }, { status: 500 });
|
|
}
|
|
}
|
|
|
|
async function generateNewCities(usedRegions: Set<string>): Promise<string[]> {
|
|
if (!OPENROUTER_API_KEY) return [];
|
|
|
|
const usedList = Array.from(usedRegions)
|
|
.filter(r => r.length > 0)
|
|
.slice(0, 150)
|
|
.join(", ");
|
|
|
|
try {
|
|
const res = await fetch(`${OPENROUTER_BASE}/chat/completions`, {
|
|
method: "POST",
|
|
headers: {
|
|
"Authorization": `Bearer ${OPENROUTER_API_KEY}`,
|
|
"Content-Type": "application/json",
|
|
"HTTP-Referer": "https://mein-solar.de",
|
|
},
|
|
body: JSON.stringify({
|
|
model: "openai/gpt-4o-mini",
|
|
messages: [
|
|
{
|
|
role: "system",
|
|
content:
|
|
"Du bist ein Experte für kommunale Energieversorger in Deutschland. Antworte ausschließlich mit einem gültigen JSON-Array aus Städtenamen, ohne Erklärungen.",
|
|
},
|
|
{
|
|
role: "user",
|
|
content: `Generiere 40 deutsche Städte und Gemeinden, in denen es lokale Stadtwerke oder kommunale Energieversorger gibt. Keine dieser Städte darf in der folgenden Liste enthalten sein:\n${usedList}\n\nGib nur ein JSON-Array zurück, z.B.: ["Landsberg am Lech", "Bühl", "Leutkirch"]`,
|
|
},
|
|
],
|
|
max_tokens: 600,
|
|
temperature: 0.7,
|
|
}),
|
|
});
|
|
|
|
const data = await res.json() as { choices?: Array<{ message?: { content?: string } }> };
|
|
const content = data.choices?.[0]?.message?.content?.trim() || "[]";
|
|
|
|
// Strip markdown code blocks if present
|
|
const cleaned = content.replace(/^```(?:json)?\s*/i, "").replace(/\s*```$/, "").trim();
|
|
const cities = JSON.parse(cleaned) as unknown;
|
|
return Array.isArray(cities) ? (cities as string[]).filter(c => typeof c === "string") : [];
|
|
} catch {
|
|
return [];
|
|
}
|
|
}
|