Add Opera cache cleaner extension baseline

This commit is contained in:
ToxicCrzay270
2026-05-15 00:41:37 +02:00
commit fe17014aff
23 changed files with 1541 additions and 0 deletions

View File

@@ -0,0 +1,373 @@
const ALARM_NAME = "cacheCleanerTimer";
const DEFAULT_RANGE_KEY = "last_24h";
const MAX_INTERVAL_MINUTES = 30 * 24 * 60;
const INTERVAL_UNITS = new Set(["minutes", "hours", "days"]);
const RANGE_OPTIONS = {
last_hour: {
label: "Letzte Stunde",
sinceMs: 60 * 60 * 1000
},
last_24h: {
label: "Letzte 24 Stunden",
sinceMs: 24 * 60 * 60 * 1000
},
last_7d: {
label: "Letzte 7 Tage",
sinceMs: 7 * 24 * 60 * 60 * 1000
},
last_4w: {
label: "Letzte 4 Wochen",
sinceMs: 28 * 24 * 60 * 60 * 1000
},
all_time: {
label: "Gesamter Zeitraum",
sinceMs: null
}
};
const DEFAULT_TIMER_CONFIG = {
enabled: false,
repeat: false,
rangeKey: DEFAULT_RANGE_KEY,
intervalValue: 1,
intervalUnit: "hours",
intervalMinutes: 60,
nextRun: null
};
function getSinceByRange(rangeKey) {
const option = RANGE_OPTIONS[rangeKey];
if (!option) {
throw new Error("Unbekannter Zeitraum.");
}
return option.sinceMs === null ? 0 : Date.now() - option.sinceMs;
}
function getStorage(keys) {
return new Promise((resolve, reject) => {
chrome.storage.local.get(keys, (result) => {
const error = chrome.runtime.lastError;
if (error) {
reject(new Error(error.message));
return;
}
resolve(result || {});
});
});
}
function setStorage(values) {
return new Promise((resolve, reject) => {
chrome.storage.local.set(values, () => {
const error = chrome.runtime.lastError;
if (error) {
reject(new Error(error.message));
return;
}
resolve();
});
});
}
function createAlarm(details) {
return new Promise((resolve, reject) => {
try {
const result = chrome.alarms.create(ALARM_NAME, details);
const error = chrome.runtime.lastError;
if (error) {
reject(new Error(error.message));
return;
}
if (result && typeof result.then === "function") {
result.then(resolve).catch(reject);
return;
}
resolve();
} catch (error) {
reject(error);
}
});
}
function clearAlarmOnly() {
return new Promise((resolve, reject) => {
chrome.alarms.clear(ALARM_NAME, () => {
const error = chrome.runtime.lastError;
if (error) {
reject(new Error(error.message));
return;
}
resolve();
});
});
}
function validateTimerConfig(config) {
if (!config || typeof config !== "object") {
throw new Error("Timer-Konfiguration fehlt.");
}
if (!RANGE_OPTIONS[config.rangeKey]) {
throw new Error("Unbekannter Zeitraum.");
}
if (!INTERVAL_UNITS.has(config.intervalUnit)) {
throw new Error("Unbekannte Timer-Einheit.");
}
if (!Number.isFinite(config.intervalValue)) {
throw new Error("Das Intervall muss eine Zahl sein.");
}
if (config.intervalValue <= 0) {
throw new Error("Das Intervall muss groesser als 0 sein.");
}
if (!Number.isFinite(config.intervalMinutes)) {
throw new Error("Das Intervall konnte nicht berechnet werden.");
}
if (config.intervalMinutes < 1) {
throw new Error("Das Mindestintervall betraegt 1 Minute.");
}
if (config.intervalMinutes > MAX_INTERVAL_MINUTES) {
throw new Error("Das Maximalintervall betraegt 30 Tage.");
}
}
async function clearCacheByRange(rangeKey) {
const resolvedRangeKey = RANGE_OPTIONS[rangeKey] ? rangeKey : DEFAULT_RANGE_KEY;
const since = getSinceByRange(resolvedRangeKey);
// Only the cache flag is set so cookies, history, downloads, and passwords stay untouched.
await new Promise((resolve, reject) => {
chrome.browsingData.remove(
{ since },
{ cache: true },
() => {
const error = chrome.runtime.lastError;
if (error) {
reject(new Error(error.message));
return;
}
resolve();
}
);
});
const lastRun = Date.now();
await setStorage({
selectedRange: resolvedRangeKey,
lastRun
});
return {
lastRun,
rangeKey: resolvedRangeKey
};
}
async function setupAlarm(config) {
validateTimerConfig(config);
const intervalMinutes = config.intervalMinutes;
const nextRun = Date.now() + intervalMinutes * 60 * 1000;
const timerConfig = {
enabled: true,
repeat: Boolean(config.repeat),
rangeKey: config.rangeKey,
intervalValue: config.intervalValue,
intervalUnit: config.intervalUnit,
intervalMinutes,
nextRun
};
const alarmDetails = timerConfig.repeat
? {
delayInMinutes: intervalMinutes,
periodInMinutes: intervalMinutes
}
: {
delayInMinutes: intervalMinutes
};
// Replacing the alarm avoids duplicate timers after repeated saves.
await clearAlarmOnly();
await createAlarm(alarmDetails);
await setStorage({
selectedRange: timerConfig.rangeKey,
timerConfig
});
return timerConfig;
}
async function clearAlarm() {
const { timerConfig } = await getStorage(["timerConfig"]);
const disabledConfig = {
...DEFAULT_TIMER_CONFIG,
...(timerConfig || {}),
enabled: false,
nextRun: null
};
await clearAlarmOnly();
await setStorage({ timerConfig: disabledConfig });
return disabledConfig;
}
async function restoreAlarmOnStartup() {
const { timerConfig } = await getStorage(["timerConfig"]);
if (!timerConfig || timerConfig.enabled !== true) {
return;
}
validateTimerConfig(timerConfig);
const now = Date.now();
let nextRun = Number(timerConfig.nextRun);
let delayInMinutes;
// Extension restarts can miss an alarm, so restart from a future run instead of firing immediately.
if (Number.isFinite(nextRun) && nextRun > now) {
delayInMinutes = Math.max(1, Math.ceil((nextRun - now) / 60000));
nextRun = now + delayInMinutes * 60000;
} else {
delayInMinutes = timerConfig.intervalMinutes;
nextRun = now + timerConfig.intervalMinutes * 60000;
}
const restoredConfig = {
...timerConfig,
nextRun
};
const alarmDetails = restoredConfig.repeat
? {
delayInMinutes,
periodInMinutes: restoredConfig.intervalMinutes
}
: {
delayInMinutes
};
await createAlarm(alarmDetails);
await setStorage({ timerConfig: restoredConfig });
}
async function handleTimerAlarm(alarm) {
if (!alarm || alarm.name !== ALARM_NAME) {
return;
}
const { timerConfig } = await getStorage(["timerConfig"]);
if (!timerConfig || timerConfig.enabled !== true) {
return;
}
const rangeKey = RANGE_OPTIONS[timerConfig.rangeKey] ? timerConfig.rangeKey : DEFAULT_RANGE_KEY;
await clearCacheByRange(rangeKey);
if (timerConfig.repeat) {
const nextRun = Date.now() + timerConfig.intervalMinutes * 60 * 1000;
await setStorage({
timerConfig: {
...timerConfig,
rangeKey,
nextRun
}
});
return;
}
await setStorage({
timerConfig: {
...timerConfig,
rangeKey,
enabled: false,
nextRun: null
}
});
}
function sendError(sendResponse, error) {
sendResponse({
ok: false,
error: error && error.message ? error.message : "Unbekannter Fehler."
});
}
chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
(async () => {
if (!message || typeof message.type !== "string") {
throw new Error("Unbekannte Nachricht.");
}
if (message.type === "CLEAR_CACHE") {
const result = await clearCacheByRange(message.rangeKey);
sendResponse({
ok: true,
...result
});
return;
}
if (message.type === "SET_TIMER") {
const timerConfig = await setupAlarm(message.config);
sendResponse({
ok: true,
timerConfig
});
return;
}
if (message.type === "CLEAR_TIMER") {
const timerConfig = await clearAlarm();
sendResponse({
ok: true,
timerConfig
});
return;
}
throw new Error("Unbekannter Nachrichtentyp.");
})().catch((error) => {
sendError(sendResponse, error);
});
return true;
});
chrome.alarms.onAlarm.addListener((alarm) => {
handleTimerAlarm(alarm).catch((error) => {
console.error("Timer konnte den Cache nicht loeschen.", error);
});
});
chrome.runtime.onInstalled.addListener(() => {
restoreAlarmOnStartup().catch((error) => {
console.error("Timer konnte nicht wiederhergestellt werden.", error);
});
});
chrome.runtime.onStartup.addListener(() => {
restoreAlarmOnStartup().catch((error) => {
console.error("Timer konnte nicht wiederhergestellt werden.", error);
});
});