feat: NPC system improvements - custom prompt, NSFW, full body generation
This commit is contained in:
+229
@@ -602,6 +602,111 @@ app.delete("/api/characters/:id", requireAuth, async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
// ============ NPC CHARACTERS ROUTES ============
|
||||
|
||||
// Получить всех NPC пользователя
|
||||
app.get("/api/npc", requireAuth, async (req, res) => {
|
||||
try {
|
||||
const npcCharacters = db.collection("npc_characters");
|
||||
const userNPCs = await npcCharacters
|
||||
.find({ userId: req.session.userId })
|
||||
.sort({ updatedAt: -1 })
|
||||
.toArray();
|
||||
|
||||
res.json(userNPCs);
|
||||
} catch (error) {
|
||||
console.error("Get NPCs error:", error);
|
||||
res.status(500).json({ error: "Failed to get NPCs" });
|
||||
}
|
||||
});
|
||||
|
||||
// Получить одного NPC
|
||||
app.get("/api/npc/:id", requireAuth, async (req, res) => {
|
||||
try {
|
||||
const npcCharacters = db.collection("npc_characters");
|
||||
const npc = await npcCharacters.findOne({
|
||||
_id: new ObjectId(req.params.id),
|
||||
userId: req.session.userId,
|
||||
});
|
||||
|
||||
if (!npc) {
|
||||
return res.status(404).json({ error: "NPC not found" });
|
||||
}
|
||||
|
||||
res.json(npc);
|
||||
} catch (error) {
|
||||
console.error("Get NPC error:", error);
|
||||
res.status(500).json({ error: "Failed to get NPC" });
|
||||
}
|
||||
});
|
||||
|
||||
// Создать NPC
|
||||
app.post("/api/npc", requireAuth, async (req, res) => {
|
||||
try {
|
||||
const npcCharacters = db.collection("npc_characters");
|
||||
const newNPC = {
|
||||
...req.body,
|
||||
userId: req.session.userId,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
};
|
||||
|
||||
const result = await npcCharacters.insertOne(newNPC);
|
||||
res.json({ ...newNPC, _id: result.insertedId });
|
||||
} catch (error) {
|
||||
console.error("Create NPC error:", error);
|
||||
res.status(500).json({ error: "Failed to create NPC" });
|
||||
}
|
||||
});
|
||||
|
||||
// Обновить NPC
|
||||
app.put("/api/npc/:id", requireAuth, async (req, res) => {
|
||||
try {
|
||||
const npcCharacters = db.collection("npc_characters");
|
||||
const result = await npcCharacters.updateOne(
|
||||
{
|
||||
_id: new ObjectId(req.params.id),
|
||||
userId: req.session.userId,
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
...req.body,
|
||||
updatedAt: new Date(),
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
if (result.matchedCount === 0) {
|
||||
return res.status(404).json({ error: "NPC not found" });
|
||||
}
|
||||
|
||||
res.json({ success: true });
|
||||
} catch (error) {
|
||||
console.error("Update NPC error:", error);
|
||||
res.status(500).json({ error: "Failed to update NPC" });
|
||||
}
|
||||
});
|
||||
|
||||
// Удалить NPC
|
||||
app.delete("/api/npc/:id", requireAuth, async (req, res) => {
|
||||
try {
|
||||
const npcCharacters = db.collection("npc_characters");
|
||||
const result = await npcCharacters.deleteOne({
|
||||
_id: new ObjectId(req.params.id),
|
||||
userId: req.session.userId,
|
||||
});
|
||||
|
||||
if (result.deletedCount === 0) {
|
||||
return res.status(404).json({ error: "NPC not found" });
|
||||
}
|
||||
|
||||
res.json({ success: true });
|
||||
} catch (error) {
|
||||
console.error("Delete NPC error:", error);
|
||||
res.status(500).json({ error: "Failed to delete NPC" });
|
||||
}
|
||||
});
|
||||
|
||||
// ============ ADMIN STATS ============
|
||||
|
||||
// Получить статистику по всем историям и токенам
|
||||
@@ -701,6 +806,130 @@ app.get("/api/admin/stats", requireAuth, async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
// ============ IMAGE GENERATION ============
|
||||
|
||||
// Прокси для генерации изображений через Grok (обход CORS)
|
||||
app.post("/api/generate-image", requireAuth, async (req, res) => {
|
||||
try {
|
||||
const { prompt } = req.body;
|
||||
const apiKey = process.env.GEMINIGEN_API_KEY;
|
||||
|
||||
if (!apiKey) {
|
||||
return res
|
||||
.status(500)
|
||||
.json({ error: "GeminiGen API key not configured" });
|
||||
}
|
||||
|
||||
console.log("Generating image with Grok, prompt:", prompt);
|
||||
|
||||
// Используем FormData для multipart/form-data
|
||||
const formData = new FormData();
|
||||
formData.append("prompt", prompt);
|
||||
formData.append("orientation", "portrait"); // 9:16
|
||||
formData.append("num_result", "1");
|
||||
|
||||
const response = await fetch(
|
||||
"https://api.geminigen.ai/uapi/v1/imagen/grok",
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
"x-api-key": apiKey,
|
||||
},
|
||||
body: formData,
|
||||
},
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
const error = await response.text();
|
||||
console.error("Grok API error:", error);
|
||||
return res
|
||||
.status(response.status)
|
||||
.json({ error: "Image generation failed", details: error });
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
console.log("Grok response:", data);
|
||||
|
||||
// Проверяем статус генерации
|
||||
if (data.status === 2 && data.generate_result) {
|
||||
// Готово - возвращаем URL
|
||||
res.json({ url: data.generate_result });
|
||||
} else if (data.status === 1) {
|
||||
// В процессе - возвращаем uuid для polling
|
||||
res.json({
|
||||
pending: true,
|
||||
uuid: data.uuid,
|
||||
status_percentage: data.status_percentage,
|
||||
});
|
||||
} else {
|
||||
res
|
||||
.status(500)
|
||||
.json({ error: data.error_message || "Generation failed" });
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Image generation error:", error);
|
||||
res.status(500).json({ error: "Failed to generate image" });
|
||||
}
|
||||
});
|
||||
|
||||
// Проверка статуса генерации
|
||||
app.get("/api/generate-image/status/:uuid", requireAuth, async (req, res) => {
|
||||
try {
|
||||
const apiKey = process.env.GEMINIGEN_API_KEY;
|
||||
const { uuid } = req.params;
|
||||
|
||||
const response = await fetch(
|
||||
`https://api.geminigen.ai/uapi/v1/history/${uuid}`,
|
||||
{
|
||||
headers: {
|
||||
"x-api-key": apiKey,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text();
|
||||
console.error("History API error:", response.status, errorText);
|
||||
return res
|
||||
.status(response.status)
|
||||
.json({ error: "Failed to check status" });
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
console.log(
|
||||
"History API status:",
|
||||
data.status,
|
||||
"images:",
|
||||
data.generated_image?.length,
|
||||
);
|
||||
|
||||
if (data.status === 2) {
|
||||
// Completed - get image URL from generated_image array
|
||||
const imageUrl =
|
||||
data.generated_image?.[0]?.image_url ||
|
||||
data.generated_image?.[0]?.file_download_url ||
|
||||
data.generate_result;
|
||||
if (imageUrl) {
|
||||
res.json({ url: imageUrl, done: true });
|
||||
} else {
|
||||
res.status(500).json({ error: "No image URL in response" });
|
||||
}
|
||||
} else if (data.status === 1) {
|
||||
res.json({ pending: true, status_percentage: data.status_percentage });
|
||||
} else if (data.status === 3) {
|
||||
res
|
||||
.status(500)
|
||||
.json({ error: data.error_message || "Generation failed" });
|
||||
} else {
|
||||
// Unknown status - keep polling
|
||||
res.json({ pending: true, status_percentage: data.status_percentage });
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Status check error:", error);
|
||||
res.status(500).json({ error: "Failed to check status" });
|
||||
}
|
||||
});
|
||||
|
||||
// Запуск сервера
|
||||
connectDB().then(() => {
|
||||
app.listen(PORT, () => {
|
||||
|
||||
Reference in New Issue
Block a user