Skip to content

Instantly share code, notes, and snippets.

@casparjones
Last active September 26, 2025 14:19
Show Gist options
  • Select an option

  • Save casparjones/c65189c45a9295803ded9b57e9b528ef to your computer and use it in GitHub Desktop.

Select an option

Save casparjones/c65189c45a9295803ded9b57e9b528ef to your computer and use it in GitHub Desktop.
Lebenslauf PouchDB Manager
// ==UserScript==
// @name Lebenslauf PouchDB Manager
// @namespace https://lebenslauf.com/
// @version 0.4.0
// @description Save & Restore vita-document and preferences with PouchDB + CouchDB Sync + User Selector Popup
// @author Frank
// @match https://lebenslauf.com/neu
// @icon https://www.google.com/s2/favicons?sz=64&domain=lebenslauf.com
// @grant none
// @require https://cdn.jsdelivr.net/npm/pouchdb@8.0.1/dist/pouchdb.min.js
// @updateURL https://gist.githubusercontent.com/casparjones/c65189c45a9295803ded9b57e9b528ef/raw/lebenslauf_pouchDBManager.user.js
// @downloadURL https://gist.githubusercontent.com/casparjones/c65189c45a9295803ded9b57e9b528ef/raw/lebenslauf_pouchDBManager.user.js
// ==/UserScript==
(function() {
'use strict';
// --- Init PouchDB ---
let db = new PouchDB('lebenslauf_vita');
// --- UI Buttons ---
function addButton(text, onclick, color = "lightblue") {
let btn = document.createElement("button");
btn.innerText = text;
btn.style.position = "fixed";
btn.style.top = `${50 + document.querySelectorAll(".vita-btn").length * 40}px`;
btn.style.right = "20px";
btn.style.zIndex = 9999;
btn.style.background = color;
btn.style.padding = "8px";
btn.style.border = "1px solid #333";
btn.classList.add("vita-btn");
btn.onclick = onclick;
document.body.appendChild(btn);
}
// --- Popup ---
function showPopup(allDocs) {
let overlay = document.createElement("div");
overlay.style.position = "fixed";
overlay.style.top = "0";
overlay.style.left = "0";
overlay.style.width = "100%";
overlay.style.height = "100%";
overlay.style.background = "rgba(0,0,0,0.5)";
overlay.style.zIndex = "10000";
overlay.id = "vita-overlay";
let popup = document.createElement("div");
popup.style.position = "fixed";
popup.style.top = "50%";
popup.style.left = "50%";
popup.style.transform = "translate(-50%, -50%)";
popup.style.background = "#fff";
popup.style.padding = "20px";
popup.style.border = "2px solid #333";
popup.style.borderRadius = "8px";
popup.style.minWidth = "300px";
popup.style.textAlign = "center";
let title = document.createElement("h3");
title.innerText = "Person wiederherstellen";
popup.appendChild(title);
let select = document.createElement("select");
select.style.width = "100%";
select.style.margin = "10px 0";
allDocs.rows.forEach(row => {
let opt = document.createElement("option");
opt.value = row.doc._id;
opt.textContent = `${row.doc._id} (${row.doc.ts || "kein Datum"})`;
select.appendChild(opt);
});
popup.appendChild(select);
let btnSync = document.createElement("button");
btnSync.innerText = "Person wiederherstellen";
btnSync.style.marginRight = "10px";
btnSync.onclick = async () => {
let id = select.value;
let person = await db.get(id);
localStorage.setItem('vita-document', JSON.stringify(person.document));
localStorage.setItem('vita-cover-letter', JSON.stringify(person.letter));
localStorage.setItem('vita-preferences', JSON.stringify(person.preferences));
alert(`Person ${person._id} wiederhergestellt!`);
document.body.removeChild(overlay);
location.reload();
};
let btnClose = document.createElement("button");
btnClose.innerText = "Abbrechen";
btnClose.onclick = () => {
document.body.removeChild(overlay);
};
popup.appendChild(btnSync);
popup.appendChild(btnClose);
overlay.appendChild(popup);
document.body.appendChild(overlay);
}
// --- Save Config ---
addButton("Save Config", async () => {
let data = JSON.parse(localStorage.getItem('vita-document') || "{}");
let letter = JSON.parse(localStorage.getItem('vita-cover-letter') || "{}");
let preferences = JSON.parse(localStorage.getItem('vita-preferences') || "{}");
if (!data?.cv?.personalInformation?.firstName) {
alert("Kein Vorname gefunden!");
return;
}
let doc = {
_id: data.cv.personalInformation.firstName,
document: data,
preferences: preferences,
letter: letter,
ts: new Date().toISOString()
};
try {
await db.put(doc);
alert("Config gespeichert!");
} catch (e) {
if (e.name === 'conflict') {
let existing = await db.get(doc._id);
doc._rev = existing._rev;
await db.put(doc);
alert("Config aktualisiert!");
} else {
console.error(e);
alert("Fehler beim Speichern!");
}
}
});
// --- Restore Personen (mit Popup) ---
addButton("Restore Personen", async () => {
try {
let allDocs = await db.allDocs({ include_docs: true });
if (allDocs.rows.length === 0) {
alert("Keine gespeicherten Personen gefunden!");
return;
}
showPopup(allDocs);
} catch (e) {
console.error(e);
alert("Fehler beim Wiederherstellen!");
}
});
// --- Set CouchDB URL ---
addButton("Set CouchDB URL", async () => {
let couchUrl = prompt("CouchDB-URL eingeben (z.B. http://user:pass@localhost:5984/lebenslauf_vita):", localStorage.getItem("couchdb_url") || "");
if (couchUrl) {
localStorage.setItem("couchdb_url", couchUrl);
alert("CouchDB-URL gespeichert!");
// Sync starten
let remoteDB = new PouchDB(couchUrl);
db.sync(remoteDB, { live: true, retry: true })
.on('change', info => console.log("Sync change:", info))
.on('error', err => console.error("Sync error:", err));
}
});
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment