Add read only sharing links
This commit is contained in:
20
server/api/share-links/[id].delete.ts
Normal file
20
server/api/share-links/[id].delete.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { getRouterParam, createError } from 'h3'
|
||||
import { prisma } from '../../utils/db'
|
||||
import { requireUser } from '../../utils/auth'
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const user = await requireUser(event)
|
||||
const id = getRouterParam(event, 'id')!
|
||||
const link = await prisma.shareLink.findFirst({ where: { id, userId: user.id } })
|
||||
|
||||
if (!link) {
|
||||
throw createError({ statusCode: 404, statusMessage: 'Share link not found' })
|
||||
}
|
||||
|
||||
await prisma.shareLink.update({
|
||||
where: { id },
|
||||
data: { revokedAt: new Date() }
|
||||
})
|
||||
|
||||
return { ok: true }
|
||||
})
|
||||
21
server/api/share-links/index.get.ts
Normal file
21
server/api/share-links/index.get.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { prisma } from '../../utils/db'
|
||||
import { requireUser } from '../../utils/auth'
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const user = await requireUser(event)
|
||||
const links = await prisma.shareLink.findMany({
|
||||
where: { userId: user.id },
|
||||
orderBy: { createdAt: 'desc' }
|
||||
})
|
||||
|
||||
return links.map(link => ({
|
||||
id: link.id,
|
||||
name: link.name,
|
||||
token: link.token,
|
||||
expiresAt: link.expiresAt,
|
||||
revokedAt: link.revokedAt,
|
||||
lastViewedAt: link.lastViewedAt,
|
||||
createdAt: link.createdAt,
|
||||
active: !link.revokedAt && link.expiresAt > new Date()
|
||||
}))
|
||||
})
|
||||
35
server/api/share-links/index.post.ts
Normal file
35
server/api/share-links/index.post.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { randomBytes } from 'node:crypto'
|
||||
import { addDays } from 'date-fns'
|
||||
import { readBody, createError } from 'h3'
|
||||
import { prisma } from '../../utils/db'
|
||||
import { requireUser } from '../../utils/auth'
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const user = await requireUser(event)
|
||||
const body = await readBody<{ name?: string; days?: number }>(event)
|
||||
const days = Number(body.days || 7)
|
||||
|
||||
if (!Number.isFinite(days) || days < 1 || days > 365) {
|
||||
throw createError({ statusCode: 400, statusMessage: 'Invalid expiration days' })
|
||||
}
|
||||
|
||||
const link = await prisma.shareLink.create({
|
||||
data: {
|
||||
userId: user.id,
|
||||
token: randomBytes(24).toString('base64url'),
|
||||
name: body.name?.trim().slice(0, 40) || '공유 링크',
|
||||
expiresAt: addDays(new Date(), days)
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
id: link.id,
|
||||
name: link.name,
|
||||
token: link.token,
|
||||
expiresAt: link.expiresAt,
|
||||
revokedAt: link.revokedAt,
|
||||
lastViewedAt: link.lastViewedAt,
|
||||
createdAt: link.createdAt,
|
||||
active: true
|
||||
}
|
||||
})
|
||||
58
server/api/shared/[token].get.ts
Normal file
58
server/api/shared/[token].get.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import { getQuery, getRouterParam, createError } from 'h3'
|
||||
import { startOfMonth, endOfMonth } from 'date-fns'
|
||||
import { prisma } from '../../utils/db'
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const token = getRouterParam(event, 'token')!
|
||||
const query = getQuery(event)
|
||||
const month = typeof query.month === 'string' ? query.month : new Date().toISOString().slice(0, 7)
|
||||
const monthStart = startOfMonth(new Date(`${month}-01T00:00:00`))
|
||||
const monthEnd = endOfMonth(monthStart)
|
||||
|
||||
const link = await prisma.shareLink.findUnique({
|
||||
where: { token },
|
||||
include: { user: { select: { id: true, name: true } } }
|
||||
})
|
||||
|
||||
if (!link || link.revokedAt || link.expiresAt <= new Date()) {
|
||||
throw createError({ statusCode: 404, statusMessage: 'Share link not found' })
|
||||
}
|
||||
|
||||
const [transactions, assets] = await Promise.all([
|
||||
prisma.transaction.findMany({
|
||||
where: {
|
||||
userId: link.userId,
|
||||
occurredAt: { gte: monthStart, lte: monthEnd }
|
||||
},
|
||||
include: {
|
||||
asset: true,
|
||||
category: { include: { parent: true } }
|
||||
},
|
||||
orderBy: { occurredAt: 'desc' }
|
||||
}),
|
||||
prisma.asset.findMany({
|
||||
where: { userId: link.userId, active: true },
|
||||
orderBy: [{ sortOrder: 'asc' }, { createdAt: 'asc' }]
|
||||
})
|
||||
])
|
||||
|
||||
await prisma.shareLink.update({
|
||||
where: { id: link.id },
|
||||
data: { lastViewedAt: new Date() }
|
||||
})
|
||||
|
||||
const summary = transactions.reduce((acc, tx) => {
|
||||
acc[tx.type] += tx.amount
|
||||
acc.diff = acc.income - acc.expense
|
||||
return acc
|
||||
}, { income: 0, expense: 0, diff: 0 })
|
||||
|
||||
return {
|
||||
owner: link.user.name,
|
||||
name: link.name,
|
||||
expiresAt: link.expiresAt,
|
||||
summary,
|
||||
assets,
|
||||
transactions
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user