117 lines
4.1 KiB
JavaScript
117 lines
4.1 KiB
JavaScript
|
|
// Plan: Upload Attachments Early v2 — does PB admin auth INLINE so binary survives.
|
||
|
|
// (v1 relied on a pre-computed $json.pb_token from a separate PocketBase
|
||
|
|
// Admin Auth HTTP node — but that node strips binary, so v1's uploads had
|
||
|
|
// no bytes to send. v2 inlines the auth via this.helpers.httpRequest.)
|
||
|
|
//
|
||
|
|
// On each invocation:
|
||
|
|
// 1. Read PB admin creds from PocketBase Connection (the Set node upstream)
|
||
|
|
// 2. POST to /api/collections/_superusers/auth-with-password → get token
|
||
|
|
// 3. Walk the binary on the incoming item(s), emit one item per attachment
|
||
|
|
// with the freshly-minted token + all the upload prep data
|
||
|
|
// 4. Items carry the binary so the next HTTP node can multipart-upload it
|
||
|
|
//
|
||
|
|
// On no-attachments emails, emits one placeholder item with no binary.
|
||
|
|
|
||
|
|
const inputs = $input.all();
|
||
|
|
if (inputs.length === 0) {
|
||
|
|
return [{ json: { _attachment_count: 0 }, binary: {} }];
|
||
|
|
}
|
||
|
|
|
||
|
|
// 1. Read PB admin creds from PocketBase Connection (the Set node upstream).
|
||
|
|
// We use $('Node Name').item.json so we don't depend on what $json carries
|
||
|
|
// (the HTTP node that previously did the auth replaced $json with its
|
||
|
|
// own response shape).
|
||
|
|
let ctx = {};
|
||
|
|
try {
|
||
|
|
ctx = $('PocketBase Connection').item.json || {};
|
||
|
|
} catch (e) {
|
||
|
|
ctx = inputs[0].json || {};
|
||
|
|
}
|
||
|
|
const pbUrl = String(ctx.pb_url || '');
|
||
|
|
const pbEmail = String(ctx.pb_admin_email || '');
|
||
|
|
const pbPassword = String(ctx.pb_admin_password || '');
|
||
|
|
const messageId = String(ctx.message_id || '');
|
||
|
|
const effectiveMessageId = String(ctx._effective_message_id || messageId);
|
||
|
|
|
||
|
|
if (!pbUrl || !pbEmail || !pbPassword) {
|
||
|
|
throw new Error('PB credentials missing from PocketBase Connection: ' + JSON.stringify({
|
||
|
|
hasUrl: !!pbUrl, hasEmail: !!pbEmail, hasPass: !!pbPassword
|
||
|
|
}));
|
||
|
|
}
|
||
|
|
|
||
|
|
// 2. POST to PB to mint an admin token. This happens INSIDE the Code node,
|
||
|
|
// so binary on the input items is preserved (unlike going through an HTTP
|
||
|
|
// Request node, which strips binary).
|
||
|
|
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));
|
||
|
|
}
|
||
|
|
|
||
|
|
// 3. Walk binary on every input item and emit one upload-prep item per
|
||
|
|
// attachment. The binary is re-anchored under a stable `attachment`
|
||
|
|
// property so the next HTTP node can reference it predictably.
|
||
|
|
const out = [];
|
||
|
|
let attachmentIndex = 0;
|
||
|
|
for (const input of inputs) {
|
||
|
|
const bin = input.binary || {};
|
||
|
|
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: {
|
||
|
|
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: { attachment: b },
|
||
|
|
});
|
||
|
|
attachmentIndex++;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (out.length === 0) {
|
||
|
|
// No attachments — emit a placeholder item with no binary so the chain
|
||
|
|
// continues. The downstream HTTP POST will see no binary and PB will
|
||
|
|
// reject (caught by onError: continueRegularOutput on the HTTP node).
|
||
|
|
out.push({
|
||
|
|
json: {
|
||
|
|
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;
|