374 lines
8.4 KiB
JavaScript
374 lines
8.4 KiB
JavaScript
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);
|
|
});
|
|
});
|