fix(storage): remove auth de /api/storage/view e try-catch nos sticker routes
- /api/storage/view/* não usa mais authMiddleware: <img>/<audio>/<video> do browser não enviam JWT header, causando 401 em todas as mídias do Wasabi - sticker.routes.ts: wrap try-catch em todos os handlers — sem isso, erro Prisma P2021 (tabela não existe) vira unhandled rejection e derruba Node 22 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -14,75 +14,85 @@ export function buildStickerRoutes(): Router {
|
||||
|
||||
// ── LIST (com listVersion para cache invalidation) ────────────────────────
|
||||
router.get('/', async (req: Request, res: Response) => {
|
||||
const tenantId = req.tenantId!
|
||||
try {
|
||||
const tenantId = req.tenantId!
|
||||
|
||||
const stickers = await prisma.sticker.findMany({
|
||||
where: { tenantId },
|
||||
orderBy: [{ isFavorite: 'desc' }, { lastSeenAt: 'desc' }],
|
||||
select: {
|
||||
id: true,
|
||||
sha256: true,
|
||||
wasabiPath: true,
|
||||
isAnimated: true,
|
||||
isFavorite: true,
|
||||
useCount: true,
|
||||
lastSeenAt: true,
|
||||
},
|
||||
})
|
||||
const stickers = await prisma.sticker.findMany({
|
||||
where: { tenantId },
|
||||
orderBy: [{ isFavorite: 'desc' }, { lastSeenAt: 'desc' }],
|
||||
select: {
|
||||
id: true,
|
||||
sha256: true,
|
||||
wasabiPath: true,
|
||||
isAnimated: true,
|
||||
isFavorite: true,
|
||||
useCount: true,
|
||||
lastSeenAt: true,
|
||||
},
|
||||
})
|
||||
|
||||
// listVersion muda apenas quando o conjunto de stickers muda
|
||||
// (novo sha256 chega ou estado de favorito muda)
|
||||
const versionSource = stickers.length === 0
|
||||
? 'empty'
|
||||
: `${stickers.length}:${stickers[0]!.sha256}:${stickers[0]!.lastSeenAt.getTime()}:${stickers.filter(s => s.isFavorite).length}`
|
||||
const listVersion = createHash('md5').update(versionSource).digest('hex').slice(0, 8)
|
||||
const versionSource = stickers.length === 0
|
||||
? 'empty'
|
||||
: `${stickers.length}:${stickers[0]!.sha256}:${stickers[0]!.lastSeenAt.getTime()}:${stickers.filter(s => s.isFavorite).length}`
|
||||
const listVersion = createHash('md5').update(versionSource).digest('hex').slice(0, 8)
|
||||
|
||||
res.json({ listVersion, stickers })
|
||||
res.json({ listVersion, stickers })
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: 'Erro ao listar stickers' })
|
||||
}
|
||||
})
|
||||
|
||||
// ── RECENT (últimos 30) ───────────────────────────────────────────────────
|
||||
router.get('/recent', async (req: Request, res: Response) => {
|
||||
const tenantId = req.tenantId!
|
||||
try {
|
||||
const tenantId = req.tenantId!
|
||||
|
||||
const stickers = await prisma.sticker.findMany({
|
||||
where: { tenantId },
|
||||
orderBy: { lastSeenAt: 'desc' },
|
||||
take: 30,
|
||||
select: {
|
||||
id: true,
|
||||
sha256: true,
|
||||
wasabiPath: true,
|
||||
isAnimated: true,
|
||||
isFavorite: true,
|
||||
},
|
||||
})
|
||||
const stickers = await prisma.sticker.findMany({
|
||||
where: { tenantId },
|
||||
orderBy: { lastSeenAt: 'desc' },
|
||||
take: 30,
|
||||
select: {
|
||||
id: true,
|
||||
sha256: true,
|
||||
wasabiPath: true,
|
||||
isAnimated: true,
|
||||
isFavorite: true,
|
||||
},
|
||||
})
|
||||
|
||||
res.json({ stickers })
|
||||
res.json({ stickers })
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: 'Erro ao listar stickers recentes' })
|
||||
}
|
||||
})
|
||||
|
||||
// ── TOGGLE FAVORITE ───────────────────────────────────────────────────────
|
||||
router.patch('/:id/favorite', async (req: Request, res: Response) => {
|
||||
const tenantId = req.tenantId!
|
||||
const idParam = req.params['id']
|
||||
const id = Array.isArray(idParam) ? idParam[0]! : idParam
|
||||
try {
|
||||
const tenantId = req.tenantId!
|
||||
const idParam = req.params['id']
|
||||
const id = Array.isArray(idParam) ? idParam[0]! : idParam
|
||||
|
||||
const sticker = await prisma.sticker.findFirst({
|
||||
where: { id, tenantId },
|
||||
select: { id: true, isFavorite: true },
|
||||
})
|
||||
const sticker = await prisma.sticker.findFirst({
|
||||
where: { id, tenantId },
|
||||
select: { id: true, isFavorite: true },
|
||||
})
|
||||
|
||||
if (!sticker) {
|
||||
res.status(404).json({ error: 'Sticker não encontrado' })
|
||||
return
|
||||
if (!sticker) {
|
||||
res.status(404).json({ error: 'Sticker não encontrado' })
|
||||
return
|
||||
}
|
||||
|
||||
const updated = await prisma.sticker.update({
|
||||
where: { id: sticker.id },
|
||||
data: { isFavorite: !sticker.isFavorite },
|
||||
select: { id: true, isFavorite: true },
|
||||
})
|
||||
|
||||
res.json(updated)
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: 'Erro ao atualizar favorito' })
|
||||
}
|
||||
|
||||
const updated = await prisma.sticker.update({
|
||||
where: { id: sticker.id },
|
||||
data: { isFavorite: !sticker.isFavorite },
|
||||
select: { id: true, isFavorite: true },
|
||||
})
|
||||
|
||||
res.json(updated)
|
||||
})
|
||||
|
||||
return router
|
||||
|
||||
@@ -91,8 +91,10 @@ async function bootstrap() {
|
||||
app.use('/api/scheduled', authMiddleware, buildScheduledRoutes(scheduler))
|
||||
app.use('/api/stickers', authMiddleware, buildStickerRoutes())
|
||||
|
||||
// Proxy genérico para arquivos no Wasabi — usado pelo frontend para exibir stickers e mídias
|
||||
app.get('/api/storage/view/*', authMiddleware, async (req, res) => {
|
||||
// Proxy genérico para arquivos no Wasabi — sem authMiddleware porque <img>/<audio>/<video>
|
||||
// do browser não enviam JWT. Paths são UUID-based (obscuros) — mesmo nível de segurança
|
||||
// que a rota /media/ estática que também não requer autenticação.
|
||||
app.get('/api/storage/view/*', async (req, res) => {
|
||||
const filePath = (req.params as any)[0] as string
|
||||
if (!filePath) { res.status(400).end(); return }
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user