Initial commit

This commit is contained in:
MDeeApp
2025-10-04 16:30:22 +02:00
commit 3a17854242
100 changed files with 14764 additions and 0 deletions

48
extension/background.js Normal file
View File

@@ -0,0 +1,48 @@
// Background script for service worker
// Currently minimal, can be extended for additional functionality
chrome.runtime.onInstalled.addListener(() => {
console.log('Facebook Post Tracker extension installed');
// Set default profile if not set
chrome.storage.sync.get(['profileNumber'], (result) => {
if (!result.profileNumber) {
chrome.storage.sync.set({ profileNumber: 1 });
}
});
// Create context menu for manual post parsing
chrome.contextMenus.create({
id: 'fb-tracker-reparse',
title: 'FB Tracker: Post neu parsen',
contexts: ['all'],
documentUrlPatterns: ['*://*.facebook.com/*']
});
});
chrome.contextMenus.onClicked.addListener((info, tab) => {
if (info.menuItemId === 'fb-tracker-reparse') {
chrome.tabs.sendMessage(tab.id, {
type: 'reparsePost',
x: info.pageX || 0,
y: info.pageY || 0
});
}
});
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message && message.type === 'captureScreenshot') {
const windowId = sender && sender.tab ? sender.tab.windowId : chrome.windows.WINDOW_ID_CURRENT;
chrome.tabs.captureVisibleTab(windowId, { format: 'jpeg', quality: 80 }, (imageData) => {
if (chrome.runtime.lastError) {
sendResponse({ error: chrome.runtime.lastError.message });
return;
}
sendResponse({ imageData });
});
return true;
}
return false;
});

3
extension/config.js Normal file
View File

@@ -0,0 +1,3 @@
// API Configuration
// Passe die URL an deine Deployment-Domain an
const API_BASE_URL = 'https://fb.srv.medeba-media.de';

64
extension/content.css Normal file
View File

@@ -0,0 +1,64 @@
.fb-tracker-ui {
margin: 8px 0;
padding: 6px 12px;
background: #f0f2f5;
border-radius: 6px;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}
.fb-tracker-status,
.fb-tracker-add {
display: flex;
align-items: center;
gap: 6px;
}
.fb-tracker-icon {
font-size: 16px;
}
.fb-tracker-text {
flex: 1;
font-size: 13px;
color: #050505;
font-weight: 500;
}
.fb-tracker-status.complete .fb-tracker-text {
color: #059669;
}
.fb-tracker-count {
padding: 4px 6px;
border: 1px solid #ccd0d5;
border-radius: 4px;
font-size: 13px;
background: white;
cursor: pointer;
}
.fb-tracker-add-btn,
.fb-tracker-check-btn {
padding: 4px 12px;
background: #1877f2;
color: white;
border: none;
border-radius: 4px;
font-size: 13px;
font-weight: 600;
cursor: pointer;
transition: background 0.2s;
}
.fb-tracker-add-btn:hover,
.fb-tracker-check-btn:hover {
background: #166fe5;
}
.fb-tracker-check-btn {
background: #059669;
}
.fb-tracker-check-btn:hover {
background: #047857;
}

3608
extension/content.js Normal file

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
extension/icons/icon-16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 209 B

BIN
extension/icons/icon-32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 340 B

BIN
extension/icons/icon-48.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 465 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

4
extension/icons/icon.svg Normal file
View File

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128">
<rect width="128" height="128" rx="20" fill="#1877f2"/>
<text x="64" y="90" font-size="80" text-anchor="middle" fill="white">📋</text>
</svg>

After

Width:  |  Height:  |  Size: 210 B

47
extension/manifest.json Normal file
View File

@@ -0,0 +1,47 @@
{
"manifest_version": 3,
"name": "Facebook Post Tracker",
"version": "1.1.0",
"description": "Track Facebook posts across multiple profiles",
"permissions": [
"storage",
"activeTab",
"tabs",
"contextMenus"
],
"host_permissions": [
"<all_urls>",
"https://www.facebook.com/*",
"https://facebook.com/*",
"http://localhost:3001/*",
"https://fb.srv.medeba-media.de/*"
],
"icons": {
"16": "icons/icon-16.png",
"32": "icons/icon-32.png",
"48": "icons/icon-48.png",
"128": "icons/icon-128.png"
},
"action": {
"default_popup": "popup.html",
"default_icon": {
"16": "icons/icon-16.png",
"32": "icons/icon-32.png",
"48": "icons/icon-48.png"
}
},
"content_scripts": [
{
"matches": [
"https://www.facebook.com/*",
"https://facebook.com/*"
],
"js": ["config.js", "content.js"],
"css": ["content.css"],
"run_at": "document_idle"
}
],
"background": {
"service_worker": "background.js"
}
}

119
extension/popup.html Normal file
View File

@@ -0,0 +1,119 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Facebook Post Tracker</title>
<style>
body {
width: 300px;
padding: 20px;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
margin: 0;
}
h1 {
font-size: 18px;
margin: 0 0 16px 0;
color: #050505;
}
.section {
margin-bottom: 20px;
}
label {
display: block;
font-size: 14px;
font-weight: 600;
margin-bottom: 8px;
color: #050505;
}
select {
width: 100%;
padding: 8px;
border: 1px solid #ccd0d5;
border-radius: 6px;
font-size: 14px;
background: white;
}
.status {
padding: 12px;
background: #f0f2f5;
border-radius: 8px;
font-size: 14px;
color: #65676b;
}
.status.saved {
background: #d1fae5;
color: #065f46;
}
.btn-group {
display: flex;
gap: 8px;
margin-top: 12px;
}
button {
flex: 1;
padding: 10px;
background: #1877f2;
color: white;
border: none;
border-radius: 6px;
font-size: 14px;
font-weight: 600;
cursor: pointer;
transition: background 0.2s;
}
button:hover {
background: #166fe5;
}
button.secondary {
background: #e4e6eb;
color: #050505;
}
button.secondary:hover {
background: #d8dadf;
}
.info {
font-size: 12px;
color: #65676b;
margin-top: 8px;
line-height: 1.4;
}
</style>
</head>
<body>
<h1>📋 Facebook Post Tracker</h1>
<div class="section">
<label for="profileSelect">Aktuelles Profil:</label>
<select id="profileSelect">
<option value="1">Profil 1</option>
<option value="2">Profil 2</option>
<option value="3">Profil 3</option>
<option value="4">Profil 4</option>
<option value="5">Profil 5</option>
</select>
<div class="info">Wähle das Browser-Profil aus, mit dem du aktuell arbeitest.</div>
</div>
<div id="status" class="status"></div>
<div class="btn-group">
<button id="saveBtn">Speichern</button>
<button id="webInterfaceBtn" class="secondary">Web-Interface</button>
</div>
<script src="config.js"></script>
<script src="popup.js"></script>
</body>
</html>

99
extension/popup.js Normal file
View File

@@ -0,0 +1,99 @@
const profileSelect = document.getElementById('profileSelect');
const statusEl = document.getElementById('status');
function apiFetch(url, options = {}) {
const config = {
...options,
credentials: 'include'
};
if (options && options.headers) {
config.headers = { ...options.headers };
}
return fetch(url, config);
}
async function fetchProfileState() {
try {
const response = await apiFetch(`${API_BASE_URL}/api/profile-state`);
if (!response.ok) {
return null;
}
const data = await response.json();
if (data && typeof data.profile_number !== 'undefined') {
const parsed = parseInt(data.profile_number, 10);
if (!Number.isNaN(parsed)) {
return parsed;
}
}
return null;
} catch (error) {
console.warn('Profile state fetch failed:', error);
return null;
}
}
async function updateProfileState(profileNumber) {
try {
const response = await apiFetch(`${API_BASE_URL}/api/profile-state`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ profile_number: profileNumber })
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
} catch (error) {
console.error('Failed to update profile state:', error);
}
}
function updateStatus(message, saved = false) {
statusEl.textContent = message;
statusEl.className = saved ? 'status saved' : 'status';
}
async function initProfileSelect() {
const backendProfile = await fetchProfileState();
if (backendProfile) {
profileSelect.value = String(backendProfile);
chrome.storage.sync.set({ profileNumber: backendProfile });
updateStatus(`Profil ${backendProfile} ausgewählt`);
return;
}
chrome.storage.sync.get(['profileNumber'], (result) => {
const profileNumber = result.profileNumber || 1;
profileSelect.value = String(profileNumber);
updateStatus(`Profil ${profileNumber} ausgewählt (lokal)`);
});
}
initProfileSelect();
function reloadFacebookTabs() {
chrome.tabs.query({ url: ['https://www.facebook.com/*', 'https://facebook.com/*'] }, (tabs) => {
tabs.forEach(tab => {
chrome.tabs.reload(tab.id);
});
});
}
document.getElementById('saveBtn').addEventListener('click', async () => {
const profileNumber = parseInt(profileSelect.value, 10);
chrome.storage.sync.set({ profileNumber }, async () => {
updateStatus(`Profil ${profileNumber} gespeichert!`, true);
await updateProfileState(profileNumber);
reloadFacebookTabs();
});
});
document.getElementById('webInterfaceBtn').addEventListener('click', () => {
chrome.tabs.create({ url: API_BASE_URL });
});