cool prestige update

This commit is contained in:
gpatruno
2026-07-19 00:29:46 +02:00
parent d0f868ffa5
commit 3a8871cbae
27 changed files with 1165 additions and 353 deletions
+182
View File
@@ -0,0 +1,182 @@
import { Router } from 'express';
import { prisma } from '../db.js';
import { requireAuth, publicUser } from '../middleware.js';
import {
maxVaultSlots,
settleVaultYield,
summarizeVaultRates,
estimatePendingYield,
} from '../vault.js';
import { isConnected } from '../presence.js';
const router = Router();
router.post('/stake', requireAuth, async (req, res) => {
try {
const ids = [...new Set((req.body.inventoryItemIds || []).map(Number).filter(Boolean))];
if (!ids.length) {
return res.status(400).json({ error: 'Select at least one item to stake' });
}
const result = await prisma.$transaction(async (tx) => {
const user = await tx.user.findUnique({ where: { id: req.session.userId } });
if (!user) {
const err = new Error('User not found');
err.status = 401;
throw err;
}
const maxSlots = maxVaultSlots(user);
const used = await tx.inventoryItem.count({
where: { userId: user.id, staked: true },
});
const free = maxSlots - used;
if (ids.length > free) {
const err = new Error(`Vault full (${used}/${maxSlots} slots)`);
err.status = 400;
throw err;
}
const invItems = await tx.inventoryItem.findMany({
where: {
id: { in: ids },
userId: user.id,
locked: false,
staked: false,
},
});
if (invItems.length !== ids.length) {
const err = new Error('Invalid, locked, or already staked items');
err.status = 400;
throw err;
}
const now = new Date();
await tx.inventoryItem.updateMany({
where: { id: { in: ids }, userId: user.id },
data: {
staked: true,
stakedAt: now,
lastYieldAt: now,
},
});
return { user, stakedIds: ids, maxSlots, used: used + ids.length };
});
res.json({
user: publicUser(result.user),
stakedIds: result.stakedIds,
vault: {
used: result.used,
slots: result.maxSlots,
},
});
} catch (err) {
const status = err.status || 500;
if (status >= 500) console.error(err);
res.status(status).json({ error: err.message || 'Failed to stake items' });
}
});
router.post('/unstake', requireAuth, async (req, res) => {
try {
const ids = [...new Set((req.body.inventoryItemIds || []).map(Number).filter(Boolean))];
if (!ids.length) {
return res.status(400).json({ error: 'Select at least one item to unstake' });
}
// Settle yield before unstake (online if connected, else offline)
const mode = isConnected(req.session.userId) ? 'online' : 'offline';
const settled = await settleVaultYield(req.session.userId, mode);
const result = await prisma.$transaction(async (tx) => {
const user = await tx.user.findUnique({ where: { id: req.session.userId } });
if (!user) {
const err = new Error('User not found');
err.status = 401;
throw err;
}
const invItems = await tx.inventoryItem.findMany({
where: {
id: { in: ids },
userId: user.id,
staked: true,
},
});
if (invItems.length !== ids.length) {
const err = new Error('Invalid or not-staked items');
err.status = 400;
throw err;
}
await tx.inventoryItem.updateMany({
where: { id: { in: ids }, userId: user.id },
data: {
staked: false,
stakedAt: null,
lastYieldAt: null,
},
});
const used = await tx.inventoryItem.count({
where: { userId: user.id, staked: true },
});
const refreshed = await tx.user.findUnique({ where: { id: user.id } });
return {
user: refreshed,
unstakedIds: ids,
maxSlots: maxVaultSlots(refreshed),
used,
};
});
res.json({
user: publicUser(result.user),
unstakedIds: result.unstakedIds,
yieldCredited: settled.credited,
vault: {
used: result.used,
slots: result.maxSlots,
},
});
} catch (err) {
const status = err.status || 500;
if (status >= 500) console.error(err);
res.status(status).json({ error: err.message || 'Failed to unstake items' });
}
});
router.post('/claim', requireAuth, async (req, res) => {
try {
const mode = isConnected(req.session.userId) ? 'online' : 'offline';
const settled = await settleVaultYield(req.session.userId, mode);
const user = await prisma.user.findUnique({ where: { id: req.session.userId } });
if (!user) return res.status(404).json({ error: 'User not found' });
const staked = await prisma.inventoryItem.findMany({
where: { userId: user.id, staked: true },
include: { item: true },
});
const rates = summarizeVaultRates(staked, user);
res.json({
user: publicUser(user),
credited: settled.credited,
ticks: settled.ticks,
vault: {
used: staked.length,
slots: maxVaultSlots(user),
...rates,
pendingYield: estimatePendingYield(staked, user, mode),
},
});
} catch (err) {
console.error(err);
res.status(500).json({ error: err.message || 'Failed to claim vault yield' });
}
});
export default router;