DO Spaces adapter (feature-flagged): route video uploads to object storage off the volume

- spaces.js: zero-dep SigV4 PUT to DO Spaces, public-read; inert unless DO_SPACES_* env is set
- /api/my/upload sends video to Spaces when configured, falls back to volume otherwise
- Images stay local; Spaces URLs are https so they pass the video/media validators + CSP

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
martbost
2026-09-06 09:24:48 -05:00
parent cb2542fca5
commit 537e7352d4
2 changed files with 69 additions and 1 deletions
+10 -1
View File
@@ -17,6 +17,7 @@ const accounts = require('./accounts');
const ads = require('./ads');
const mailer = require('./mailer');
const messages = require('./messages');
const spaces = require('./spaces'); // DO Spaces video storage (inert unless DO_SPACES_* set)
let QR = null; try { QR = require('qrcode'); } catch (e) { /* optional */ }
const chatbot = require('./chatbot');
@@ -855,8 +856,16 @@ const server = http.createServer(async (req, res) => {
(ct === 'video/webm' && buf[0] === 0x1a && buf[1] === 0x45 && buf[2] === 0xdf && buf[3] === 0xa3));
if (!magicOk) return json(res, 400, { error: 'That file does not look like a real ' + EXT[ct].toUpperCase() + '.' });
const name = crypto.randomBytes(12).toString('hex') + '.' + EXT[ct];
fs.writeFileSync(path.join(UPLOADS_DIR, name), buf);
uploadCounts.set(key, (uploadCounts.get(key) || 0) + 1);
// video goes to DO Spaces when configured (keeps big files off the volume);
// images stay local. Falls back to the volume if Spaces isn't set or errors.
if (isVideo && spaces.enabled()) {
try {
const url = await spaces.put('uploads/' + name, buf, ct);
return json(res, 200, { url, type: 'video' });
} catch (e) { console.error('spaces put', e.message); /* fall through to volume */ }
}
fs.writeFileSync(path.join(UPLOADS_DIR, name), buf);
return json(res, 200, { url: '/uploads/' + name, type: isVideo ? 'video' : 'image' });
}
m = /^\/api\/my\/inbox\/(\d+)\/visit$/.exec(p);