Files
bitvid/js/storage/s3-settings.js
T

156 lines
4.2 KiB
JavaScript

import { userLogger } from "../utils/logger.js";
import { normalizeS3PublicBaseUrl } from "./s3-url.js";
const DB_NAME = "bitvidSettings";
const DB_VERSION = 1;
const STORE_NAME = "kv";
const SETTINGS_KEY = "s3Settings";
const LOCALSTORAGE_FALLBACK_KEY = "bitvid:s3Settings";
function normalizeSettings(raw) {
const base = raw && typeof raw === "object" ? raw : {};
const forcePathStyle = Boolean(base.forcePathStyle);
const createBucketIfMissing = Boolean(base.createBucketIfMissing);
const endpoint = String(base.endpoint || "").trim();
const region = String(base.region || "").trim() || "auto";
const bucket = String(base.bucket || "").trim();
const accessKeyId = String(base.accessKeyId || "").trim();
const secretAccessKey = String(base.secretAccessKey || "").trim();
const publicBaseUrl = normalizeS3PublicBaseUrl(base.publicBaseUrl || "");
return {
endpoint,
region,
bucket,
accessKeyId,
secretAccessKey,
forcePathStyle,
publicBaseUrl,
createBucketIfMissing,
};
}
function isIndexedDbAvailable() {
try {
return typeof indexedDB !== "undefined";
} catch (err) {
return false;
}
}
function openSettingsDb() {
return new Promise((resolve, reject) => {
if (!isIndexedDbAvailable()) {
resolve(null);
return;
}
const request = indexedDB.open(DB_NAME, DB_VERSION);
request.onupgradeneeded = () => {
const db = request.result;
if (!db.objectStoreNames.contains(STORE_NAME)) {
db.createObjectStore(STORE_NAME);
}
};
request.onsuccess = () => {
resolve(request.result);
};
request.onerror = () => {
reject(request.error || new Error("Failed to open settings DB"));
};
});
}
export async function loadS3Settings() {
try {
const db = await openSettingsDb();
if (db) {
return await new Promise((resolve, reject) => {
const tx = db.transaction(STORE_NAME, "readonly");
const store = tx.objectStore(STORE_NAME);
const req = store.get(SETTINGS_KEY);
req.onsuccess = () => {
resolve(normalizeSettings(req.result));
};
req.onerror = () => {
reject(req.error || new Error("Failed to load S3 settings"));
};
});
}
} catch (err) {
userLogger.warn("Failed to open IndexedDB for S3 settings, falling back:", err);
}
try {
if (typeof localStorage !== "undefined") {
const raw = localStorage.getItem(LOCALSTORAGE_FALLBACK_KEY);
if (raw) {
return normalizeSettings(JSON.parse(raw));
}
}
} catch (err) {
userLogger.warn("Failed to read fallback S3 settings:", err);
}
return normalizeSettings(null);
}
export async function saveS3Settings(settings) {
const normalized = normalizeSettings(settings);
let db = null;
try {
db = await openSettingsDb();
} catch (err) {
userLogger.warn("Unable to open IndexedDB for S3 settings, continuing with fallback:", err);
}
if (db) {
await new Promise((resolve, reject) => {
const tx = db.transaction(STORE_NAME, "readwrite");
const store = tx.objectStore(STORE_NAME);
const req = store.put(normalized, SETTINGS_KEY);
req.onsuccess = () => resolve();
req.onerror = () =>
reject(req.error || new Error("Failed to save S3 settings"));
});
} else if (typeof localStorage !== "undefined") {
localStorage.setItem(LOCALSTORAGE_FALLBACK_KEY, JSON.stringify(normalized));
}
return normalized;
}
export async function clearS3Settings() {
let cleared = false;
try {
const db = await openSettingsDb();
if (db) {
await new Promise((resolve, reject) => {
const tx = db.transaction(STORE_NAME, "readwrite");
const store = tx.objectStore(STORE_NAME);
const req = store.delete(SETTINGS_KEY);
req.onsuccess = () => resolve();
req.onerror = () => reject(req.error || new Error("Failed to clear"));
});
cleared = true;
}
} catch (err) {
userLogger.warn("Failed to clear IndexedDB S3 settings:", err);
}
try {
if (typeof localStorage !== "undefined") {
localStorage.removeItem(LOCALSTORAGE_FALLBACK_KEY);
cleared = true;
}
} catch (err) {
userLogger.warn("Failed to clear fallback S3 settings:", err);
}
return cleared;
}