nodebb-plugin-pdf-secure 1.0.1 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodebb-plugin-pdf-secure",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "Secure PDF viewer plugin for NodeBB - prevents downloading, enables canvas-only rendering with Premium group support",
5
5
  "main": "library.js",
6
6
  "repository": {
@@ -65,24 +65,42 @@
65
65
  const result = await response.json();
66
66
  const { nonce, isPremium } = result.response;
67
67
 
68
+ // Fetch PDF binary in parent (before iframe creation)
69
+ const pdfResponse = await fetch(
70
+ `${config.relative_path}/api/v3/plugins/pdf-secure/pdf-data?nonce=${encodeURIComponent(nonce)}`,
71
+ { credentials: 'same-origin' }
72
+ );
73
+ if (!pdfResponse.ok) {
74
+ app.alertError('Failed to load PDF data.');
75
+ return;
76
+ }
77
+ const pdfArrayBuffer = await pdfResponse.arrayBuffer();
78
+
68
79
  // Create overlay with iframe
69
80
  const overlay = document.createElement('div');
70
81
  overlay.className = 'pdf-secure-overlay';
71
82
  overlay.id = 'pdf-secure-overlay';
72
83
 
73
84
  const iframe = document.createElement('iframe');
74
- const viewerUrl = `${config.relative_path}/plugins/nodebb-plugin-pdf-secure/static/lib/viewer.html?nonce=${encodeURIComponent(nonce)}&premium=${isPremium}&apiBase=${encodeURIComponent(config.relative_path)}`;
85
+ const viewerUrl = `${config.relative_path}/plugins/nodebb-plugin-pdf-secure/static/lib/viewer.html`;
75
86
  iframe.src = viewerUrl;
76
87
  iframe.className = 'pdf-secure-iframe';
77
- iframe.setAttribute('sandbox', 'allow-scripts allow-same-origin');
88
+ iframe.setAttribute('sandbox', 'allow-scripts');
78
89
 
79
90
  overlay.appendChild(iframe);
80
91
  document.body.appendChild(overlay);
81
92
 
82
- // Listen for close message from viewer
93
+ // Listen for messages from viewer
83
94
  function onMessage(e) {
95
+ if (e.source !== iframe.contentWindow) return;
84
96
  if (e.data && e.data.type === 'pdf-viewer-close') {
85
97
  closeOverlay();
98
+ } else if (e.data && e.data.type === 'pdf-viewer-ready') {
99
+ iframe.contentWindow.postMessage(
100
+ { type: 'pdf-data', pdf: pdfArrayBuffer, isPremium: isPremium },
101
+ '*',
102
+ [pdfArrayBuffer]
103
+ );
86
104
  }
87
105
  }
88
106
  window.addEventListener('message', onMessage);
@@ -49,17 +49,6 @@ let currentPage = 1;
49
49
  let totalPages = 0;
50
50
  let rendering = false;
51
51
 
52
- // Parse URL parameters
53
- const params = new URLSearchParams(window.location.search);
54
- const nonce = params.get('nonce');
55
- const isPremium = params.get('premium') === 'true';
56
- const apiBase = params.get('apiBase') || '';
57
-
58
- // Show premium banner for non-premium users
59
- if (!isPremium) {
60
- premiumBanner.style.display = 'block';
61
- }
62
-
63
52
  // Close button handler
64
53
  closeBtn.addEventListener('click', () => {
65
54
  if (window.parent && window.parent !== window) {
@@ -129,53 +118,41 @@ document.addEventListener('keydown', (e) => {
129
118
  }
130
119
  });
131
120
 
132
- // Load PDF
133
- async function init() {
134
- if (!nonce) {
135
- showError('Missing access token.');
136
- return;
121
+ // Handle window resize
122
+ let resizeTimeout;
123
+ window.addEventListener('resize', () => {
124
+ clearTimeout(resizeTimeout);
125
+ resizeTimeout = setTimeout(() => {
126
+ if (pdfDoc) renderPage(currentPage);
127
+ }, 250);
128
+ });
129
+
130
+ // 30 second timeout for receiving PDF data
131
+ const dataTimeout = setTimeout(() => {
132
+ showError('Timed out waiting for PDF data.');
133
+ }, 30000);
134
+
135
+ // Listen for PDF data from parent
136
+ window.addEventListener('message', async (e) => {
137
+ if (!e.data || e.data.type !== 'pdf-data') return;
138
+ clearTimeout(dataTimeout);
139
+
140
+ const { pdf, isPremium } = e.data;
141
+ if (!isPremium) {
142
+ premiumBanner.style.display = 'block';
137
143
  }
138
144
 
139
145
  try {
140
- const response = await fetch(`${apiBase}/api/v3/plugins/pdf-secure/pdf-data?nonce=${encodeURIComponent(nonce)}`, {
141
- credentials: 'same-origin',
142
- });
143
-
144
- if (!response.ok) {
145
- if (response.status === 401) {
146
- showError('You must be logged in to view this PDF.');
147
- } else if (response.status === 403) {
148
- showError('Access denied. The link may have expired.');
149
- } else if (response.status === 404) {
150
- showError('PDF file not found.');
151
- } else {
152
- showError('Failed to load PDF.');
153
- }
154
- return;
155
- }
156
-
157
- const arrayBuffer = await response.arrayBuffer();
158
- const data = new Uint8Array(arrayBuffer);
159
-
146
+ const data = new Uint8Array(pdf);
160
147
  pdfDoc = await getDocument({ data }).promise;
161
148
  totalPages = pdfDoc.numPages;
162
-
163
149
  loadingEl.style.display = 'none';
164
150
  canvas.style.display = 'block';
165
-
166
151
  await renderPage(1);
167
152
  } catch (err) {
168
153
  showError('Failed to load PDF. Please try again.');
169
154
  }
170
- }
171
-
172
- // Handle window resize
173
- let resizeTimeout;
174
- window.addEventListener('resize', () => {
175
- clearTimeout(resizeTimeout);
176
- resizeTimeout = setTimeout(() => {
177
- if (pdfDoc) renderPage(currentPage);
178
- }, 250);
179
155
  });
180
156
 
181
- init();
157
+ // Notify parent that viewer is ready (after listener is registered)
158
+ window.parent.postMessage({ type: 'pdf-viewer-ready' }, '*');