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) })