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:
2026-01-14 16:50:57 +01:00
parent 0be7b56506
commit be4377253e
16 changed files with 5079 additions and 114 deletions

289
DEBUG_DOWNLOAD_DETECTION.md Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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

View File

@@ -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

Binary file not shown.

2825
specs/v1_swagger_api_kernel.json Executable file

File diff suppressed because it is too large Load Diff

View File

@@ -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());

View File

@@ -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%;

View File

@@ -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>
<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 -->

View File

@@ -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,18 +46,25 @@ 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;
const isConnected = await testConnection(credentials.username, credentials.password);
if (isConnected) {
currentCredentials = credentials;
await updateLastUse();
showMainView();
await loadLastPDF();
} catch (error) {
} else {
// Authentication failed, credentials may be invalid
showAuthView();
}
} catch (error) {
// Connection test failed
console.error('[Popup] Connection test failed:', error);
showAuthView();
}
} else {
showAuthView();
}
@@ -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) {
currentPDF = response.pdf;
if (currentPDF) {
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;
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`;

View File

@@ -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;
}
}

View File

@@ -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');
}
/**