diff --git a/public/chat-composer.js b/public/chat-composer.js index eadf7e8..b90d7af 100644 --- a/public/chat-composer.js +++ b/public/chat-composer.js @@ -535,7 +535,9 @@ /** * Eingabezeile mit Anhaengen und Sprachaufnahme. * options: root, dropZone, uploadUrl (POST roh, DELETE uploadUrl/id), send({ text, attachmentIds }) -> message, - * onSent(message), placeholder, placeholderShort (schmale Bildschirme), disabled, disabledText + * onSent(message), placeholder, placeholderShort (schmale Bildschirme), disabled, disabledText, + * requestHeaders() -> Objekt oder Promise (zusaetzliche Header fuer Hochladen und Entfernen, z. B. das + * Bearer-JWT des Ticketsystems; ohne die Option wie bisher nur das Cookie) */ function create(options) { const opts = { placeholder: 'Nachricht schreiben…', disabled: false, disabledText: '', onSent() {}, ...options } @@ -708,9 +710,15 @@ return item } + function extraHeaders() { + return Promise.resolve(opts.requestHeaders ? opts.requestHeaders() : {}).then((headers) => headers || {}) + } + function deleteOnServer(item) { if (!item.meta || !item.meta.id) return - fetch(`${opts.uploadUrl}/${encodeURIComponent(item.meta.id)}`, { method: 'DELETE', credentials: 'same-origin' }).catch(() => {}) + extraHeaders() + .then((headers) => fetch(`${opts.uploadUrl}/${encodeURIComponent(item.meta.id)}`, { method: 'DELETE', credentials: 'same-origin', headers })) + .catch(() => {}) } function dropItem(item) { @@ -743,9 +751,26 @@ item.status = 'uploading' item.progress = 0 item.error = '' + renderTray() + extraHeaders().then( + (headers) => { + // Waehrend des Wartens entfernt oder Eingabezeile weg: nicht mehr hochladen + if (destroyed || !items.includes(item)) return + sendUpload(item, headers) + }, + (err) => { + item.status = 'error' + item.error = (err && err.message) || 'Anmeldung abgelaufen. Bitte Seite neu laden.' + afterUpload(item) + } + ) + } + + function sendUpload(item, headers) { const xhr = new XMLHttpRequest() item.xhr = xhr xhr.open('POST', opts.uploadUrl) + for (const [name, value] of Object.entries(headers)) xhr.setRequestHeader(name, value) xhr.setRequestHeader('Content-Type', 'application/octet-stream') xhr.setRequestHeader('X-File-Name', encodeURIComponent(item.name)) if (item.hint) xhr.setRequestHeader('X-Attachment-Kind', item.hint) diff --git a/test/ui/composer-headers.ui.mjs b/test/ui/composer-headers.ui.mjs new file mode 100644 index 0000000..977351b --- /dev/null +++ b/test/ui/composer-headers.ui.mjs @@ -0,0 +1,84 @@ +// Playwright-Checks Chat-Baustein: Option requestHeaders (Ticketsystem mit JWT) fuer Hochladen und Entfernen. +// Nicht Teil von npm test. Aufruf: node test/ui/composer-headers.ui.mjs +import http from 'node:http' +import { readFile } from 'node:fs/promises' +import path from 'node:path' +import { fileURLToPath } from 'node:url' +import { chromium } from '/root/.npm/_npx/db89d7302a373f10/node_modules/playwright-core/index.mjs' + +const pub = path.join(path.dirname(fileURLToPath(import.meta.url)), '../../public') +// Testseite: ?mode=ok (async Header), none (ohne Option), fail (Header verworfen) +const PAGE = `
+