Files
scrot/server/src/routes.ts
T

90 lines
2.4 KiB
TypeScript
Raw Normal View History

import { Router } from 'express'
import fs from 'node:fs'
import { config } from './config.js'
import { db, blobPath } from './db.js'
import { newBlobId } from './ids.js'
import { checkDeviceToken } from './auth.js'
import { insertBlobRecord } from './blobs.js'
export const router = Router()
interface BlobRow {
id: string
kind: 'clip' | 'shot'
content_type: string
size_bytes: number
created_at: number
expires_at: number
}
router.post('/blobs', (req, res) => {
if (!checkDeviceToken(req)) {
res.status(401).json({ error: 'bad device token' })
return
}
const kind = req.get('X-Blob-Kind')
if (kind !== 'clip' && kind !== 'shot') {
res.status(400).json({ error: 'X-Blob-Kind must be "clip" or "shot"' })
return
}
const contentLength = Number(req.get('Content-Length') ?? 0)
if (contentLength > config.maxUploadBytes) {
res.status(413).json({ error: 'blob too large' })
return
}
const id = newBlobId()
const dest = blobPath(id)
const writeStream = fs.createWriteStream(dest, { flags: 'wx' })
let bytesReceived = 0
req.on('data', (chunk: Buffer) => {
bytesReceived += chunk.length
if (bytesReceived > config.maxUploadBytes) {
req.destroy()
writeStream.destroy()
fs.rm(dest, { force: true }, () => {})
}
})
req.pipe(writeStream)
writeStream.on('finish', () => {
insertBlobRecord(id, kind, req.get('Content-Type') ?? 'application/octet-stream', bytesReceived)
res.json({ id, url: kind === 'shot' ? `/s/${id}` : null })
})
writeStream.on('error', () => {
fs.rm(dest, { force: true }, () => {})
if (!res.headersSent) res.status(500).json({ error: 'write failed' })
})
})
// Latest non-expired clip, for the pull side of clipboard sync — no
// history, no listing, just "what's the newest thing I copied elsewhere."
router.get('/clip/latest', (req, res) => {
if (!checkDeviceToken(req)) {
res.status(401).json({ error: 'bad device token' })
return
}
const now = Math.floor(Date.now() / 1000)
const row = db
.prepare(
`SELECT * FROM blobs WHERE kind = 'clip' AND expires_at > ? ORDER BY created_at DESC LIMIT 1`,
)
.get(now) as BlobRow | undefined
if (!row) {
res.status(204).end()
return
}
res.set('Content-Type', row.content_type)
res.set('X-Blob-Id', row.id)
res.set('X-Blob-Created-At', String(row.created_at))
fs.createReadStream(blobPath(row.id)).pipe(res)
})