feat: add token create/validate utilities
This commit is contained in:
@@ -0,0 +1,63 @@
|
|||||||
|
import { createToken, validateToken } from '../token'
|
||||||
|
import { prisma } from '../prisma'
|
||||||
|
|
||||||
|
jest.mock('../prisma', () => ({
|
||||||
|
prisma: {
|
||||||
|
downloadToken: {
|
||||||
|
create: jest.fn(),
|
||||||
|
findUnique: jest.fn(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
|
const mockCreate = (prisma.downloadToken.create as jest.Mock)
|
||||||
|
const mockFindUnique = (prisma.downloadToken.findUnique as jest.Mock)
|
||||||
|
|
||||||
|
describe('createToken', () => {
|
||||||
|
it('inserts a DownloadToken and returns the token string', async () => {
|
||||||
|
mockCreate.mockResolvedValue({ token: 'generated-token' })
|
||||||
|
|
||||||
|
const result = await createToken('dl-id-1')
|
||||||
|
|
||||||
|
expect(mockCreate).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
data: expect.objectContaining({ downloadId: 'dl-id-1' }),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
expect(result).toBe('generated-token')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('validateToken', () => {
|
||||||
|
it('returns null when token not found', async () => {
|
||||||
|
mockFindUnique.mockResolvedValue(null)
|
||||||
|
expect(await validateToken('bad')).toBeNull()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns null when token is expired', async () => {
|
||||||
|
mockFindUnique.mockResolvedValue({
|
||||||
|
expiresAt: new Date(Date.now() - 1000),
|
||||||
|
download: { id: 'dl-1', filePath: '/storage/abc.mp4' },
|
||||||
|
})
|
||||||
|
expect(await validateToken('expired-tok')).toBeNull()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns null when filePath is null', async () => {
|
||||||
|
mockFindUnique.mockResolvedValue({
|
||||||
|
expiresAt: new Date(Date.now() + 3_600_000),
|
||||||
|
download: { id: 'dl-1', filePath: null },
|
||||||
|
})
|
||||||
|
expect(await validateToken('tok')).toBeNull()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns downloadId and filePath for a valid token', async () => {
|
||||||
|
mockFindUnique.mockResolvedValue({
|
||||||
|
expiresAt: new Date(Date.now() + 3_600_000),
|
||||||
|
download: { id: 'dl-1', filePath: '/storage/abc.mp4' },
|
||||||
|
})
|
||||||
|
expect(await validateToken('tok')).toEqual({
|
||||||
|
downloadId: 'dl-1',
|
||||||
|
filePath: '/storage/abc.mp4',
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import { prisma } from './prisma'
|
||||||
|
import { config } from '../../config/app.config'
|
||||||
|
|
||||||
|
export async function createToken(downloadId: string): Promise<string> {
|
||||||
|
const expiresAt = new Date(
|
||||||
|
Date.now() + config.DOWNLOAD_LINK_TTL_HOURS * 60 * 60 * 1000
|
||||||
|
)
|
||||||
|
const record = await prisma.downloadToken.create({
|
||||||
|
data: { downloadId, expiresAt },
|
||||||
|
})
|
||||||
|
return record.token
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function validateToken(
|
||||||
|
token: string
|
||||||
|
): Promise<{ downloadId: string; filePath: string } | null> {
|
||||||
|
const record = await prisma.downloadToken.findUnique({
|
||||||
|
where: { token },
|
||||||
|
include: { download: { select: { id: true, filePath: true } } },
|
||||||
|
})
|
||||||
|
if (!record) return null
|
||||||
|
if (record.expiresAt < new Date()) return null
|
||||||
|
if (!record.download.filePath) return null
|
||||||
|
return { downloadId: record.download.id, filePath: record.download.filePath }
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user