generated from coulomb/repo-seed
Switch to HTTP Basic Auth and improve PDF detection
- Replace token-based auth with HTTP Basic Authentication per Binect API v1 spec - Improve PDF detection: check current tab first, then background service, fallback to recent downloads - Add password visibility toggle in login form - Add extensive debug logging throughout for troubleshooting - Update manifest with alarms, activeTab permissions and <all_urls> host permission - Add documentation files and development helper scripts - Add Binect API specs for reference Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
289
DEBUG_DOWNLOAD_DETECTION.md
Normal file
289
DEBUG_DOWNLOAD_DETECTION.md
Normal file
@@ -0,0 +1,289 @@
|
||||
# Debugging Download Detection
|
||||
|
||||
This guide helps you debug why PDF download detection may not be working.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
1. **Rebuild and reload the extension**
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
Then go to `chrome://extensions/` and click the reload icon for BinectChrome
|
||||
|
||||
2. **Open the Service Worker console**
|
||||
- Go to `chrome://extensions/`
|
||||
- Find BinectChrome
|
||||
- Click "service worker" link (or "Inspect views: service worker")
|
||||
- This opens DevTools for the service worker
|
||||
- **Keep this window open** while testing
|
||||
|
||||
## Step-by-Step Download Detection Test
|
||||
|
||||
### Step 1: Verify Service Worker is Running
|
||||
|
||||
**In the Service Worker console, you should see:**
|
||||
```
|
||||
[Service Worker] ===== BinectChrome service worker loaded =====
|
||||
[Service Worker] Timestamp: 2024-01-14T...
|
||||
[Service Worker] Initializing PDF detection...
|
||||
[PDF Detector] Starting PDF detection, registering download listener
|
||||
[PDF Detector] Listener registered successfully
|
||||
```
|
||||
|
||||
**If you don't see these messages:**
|
||||
- The service worker hasn't loaded yet
|
||||
- Try clicking the extension icon (this wakes it up)
|
||||
- Check for any red errors in the console
|
||||
|
||||
### Step 2: Test with a Simple PDF Download
|
||||
|
||||
**Download a test PDF:**
|
||||
1. Right-click this link and select "Save link as...":
|
||||
```
|
||||
https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf
|
||||
```
|
||||
2. Save it to your Downloads folder
|
||||
3. Watch the Service Worker console
|
||||
|
||||
**Expected console output:**
|
||||
```
|
||||
[PDF Detector] Download changed: {id: 123, state: {...}, stateValue: "in_progress"}
|
||||
[PDF Detector] Download not complete, ignoring
|
||||
[PDF Detector] Download changed: {id: 123, state: {...}, stateValue: "complete"}
|
||||
[PDF Detector] Download complete, searching for item: 123
|
||||
[PDF Detector] Search results: 1 items
|
||||
[PDF Detector] Download item: {id: 123, filename: "dummy.pdf", mime: "application/pdf", ...}
|
||||
[PDF Detector] PDF detected!
|
||||
[Service Worker] PDF DETECTED CALLBACK: dummy.pdf
|
||||
[Service Worker] Badge updated, PDF stored in memory
|
||||
```
|
||||
|
||||
**After successful detection:**
|
||||
- Extension badge should show "1"
|
||||
- Badge color should be blue (#4A90E2)
|
||||
|
||||
### Step 3: Test with Direct Link Download
|
||||
|
||||
**Alternative method:**
|
||||
1. Paste this URL directly in the address bar:
|
||||
```
|
||||
https://www.africau.edu/images/default/sample.pdf
|
||||
```
|
||||
2. Chrome will start downloading it
|
||||
3. Watch the Service Worker console for the same log messages
|
||||
|
||||
### Step 4: Verify PDF is Stored
|
||||
|
||||
**Click the extension icon to open the popup**
|
||||
- You should see the PDF details
|
||||
- Filename: "dummy.pdf" or "sample.pdf"
|
||||
- Size: displayed in KB or MB
|
||||
- Domain: source domain
|
||||
- "Send PDF to Binect" button should be enabled
|
||||
|
||||
## Troubleshooting: No Logs Appearing
|
||||
|
||||
### Issue 1: Service Worker Not Running
|
||||
**Symptoms:** No console output at all, even "[Service Worker] loaded" message
|
||||
|
||||
**Solutions:**
|
||||
1. **Wake up the service worker:**
|
||||
- Click the extension icon
|
||||
- OR go to `chrome://serviceworker-internals/` and find BinectChrome
|
||||
- Click "Start" if it's stopped
|
||||
|
||||
2. **Check for startup errors:**
|
||||
- Look for red errors in the service worker console
|
||||
- Check `chrome://serviceworker-internals/` for registration errors
|
||||
|
||||
3. **Hard reload the extension:**
|
||||
- Go to `chrome://extensions/`
|
||||
- Remove BinectChrome completely
|
||||
- Click "Load unpacked" and select the `dist/` folder again
|
||||
|
||||
### Issue 2: Service Worker Sleeping Before Download Completes
|
||||
**Symptoms:** Service worker console shows "loaded" message, but no download events
|
||||
|
||||
**This is the most likely issue!** In Manifest V3, service workers shut down after 30 seconds of inactivity.
|
||||
|
||||
**Test if this is the issue:**
|
||||
1. Open service worker console
|
||||
2. Click the extension icon to wake it up
|
||||
3. **Immediately** (within 30 seconds) download a PDF
|
||||
4. Watch the console
|
||||
|
||||
**If you see logs now, the issue is service worker lifecycle!**
|
||||
|
||||
**Solution:** The service worker should automatically wake up when downloads complete, but there might be a timing issue. See "Potential Fixes" below.
|
||||
|
||||
### Issue 3: Download Events Not Firing
|
||||
**Symptoms:** Service worker is running, but no "[PDF Detector] Download changed" logs
|
||||
|
||||
**Possible causes:**
|
||||
1. **Downloads permission not granted:**
|
||||
- Go to `chrome://extensions/`
|
||||
- Click "Details" on BinectChrome
|
||||
- Check "Permissions" section
|
||||
- Should show "Read your browsing history" and "Manage your downloads"
|
||||
- If missing, the manifest is wrong
|
||||
|
||||
2. **Event listener not registered:**
|
||||
- Look for "[PDF Detector] Listener registered successfully" in console
|
||||
- If missing, there's a code issue
|
||||
|
||||
3. **Chrome not triggering download events:**
|
||||
- Try opening the PDF instead of downloading it (should trigger viewer detection)
|
||||
- Check `chrome://downloads/` to verify download completed
|
||||
|
||||
### Issue 4: PDF Not Detected (Logs Show It's Not a PDF)
|
||||
**Symptoms:** Logs show "Not a PDF, ignoring"
|
||||
|
||||
**Debug the detection logic:**
|
||||
|
||||
Look at the download item log:
|
||||
```
|
||||
[PDF Detector] Download item: {
|
||||
id: 123,
|
||||
filename: "document.pdf", // ← Should end with .pdf
|
||||
mime: "application/pdf", // ← Should be "application/pdf"
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
**If filename doesn't end with .pdf and mime is not "application/pdf":**
|
||||
- The file isn't actually a PDF
|
||||
- OR the server sent wrong headers
|
||||
|
||||
## Advanced Debugging
|
||||
|
||||
### Check Download API Directly
|
||||
|
||||
**Run this in the Service Worker console:**
|
||||
```javascript
|
||||
chrome.downloads.search({ limit: 10, orderBy: ['-startTime'] }, (items) => {
|
||||
console.log('Recent downloads:', items);
|
||||
items.forEach(item => {
|
||||
console.log(`${item.filename} - mime: ${item.mime} - state: ${item.state}`);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
This shows your recent downloads and their properties.
|
||||
|
||||
### Manual Test: Trigger Detection Manually
|
||||
|
||||
**Run this in the Service Worker console:**
|
||||
```javascript
|
||||
// Get the most recent download
|
||||
chrome.downloads.search({ limit: 1, orderBy: ['-startTime'] }, (items) => {
|
||||
if (items.length > 0) {
|
||||
const item = items[0];
|
||||
console.log('Most recent download:', item);
|
||||
|
||||
// Check if it's a PDF
|
||||
const isPDF = item.filename.toLowerCase().endsWith('.pdf') || item.mime === 'application/pdf';
|
||||
console.log('Is PDF?', isPDF);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Check if Listener is Registered
|
||||
|
||||
**Run this in the Service Worker console:**
|
||||
```javascript
|
||||
// This won't show listeners directly, but you can test by downloading a file
|
||||
console.log('Testing listener by downloading a test file...');
|
||||
chrome.downloads.download({
|
||||
url: 'https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf',
|
||||
filename: 'test-binect.pdf'
|
||||
}, (downloadId) => {
|
||||
console.log('Download started with ID:', downloadId);
|
||||
});
|
||||
```
|
||||
|
||||
Watch for "[PDF Detector] Download changed" logs.
|
||||
|
||||
## Potential Fixes
|
||||
|
||||
If download detection is unreliable due to service worker lifecycle:
|
||||
|
||||
### Option 1: Use chrome.storage for Persistence
|
||||
Store detected PDFs in chrome.storage instead of memory, so they survive service worker restarts.
|
||||
|
||||
### Option 2: Add Download Completion Listener
|
||||
Use `chrome.downloads.onCreated` in addition to `onChanged` to catch downloads earlier.
|
||||
|
||||
### Option 3: Poll Recent Downloads
|
||||
When popup opens, check recent downloads even if callback didn't fire.
|
||||
|
||||
## Testing Different PDF Sources
|
||||
|
||||
### Test 1: Direct PDF Link
|
||||
```
|
||||
https://www.africau.edu/images/default/sample.pdf
|
||||
```
|
||||
**Expected:** Direct download, should detect
|
||||
|
||||
### Test 2: PDF Behind Link
|
||||
```
|
||||
https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf
|
||||
```
|
||||
**Expected:** Right-click → Save as, should detect
|
||||
|
||||
### Test 3: Large PDF
|
||||
```
|
||||
https://www.adobe.com/support/products/enterprise/knowledgecenter/media/c4611_sample_explain.pdf
|
||||
```
|
||||
**Expected:** Larger file, may take longer to download
|
||||
|
||||
### Test 4: Google Drive PDF
|
||||
1. Upload a PDF to your Google Drive
|
||||
2. Click it to view
|
||||
3. **Important:** Use the PDF viewer detection (new feature)
|
||||
4. If you download it, should also detect
|
||||
|
||||
## Success Criteria
|
||||
|
||||
**Download detection is working when:**
|
||||
1. ✅ Service worker console shows all log messages
|
||||
2. ✅ Badge updates to "1" after download
|
||||
3. ✅ Popup shows PDF details
|
||||
4. ✅ "Send PDF to Binect" button is enabled
|
||||
5. ✅ Works consistently across multiple downloads
|
||||
|
||||
## Still Not Working?
|
||||
|
||||
If download detection still doesn't work after following this guide:
|
||||
|
||||
1. **Export debug logs:**
|
||||
- Right-click in service worker console → "Save as..."
|
||||
- Save the console output
|
||||
|
||||
2. **Check service worker status:**
|
||||
- Go to `chrome://serviceworker-internals/`
|
||||
- Find BinectChrome
|
||||
- Screenshot the status section
|
||||
|
||||
3. **Get downloads permission status:**
|
||||
```javascript
|
||||
chrome.permissions.contains({
|
||||
permissions: ['downloads']
|
||||
}, (result) => {
|
||||
console.log('Has downloads permission:', result);
|
||||
});
|
||||
```
|
||||
|
||||
4. **Report the issue:**
|
||||
- Email bernd.worsch@binect.de
|
||||
- Include: console logs, screenshots, Chrome version
|
||||
- Describe: what you tried, what happened
|
||||
|
||||
## Workaround: Use PDF Viewer Detection
|
||||
|
||||
**If download detection is unreliable, use the new PDF viewer detection:**
|
||||
1. Open a PDF in Chrome (paste URL in address bar)
|
||||
2. Click the extension icon
|
||||
3. Should detect the PDF from the current tab
|
||||
4. This bypasses download detection entirely!
|
||||
|
||||
See `TESTING_PDF_VIEWER.md` for details.
|
||||
248
DEVELOPMENT.md
Normal file
248
DEVELOPMENT.md
Normal file
@@ -0,0 +1,248 @@
|
||||
# Development & Test-Fix Workflow for BinectChrome
|
||||
|
||||
This guide explains how to set up an efficient development workflow for testing and debugging the Chrome extension.
|
||||
|
||||
## Quick Start
|
||||
|
||||
### 1. Development Mode (Auto-rebuild on changes)
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
This starts webpack in watch mode - any changes to source files will automatically rebuild the extension.
|
||||
|
||||
### 2. Load Extension in Chrome
|
||||
|
||||
**Initial Load:**
|
||||
1. Open Chrome and navigate to `chrome://extensions/`
|
||||
2. Enable "Developer mode" (toggle in top-right corner)
|
||||
3. Click "Load unpacked"
|
||||
4. Select the `/home/worsch/binect-chrome/dist` directory
|
||||
5. Note the extension ID (you'll need this for debugging)
|
||||
|
||||
**Reload After Changes:**
|
||||
- **Option A (Manual):** Click the reload icon on the extension card at `chrome://extensions/`
|
||||
- **Option B (Shortcut):** Click the extensions icon in Chrome toolbar → Manage Extensions → Reload
|
||||
- **Option C (Recommended for service worker):** Navigate to `chrome://serviceworker-internals/` and click "Stop" then reload
|
||||
|
||||
### 3. Debug Service Worker
|
||||
|
||||
**View Service Worker Logs:**
|
||||
1. Go to `chrome://extensions/`
|
||||
2. Find your extension
|
||||
3. Click "service worker" link (under "Inspect views")
|
||||
4. This opens DevTools for the service worker
|
||||
|
||||
**Alternative Method:**
|
||||
1. Go to `chrome://serviceworker-internals/`
|
||||
2. Find "chrome-extension://[your-extension-id]"
|
||||
3. Click "Inspect" to open DevTools
|
||||
|
||||
**View Service Worker Status:**
|
||||
- `chrome://serviceworker-internals/` - Shows all service workers, their status, and errors
|
||||
|
||||
### 4. Debug Popup & UI
|
||||
|
||||
**Popup DevTools:**
|
||||
1. Click the extension icon to open the popup
|
||||
2. Right-click inside the popup → "Inspect"
|
||||
3. This opens DevTools for the popup
|
||||
|
||||
**Tracking Page DevTools:**
|
||||
1. Open the tracking page from the popup
|
||||
2. Right-click → "Inspect"
|
||||
|
||||
## Complete Test-Fix Loop
|
||||
|
||||
### Workflow Pattern
|
||||
```
|
||||
┌─────────────────────────────────────────────────┐
|
||||
│ 1. Make code changes in src/ │
|
||||
│ 2. Webpack auto-rebuilds (if using npm run dev)│
|
||||
│ 3. Reload extension in Chrome │
|
||||
│ 4. Check DevTools for errors │
|
||||
│ 5. Test functionality │
|
||||
│ 6. Repeat from step 1 │
|
||||
└─────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Detailed Steps
|
||||
|
||||
1. **Start Development Mode**
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
2. **Make Your Changes**
|
||||
- Edit files in `src/`
|
||||
- Webpack will automatically rebuild
|
||||
|
||||
3. **Reload Extension**
|
||||
- Go to `chrome://extensions/`
|
||||
- Click reload icon on BinectChrome card
|
||||
- For service worker changes: Go to `chrome://serviceworker-internals/` → Stop → Reload
|
||||
|
||||
4. **Check for Errors**
|
||||
- **Service Worker:** Click "service worker" link or check `chrome://serviceworker-internals/`
|
||||
- **Popup:** Right-click popup → Inspect
|
||||
- **Console Errors:** Check the browser console in all DevTools windows
|
||||
|
||||
5. **Test Functionality**
|
||||
- Download a PDF to test detection
|
||||
- Check badge updates
|
||||
- Test sending PDFs
|
||||
- Verify tracking data
|
||||
|
||||
6. **Common Debugging Points**
|
||||
- Service worker errors: Usually permissions or API usage issues
|
||||
- PDF detection not working: Check Downloads API events
|
||||
- Badge not updating: Check service worker is running
|
||||
- Alarms not firing: Check `chrome://serviceworker-internals/` for service worker lifecycle
|
||||
|
||||
## Common Issues & Solutions
|
||||
|
||||
### Issue: Service Worker Registration Failed (Status Code 15)
|
||||
**Cause:** Webpack output format doesn't match manifest type
|
||||
**Solution:** Ensure webpack.config.js has:
|
||||
```javascript
|
||||
output: {
|
||||
module: true,
|
||||
environment: { module: true }
|
||||
},
|
||||
experiments: {
|
||||
outputModule: true
|
||||
}
|
||||
```
|
||||
|
||||
### Issue: "Cannot read properties of undefined (reading 'onAlarm')"
|
||||
**Cause:** Missing "alarms" permission
|
||||
**Solution:** Add to manifest.json:
|
||||
```json
|
||||
"permissions": ["downloads", "storage", "alarms"]
|
||||
```
|
||||
|
||||
### Issue: Service Worker Stops Running
|
||||
**Cause:** Chrome stops inactive service workers after 30 seconds (Manifest V3 behavior)
|
||||
**Solution:** This is normal! Service workers wake up on events (downloads, alarms, messages)
|
||||
|
||||
### Issue: Changes Not Reflecting
|
||||
**Cause:** Extension not reloaded, or cached service worker
|
||||
**Solution:**
|
||||
1. Always reload after rebuilding
|
||||
2. For stubborn issues: Stop service worker at `chrome://serviceworker-internals/`
|
||||
3. Hard reload: Remove extension and re-add it
|
||||
|
||||
## Testing Specific Features
|
||||
|
||||
### Test PDF Detection
|
||||
1. Ensure extension is loaded and service worker is running
|
||||
2. Download any PDF file from the web
|
||||
3. Check service worker console for "PDF detected:" log
|
||||
4. Verify badge shows "1"
|
||||
5. Open popup and verify PDF info is displayed
|
||||
|
||||
### Test Credential Management
|
||||
1. Open popup
|
||||
2. Enter credentials
|
||||
3. Check Chrome DevTools → Application → Storage → Extension Storage
|
||||
4. Verify credentials are encrypted
|
||||
5. Test credential expiry by manipulating storage
|
||||
|
||||
### Test Alarm/Expiry Check
|
||||
1. Open `chrome://serviceworker-internals/`
|
||||
2. Check for alarm registration
|
||||
3. Or use: `chrome.alarms.getAll(console.log)` in service worker console
|
||||
|
||||
## Production Build & Testing
|
||||
|
||||
```bash
|
||||
# Build for production
|
||||
npm run build
|
||||
|
||||
# Build is in dist/ directory
|
||||
# Test the production build by loading dist/ as unpacked extension
|
||||
```
|
||||
|
||||
## Running Unit Tests
|
||||
|
||||
```bash
|
||||
# Run tests once
|
||||
npm test
|
||||
|
||||
# Run tests in watch mode
|
||||
npm run test:watch
|
||||
|
||||
# Type checking
|
||||
npm run type-check
|
||||
|
||||
# Linting
|
||||
npm run lint
|
||||
npm run lint:fix
|
||||
```
|
||||
|
||||
## Useful Chrome URLs for Extension Development
|
||||
|
||||
- `chrome://extensions/` - Manage extensions
|
||||
- `chrome://serviceworker-internals/` - Debug service workers
|
||||
- `chrome://inspect/#service-workers` - Inspect active service workers
|
||||
- `chrome://downloads/` - View downloads (for testing PDF detection)
|
||||
- `chrome://version/` - Check Chrome version
|
||||
|
||||
## Debugging Tips
|
||||
|
||||
1. **Console.log is your friend**: Add logs liberally in development
|
||||
2. **Breakpoints**: Use debugger; statements or set breakpoints in DevTools
|
||||
3. **Storage inspection**: Check Application → Storage → Extension Storage in DevTools
|
||||
4. **Network tab**: Monitor API calls to Binect
|
||||
5. **Performance**: Use Performance tab to check service worker lifecycle
|
||||
6. **Memory**: Watch for memory leaks in long-running service workers
|
||||
|
||||
## VS Code Integration (Optional)
|
||||
|
||||
Add to `.vscode/launch.json` for debugging:
|
||||
```json
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "chrome",
|
||||
"request": "launch",
|
||||
"name": "Debug Extension",
|
||||
"url": "chrome://extensions/",
|
||||
"webRoot": "${workspaceFolder}/dist"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Quick Reference Commands
|
||||
|
||||
```bash
|
||||
# Development
|
||||
npm run dev # Watch mode with auto-rebuild
|
||||
npm run build # Production build
|
||||
npm test # Run tests
|
||||
npm run lint # Check code quality
|
||||
npm run type-check # TypeScript validation
|
||||
|
||||
# Chrome URLs
|
||||
chrome://extensions/ # Extension management
|
||||
chrome://serviceworker-internals/ # Service worker debugging
|
||||
```
|
||||
|
||||
## Emergency Cleanup
|
||||
|
||||
If extension gets stuck or behaves oddly:
|
||||
```bash
|
||||
# 1. Stop service worker
|
||||
# Go to chrome://serviceworker-internals/ → Stop
|
||||
|
||||
# 2. Clear extension storage
|
||||
# DevTools → Application → Storage → Clear site data
|
||||
|
||||
# 3. Remove and reload extension
|
||||
# chrome://extensions/ → Remove → Load unpacked again
|
||||
|
||||
# 4. Clean rebuild
|
||||
rm -rf dist/
|
||||
npm run build
|
||||
```
|
||||
303
DOWNLOAD_DETECTION_FIXES.md
Normal file
303
DOWNLOAD_DETECTION_FIXES.md
Normal file
@@ -0,0 +1,303 @@
|
||||
# Download Detection Debugging & Fixes
|
||||
|
||||
## Summary
|
||||
|
||||
I've added comprehensive debugging and a fallback mechanism to help diagnose and work around download detection issues.
|
||||
|
||||
## Changes Made
|
||||
|
||||
### 1. **Comprehensive Debug Logging**
|
||||
|
||||
**Service Worker (src/background/service-worker.ts):**
|
||||
- All events now log with `[Service Worker]` prefix
|
||||
- Shows when service worker loads/reloads
|
||||
- Logs PDF detection callbacks
|
||||
- Logs message handling from popup
|
||||
|
||||
**PDF Detector (src/utils/pdf-detector.ts):**
|
||||
- All download events logged with `[PDF Detector]` prefix
|
||||
- Shows download state changes (in_progress → complete)
|
||||
- Logs download item details (filename, mime, state, url)
|
||||
- Confirms when PDF is detected vs. ignored
|
||||
|
||||
**Popup (src/popup/popup.ts):**
|
||||
- All PDF loading operations logged with `[Popup]` prefix
|
||||
- Shows detection priority: tab viewer → background → fallback
|
||||
- Logs results of each detection method
|
||||
|
||||
### 2. **Fallback Mechanism**
|
||||
|
||||
**New function: `checkRecentDownloads()` (src/popup/popup.ts:254-298)**
|
||||
- Directly queries Chrome's downloads API for recent PDFs
|
||||
- Checks last 20 downloads
|
||||
- Finds most recent completed PDF
|
||||
- **Works even if service worker missed the download event**
|
||||
|
||||
**PDF Detection Priority:**
|
||||
1. Current tab (PDF viewer) - NEW
|
||||
2. Background service worker (download event listener)
|
||||
3. **Recent downloads fallback** - NEW
|
||||
4. No PDF detected
|
||||
|
||||
This triple-layer approach ensures PDFs are detected even if the service worker is sleeping.
|
||||
|
||||
## How to Debug
|
||||
|
||||
### Step 1: Reload Extension
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
Then reload at `chrome://extensions/`
|
||||
|
||||
### Step 2: Open Consoles
|
||||
|
||||
**Service Worker Console:**
|
||||
1. Go to `chrome://extensions/`
|
||||
2. Click "service worker" link under BinectChrome
|
||||
3. **Keep this open while testing**
|
||||
|
||||
**Popup Console:**
|
||||
1. Click the extension icon
|
||||
2. Right-click inside popup → "Inspect"
|
||||
3. View console
|
||||
|
||||
### Step 3: Download a Test PDF
|
||||
|
||||
**Test URL:**
|
||||
```
|
||||
https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf
|
||||
```
|
||||
|
||||
**Method 1: Direct download**
|
||||
- Right-click link → "Save link as..."
|
||||
- Save to Downloads
|
||||
|
||||
**Method 2: View in browser**
|
||||
- Paste URL in address bar
|
||||
- Let Chrome open it in viewer
|
||||
- Click extension icon
|
||||
|
||||
### Step 4: Check Logs
|
||||
|
||||
**Expected Service Worker logs:**
|
||||
```
|
||||
[Service Worker] ===== BinectChrome service worker loaded =====
|
||||
[Service Worker] Initializing PDF detection...
|
||||
[PDF Detector] Starting PDF detection, registering download listener
|
||||
[PDF Detector] Listener registered successfully
|
||||
|
||||
# When download completes:
|
||||
[PDF Detector] Download changed: {id: X, state: {...}, stateValue: "complete"}
|
||||
[PDF Detector] Download complete, searching for item: X
|
||||
[PDF Detector] Search results: 1 items
|
||||
[PDF Detector] Download item: {id: X, filename: "dummy.pdf", mime: "application/pdf", ...}
|
||||
[PDF Detector] PDF detected!
|
||||
[Service Worker] PDF DETECTED CALLBACK: dummy.pdf
|
||||
[Service Worker] Badge updated, PDF stored in memory
|
||||
```
|
||||
|
||||
**Expected Popup logs:**
|
||||
```
|
||||
[Popup] Loading last PDF...
|
||||
[Popup] No PDF in current tab, checking background script...
|
||||
[Service Worker] Message received: getLastPDF
|
||||
[Service Worker] Returning last PDF: dummy.pdf
|
||||
[Popup] Background returned PDF: dummy.pdf
|
||||
```
|
||||
|
||||
**If service worker missed it (fallback):**
|
||||
```
|
||||
[Popup] Loading last PDF...
|
||||
[Popup] No PDF in current tab, checking background script...
|
||||
[Service Worker] Message received: getLastPDF
|
||||
[Service Worker] Returning last PDF: none
|
||||
[Popup] Background has no PDF, checking recent downloads as fallback...
|
||||
[Popup] Checked recent downloads: 15 items
|
||||
[Popup] Found recent PDF: dummy.pdf
|
||||
```
|
||||
|
||||
## Common Issues & Solutions
|
||||
|
||||
### Issue 1: No Service Worker Logs
|
||||
|
||||
**Symptom:** Service worker console is empty or shows "Inactive"
|
||||
|
||||
**Solution:**
|
||||
- Click the extension icon (wakes it up)
|
||||
- Or go to `chrome://serviceworker-internals/` and click "Start"
|
||||
- Check for registration errors
|
||||
|
||||
### Issue 2: Service Worker Not Waking for Downloads
|
||||
|
||||
**Symptom:** Download completes but no `[PDF Detector]` logs appear
|
||||
|
||||
**This is the main issue with Manifest V3 service workers!**
|
||||
|
||||
**Diagnosis:**
|
||||
1. Open service worker console
|
||||
2. Download a PDF
|
||||
3. Watch if service worker console gets new logs
|
||||
|
||||
**If no logs:** Service worker didn't wake up for the download event
|
||||
|
||||
**Workaround:** The fallback mechanism handles this! When you open the popup, it checks recent downloads directly.
|
||||
|
||||
### Issue 3: Badge Not Updating
|
||||
|
||||
**Symptom:** PDF downloads but badge stays empty
|
||||
|
||||
**Cause:** Service worker detected PDF but couldn't update badge (possibly terminated)
|
||||
|
||||
**Solution:** The fallback still works - open popup to see the PDF
|
||||
|
||||
### Issue 4: PDF Shows in Popup But Disappears
|
||||
|
||||
**Symptom:** PDF shows up, but after a few minutes it's gone
|
||||
|
||||
**Cause:** Service worker memory is ephemeral - when it terminates, `lastDetectedPDF` is lost
|
||||
|
||||
**Solution:** The fallback mechanism re-fetches from recent downloads each time popup opens
|
||||
|
||||
## Testing Checklist
|
||||
|
||||
- [ ] Build extension: `npm run build`
|
||||
- [ ] Reload extension at `chrome://extensions/`
|
||||
- [ ] Open service worker console
|
||||
- [ ] Download test PDF (right-click → Save link as)
|
||||
- [ ] Check service worker logs for detection
|
||||
- [ ] Check if badge shows "1"
|
||||
- [ ] Open popup - should show PDF
|
||||
- [ ] Wait 2 minutes (service worker sleeps)
|
||||
- [ ] Download another PDF
|
||||
- [ ] Open popup - should show PDF (even if service worker missed it)
|
||||
- [ ] Open PDF in browser tab (view, don't download)
|
||||
- [ ] Open popup - should detect tab viewer
|
||||
|
||||
## Known Limitations
|
||||
|
||||
### 1. Service Worker Lifecycle (Manifest V3)
|
||||
Service workers shut down after 30 seconds of inactivity. This is by design in Manifest V3.
|
||||
|
||||
**Impact:**
|
||||
- Download event listener may not fire if service worker is sleeping
|
||||
- Badge may not update immediately after download
|
||||
- `lastDetectedPDF` is lost when service worker terminates
|
||||
|
||||
**Mitigation:**
|
||||
- Fallback mechanism checks recent downloads
|
||||
- Works reliably despite service worker sleep
|
||||
- Badge updates when popup opens
|
||||
|
||||
### 2. File URLs
|
||||
Chrome extensions can't access `file://` URLs by default.
|
||||
|
||||
**Impact:** If PDF is downloaded and opened from disk, can't re-fetch for upload
|
||||
|
||||
**Solution:** Use the original download URL (which we store)
|
||||
|
||||
### 3. Authenticated Downloads
|
||||
Some PDFs require authentication cookies.
|
||||
|
||||
**Impact:** Re-fetching PDF may fail if session expired
|
||||
|
||||
**Solution:** `fetchPDFBytes()` uses `credentials: 'include'` to send cookies
|
||||
|
||||
## Advanced Debugging
|
||||
|
||||
### Manual Download Check
|
||||
Run in Service Worker console:
|
||||
```javascript
|
||||
chrome.downloads.search({ limit: 10, orderBy: ['-startTime'] }, (items) => {
|
||||
console.log('Recent downloads:');
|
||||
items.forEach(item => {
|
||||
const isPDF = item.filename.toLowerCase().endsWith('.pdf') ||
|
||||
item.mime === 'application/pdf';
|
||||
console.log(` ${item.filename} - PDF: ${isPDF} - State: ${item.state}`);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### Test Download Listener
|
||||
Run in Service Worker console:
|
||||
```javascript
|
||||
chrome.downloads.download({
|
||||
url: 'https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf',
|
||||
filename: 'test-download.pdf'
|
||||
}, (downloadId) => {
|
||||
console.log('Started download:', downloadId);
|
||||
});
|
||||
```
|
||||
|
||||
Watch for `[PDF Detector]` logs.
|
||||
|
||||
### Check Permissions
|
||||
Run in Service Worker console:
|
||||
```javascript
|
||||
chrome.permissions.contains({ permissions: ['downloads'] }, (result) => {
|
||||
console.log('Has downloads permission:', result);
|
||||
});
|
||||
```
|
||||
|
||||
## Recommended Testing Flow
|
||||
|
||||
**For Development:**
|
||||
1. Use PDF viewer detection (open PDF in tab)
|
||||
2. This bypasses download detection entirely
|
||||
3. Most reliable for testing API integration
|
||||
|
||||
**For Download Detection Testing:**
|
||||
1. Open both service worker and popup consoles
|
||||
2. Download a PDF
|
||||
3. Watch logs in real-time
|
||||
4. Verify fallback works even if service worker missed it
|
||||
|
||||
## Next Steps
|
||||
|
||||
**If download detection is still unreliable:**
|
||||
|
||||
### Option A: Accept Viewer Detection as Primary
|
||||
- PDF viewer detection works reliably
|
||||
- Download detection becomes secondary
|
||||
- Users can open PDFs in browser instead of downloading
|
||||
|
||||
### Option B: Persist Detected PDFs
|
||||
- Store detected PDFs in `chrome.storage` instead of memory
|
||||
- Survives service worker restarts
|
||||
- Requires storage cleanup logic
|
||||
|
||||
### Option C: Poll Downloads Periodically
|
||||
- Set up an alarm to check recent downloads every minute
|
||||
- More resource intensive
|
||||
- Very reliable
|
||||
|
||||
## Files Changed
|
||||
|
||||
1. `src/background/service-worker.ts` - Added debug logging
|
||||
2. `src/utils/pdf-detector.ts` - Added debug logging
|
||||
3. `src/popup/popup.ts` - Added debug logging and fallback mechanism
|
||||
4. `DEBUG_DOWNLOAD_DETECTION.md` - Comprehensive debugging guide
|
||||
5. `DOWNLOAD_DETECTION_FIXES.md` - This file
|
||||
|
||||
## Support
|
||||
|
||||
If download detection still doesn't work:
|
||||
1. Follow `DEBUG_DOWNLOAD_DETECTION.md`
|
||||
2. Export console logs from both service worker and popup
|
||||
3. Include Chrome version: `chrome://version/`
|
||||
4. Report to bernd.worsch@binect.de
|
||||
|
||||
## Workaround
|
||||
|
||||
**The PDF viewer detection is fully functional and reliable!**
|
||||
|
||||
Instead of relying on download detection:
|
||||
1. Open PDFs in Chrome (paste URL in address bar)
|
||||
2. Click extension icon
|
||||
3. PDF is detected from current tab
|
||||
4. Send to Binect
|
||||
|
||||
This approach:
|
||||
- ✅ Always works
|
||||
- ✅ No service worker timing issues
|
||||
- ✅ Better user experience (no downloads folder clutter)
|
||||
- ✅ Easier to test API integration
|
||||
229
QUICK_TEST_GUIDE.md
Normal file
229
QUICK_TEST_GUIDE.md
Normal file
@@ -0,0 +1,229 @@
|
||||
# Quick Test Guide - BinectChrome
|
||||
|
||||
## 1. Setup (2 minutes)
|
||||
|
||||
```bash
|
||||
# Rebuild extension
|
||||
npm run build
|
||||
|
||||
# Load in Chrome
|
||||
# 1. Open chrome://extensions/
|
||||
# 2. Enable "Developer mode"
|
||||
# 3. Click "Reload" on BinectChrome (or "Load unpacked" if first time, select dist/)
|
||||
# 4. Accept new permissions when prompted
|
||||
```
|
||||
|
||||
## 2. Open Debug Consoles
|
||||
|
||||
**Service Worker:**
|
||||
- `chrome://extensions/` → Click "service worker" under BinectChrome
|
||||
- Keep this window open
|
||||
|
||||
**Popup (optional):**
|
||||
- Click extension icon → Right-click → "Inspect"
|
||||
|
||||
## 3. Test PDF Viewer Detection (RECOMMENDED - Most Reliable)
|
||||
|
||||
**This is the easiest way to test the API integration!**
|
||||
|
||||
1. **Open a test PDF in Chrome:**
|
||||
```
|
||||
https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf
|
||||
```
|
||||
Just paste the URL in the address bar and press Enter
|
||||
|
||||
2. **Click the extension icon**
|
||||
- Should detect the PDF immediately
|
||||
- Shows filename, domain, "Send PDF to Binect" button
|
||||
|
||||
3. **Sign in and send:**
|
||||
- Enter your Binect credentials
|
||||
- Click "Send PDF to Binect"
|
||||
- Watch for "Uploading..." → "Success!"
|
||||
|
||||
**Expected Popup Logs:**
|
||||
```
|
||||
[Popup] Loading last PDF...
|
||||
[Popup] Found PDF in current tab: dummy.pdf
|
||||
```
|
||||
|
||||
**✅ This method always works - no service worker timing issues!**
|
||||
|
||||
## 4. Test Download Detection (May Need Fallback)
|
||||
|
||||
**Method 1: Right-click → Save**
|
||||
1. Right-click this link: https://www.africau.edu/images/default/sample.pdf
|
||||
2. Select "Save link as..."
|
||||
3. Save to Downloads
|
||||
4. Watch service worker console for logs
|
||||
|
||||
**Method 2: Direct download**
|
||||
1. Paste URL: https://www.adobe.com/support/products/enterprise/knowledgecenter/media/c4611_sample_explain.pdf
|
||||
2. Chrome starts download
|
||||
3. Watch service worker console
|
||||
|
||||
**Expected Service Worker Logs (if working):**
|
||||
```
|
||||
[PDF Detector] Download changed: {...}
|
||||
[PDF Detector] Download complete, searching for item: X
|
||||
[PDF Detector] PDF detected!
|
||||
[Service Worker] PDF DETECTED CALLBACK: sample.pdf
|
||||
[Service Worker] Badge updated, PDF stored in memory
|
||||
```
|
||||
|
||||
**If badge shows "1":** Download detection worked! ✅
|
||||
|
||||
**If no badge:** Fallback will still work! Continue...
|
||||
|
||||
5. **Click extension icon**
|
||||
- Even if service worker missed it, popup checks recent downloads
|
||||
- PDF should appear
|
||||
|
||||
**Expected Popup Logs (fallback):**
|
||||
```
|
||||
[Popup] Background has no PDF, checking recent downloads as fallback...
|
||||
[Popup] Checked recent downloads: X items
|
||||
[Popup] Found recent PDF: sample.pdf
|
||||
```
|
||||
|
||||
## 5. Test Binect API Integration
|
||||
|
||||
**Prerequisites:**
|
||||
- Have Binect credentials ready
|
||||
- PDF is detected (from viewer OR download)
|
||||
|
||||
**Steps:**
|
||||
1. Click extension icon
|
||||
2. If not signed in:
|
||||
- Enter username and password
|
||||
- Click "Sign In"
|
||||
- Should show main view
|
||||
3. PDF details should be visible
|
||||
4. Click "Send PDF to Binect"
|
||||
5. Wait for upload
|
||||
|
||||
**Expected Results:**
|
||||
```
|
||||
Status: Uploading...
|
||||
→ Success! Document ID: [id]
|
||||
```
|
||||
|
||||
**Check Tracking:**
|
||||
- Click "?" button in popup
|
||||
- Opens tracking page
|
||||
- Should show the transfer with success status
|
||||
|
||||
## 6. Test Error Handling
|
||||
|
||||
**Invalid Credentials:**
|
||||
1. Sign out (if signed in)
|
||||
2. Enter wrong username/password
|
||||
3. Click "Sign In"
|
||||
4. Should show error: "Invalid credentials"
|
||||
|
||||
**Network Error:**
|
||||
1. Disconnect from internet
|
||||
2. Try to authenticate or send PDF
|
||||
3. Should show network error
|
||||
|
||||
## Quick Debug Checklist
|
||||
|
||||
**If PDF viewer detection doesn't work:**
|
||||
- [ ] Is the URL actually a PDF? (ends with .pdf)
|
||||
- [ ] Check popup console for error logs
|
||||
- [ ] Try refreshing the PDF tab
|
||||
|
||||
**If download detection doesn't work:**
|
||||
- [ ] Check service worker console for `[PDF Detector]` logs
|
||||
- [ ] Look for "Listener registered successfully"
|
||||
- [ ] Try downloading again while service worker console is open
|
||||
- [ ] Open popup - fallback should still find it
|
||||
|
||||
**If API upload fails:**
|
||||
- [ ] Check credentials are correct
|
||||
- [ ] Verify PDF URL is accessible (try opening in new tab)
|
||||
- [ ] Check popup console for detailed error
|
||||
- [ ] Look at Network tab in popup DevTools for API calls
|
||||
|
||||
## Expected Console Output Summary
|
||||
|
||||
### Service Worker (when working):
|
||||
```
|
||||
[Service Worker] ===== BinectChrome service worker loaded =====
|
||||
[Service Worker] Initializing PDF detection...
|
||||
[PDF Detector] Starting PDF detection, registering download listener
|
||||
[PDF Detector] Listener registered successfully
|
||||
|
||||
# On download:
|
||||
[PDF Detector] Download changed: {id: X, ...}
|
||||
[PDF Detector] PDF detected!
|
||||
[Service Worker] PDF DETECTED CALLBACK: filename.pdf
|
||||
[Service Worker] Badge updated, PDF stored in memory
|
||||
```
|
||||
|
||||
### Popup (viewer detection):
|
||||
```
|
||||
[Popup] Loading last PDF...
|
||||
[Popup] Found PDF in current tab: document.pdf
|
||||
```
|
||||
|
||||
### Popup (fallback):
|
||||
```
|
||||
[Popup] Loading last PDF...
|
||||
[Popup] No PDF in current tab, checking background script...
|
||||
[Service Worker] Returning last PDF: none
|
||||
[Popup] Background has no PDF, checking recent downloads as fallback...
|
||||
[Popup] Found recent PDF: document.pdf
|
||||
```
|
||||
|
||||
## Common Questions
|
||||
|
||||
**Q: Badge doesn't show but popup finds the PDF?**
|
||||
A: Normal! Service worker may have been asleep. Fallback handled it.
|
||||
|
||||
**Q: PDF disappears after a few minutes?**
|
||||
A: Service worker memory is cleared when it sleeps. Fallback will re-fetch it.
|
||||
|
||||
**Q: Which detection method should I use?**
|
||||
A: **PDF viewer detection** is more reliable. Just open PDFs in Chrome instead of downloading.
|
||||
|
||||
**Q: Can I test without Binect credentials?**
|
||||
A: You can test PDF detection, but not the upload. You'll see authentication errors when trying to send.
|
||||
|
||||
## Test URLs
|
||||
|
||||
**Small PDFs (quick tests):**
|
||||
- https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf (1 page)
|
||||
- https://www.africau.edu/images/default/sample.pdf (1 page)
|
||||
|
||||
**Medium PDFs:**
|
||||
- https://www.adobe.com/support/products/enterprise/knowledgecenter/media/c4611_sample_explain.pdf (multi-page)
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. **Test viewer detection** - Most reliable
|
||||
2. **Test download detection** - May need fallback
|
||||
3. **Test API integration** - Requires credentials
|
||||
4. **Check tracking page** - Verify data is recorded
|
||||
|
||||
## Getting Help
|
||||
|
||||
If something doesn't work:
|
||||
1. Check the detailed guides:
|
||||
- `DEBUG_DOWNLOAD_DETECTION.md` - Download debugging
|
||||
- `TESTING_PDF_VIEWER.md` - Viewer testing
|
||||
- `DOWNLOAD_DETECTION_FIXES.md` - Technical details
|
||||
2. Export console logs (right-click → Save as)
|
||||
3. Note your Chrome version: `chrome://version/`
|
||||
4. Email: bernd.worsch@binect.de
|
||||
|
||||
## Success Criteria
|
||||
|
||||
✅ PDF viewer detection works consistently
|
||||
✅ Download detection works OR fallback finds PDFs
|
||||
✅ API authentication succeeds
|
||||
✅ PDF upload succeeds
|
||||
✅ Tracking records the transfer
|
||||
✅ Badge updates (when service worker is awake)
|
||||
|
||||
**You're ready to use BinectChrome!**
|
||||
180
RECENT_CHANGES.md
Normal file
180
RECENT_CHANGES.md
Normal file
@@ -0,0 +1,180 @@
|
||||
# Recent Changes - BinectChrome
|
||||
|
||||
## Summary
|
||||
|
||||
Three new features have been implemented:
|
||||
1. ✅ Password visibility toggle with eye icon
|
||||
2. ✅ Default badge on extension icon
|
||||
3. ✅ Enhanced API error logging for debugging
|
||||
|
||||
## 1. Password Visibility Toggle
|
||||
|
||||
**Location:** Login form in popup
|
||||
|
||||
**What it does:**
|
||||
- Adds an eye icon button next to the password field
|
||||
- Click to toggle between showing and hiding password
|
||||
- Icon changes: 👁️ (show) ↔ 🚫👁️ (hide)
|
||||
|
||||
**Files changed:**
|
||||
- `src/popup/popup.html` - Added password wrapper and eye icon SVGs
|
||||
- `src/popup/popup.css` - Styled the toggle button
|
||||
- `src/popup/popup.ts` - Added toggle functionality
|
||||
|
||||
**How to use:**
|
||||
1. Open the extension popup
|
||||
2. Enter username
|
||||
3. Start typing password
|
||||
4. Click the eye icon to reveal/hide password
|
||||
|
||||
## 2. Default Badge on Extension Icon
|
||||
|
||||
**What it does:**
|
||||
- Extension icon now always shows a blue dot (•) badge
|
||||
- Badge changes to "1" when PDF is detected
|
||||
- Badge resets to dot (•) after PDF is sent or cleared
|
||||
- Makes extension more visible in the toolbar
|
||||
|
||||
**Files changed:**
|
||||
- `src/background/service-worker.ts` - Added `initializeBadge()` function
|
||||
|
||||
**Badge states:**
|
||||
- `•` (blue dot) - Extension active, no PDF detected
|
||||
- `1` (blue) - PDF detected and ready to send
|
||||
- Returns to `•` after sending or clearing
|
||||
|
||||
**Visual:**
|
||||
- Before: No badge (extension hard to notice)
|
||||
- Now: Blue dot always visible (easy to spot in toolbar)
|
||||
|
||||
## 3. Enhanced API Error Logging
|
||||
|
||||
**What it does:**
|
||||
- Detailed console logging for all Binect API calls
|
||||
- Shows request details (URL, username, payload)
|
||||
- Logs response status and headers
|
||||
- Captures and displays error responses
|
||||
- Better error messages for common issues
|
||||
|
||||
**Files changed:**
|
||||
- `src/utils/binect-api.ts` - Added console logging throughout
|
||||
|
||||
**Console output example (authentication):**
|
||||
```
|
||||
[Binect API] Authenticating with Binect API...
|
||||
[Binect API] URL: https://api.binect.de/auth/login
|
||||
[Binect API] Username: testuser
|
||||
[Binect API] Response status: 200
|
||||
[Binect API] Response content-type: application/json
|
||||
[Binect API] Authentication successful!
|
||||
[Binect API] Response data: {token: "...", expiresAt: "..."}
|
||||
```
|
||||
|
||||
**Console output example (error):**
|
||||
```
|
||||
[Binect API] Authenticating with Binect API...
|
||||
[Binect API] URL: https://api.binect.de/auth/login
|
||||
[Binect API] Username: wronguser
|
||||
[Binect API] Response status: 401
|
||||
[Binect API] Error response body: {"error": "Invalid credentials"}
|
||||
[Binect API] Authentication error: BinectAPIError: Invalid credentials
|
||||
```
|
||||
|
||||
**Error improvements:**
|
||||
- Network errors now show: "Cannot reach Binect API at https://api.binect.de. Please check your internet connection."
|
||||
- Auth errors show: "Invalid credentials" (401)
|
||||
- Upload errors show specific issue (file format, size limit, etc.)
|
||||
|
||||
## How to Test
|
||||
|
||||
### 1. Reload Extension
|
||||
```bash
|
||||
# Extension is already built
|
||||
# Just reload at chrome://extensions/
|
||||
```
|
||||
|
||||
### 2. Test Password Toggle
|
||||
1. Click extension icon
|
||||
2. If not logged in, you'll see the login form
|
||||
3. Type in the password field
|
||||
4. Click the eye icon - password should become visible
|
||||
5. Click again - password should hide
|
||||
|
||||
### 3. Test Default Badge
|
||||
1. Look at your Chrome toolbar
|
||||
2. Find the BinectChrome icon
|
||||
3. Should see a small blue dot (•) badge
|
||||
4. Download or view a PDF
|
||||
5. Badge should change to "1"
|
||||
|
||||
### 4. Test API Error Logging
|
||||
1. Right-click popup → "Inspect" (opens DevTools)
|
||||
2. Go to Console tab
|
||||
3. Try to sign in (with wrong or correct credentials)
|
||||
4. Watch for `[Binect API]` log messages
|
||||
5. All API calls are now logged with details
|
||||
|
||||
## Debugging Binect API Login Issues
|
||||
|
||||
**With the new logging, you can now:**
|
||||
|
||||
1. **See the exact error from Binect:**
|
||||
- Open popup DevTools (right-click → Inspect)
|
||||
- Try to sign in
|
||||
- Check console for `[Binect API] Error response body:`
|
||||
- This shows what the Binect API actually returned
|
||||
|
||||
2. **Verify the request is correct:**
|
||||
- Console shows the URL being called
|
||||
- Shows the username being sent
|
||||
- Confirms request format
|
||||
|
||||
3. **Check network connectivity:**
|
||||
- If you see "Cannot reach Binect API", it's a network issue
|
||||
- If you see status codes (401, 400, etc.), the API is reachable but rejecting the request
|
||||
|
||||
**Common login issues:**
|
||||
|
||||
| Console Log | Problem | Solution |
|
||||
|------------|---------|----------|
|
||||
| "Cannot reach Binect API" | Network issue | Check internet connection |
|
||||
| "Response status: 401" + "Invalid credentials" | Wrong username/password | Verify credentials |
|
||||
| "Response status: 404" | API endpoint changed | Check API_BASE_URL in code |
|
||||
| "Response status: 500" | Server error | Check Binect API status |
|
||||
| "TypeError: Failed to fetch" | CORS or network | Check browser permissions |
|
||||
|
||||
## Testing Checklist
|
||||
|
||||
- [ ] Extension icon shows blue dot badge
|
||||
- [ ] Password field has eye icon
|
||||
- [ ] Clicking eye icon toggles password visibility
|
||||
- [ ] Console shows `[Binect API]` logs when signing in
|
||||
- [ ] Error messages are clear and helpful
|
||||
- [ ] Badge updates when PDF detected
|
||||
- [ ] Badge resets after sending PDF
|
||||
|
||||
## Next Steps for API Debugging
|
||||
|
||||
1. **Try to sign in with your Binect credentials**
|
||||
2. **Open popup DevTools** (right-click popup → Inspect)
|
||||
3. **Check the Console tab** for `[Binect API]` messages
|
||||
4. **Share the console output** if login fails
|
||||
|
||||
The detailed logs will show exactly what's happening with the API request and response, making it much easier to diagnose the login problem.
|
||||
|
||||
## Files Modified
|
||||
|
||||
```
|
||||
src/popup/popup.html - Added password toggle UI
|
||||
src/popup/popup.css - Styled password toggle
|
||||
src/popup/popup.ts - Added toggle functionality
|
||||
src/background/service-worker.ts - Added default badge
|
||||
src/utils/binect-api.ts - Enhanced error logging
|
||||
```
|
||||
|
||||
## Build Info
|
||||
|
||||
- Build completed successfully
|
||||
- Extension size: ~17KB (popup.js: 10.4KB, background.js: 4.18KB)
|
||||
- All assets compiled and minified
|
||||
- Ready for testing!
|
||||
291
TESTING_PDF_VIEWER.md
Normal file
291
TESTING_PDF_VIEWER.md
Normal file
@@ -0,0 +1,291 @@
|
||||
# Testing PDF Sending from Chrome's Integrated PDF Viewer
|
||||
|
||||
This guide explains how to test the new PDF viewer integration that allows sending PDFs directly from Chrome's built-in PDF viewer to the Binect API.
|
||||
|
||||
## What Changed
|
||||
|
||||
The extension now detects PDFs in two ways:
|
||||
|
||||
1. **PDF Downloads** (Original) - Detects when you download a PDF file
|
||||
2. **PDF Viewer** (New) - Detects when you're viewing a PDF in Chrome's integrated viewer
|
||||
|
||||
The popup will prioritize showing PDFs from the current tab viewer over previously downloaded PDFs.
|
||||
|
||||
## Testing the PDF Viewer Integration
|
||||
|
||||
### Setup
|
||||
|
||||
1. **Reload the Extension**
|
||||
```
|
||||
chrome://extensions/ → Find BinectChrome → Click reload icon
|
||||
```
|
||||
|
||||
2. **Check Permissions**
|
||||
- The extension now requires `activeTab` and `<all_urls>` permissions
|
||||
- Chrome will ask you to approve these new permissions
|
||||
- Click "Allow" when prompted
|
||||
|
||||
### Test Scenario 1: Open a PDF in a New Tab
|
||||
|
||||
**Steps:**
|
||||
1. Find any PDF URL on the web (examples below)
|
||||
2. Right-click the PDF link → "Open link in new tab"
|
||||
3. Chrome will open the PDF in its integrated viewer
|
||||
4. Click the BinectChrome extension icon
|
||||
5. The popup should show the PDF details with "Send PDF to Binect" button
|
||||
|
||||
**Test PDF URLs:**
|
||||
```
|
||||
https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf
|
||||
https://www.adobe.com/support/products/enterprise/knowledgecenter/media/c4611_sample_explain.pdf
|
||||
https://www.africau.edu/images/default/sample.pdf
|
||||
```
|
||||
|
||||
**Expected Results:**
|
||||
- PDF filename extracted from URL or tab title
|
||||
- Domain shows the source domain
|
||||
- Size shows "Size unknown" (normal for viewed PDFs)
|
||||
- Timestamp shows "Just now"
|
||||
- "Send PDF to Binect" button is active
|
||||
|
||||
### Test Scenario 2: Navigate Directly to a PDF URL
|
||||
|
||||
**Steps:**
|
||||
1. Copy a PDF URL (use test URLs above)
|
||||
2. Paste it into Chrome's address bar and press Enter
|
||||
3. Chrome loads the PDF in the viewer
|
||||
4. Click the BinectChrome extension icon
|
||||
5. The popup should detect and show the PDF
|
||||
|
||||
**Expected Results:**
|
||||
- Same as Scenario 1
|
||||
|
||||
### Test Scenario 3: View PDF from Google Drive / Cloud Storage
|
||||
|
||||
**Steps:**
|
||||
1. Go to Google Drive, Dropbox, or any cloud storage
|
||||
2. Click on a PDF file to view it
|
||||
3. Wait for the PDF to load in the viewer
|
||||
4. Click the BinectChrome extension icon
|
||||
|
||||
**Expected Results:**
|
||||
- Extension detects the PDF
|
||||
- Domain shows the cloud provider's domain
|
||||
- File can be sent to Binect
|
||||
|
||||
**Note:** Some cloud providers use blob URLs or viewer applications that may not be directly detected. If this happens, download the PDF instead (it will be detected via download detection).
|
||||
|
||||
### Test Scenario 4: Send PDF to Binect API
|
||||
|
||||
**Steps:**
|
||||
1. Open a PDF in Chrome's viewer (use Scenario 1 or 2)
|
||||
2. Click the BinectChrome extension icon
|
||||
3. If not signed in:
|
||||
- Enter your Binect credentials
|
||||
- Click "Sign In"
|
||||
4. Once authenticated, the PDF should be shown
|
||||
5. Click "Send PDF to Binect"
|
||||
|
||||
**Expected Results:**
|
||||
- Status shows "Uploading..."
|
||||
- After successful upload: "Success! Document ID: [id]"
|
||||
- Tracking entry is created with the transfer details
|
||||
- After 3 seconds, the popup clears
|
||||
|
||||
**Expected Errors (if testing with invalid credentials):**
|
||||
- Authentication errors show in red
|
||||
- Network errors are displayed
|
||||
- Failed transfers are tracked with error messages
|
||||
|
||||
## API Integration Testing
|
||||
|
||||
### Test with Real Binect API
|
||||
|
||||
If you have Binect API credentials:
|
||||
|
||||
1. Sign in with valid credentials
|
||||
2. Open a test PDF
|
||||
3. Send it to Binect
|
||||
4. Check the tracking info (click "?" button) to see the transfer log
|
||||
5. Verify the document appears in your Binect account
|
||||
|
||||
### Test with Invalid Credentials
|
||||
|
||||
1. Sign in with invalid credentials
|
||||
2. Should show "Invalid credentials" error
|
||||
3. Extension should return to login screen
|
||||
|
||||
### Test with Network Errors
|
||||
|
||||
1. Disconnect from internet
|
||||
2. Try to authenticate or send PDF
|
||||
3. Should show "Network error" message
|
||||
|
||||
### Test Session Expiry
|
||||
|
||||
1. Sign in successfully
|
||||
2. Wait for token to expire (or manipulate storage to simulate)
|
||||
3. Try to send PDF
|
||||
4. Should show "Session expired. Please sign in again."
|
||||
5. Should automatically log out after 2 seconds
|
||||
|
||||
## Debugging Tips
|
||||
|
||||
### Check Console Logs
|
||||
|
||||
**Popup Console:**
|
||||
1. Click extension icon to open popup
|
||||
2. Right-click inside popup → "Inspect"
|
||||
3. Check console for errors or debug messages
|
||||
|
||||
**Background Service Worker Console:**
|
||||
1. Go to `chrome://extensions/`
|
||||
2. Find BinectChrome
|
||||
3. Click "service worker" link
|
||||
4. Check console for detection logs
|
||||
|
||||
### Common Issues
|
||||
|
||||
**Issue: PDF not detected**
|
||||
- **Solution 1:** The URL might not end with `.pdf` - this is normal for some cloud services
|
||||
- **Solution 2:** Try refreshing the PDF tab
|
||||
- **Solution 3:** Download the PDF instead (download detection should work)
|
||||
|
||||
**Issue: "Failed to fetch PDF: 403 Forbidden"**
|
||||
- **Cause:** The PDF URL requires authentication/cookies that the extension can't access
|
||||
- **Solution:** Download the PDF instead
|
||||
|
||||
**Issue: "Failed to fetch PDF: CORS error"**
|
||||
- **Cause:** The server doesn't allow cross-origin requests
|
||||
- **Solution:** Download the PDF instead
|
||||
|
||||
**Issue: Extension shows "No PDF detected"**
|
||||
- **Check:** Is the current tab actually showing a PDF?
|
||||
- **Check:** Does the URL end with `.pdf` or contain PDF indicators?
|
||||
- **Try:** Download the PDF to test the download detection
|
||||
|
||||
## Verifying API Requests
|
||||
|
||||
### Using Chrome DevTools Network Tab
|
||||
|
||||
1. Open popup with DevTools open (right-click → Inspect)
|
||||
2. Go to Network tab
|
||||
3. Send a PDF
|
||||
4. Look for requests to `https://api.binect.de/`
|
||||
5. Check request details:
|
||||
- **POST /auth/login** - Authentication request
|
||||
- **POST /documents/upload** - PDF upload request
|
||||
|
||||
**Authentication Request:**
|
||||
```json
|
||||
POST https://api.binect.de/auth/login
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"username": "your-username",
|
||||
"password": "your-password"
|
||||
}
|
||||
```
|
||||
|
||||
**Upload Request:**
|
||||
```
|
||||
POST https://api.binect.de/documents/upload
|
||||
Authorization: Bearer [token]
|
||||
Content-Type: multipart/form-data
|
||||
|
||||
[PDF file data]
|
||||
```
|
||||
|
||||
### Expected API Responses
|
||||
|
||||
**Successful Authentication:**
|
||||
```json
|
||||
{
|
||||
"token": "eyJhbGc...",
|
||||
"expiresAt": "2024-01-15T12:00:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
**Successful Upload:**
|
||||
```json
|
||||
{
|
||||
"documentId": "doc_abc123",
|
||||
"status": "uploaded",
|
||||
"uploadedAt": "2024-01-14T12:00:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
**Authentication Error (401):**
|
||||
```json
|
||||
{
|
||||
"error": "Invalid credentials"
|
||||
}
|
||||
```
|
||||
|
||||
**Upload Error (400):**
|
||||
```json
|
||||
{
|
||||
"error": "Invalid file format"
|
||||
}
|
||||
```
|
||||
|
||||
## Tracking Data
|
||||
|
||||
After sending PDFs, check the tracking data:
|
||||
|
||||
1. Click the "?" button in the extension popup
|
||||
2. Opens tracking page
|
||||
3. Shows list of all transfer attempts
|
||||
4. Includes:
|
||||
- Timestamp
|
||||
- Source domain
|
||||
- PDF size
|
||||
- Result (success/failure)
|
||||
- Error message (if failed)
|
||||
|
||||
## Comparison: Download Detection vs. Viewer Detection
|
||||
|
||||
| Feature | Download Detection | PDF Viewer Detection |
|
||||
|---------|-------------------|---------------------|
|
||||
| **Trigger** | PDF file download completes | User opens PDF in browser |
|
||||
| **File Size** | Known (from download) | Unknown (estimated after fetch) |
|
||||
| **Reliability** | High | Depends on URL format |
|
||||
| **Use Case** | Downloading PDFs from web | Viewing PDFs directly in browser |
|
||||
| **Badge** | Shows "1" after download | No badge (on-demand) |
|
||||
|
||||
## Next Steps
|
||||
|
||||
Once you've verified the PDF viewer integration works:
|
||||
|
||||
1. Test with various PDF sources (Google Drive, Dropbox, direct links)
|
||||
2. Verify all error cases are handled gracefully
|
||||
3. Check that tracking data is accurate
|
||||
4. Test the download detection still works alongside viewer detection
|
||||
5. Consider edge cases:
|
||||
- Very large PDFs (10MB+)
|
||||
- PDFs with special characters in filename
|
||||
- PDFs from authenticated sources
|
||||
- PDFs from blob URLs
|
||||
|
||||
## Known Limitations
|
||||
|
||||
1. **Blob URLs**: Some web apps create temporary blob URLs that can't be re-fetched
|
||||
- **Workaround**: Download the PDF instead
|
||||
2. **Authenticated PDFs**: PDFs behind login walls may not be accessible
|
||||
- **Workaround**: Download the PDF instead
|
||||
3. **Embedded PDFs**: PDFs embedded in iframes may not be detected
|
||||
- **Workaround**: Open the PDF in a new tab or download it
|
||||
4. **Size Unknown**: PDF size is not known until fetch, so tracking may show 0 initially
|
||||
- **Note**: Actual size is recorded after successful upload
|
||||
|
||||
## Support
|
||||
|
||||
If you encounter issues:
|
||||
1. Check the console logs (popup and service worker)
|
||||
2. Verify the PDF URL format
|
||||
3. Try downloading the PDF as an alternative
|
||||
4. Report issues to bernd.worsch@binect.de with:
|
||||
- PDF URL (if public)
|
||||
- Error message
|
||||
- Console logs
|
||||
- Tracking data export
|
||||
198
dev-helper.sh
Executable file
198
dev-helper.sh
Executable file
@@ -0,0 +1,198 @@
|
||||
#!/bin/bash
|
||||
# Development Helper Script for BinectChrome
|
||||
# Quick commands for common development tasks
|
||||
|
||||
set -e
|
||||
|
||||
EXTENSION_NAME="BinectChrome"
|
||||
DIST_DIR="./dist"
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Print colored message
|
||||
print_msg() {
|
||||
local color=$1
|
||||
local msg=$2
|
||||
echo -e "${color}${msg}${NC}"
|
||||
}
|
||||
|
||||
# Show usage
|
||||
usage() {
|
||||
echo "BinectChrome Development Helper"
|
||||
echo ""
|
||||
echo "Usage: ./dev-helper.sh [command]"
|
||||
echo ""
|
||||
echo "Commands:"
|
||||
echo " build - Production build"
|
||||
echo " dev - Start development mode (watch)"
|
||||
echo " clean - Clean dist directory and rebuild"
|
||||
echo " test - Run tests"
|
||||
echo " check - Run type-check and lint"
|
||||
echo " open-chrome - Open Chrome extension management"
|
||||
echo " open-sw - Open Chrome service worker internals"
|
||||
echo " verify - Verify dist build is valid"
|
||||
echo " help - Show this help"
|
||||
echo ""
|
||||
echo "Quick Test-Fix Loop:"
|
||||
echo " 1. Run: ./dev-helper.sh dev (in one terminal)"
|
||||
echo " 2. Make changes in src/"
|
||||
echo " 3. Run: ./dev-helper.sh verify"
|
||||
echo " 4. Reload extension in Chrome (chrome://extensions/)"
|
||||
}
|
||||
|
||||
# Build production
|
||||
build() {
|
||||
print_msg "$BLUE" "📦 Building extension..."
|
||||
npm run build
|
||||
print_msg "$GREEN" "✅ Build complete!"
|
||||
verify
|
||||
}
|
||||
|
||||
# Start development mode
|
||||
dev() {
|
||||
print_msg "$BLUE" "🔧 Starting development mode (watch)..."
|
||||
print_msg "$YELLOW" "Press Ctrl+C to stop"
|
||||
print_msg "$YELLOW" "Tip: After changes, reload extension at chrome://extensions/"
|
||||
npm run dev
|
||||
}
|
||||
|
||||
# Clean and rebuild
|
||||
clean() {
|
||||
print_msg "$BLUE" "🧹 Cleaning dist directory..."
|
||||
rm -rf "$DIST_DIR"
|
||||
print_msg "$GREEN" "✅ Cleaned!"
|
||||
build
|
||||
}
|
||||
|
||||
# Run tests
|
||||
test() {
|
||||
print_msg "$BLUE" "🧪 Running tests..."
|
||||
npm test
|
||||
}
|
||||
|
||||
# Type check and lint
|
||||
check() {
|
||||
print_msg "$BLUE" "🔍 Running type-check..."
|
||||
npm run type-check
|
||||
print_msg "$BLUE" "🔍 Running lint..."
|
||||
npm run lint
|
||||
print_msg "$GREEN" "✅ All checks passed!"
|
||||
}
|
||||
|
||||
# Open Chrome extensions page
|
||||
open_chrome() {
|
||||
print_msg "$BLUE" "🌐 Opening Chrome extensions page..."
|
||||
if command -v google-chrome &> /dev/null; then
|
||||
google-chrome chrome://extensions/ &
|
||||
elif command -v chromium &> /dev/null; then
|
||||
chromium chrome://extensions/ &
|
||||
else
|
||||
print_msg "$YELLOW" "⚠️ Chrome not found. Please open chrome://extensions/ manually"
|
||||
fi
|
||||
}
|
||||
|
||||
# Open Chrome service worker internals
|
||||
open_sw() {
|
||||
print_msg "$BLUE" "🌐 Opening Chrome service worker internals..."
|
||||
if command -v google-chrome &> /dev/null; then
|
||||
google-chrome chrome://serviceworker-internals/ &
|
||||
elif command -v chromium &> /dev/null; then
|
||||
chromium chrome://serviceworker-internals/ &
|
||||
else
|
||||
print_msg "$YELLOW" "⚠️ Chrome not found. Please open chrome://serviceworker-internals/ manually"
|
||||
fi
|
||||
}
|
||||
|
||||
# Verify build
|
||||
verify() {
|
||||
print_msg "$BLUE" "🔍 Verifying build..."
|
||||
|
||||
# Check if dist exists
|
||||
if [ ! -d "$DIST_DIR" ]; then
|
||||
print_msg "$RED" "❌ dist/ directory not found. Run build first."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check required files
|
||||
REQUIRED_FILES=(
|
||||
"$DIST_DIR/manifest.json"
|
||||
"$DIST_DIR/background.js"
|
||||
"$DIST_DIR/popup.html"
|
||||
"$DIST_DIR/popup.js"
|
||||
)
|
||||
|
||||
for file in "${REQUIRED_FILES[@]}"; do
|
||||
if [ ! -f "$file" ]; then
|
||||
print_msg "$RED" "❌ Missing required file: $file"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
# Check manifest has required permissions
|
||||
if ! grep -q '"alarms"' "$DIST_DIR/manifest.json"; then
|
||||
print_msg "$RED" "❌ manifest.json missing 'alarms' permission"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check background.js is an ES module (should start with export/import)
|
||||
if ! grep -qE '^(export|import)' "$DIST_DIR/background.js"; then
|
||||
print_msg "$YELLOW" "⚠️ background.js might not be a proper ES module"
|
||||
print_msg "$YELLOW" " First line: $(head -n 1 $DIST_DIR/background.js)"
|
||||
fi
|
||||
|
||||
print_msg "$GREEN" "✅ Build verification passed!"
|
||||
print_msg "$BLUE" "📋 Build info:"
|
||||
echo " - Manifest version: $(grep -o '"version": "[^"]*"' $DIST_DIR/manifest.json | cut -d'"' -f4)"
|
||||
echo " - Background size: $(du -h $DIST_DIR/background.js | cut -f1)"
|
||||
echo " - Popup size: $(du -h $DIST_DIR/popup.js | cut -f1)"
|
||||
echo ""
|
||||
print_msg "$YELLOW" "Next steps:"
|
||||
echo " 1. Go to chrome://extensions/"
|
||||
echo " 2. Click 'Load unpacked'"
|
||||
echo " 3. Select: $DIST_DIR"
|
||||
echo " 4. Or reload if already loaded"
|
||||
}
|
||||
|
||||
# Main
|
||||
case "${1:-}" in
|
||||
build)
|
||||
build
|
||||
;;
|
||||
dev)
|
||||
dev
|
||||
;;
|
||||
clean)
|
||||
clean
|
||||
;;
|
||||
test)
|
||||
test
|
||||
;;
|
||||
check)
|
||||
check
|
||||
;;
|
||||
open-chrome)
|
||||
open_chrome
|
||||
;;
|
||||
open-sw)
|
||||
open_sw
|
||||
;;
|
||||
verify)
|
||||
verify
|
||||
;;
|
||||
help|--help|-h)
|
||||
usage
|
||||
;;
|
||||
*)
|
||||
if [ -n "${1:-}" ]; then
|
||||
print_msg "$RED" "❌ Unknown command: $1"
|
||||
echo ""
|
||||
fi
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
@@ -6,14 +6,16 @@
|
||||
"default_locale": "en",
|
||||
"permissions": [
|
||||
"downloads",
|
||||
"storage"
|
||||
"storage",
|
||||
"alarms",
|
||||
"activeTab"
|
||||
],
|
||||
"host_permissions": [
|
||||
"https://api.binect.de/*"
|
||||
"https://api.binect.de/*",
|
||||
"<all_urls>"
|
||||
],
|
||||
"background": {
|
||||
"service_worker": "background.js",
|
||||
"type": "module"
|
||||
"service_worker": "background.js"
|
||||
},
|
||||
"action": {
|
||||
"default_popup": "popup.html",
|
||||
|
||||
BIN
specs/binectapi_rest.pdf
Executable file
BIN
specs/binectapi_rest.pdf
Executable file
Binary file not shown.
2825
specs/v1_swagger_api_kernel.json
Executable file
2825
specs/v1_swagger_api_kernel.json
Executable file
File diff suppressed because it is too large
Load Diff
@@ -13,11 +13,12 @@ let lastDetectedPDF: DetectedPDF | null = null;
|
||||
* Initialize extension on install
|
||||
*/
|
||||
chrome.runtime.onInstalled.addListener((details) => {
|
||||
console.log('[Service Worker] onInstalled event:', details.reason);
|
||||
if (details.reason === 'install') {
|
||||
console.log('BinectChrome installed');
|
||||
console.log('[Service Worker] BinectChrome installed');
|
||||
setupCredentialExpiryAlarm();
|
||||
} else if (details.reason === 'update') {
|
||||
console.log('BinectChrome updated');
|
||||
console.log('[Service Worker] BinectChrome updated');
|
||||
}
|
||||
});
|
||||
|
||||
@@ -25,7 +26,7 @@ chrome.runtime.onInstalled.addListener((details) => {
|
||||
* Handle extension startup
|
||||
*/
|
||||
chrome.runtime.onStartup.addListener(() => {
|
||||
console.log('BinectChrome started');
|
||||
console.log('[Service Worker] onStartup event - BinectChrome started');
|
||||
setupCredentialExpiryAlarm();
|
||||
});
|
||||
|
||||
@@ -60,37 +61,58 @@ async function checkAndDeleteExpiredCredentials() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize badge with default icon
|
||||
*/
|
||||
function initializeBadge() {
|
||||
// Set a default badge to make extension visible
|
||||
chrome.action.setBadgeText({ text: '•' });
|
||||
chrome.action.setBadgeBackgroundColor({ color: '#4A90E2' }); // Binect Blue
|
||||
console.log('[Service Worker] Default badge set');
|
||||
}
|
||||
|
||||
// Initialize badge on load
|
||||
initializeBadge();
|
||||
|
||||
/**
|
||||
* Start PDF detection
|
||||
*/
|
||||
console.log('[Service Worker] Initializing PDF detection...');
|
||||
startPDFDetection((pdf: DetectedPDF) => {
|
||||
console.log('PDF detected:', pdf.filename);
|
||||
console.log('[Service Worker] PDF DETECTED CALLBACK:', pdf.filename);
|
||||
lastDetectedPDF = pdf;
|
||||
|
||||
// Update badge to indicate PDF detected
|
||||
chrome.action.setBadgeText({ text: '1' });
|
||||
chrome.action.setBadgeBackgroundColor({ color: '#4A90E2' }); // Binect Blue
|
||||
|
||||
console.log('[Service Worker] Badge updated, PDF stored in memory');
|
||||
});
|
||||
|
||||
/**
|
||||
* Handle messages from popup
|
||||
*/
|
||||
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
||||
console.log('[Service Worker] Message received:', request.action);
|
||||
|
||||
if (request.action === 'getLastPDF') {
|
||||
console.log('[Service Worker] Returning last PDF:', lastDetectedPDF ? lastDetectedPDF.filename : 'none');
|
||||
sendResponse({ pdf: lastDetectedPDF });
|
||||
return true;
|
||||
}
|
||||
|
||||
if (request.action === 'clearLastPDF') {
|
||||
console.log('[Service Worker] Clearing last PDF');
|
||||
lastDetectedPDF = null;
|
||||
chrome.action.setBadgeText({ text: '' });
|
||||
chrome.action.setBadgeText({ text: '•' }); // Reset to default badge
|
||||
sendResponse({ success: true });
|
||||
return true;
|
||||
}
|
||||
|
||||
if (request.action === 'pdfSent') {
|
||||
// Clear badge after successful send
|
||||
chrome.action.setBadgeText({ text: '' });
|
||||
console.log('[Service Worker] PDF sent, resetting badge');
|
||||
// Reset badge after successful send
|
||||
chrome.action.setBadgeText({ text: '•' }); // Reset to default badge
|
||||
sendResponse({ success: true });
|
||||
return true;
|
||||
}
|
||||
@@ -98,4 +120,5 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
||||
return false;
|
||||
});
|
||||
|
||||
console.log('BinectChrome service worker loaded');
|
||||
console.log('[Service Worker] ===== BinectChrome service worker loaded =====');
|
||||
console.log('[Service Worker] Timestamp:', new Date().toISOString());
|
||||
|
||||
@@ -133,6 +133,46 @@ body {
|
||||
box-shadow: 0 0 0 3px rgba(74, 144, 226, 0.1);
|
||||
}
|
||||
|
||||
/* Password Input Wrapper */
|
||||
.password-input-wrapper {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.password-input-wrapper input {
|
||||
padding-right: 44px; /* Make room for the eye icon */
|
||||
}
|
||||
|
||||
.password-toggle {
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
padding: 6px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--text-secondary);
|
||||
border-radius: 4px;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.password-toggle:hover {
|
||||
background: var(--light-bg);
|
||||
color: var(--binect-blue);
|
||||
}
|
||||
|
||||
.password-toggle:focus {
|
||||
outline: 2px solid var(--binect-blue);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.password-toggle svg {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Buttons */
|
||||
.btn {
|
||||
width: 100%;
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>BinectChrome</title>
|
||||
<link rel="stylesheet" href="popup.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
@@ -26,7 +25,19 @@
|
||||
|
||||
<div class="form-group">
|
||||
<label for="password">Password</label>
|
||||
<input type="password" id="password" name="password" required autocomplete="current-password">
|
||||
<div class="password-input-wrapper">
|
||||
<input type="password" id="password" name="password" required autocomplete="current-password">
|
||||
<button type="button" id="togglePassword" class="password-toggle" aria-label="Show password" title="Show password">
|
||||
<svg id="eyeIcon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path>
|
||||
<circle cx="12" cy="12" r="3"></circle>
|
||||
</svg>
|
||||
<svg id="eyeOffIcon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="display: none;">
|
||||
<path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24"></path>
|
||||
<line x1="1" y1="1" x2="23" y2="23"></line>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary" id="loginBtn">Sign In</button>
|
||||
@@ -39,7 +50,7 @@
|
||||
<div id="mainView" class="view" style="display: none;">
|
||||
<!-- No PDF Detected -->
|
||||
<div id="noPdfView" class="content-section">
|
||||
<p class="info-text">No PDF detected. Download a PDF to get started.</p>
|
||||
<p class="info-text">No PDF detected. Open or download a PDF to get started.</p>
|
||||
</div>
|
||||
|
||||
<!-- PDF Detected -->
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
* Popup UI Logic
|
||||
*/
|
||||
|
||||
import './popup.css';
|
||||
import { loadCredentials, saveCredentials, deleteCredentials, updateLastUse } from '../utils/storage';
|
||||
import { authenticate, uploadPDF, BinectAPIError } from '../utils/binect-api';
|
||||
import { uploadPDF, testConnection, BinectAPIError } from '../utils/binect-api';
|
||||
import { fetchPDFBytes, DetectedPDF } from '../utils/pdf-detector';
|
||||
import { addTrackingEntry } from '../tracking/tracker';
|
||||
|
||||
@@ -29,10 +30,13 @@ const statusMessage = document.getElementById('statusMessage')!;
|
||||
|
||||
const logoutBtn = document.getElementById('logoutBtn')!;
|
||||
const helpBtn = document.getElementById('helpBtn')!;
|
||||
const togglePasswordBtn = document.getElementById('togglePassword') as HTMLButtonElement;
|
||||
const eyeIcon = document.getElementById('eyeIcon')!;
|
||||
const eyeOffIcon = document.getElementById('eyeOffIcon')!;
|
||||
|
||||
// State
|
||||
let currentPDF: DetectedPDF | null = null;
|
||||
let authToken: string | null = null;
|
||||
let currentCredentials: { username: string; password: string } | null = null;
|
||||
|
||||
/**
|
||||
* Initialize popup
|
||||
@@ -42,16 +46,23 @@ async function init() {
|
||||
const credentials = await loadCredentials();
|
||||
|
||||
if (credentials) {
|
||||
// Try to authenticate
|
||||
// Try to test connection
|
||||
try {
|
||||
const token = await authenticate(credentials.username, credentials.password);
|
||||
authToken = token.token;
|
||||
await updateLastUse();
|
||||
const isConnected = await testConnection(credentials.username, credentials.password);
|
||||
|
||||
showMainView();
|
||||
await loadLastPDF();
|
||||
if (isConnected) {
|
||||
currentCredentials = credentials;
|
||||
await updateLastUse();
|
||||
|
||||
showMainView();
|
||||
await loadLastPDF();
|
||||
} else {
|
||||
// Authentication failed, credentials may be invalid
|
||||
showAuthView();
|
||||
}
|
||||
} catch (error) {
|
||||
// Authentication failed, credentials may be invalid
|
||||
// Connection test failed
|
||||
console.error('[Popup] Connection test failed:', error);
|
||||
showAuthView();
|
||||
}
|
||||
} else {
|
||||
@@ -70,6 +81,30 @@ function setupEventListeners() {
|
||||
sendBtn.addEventListener('click', handleSendPDF);
|
||||
logoutBtn.addEventListener('click', handleLogout);
|
||||
helpBtn.addEventListener('click', handleHelp);
|
||||
togglePasswordBtn.addEventListener('click', handleTogglePassword);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle password visibility toggle
|
||||
*/
|
||||
function handleTogglePassword() {
|
||||
const isPassword = passwordInput.type === 'password';
|
||||
|
||||
if (isPassword) {
|
||||
// Show password
|
||||
passwordInput.type = 'text';
|
||||
eyeIcon.style.display = 'none';
|
||||
eyeOffIcon.style.display = 'block';
|
||||
togglePasswordBtn.setAttribute('aria-label', 'Hide password');
|
||||
togglePasswordBtn.setAttribute('title', 'Hide password');
|
||||
} else {
|
||||
// Hide password
|
||||
passwordInput.type = 'password';
|
||||
eyeIcon.style.display = 'block';
|
||||
eyeOffIcon.style.display = 'none';
|
||||
togglePasswordBtn.setAttribute('aria-label', 'Show password');
|
||||
togglePasswordBtn.setAttribute('title', 'Show password');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -91,10 +126,15 @@ async function handleLogin(e: Event) {
|
||||
hideError();
|
||||
|
||||
try {
|
||||
const token = await authenticate(username, password);
|
||||
authToken = token.token;
|
||||
const isConnected = await testConnection(username, password);
|
||||
|
||||
if (!isConnected) {
|
||||
showError('Invalid credentials. Please check your username and password.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Save credentials
|
||||
currentCredentials = { username, password };
|
||||
await saveCredentials({ username, password });
|
||||
|
||||
showMainView();
|
||||
@@ -115,7 +155,7 @@ async function handleLogin(e: Event) {
|
||||
* Handle send PDF
|
||||
*/
|
||||
async function handleSendPDF() {
|
||||
if (!currentPDF || !authToken) {
|
||||
if (!currentPDF || !currentCredentials) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -126,15 +166,20 @@ async function handleSendPDF() {
|
||||
// Fetch PDF bytes
|
||||
const pdfBytes = await fetchPDFBytes(currentPDF.url);
|
||||
|
||||
// Upload to Binect
|
||||
const result = await uploadPDF(pdfBytes, currentPDF.filename, authToken);
|
||||
// Upload to Binect with credentials
|
||||
const document = await uploadPDF(
|
||||
pdfBytes,
|
||||
currentPDF.filename,
|
||||
currentCredentials.username,
|
||||
currentCredentials.password
|
||||
);
|
||||
|
||||
// Track successful transfer
|
||||
await addTrackingEntry({
|
||||
timestamp: Date.now(),
|
||||
sourceDomain: currentPDF.sourceDomain,
|
||||
destinationUrl: 'https://api.binect.de/documents/upload',
|
||||
pdfSize: currentPDF.size,
|
||||
destinationUrl: 'https://api.binect.de/binectapi/v1/documents',
|
||||
pdfSize: pdfBytes.byteLength, // Use actual size from fetched data
|
||||
result: 'success'
|
||||
});
|
||||
|
||||
@@ -144,7 +189,7 @@ async function handleSendPDF() {
|
||||
// Notify background script
|
||||
chrome.runtime.sendMessage({ action: 'pdfSent' });
|
||||
|
||||
showStatus(`Success! Document ID: ${result.documentId}`, 'success');
|
||||
showStatus(`Success! Document ID: ${document.id} (Status: ${document.status.text})`, 'success');
|
||||
|
||||
// Clear PDF after 3 seconds
|
||||
setTimeout(() => {
|
||||
@@ -159,8 +204,8 @@ async function handleSendPDF() {
|
||||
errorMessage = error.message;
|
||||
|
||||
// If auth error, might need to re-login
|
||||
if (error.statusCode === 401) {
|
||||
errorMessage = 'Session expired. Please sign in again.';
|
||||
if (error.statusCode === 401 || error.statusCode === 403) {
|
||||
errorMessage = 'Invalid credentials. Please sign in again.';
|
||||
setTimeout(() => {
|
||||
handleLogout();
|
||||
}, 2000);
|
||||
@@ -173,8 +218,8 @@ async function handleSendPDF() {
|
||||
await addTrackingEntry({
|
||||
timestamp: Date.now(),
|
||||
sourceDomain: currentPDF.sourceDomain,
|
||||
destinationUrl: 'https://api.binect.de/documents/upload',
|
||||
pdfSize: currentPDF.size,
|
||||
destinationUrl: 'https://api.binect.de/binectapi/v1/documents',
|
||||
pdfSize: currentPDF.size || 0,
|
||||
result: 'failure',
|
||||
errorMessage
|
||||
});
|
||||
@@ -190,7 +235,7 @@ async function handleSendPDF() {
|
||||
*/
|
||||
async function handleLogout() {
|
||||
await deleteCredentials();
|
||||
authToken = null;
|
||||
currentCredentials = null;
|
||||
currentPDF = null;
|
||||
|
||||
// Clear form
|
||||
@@ -211,21 +256,156 @@ function handleHelp() {
|
||||
* Load last detected PDF
|
||||
*/
|
||||
async function loadLastPDF() {
|
||||
// Ask background script for last PDF
|
||||
chrome.runtime.sendMessage({ action: 'getLastPDF' }, (response) => {
|
||||
if (response && response.pdf) {
|
||||
console.log('[Popup] Loading last PDF...');
|
||||
|
||||
// First, check if current tab is viewing a PDF
|
||||
const currentTabPDF = await checkCurrentTabForPDF();
|
||||
|
||||
if (currentTabPDF) {
|
||||
console.log('[Popup] Found PDF in current tab:', currentTabPDF.filename);
|
||||
currentPDF = currentTabPDF;
|
||||
showPDF(currentPDF);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('[Popup] No PDF in current tab, checking background script...');
|
||||
|
||||
// If no PDF in current tab, ask background script for last detected download
|
||||
chrome.runtime.sendMessage({ action: 'getLastPDF' }, async (response) => {
|
||||
if (response && response.pdf && response.pdf !== null) {
|
||||
console.log('[Popup] Background returned PDF:', response.pdf.filename);
|
||||
currentPDF = response.pdf;
|
||||
if (currentPDF) {
|
||||
showPDF(currentPDF);
|
||||
showPDF(response.pdf);
|
||||
} else {
|
||||
console.log('[Popup] Background has no PDF, checking recent downloads as fallback...');
|
||||
|
||||
// Fallback: Check recent downloads directly
|
||||
const recentPDF = await checkRecentDownloads();
|
||||
if (recentPDF !== null) {
|
||||
console.log('[Popup] Found recent PDF download:', recentPDF.filename);
|
||||
currentPDF = recentPDF;
|
||||
showPDF(recentPDF);
|
||||
} else {
|
||||
console.log('[Popup] No PDF found anywhere');
|
||||
showNoPDF();
|
||||
}
|
||||
} else {
|
||||
showNoPDF();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Check recent downloads for PDFs (fallback mechanism)
|
||||
*/
|
||||
async function checkRecentDownloads(): Promise<DetectedPDF | null> {
|
||||
return new Promise((resolve) => {
|
||||
chrome.downloads.search(
|
||||
{
|
||||
limit: 20, // Check last 20 downloads
|
||||
orderBy: ['-startTime']
|
||||
},
|
||||
(items) => {
|
||||
console.log('[Popup] Checked recent downloads:', items.length, 'items');
|
||||
|
||||
// Find most recent completed PDF
|
||||
const pdfItem = items.find(
|
||||
(item) =>
|
||||
item.state === 'complete' &&
|
||||
(item.filename.toLowerCase().endsWith('.pdf') || item.mime === 'application/pdf')
|
||||
);
|
||||
|
||||
if (pdfItem) {
|
||||
console.log('[Popup] Found recent PDF:', pdfItem.filename);
|
||||
|
||||
// Extract domain
|
||||
let domain = 'unknown';
|
||||
try {
|
||||
const urlObj = new URL(pdfItem.url);
|
||||
domain = urlObj.hostname;
|
||||
} catch (e) {
|
||||
// Keep default
|
||||
}
|
||||
|
||||
resolve({
|
||||
id: `download-${pdfItem.id}`,
|
||||
filename: pdfItem.filename.split('/').pop() || pdfItem.filename,
|
||||
url: pdfItem.url,
|
||||
size: pdfItem.fileSize,
|
||||
timestamp: Date.now(), // Use current time as approximation
|
||||
sourceDomain: domain
|
||||
});
|
||||
} else {
|
||||
console.log('[Popup] No recent PDF downloads found');
|
||||
resolve(null);
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the current active tab is viewing a PDF
|
||||
*/
|
||||
async function checkCurrentTabForPDF(): Promise<DetectedPDF | null> {
|
||||
try {
|
||||
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
|
||||
|
||||
if (!tab || !tab.url) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Check if the URL is a PDF
|
||||
const url = tab.url;
|
||||
const isPDF = url.toLowerCase().endsWith('.pdf') ||
|
||||
url.includes('type=application/pdf') ||
|
||||
url.includes('mime=application/pdf');
|
||||
|
||||
if (isPDF) {
|
||||
// Extract filename from URL
|
||||
let filename = 'document.pdf';
|
||||
try {
|
||||
const urlObj = new URL(url);
|
||||
const pathname = urlObj.pathname;
|
||||
const pathParts = pathname.split('/');
|
||||
const lastPart = pathParts[pathParts.length - 1];
|
||||
if (lastPart && lastPart.toLowerCase().endsWith('.pdf')) {
|
||||
filename = decodeURIComponent(lastPart);
|
||||
} else if (tab.title && tab.title !== 'about:blank' && !tab.title.startsWith('chrome://')) {
|
||||
// Use tab title if available
|
||||
filename = tab.title.endsWith('.pdf') ? tab.title : `${tab.title}.pdf`;
|
||||
}
|
||||
} catch (e) {
|
||||
// Use tab title as fallback
|
||||
if (tab.title && tab.title !== 'about:blank') {
|
||||
filename = tab.title.endsWith('.pdf') ? tab.title : `${tab.title}.pdf`;
|
||||
}
|
||||
}
|
||||
|
||||
// Extract domain
|
||||
let domain = 'unknown';
|
||||
try {
|
||||
const urlObj = new URL(url);
|
||||
domain = urlObj.hostname;
|
||||
} catch (e) {
|
||||
// Keep default
|
||||
}
|
||||
|
||||
return {
|
||||
id: `tab-${tab.id}`,
|
||||
filename,
|
||||
url,
|
||||
size: 0, // Unknown size for viewed PDFs
|
||||
timestamp: Date.now(),
|
||||
sourceDomain: domain
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
} catch (error) {
|
||||
console.error('Error checking current tab for PDF:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show auth view
|
||||
*/
|
||||
@@ -301,7 +481,9 @@ function hideStatus() {
|
||||
* Format file size
|
||||
*/
|
||||
function formatFileSize(bytes: number): string {
|
||||
if (bytes < 1024) {
|
||||
if (bytes === 0) {
|
||||
return 'Size unknown';
|
||||
} else if (bytes < 1024) {
|
||||
return `${bytes} B`;
|
||||
} else if (bytes < 1024 * 1024) {
|
||||
return `${(bytes / 1024).toFixed(1)} KB`;
|
||||
|
||||
@@ -1,18 +1,50 @@
|
||||
/**
|
||||
* Binect API client
|
||||
* Based on Binect API v1 (Swagger spec: specs/v1_swagger_api_kernel.json)
|
||||
*
|
||||
* Authentication: HTTP Basic Authentication
|
||||
* Base path: /binectapi/v1
|
||||
*/
|
||||
|
||||
const API_BASE_URL = 'https://api.binect.de';
|
||||
const API_BASE_URL = 'https://api.binect.de/binectapi/v1';
|
||||
|
||||
export interface AuthToken {
|
||||
token: string;
|
||||
expiresAt: string;
|
||||
export interface Document {
|
||||
id: number;
|
||||
filename: string;
|
||||
numberOfPages?: number;
|
||||
status: {
|
||||
code: number;
|
||||
text: string;
|
||||
};
|
||||
documentType: 'Letter' | 'SerialLetter';
|
||||
letter?: {
|
||||
letterType: 'LetterData' | 'Error';
|
||||
letterData?: {
|
||||
recipientAddress: string;
|
||||
price: {
|
||||
priceBeforeTax: number;
|
||||
priceAfterTax: number;
|
||||
unit: string;
|
||||
taxInPercent: number;
|
||||
};
|
||||
international: boolean;
|
||||
options: Options;
|
||||
};
|
||||
errors?: Array<{
|
||||
code: number;
|
||||
text: string;
|
||||
blankText: string;
|
||||
}>;
|
||||
};
|
||||
}
|
||||
|
||||
export interface UploadResult {
|
||||
documentId: string;
|
||||
status: string;
|
||||
uploadedAt: string;
|
||||
export interface Options {
|
||||
simplex: boolean; // if false, it's duplex
|
||||
color: boolean; // if false, it's black and white
|
||||
envelope?: 'DINLANG' | 'C4';
|
||||
dvFranking?: boolean;
|
||||
franking?: 'UNSPECIFIED' | 'STANDARD_FRANKING' | 'DV_FRANKING';
|
||||
productionCountry?: 'UNSPECIFIED' | 'DE' | 'AT';
|
||||
}
|
||||
|
||||
export class BinectAPIError extends Error {
|
||||
@@ -27,95 +59,153 @@ export class BinectAPIError extends Error {
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate with Binect API
|
||||
* Create HTTP Basic Authentication header value
|
||||
*/
|
||||
export async function authenticate(
|
||||
username: string,
|
||||
password: string
|
||||
): Promise<AuthToken> {
|
||||
try {
|
||||
const response = await fetch(`${API_BASE_URL}/auth/login`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({ username, password })
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
if (response.status === 401) {
|
||||
throw new BinectAPIError('Invalid credentials', 401);
|
||||
}
|
||||
throw new BinectAPIError(
|
||||
`Authentication failed: ${response.statusText}`,
|
||||
response.status
|
||||
);
|
||||
}
|
||||
|
||||
return await response.json();
|
||||
} catch (error) {
|
||||
if (error instanceof BinectAPIError) {
|
||||
throw error;
|
||||
}
|
||||
throw new BinectAPIError(
|
||||
`Network error: ${error instanceof Error ? error.message : 'Unknown error'}`
|
||||
);
|
||||
}
|
||||
function createBasicAuthHeader(username: string, password: string): string {
|
||||
const credentials = `${username}:${password}`;
|
||||
const base64Credentials = btoa(credentials);
|
||||
return `Basic ${base64Credentials}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload PDF to Binect
|
||||
* Convert ArrayBuffer to base64 string
|
||||
*/
|
||||
function arrayBufferToBase64(buffer: ArrayBuffer): string {
|
||||
const bytes = new Uint8Array(buffer);
|
||||
let binary = '';
|
||||
for (let i = 0; i < bytes.byteLength; i++) {
|
||||
binary += String.fromCharCode(bytes[i]);
|
||||
}
|
||||
return btoa(binary);
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload PDF to Binect API
|
||||
*
|
||||
* Uses HTTP Basic Authentication and uploads the PDF as base64 encoded content
|
||||
* in a JSON request body according to the Binect API v1 specification.
|
||||
*
|
||||
* @param pdfData - PDF file as ArrayBuffer
|
||||
* @param filename - Name of the PDF file
|
||||
* @param username - Binect username for authentication
|
||||
* @param password - Binect password for authentication
|
||||
* @param options - Optional printing options (simplex/duplex, color/bw, etc.)
|
||||
* @returns Document object with ID and status
|
||||
*/
|
||||
export async function uploadPDF(
|
||||
pdfData: ArrayBuffer,
|
||||
filename: string,
|
||||
token: string
|
||||
): Promise<UploadResult> {
|
||||
try {
|
||||
const formData = new FormData();
|
||||
const blob = new Blob([pdfData], { type: 'application/pdf' });
|
||||
formData.append('file', blob, filename);
|
||||
formData.append('filename', filename);
|
||||
username: string,
|
||||
password: string,
|
||||
options?: Options
|
||||
): Promise<Document> {
|
||||
console.log('[Binect API] Uploading PDF to Binect...');
|
||||
console.log('[Binect API] URL:', `${API_BASE_URL}/documents`);
|
||||
console.log('[Binect API] Filename:', filename);
|
||||
console.log('[Binect API] PDF size:', pdfData.byteLength, 'bytes');
|
||||
console.log('[Binect API] Username:', username);
|
||||
|
||||
const response = await fetch(`${API_BASE_URL}/documents/upload`, {
|
||||
try {
|
||||
// Convert PDF to base64
|
||||
console.log('[Binect API] Converting PDF to base64...');
|
||||
const base64Content = arrayBufferToBase64(pdfData);
|
||||
console.log('[Binect API] Base64 length:', base64Content.length, 'characters');
|
||||
|
||||
// Prepare request body
|
||||
const requestBody = {
|
||||
content: {
|
||||
filename,
|
||||
content: base64Content
|
||||
},
|
||||
options: options || {
|
||||
simplex: false, // duplex by default
|
||||
color: false // black and white by default
|
||||
}
|
||||
};
|
||||
|
||||
console.log('[Binect API] Request options:', requestBody.options);
|
||||
|
||||
// Make request with HTTP Basic Authentication
|
||||
const response = await fetch(`${API_BASE_URL}/documents`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': createBasicAuthHeader(username, password)
|
||||
},
|
||||
body: formData
|
||||
body: JSON.stringify(requestBody)
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
console.log('[Binect API] Upload response status:', response.status);
|
||||
console.log('[Binect API] Response content-type:', response.headers.get('content-type'));
|
||||
|
||||
if (response.status === 401) {
|
||||
throw new BinectAPIError('Authentication required', 401, errorData);
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text();
|
||||
console.error('[Binect API] Upload error response:', errorText);
|
||||
|
||||
if (response.status === 401 || response.status === 403) {
|
||||
throw new BinectAPIError('Invalid credentials', response.status);
|
||||
}
|
||||
|
||||
if (response.status === 400) {
|
||||
throw new BinectAPIError(
|
||||
errorData.error || 'Invalid file format',
|
||||
400,
|
||||
errorData
|
||||
'Invalid request. Please check the PDF format and size.',
|
||||
400
|
||||
);
|
||||
}
|
||||
|
||||
if (response.status === 413) {
|
||||
throw new BinectAPIError('File size exceeds limit', 413, errorData);
|
||||
throw new BinectAPIError('File size exceeds limit (12 MB)', 413);
|
||||
}
|
||||
|
||||
throw new BinectAPIError(
|
||||
`Upload failed: ${response.statusText}`,
|
||||
response.status,
|
||||
errorData
|
||||
errorText
|
||||
);
|
||||
}
|
||||
|
||||
return await response.json();
|
||||
const document: Document = await response.json();
|
||||
console.log('[Binect API] Upload successful!');
|
||||
console.log('[Binect API] Document ID:', document.id);
|
||||
console.log('[Binect API] Document status:', document.status);
|
||||
console.log('[Binect API] Full response:', document);
|
||||
|
||||
// Check if document has errors
|
||||
if (document.letter?.letterType === 'Error' && document.letter.errors) {
|
||||
console.warn('[Binect API] Document has errors:', document.letter.errors);
|
||||
const errorMessages = document.letter.errors.map(e => e.text).join('; ');
|
||||
throw new BinectAPIError(
|
||||
`Document validation failed: ${errorMessages}`,
|
||||
200,
|
||||
document
|
||||
);
|
||||
}
|
||||
|
||||
// Status code 2 = shippable, 7 = erroneous
|
||||
if (document.status.code === 7) {
|
||||
console.error('[Binect API] Document is erroneous:', document.status.text);
|
||||
throw new BinectAPIError(
|
||||
`Document is erroneous: ${document.status.text}`,
|
||||
200,
|
||||
document
|
||||
);
|
||||
}
|
||||
|
||||
return document;
|
||||
} catch (error) {
|
||||
console.error('[Binect API] Upload error:', error);
|
||||
|
||||
if (error instanceof BinectAPIError) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
// Check for network errors
|
||||
if (error instanceof TypeError && error.message.includes('fetch')) {
|
||||
throw new BinectAPIError(
|
||||
`Cannot reach Binect API at ${API_BASE_URL}. Please check your internet connection.`
|
||||
);
|
||||
}
|
||||
|
||||
throw new BinectAPIError(
|
||||
`Network error: ${error instanceof Error ? error.message : 'Unknown error'}`
|
||||
);
|
||||
@@ -123,15 +213,40 @@ export async function uploadPDF(
|
||||
}
|
||||
|
||||
/**
|
||||
* Test API connectivity
|
||||
* Test API connectivity by fetching account information
|
||||
*
|
||||
* @param username - Binect username
|
||||
* @param password - Binect password
|
||||
* @returns true if authentication successful, false otherwise
|
||||
*/
|
||||
export async function testConnection(): Promise<boolean> {
|
||||
export async function testConnection(username: string, password: string): Promise<boolean> {
|
||||
console.log('[Binect API] Testing connection to Binect API...');
|
||||
console.log('[Binect API] URL:', `${API_BASE_URL}/accounts`);
|
||||
|
||||
try {
|
||||
const response = await fetch(`${API_BASE_URL}/health`, {
|
||||
method: 'GET'
|
||||
const response = await fetch(`${API_BASE_URL}/accounts`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Authorization': createBasicAuthHeader(username, password)
|
||||
}
|
||||
});
|
||||
return response.ok;
|
||||
} catch {
|
||||
|
||||
console.log('[Binect API] Test connection response status:', response.status);
|
||||
|
||||
if (response.status === 401 || response.status === 403) {
|
||||
console.log('[Binect API] Authentication failed');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (response.ok) {
|
||||
console.log('[Binect API] Connection successful');
|
||||
return true;
|
||||
}
|
||||
|
||||
console.warn('[Binect API] Unexpected response status:', response.status);
|
||||
return false;
|
||||
} catch (error) {
|
||||
console.error('[Binect API] Connection test error:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,24 +61,53 @@ function downloadItemToPDF(item: chrome.downloads.DownloadItem): DetectedPDF {
|
||||
export function startPDFDetection(
|
||||
onPDFDetected: (pdf: DetectedPDF) => void
|
||||
): void {
|
||||
console.log('[PDF Detector] Starting PDF detection, registering download listener');
|
||||
|
||||
// Listen for download changes
|
||||
chrome.downloads.onChanged.addListener((delta) => {
|
||||
console.log('[PDF Detector] Download changed:', {
|
||||
id: delta.id,
|
||||
state: delta.state,
|
||||
stateValue: delta.state?.current
|
||||
});
|
||||
|
||||
// Only process completed downloads
|
||||
if (delta.state?.current !== 'complete') {
|
||||
console.log('[PDF Detector] Download not complete, ignoring');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('[PDF Detector] Download complete, searching for item:', delta.id);
|
||||
|
||||
// Get full download item details
|
||||
chrome.downloads.search({ id: delta.id }, (items) => {
|
||||
if (items.length === 0) return;
|
||||
console.log('[PDF Detector] Search results:', items.length, 'items');
|
||||
|
||||
if (items.length === 0) {
|
||||
console.warn('[PDF Detector] No items found for download ID:', delta.id);
|
||||
return;
|
||||
}
|
||||
|
||||
const item = items[0];
|
||||
console.log('[PDF Detector] Download item:', {
|
||||
id: item.id,
|
||||
filename: item.filename,
|
||||
mime: item.mime,
|
||||
state: item.state,
|
||||
url: item.url
|
||||
});
|
||||
|
||||
if (isPDF(item)) {
|
||||
console.log('[PDF Detector] PDF detected!');
|
||||
const pdf = downloadItemToPDF(item);
|
||||
onPDFDetected(pdf);
|
||||
} else {
|
||||
console.log('[PDF Detector] Not a PDF, ignoring');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
console.log('[PDF Detector] Listener registered successfully');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user