119 lines
3.9 KiB
JavaScript
119 lines
3.9 KiB
JavaScript
|
|
// Plan: Upload Attachments Early v3 — minimal change from v2:
|
||
|
|
// - Don't re-anchor binary under 'attachment'; preserve original keys
|
||
|
|
// - Read context from inputs[0].json (the Set node output) AND fallback
|
||
|
|
// to $('PocketBase Connection').item.json
|
||
|
|
// - HTTP node downstream uses {{ $binary[$json.binary_key] }} like v4
|
||
|
|
//
|
||
|
|
// Why v2 broke: re-anchoring binary under a new key plus the HTTP node's
|
||
|
|
// multipart file param `$binary.attachment` triggered a n8n streams error
|
||
|
|
// ("source.on is not a function"). Sticking to the original key (which
|
||
|
|
// matches the input binary's keys, e.g. "attachment_0") makes the HTTP
|
||
|
|
// node happy.
|
||
|
|
|
||
|
|
const inputs = $input.all();
|
||
|
|
if (inputs.length === 0) {
|
||
|
|
return [{ json: { _attachment_count: 0 }, binary: {} }];
|
||
|
|
}
|
||
|
|
|
||
|
|
// Read PB admin creds from PocketBase Connection (Set node upstream) so
|
||
|
|
// we're not at the mercy of $json shape after the Set node runs.
|
||
|
|
let pbCtx = {};
|
||
|
|
try {
|
||
|
|
pbCtx = $('PocketBase Connection').item.json || {};
|
||
|
|
} catch (e) {
|
||
|
|
pbCtx = inputs[0].json || {};
|
||
|
|
}
|
||
|
|
|
||
|
|
// Also pull email context from the first input item (PocketBase Connection
|
||
|
|
// preserves it via includeOtherFields: true).
|
||
|
|
const emailCtx = inputs[0].json || {};
|
||
|
|
|
||
|
|
const pbUrl = String(pbCtx.pb_url || '');
|
||
|
|
const pbEmail = String(pbCtx.pb_admin_email || '');
|
||
|
|
const pbPassword = String(pbCtx.pb_admin_password || '');
|
||
|
|
const messageId = String(emailCtx.message_id || '');
|
||
|
|
const effectiveMessageId = String(emailCtx._effective_message_id || messageId);
|
||
|
|
|
||
|
|
if (!pbUrl || !pbEmail || !pbPassword) {
|
||
|
|
throw new Error('PB credentials missing from PocketBase Connection');
|
||
|
|
}
|
||
|
|
|
||
|
|
// Mint admin token INSIDE this Code node so binary on inputs is preserved
|
||
|
|
// (an HTTP Request node between here and the source would strip it).
|
||
|
|
let pbToken = '';
|
||
|
|
let pbAdminId = '';
|
||
|
|
try {
|
||
|
|
const authResp = await this.helpers.httpRequest({
|
||
|
|
method: 'POST',
|
||
|
|
url: pbUrl + '/api/collections/_superusers/auth-with-password',
|
||
|
|
headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' },
|
||
|
|
body: { identity: pbEmail, password: pbPassword },
|
||
|
|
json: true,
|
||
|
|
timeout: 15000,
|
||
|
|
});
|
||
|
|
pbToken = String(authResp.token || '');
|
||
|
|
pbAdminId = String((authResp.admin && authResp.admin.id) || '');
|
||
|
|
if (!pbToken) {
|
||
|
|
throw new Error('PB auth returned no token: ' + JSON.stringify(authResp));
|
||
|
|
}
|
||
|
|
} catch (authErr) {
|
||
|
|
throw new Error('PB admin auth failed: ' + (authErr.message || authErr));
|
||
|
|
}
|
||
|
|
|
||
|
|
// Emit one item per attachment. PRESERVE the original binary keys so the
|
||
|
|
// downstream HTTP node can use {{ $binary[$json.binary_key] }} (v4 pattern).
|
||
|
|
const out = [];
|
||
|
|
let attachmentIndex = 0;
|
||
|
|
for (const input of inputs) {
|
||
|
|
const bin = input.binary || {};
|
||
|
|
// Preserve the original binary blob under each original key
|
||
|
|
const outBin = {};
|
||
|
|
for (const key of Object.keys(bin)) {
|
||
|
|
outBin[key] = bin[key];
|
||
|
|
}
|
||
|
|
for (const key of Object.keys(bin)) {
|
||
|
|
const b = bin[key];
|
||
|
|
if (!b) continue;
|
||
|
|
const filename = String(b.fileName || 'attachment');
|
||
|
|
const contentType = String(b.mimeType || 'application/octet-stream');
|
||
|
|
out.push({
|
||
|
|
json: Object.assign({}, emailCtx, {
|
||
|
|
pb_url: pbUrl,
|
||
|
|
pb_token: pbToken,
|
||
|
|
pb_admin_id: pbAdminId,
|
||
|
|
email_message_id: effectiveMessageId,
|
||
|
|
filename: filename,
|
||
|
|
content_type: contentType,
|
||
|
|
size_bytes: Number(b.fileSize || 0),
|
||
|
|
binary_key: key,
|
||
|
|
_upload_index: attachmentIndex,
|
||
|
|
}),
|
||
|
|
binary: outBin, // same binary blob, original keys
|
||
|
|
});
|
||
|
|
attachmentIndex++;
|
||
|
|
// Only emit one item per binary blob, not one per key, to avoid dup
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (out.length === 0) {
|
||
|
|
// No attachments — emit a single placeholder with no binary
|
||
|
|
out.push({
|
||
|
|
json: Object.assign({}, emailCtx, {
|
||
|
|
pb_url: pbUrl,
|
||
|
|
pb_token: pbToken,
|
||
|
|
pb_admin_id: pbAdminId,
|
||
|
|
email_message_id: effectiveMessageId,
|
||
|
|
filename: '',
|
||
|
|
content_type: '',
|
||
|
|
size_bytes: 0,
|
||
|
|
binary_key: '__none__',
|
||
|
|
_upload_index: 0,
|
||
|
|
_no_attachments: true,
|
||
|
|
}),
|
||
|
|
binary: {},
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
return out;
|