feat(imggen): фон питомца, обложки курсов, аватары и доска через ИИ
Питомец: кастомный фон (миграция 068 pet_bg_custom, POST /api/pet/bg/custom, карточка «Свой фон (ИИ)» в гардеробной, применение картинкой). Курсы: обложка-картинка (миграция 069 cover_image, генерация в модалке редактирования, рендер вместо эмодзи). Аватар: кнопка «Сгенерировать (ИИ)» в загрузке → кадрирование → модерация. Доска (classroom): кнопка-инструмент «Сгенерировать картинку (ИИ)». Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -178,7 +178,7 @@ function getPet(req, res) {
|
||||
const user = db.prepare(
|
||||
`SELECT xp, level, streak_current, streak_best, streak_date, coins,
|
||||
pet_name, last_login, pet_color, pet_last_petted, pet_petting_streak,
|
||||
pet_bg, pet_bg_owned, pet_last_fed, pet_equipped, pet_pattern
|
||||
pet_bg, pet_bg_owned, pet_last_fed, pet_equipped, pet_pattern, pet_bg_custom
|
||||
FROM users WHERE id = ?`
|
||||
).get(req.user.id);
|
||||
|
||||
@@ -277,6 +277,7 @@ function getPet(req, res) {
|
||||
feedCooldown,
|
||||
petBg: user.pet_bg || 'default',
|
||||
petBgOwned: _parseOwned(user.pet_bg_owned),
|
||||
petBgCustom: user.pet_bg_custom || null,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -391,7 +392,10 @@ function buyBg(req, res) {
|
||||
/* ── PATCH /api/pet/bg ────────────────────────────────────────────────── */
|
||||
function setBg(req, res) {
|
||||
const { id } = req.body;
|
||||
if (id !== 'default') {
|
||||
if (id === 'custom') {
|
||||
const url = db.prepare('SELECT pet_bg_custom FROM users WHERE id=?').get(req.user.id)?.pet_bg_custom;
|
||||
if (!url) return res.status(400).json({ error: 'no_custom_bg' });
|
||||
} else if (id !== 'default') {
|
||||
const owned = _parseOwned(db.prepare('SELECT pet_bg_owned FROM users WHERE id=?').get(req.user.id)?.pet_bg_owned);
|
||||
if (!owned.includes(id)) return res.status(403).json({ error: 'not owned' });
|
||||
}
|
||||
@@ -399,6 +403,18 @@ function setBg(req, res) {
|
||||
res.json({ ok: true, bg: id });
|
||||
}
|
||||
|
||||
/* ── POST /api/pet/bg/custom ──────────────────────────────────────────────
|
||||
Сохранить сгенерированную ИИ картинку как кастомный фон и сделать активной.
|
||||
URL принимается только из /uploads/generated/ (то, что отдаёт /api/imggen). */
|
||||
function setCustomBg(req, res) {
|
||||
const url = String(req.body && req.body.url || '');
|
||||
if (!/^\/uploads\/generated\/[A-Za-z0-9._-]+\.(png|jpg|jpeg|webp)$/.test(url)) {
|
||||
return res.status(400).json({ error: 'invalid_url' });
|
||||
}
|
||||
db.prepare('UPDATE users SET pet_bg_custom=?, pet_bg=? WHERE id=?').run(url, 'custom', req.user.id);
|
||||
res.json({ ok: true, bg: 'custom', url });
|
||||
}
|
||||
|
||||
/* ── POST /api/pet/star ───────────────────────────────────────────────── */
|
||||
function starCatch(req, res) {
|
||||
const user = db.prepare('SELECT pet_last_star FROM users WHERE id=?').get(req.user.id);
|
||||
@@ -436,4 +452,4 @@ function feedPet(req, res) {
|
||||
res.json({ ok: true, xpAwarded: 15, xp: updated.xp, coins: updated.coins });
|
||||
}
|
||||
|
||||
module.exports = { getPet, renamePet, petAction, updateColor, starCatch, getShop, buyBg, setBg, feedPet, equipAccessories, updatePattern };
|
||||
module.exports = { getPet, renamePet, petAction, updateColor, starCatch, getShop, buyBg, setBg, setCustomBg, feedPet, equipAccessories, updatePattern };
|
||||
|
||||
Reference in New Issue
Block a user