mirror of
https://github.com/ancsemi/Haven.git
synced 2026-10-27 09:05:01 +00:00
88 lines
3.9 KiB
JavaScript
88 lines
3.9 KiB
JavaScript
'use strict';
|
|
|
|
// Writing a Discord import copies its attachments into Haven (#5811), unless
|
|
// the page turned that off; either way the messages go in.
|
|
|
|
const assert = require('node:assert/strict');
|
|
const crypto = require('node:crypto');
|
|
const fs = require('node:fs');
|
|
const os = require('node:os');
|
|
const path = require('node:path');
|
|
const test = require('node:test');
|
|
|
|
process.env.HAVEN_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), 'haven-importcopy-'));
|
|
process.env.JWT_SECRET = process.env.JWT_SECRET || 'j'.repeat(64);
|
|
const auth = require('../src/auth');
|
|
auth.verifyToken = (token) => (token === 'admin' ? { id: 1 } : null);
|
|
const { initDatabase } = require('../src/database');
|
|
const { UPLOADS_DIR } = require('../src/paths');
|
|
const db = initDatabase();
|
|
db.prepare("INSERT INTO users (id, username, password_hash, is_admin) VALUES (1, 'admin', 'x', 1)").run();
|
|
const registerImport = require('../src/routes/import');
|
|
|
|
const routes = {};
|
|
const app = {
|
|
post: (p, ...fns) => { routes['POST ' + p] = fns[fns.length - 1]; },
|
|
get: (p, ...fns) => { routes['GET ' + p] = fns[fns.length - 1]; },
|
|
};
|
|
registerImport({ app, uploadDiskGuard: (q, s, n) => n(), uploadLimiter: (q, s, n) => n(), importUpload: {}, verifyAdminFromDb: () => true });
|
|
|
|
const call = (key, req) => new Promise((resolve) => {
|
|
const res = { statusCode: 200, status(c) { this.statusCode = c; return this; }, json(b) { resolve({ status: this.statusCode, body: b }); } };
|
|
routes[key](req, res);
|
|
});
|
|
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
|
|
const fetched = [];
|
|
global.fetch = async (url) => {
|
|
fetched.push(String(url));
|
|
const body = Buffer.from('meeting notes');
|
|
return new Response(body, { status: 200, headers: { 'content-length': String(body.length) } });
|
|
};
|
|
|
|
async function runImport(channelId, name, copyFiles) {
|
|
const importId = crypto.randomBytes(16).toString('hex');
|
|
fs.writeFileSync(path.join(os.tmpdir(), `haven-import-${importId}.json`), JSON.stringify({ channels: [{
|
|
discordId: channelId, name, topic: '', kind: 'text',
|
|
messages: [{ discordId: channelId + '1', author: 'dt64', timestamp: new Date(2026, 4, 1).toISOString(),
|
|
content: '📎 [notes.txt](https://cdn.discordapp.com/attachments/1/2/notes.txt?ex=1)' }],
|
|
}] }));
|
|
const started = await call('POST /api/import/discord/execute', {
|
|
headers: { authorization: 'Bearer admin' },
|
|
body: { importId, selectedChannels: [{ discordId: channelId, name }], ...(copyFiles === undefined ? {} : { copyFiles }) },
|
|
});
|
|
assert.equal(started.status, 202);
|
|
for (let i = 0; i < 400; i++) {
|
|
const r = await call('GET /api/import/discord/execute/:jobId', { headers: { authorization: 'Bearer admin' }, params: { jobId: started.body.jobId } });
|
|
if (r.body.state === 'done') return r.body;
|
|
await sleep(10);
|
|
}
|
|
throw new Error('the import did not finish');
|
|
}
|
|
const contentIn = (name) => db.prepare('SELECT m.content FROM messages m JOIN channels c ON c.id = m.channel_id WHERE c.name = ?').get(name).content;
|
|
|
|
test('attachments are copied by default', async () => {
|
|
const done = await runImport('700000001', 'copied', undefined);
|
|
assert.equal(done.success, true);
|
|
assert.equal(done.filesCopied, 1);
|
|
const content = contentIn('copied');
|
|
const m = /^\[file:notes\.txt\]\(\/uploads\/([^|)]+)\|13 B\)$/.exec(content);
|
|
assert.ok(m, content);
|
|
assert.equal(fs.readFileSync(path.join(UPLOADS_DIR, m[1]), 'utf8'), 'meeting notes');
|
|
});
|
|
|
|
test('switched off, they stay links and nothing is fetched', async () => {
|
|
fetched.length = 0;
|
|
const done = await runImport('700000002', 'linked', false);
|
|
assert.equal(done.filesCopied, 0);
|
|
assert.match(contentIn('linked'), /^📎 \[notes\.txt\]\(https:\/\/cdn\.discordapp\.com\//u);
|
|
assert.equal(fetched.length, 0);
|
|
});
|
|
|
|
// import.js keeps a cleanup timer running, so end the process once done.
|
|
test.after(() => {
|
|
db.close();
|
|
fs.rmSync(process.env.HAVEN_DATA_DIR, { recursive: true, force: true });
|
|
setImmediate(() => process.exit(0));
|
|
});
|