save
This commit is contained in:
@@ -1,30 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<title>TypeWords 数据迁移</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: sans-serif;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
button {
|
||||
padding: 10px 20px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
pre {
|
||||
background: #f7f7f7;
|
||||
padding: 10px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
</style>
|
||||
<meta charset="UTF-8">
|
||||
<title>TypeWords 数据迁移(旧域名)</title>
|
||||
</head>
|
||||
<body>
|
||||
<h2>TypeWords 数据迁移(旧域名)</h2>
|
||||
<p>等待新域名发送迁移指令 ...</p>
|
||||
<h2>等待新域名发送迁移指令...</h2>
|
||||
<pre id="log"></pre>
|
||||
|
||||
<script>
|
||||
@@ -33,154 +14,66 @@
|
||||
document.getElementById('log').textContent += msg + "\n";
|
||||
}
|
||||
|
||||
let dbReadyPromise = null;
|
||||
let name = 'keyval-store';
|
||||
let keys = [
|
||||
'type-words-app-version',
|
||||
'typing-word-dict',
|
||||
'typing-word-setting',
|
||||
'typing-word-files'
|
||||
]
|
||||
|
||||
function openDB(dbName, keys) {
|
||||
if (dbReadyPromise) return dbReadyPromise;
|
||||
|
||||
dbReadyPromise = new Promise((resolve, reject) => {
|
||||
const request = indexedDB.open(dbName, 1);
|
||||
|
||||
request.onupgradeneeded = (event) => {
|
||||
const db = event.target.result;
|
||||
|
||||
keys.forEach((name) => {
|
||||
if (!db.objectStoreNames.contains(name)) {
|
||||
db.createObjectStore(name);
|
||||
}
|
||||
});
|
||||
// 1️⃣ 先动态加载 idb-keyval
|
||||
function loadIDBKeyval() {
|
||||
return new Promise((resolve) => {
|
||||
let script = document.createElement("script");
|
||||
script.src = 'https://cdn.jsdelivr.net/npm/idb-keyval@6.2.2/dist/umd.js';
|
||||
script.onload = function () {
|
||||
log("idb-keyval 加载完成");
|
||||
resolve(window.idbKeyval);
|
||||
};
|
||||
|
||||
request.onsuccess = () => resolve(request.result);
|
||||
request.onerror = () => reject(request.error);
|
||||
});
|
||||
|
||||
return dbReadyPromise;
|
||||
}
|
||||
|
||||
openDB(name, keys)
|
||||
|
||||
// --- localStorage 读取 ---
|
||||
function readLocalStorageKeys(keys) {
|
||||
const out = {};
|
||||
keys.forEach(k => {
|
||||
out[k] = localStorage.getItem(k);
|
||||
});
|
||||
return out;
|
||||
}
|
||||
|
||||
// --- IndexedDB(兼容 idb-keyval)读取 ---
|
||||
function readIndexedDBCompatible(dbName, keys) {
|
||||
return new Promise(async (resolve) => {
|
||||
const db = await openDB(dbName, keys);
|
||||
const stores = Array.from(db.objectStoreNames);
|
||||
|
||||
if (stores.length === 0) {
|
||||
db.close();
|
||||
tryOpenKeyvalDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
if (stores.length === 1 && stores[0] === 'keyval') {
|
||||
readFromKeyvalDB(db).then(result => {
|
||||
db.close();
|
||||
resolve(result);
|
||||
});
|
||||
} else {
|
||||
readFromMultipleStores(db, stores).then(result => {
|
||||
db.close();
|
||||
resolve(result);
|
||||
});
|
||||
}
|
||||
|
||||
function tryOpenKeyvalDefault() {
|
||||
const req = indexedDB.open('keyval');
|
||||
req.onerror = () => resolve({indexedDB: {}, reason: 'no-db'});
|
||||
req.onsuccess = function () {
|
||||
const db2 = req.result;
|
||||
readFromKeyvalDB(db2).then(result => {
|
||||
db2.close();
|
||||
resolve(result);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
function readFromKeyvalDB(db) {
|
||||
return new Promise((res) => {
|
||||
const tx = db.transaction('keyval', 'readonly');
|
||||
const store = tx.objectStore('keyval');
|
||||
|
||||
const out = {};
|
||||
let finished = 0;
|
||||
keys.forEach(k => {
|
||||
const r = store.get(k);
|
||||
r.onsuccess = () => {
|
||||
out[k] = r.result ?? null;
|
||||
if (++finished === keys.length) res({indexedDB: out});
|
||||
};
|
||||
r.onerror = () => {
|
||||
out[k] = null;
|
||||
if (++finished === keys.length) res({indexedDB: out});
|
||||
};
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function readFromMultipleStores(db, stores) {
|
||||
return new Promise(res => {
|
||||
const result = {};
|
||||
let count = 0;
|
||||
stores.forEach(storeName => {
|
||||
try {
|
||||
const tx = db.transaction(storeName, 'readonly');
|
||||
const store = tx.objectStore(storeName);
|
||||
const req = store.getAll();
|
||||
|
||||
req.onsuccess = () => {
|
||||
result[storeName] = req.result;
|
||||
if (++count === stores.length) res({indexedDB: result});
|
||||
};
|
||||
req.onerror = () => {
|
||||
result[storeName] = [];
|
||||
if (++count === stores.length) res({indexedDB: result});
|
||||
};
|
||||
} catch {
|
||||
result[storeName] = [];
|
||||
if (++count === stores.length) res({indexedDB: result});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
document.head.appendChild(script);
|
||||
});
|
||||
}
|
||||
|
||||
async function readAllStorageForMigration() {
|
||||
const local = readLocalStorageKeys(['PracticeSaveWord', 'PracticeSaveArticle']);
|
||||
const indexed = await readIndexedDBCompatible(name, keys);
|
||||
return {localStorage: local, indexedDB: indexed.indexedDB ?? {}};
|
||||
}
|
||||
// 2️⃣ 读取 IndexedDB
|
||||
async function readAllStorageForMigration(db) {
|
||||
// localStorage 数据
|
||||
const localStorageData = {
|
||||
PracticeSaveWord: localStorage.getItem('PracticeSaveWord'),
|
||||
PracticeSaveArticle: localStorage.getItem('PracticeSaveArticle')
|
||||
};
|
||||
|
||||
// =====================
|
||||
// 🔥 自动监听迁移指令
|
||||
// =====================
|
||||
window.addEventListener('message', async (event) => {
|
||||
if (event.data?.type === 'REQUEST_MIGRATION_DATA') {
|
||||
log('收到迁移指令,开始读取本地数据...');
|
||||
const data = await readAllStorageForMigration();
|
||||
log('读取完成,开始发送数据到新域名');
|
||||
// IndexedDB 数据,key 对应你的老项目
|
||||
const keys = [
|
||||
'type-words-app-version',
|
||||
'typing-word-dict',
|
||||
'typing-word-setting',
|
||||
'typing-word-files'
|
||||
];
|
||||
|
||||
event.source.postMessage({type: 'MIGRATE_DATA', payload: data}, event.origin);
|
||||
|
||||
log('已发送迁移数据');
|
||||
const indexedDBData = {};
|
||||
for (let key of keys) {
|
||||
indexedDBData[key] = await db.get(key);
|
||||
}
|
||||
|
||||
return {
|
||||
localStorage: localStorageData,
|
||||
indexedDB: indexedDBData
|
||||
};
|
||||
}
|
||||
|
||||
// 3️⃣ 接收新域名指令
|
||||
window.addEventListener('message', async (event) => {
|
||||
if (event.data?.type !== 'REQUEST_MIGRATION_DATA') return;
|
||||
|
||||
// 安全校验 origin,可选
|
||||
// if (event.origin !== 'https://typewords.cc') return;
|
||||
|
||||
log("收到迁移指令,开始读取数据...");
|
||||
|
||||
const db = await loadIDBKeyval(); // 确保 idb-keyval 已经加载
|
||||
const data = await readAllStorageForMigration(db);
|
||||
|
||||
log("读取完成,发送数据给新域名");
|
||||
event.source.postMessage({
|
||||
type: 'MIGRATION_RESULT',
|
||||
payload: data
|
||||
}, event.origin);
|
||||
|
||||
log("已发送迁移数据");
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
@@ -326,122 +326,97 @@
|
||||
</script>
|
||||
|
||||
<script>
|
||||
// 旧域名
|
||||
const OLD_ORIGIN = "https://2study.top";
|
||||
// 旧域名地址
|
||||
const OLD_ORIGIN = 'https://2study.top';
|
||||
|
||||
// 需要迁移的 IndexedDB store 名称
|
||||
const IDB_STORE_NAMES = [
|
||||
"type-words-app-version",
|
||||
"typing-word-dict",
|
||||
"typing-word-setting",
|
||||
"typing-word-files",
|
||||
// 需要迁移的 IndexedDB key
|
||||
const IDB_KEYS = [
|
||||
'type-words-app-version',
|
||||
'typing-word-dict',
|
||||
'typing-word-setting',
|
||||
'typing-word-files'
|
||||
];
|
||||
|
||||
// 需要迁移的 localStorage key
|
||||
const LS_KEYS = [
|
||||
"PracticeSaveWord",
|
||||
"PracticeSaveArticle"
|
||||
'PracticeSaveWord',
|
||||
'PracticeSaveArticle'
|
||||
];
|
||||
let key = 'keyval-store'
|
||||
|
||||
// 打开或创建新的 IndexedDB
|
||||
function openIndexedDB() {
|
||||
return new Promise((resolve, reject) => {
|
||||
const request = indexedDB.open(key, 1);
|
||||
// 1️⃣ 动态加载 idb-keyval
|
||||
function loadIDBKeyval() {
|
||||
return new Promise((resolve) => {
|
||||
if (window.idbKeyval) return resolve(window.idbKeyval);
|
||||
|
||||
request.onupgradeneeded = (event) => {
|
||||
const db = event.target.result;
|
||||
IDB_STORE_NAMES.forEach((storeName) => {
|
||||
if (!db.objectStoreNames.contains(storeName)) {
|
||||
db.createObjectStore(storeName);
|
||||
const script = document.createElement('script');
|
||||
script.src = 'https://cdn.jsdelivr.net/npm/idb-keyval@6.2.2/dist/umd.js';
|
||||
script.onload = () => resolve(window.idbKeyval);
|
||||
document.head.appendChild(script);
|
||||
});
|
||||
}
|
||||
|
||||
// 2️⃣ 创建 iframe 并发送迁移请求
|
||||
async function migrateFromOldSite() {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
const db = await loadIDBKeyval();
|
||||
|
||||
const iframe = document.createElement('iframe');
|
||||
iframe.style.display = 'none';
|
||||
iframe.src = `${OLD_ORIGIN}/migrate.html`;
|
||||
|
||||
// 安全接收旧域名返回的数据
|
||||
function onMessage(event) {
|
||||
if (event.data?.type !== 'MIGRATION_RESULT') return;
|
||||
|
||||
const payload = event.data.payload;
|
||||
|
||||
// 写入 localStorage
|
||||
LS_KEYS.forEach(key => {
|
||||
if (payload.localStorage[key] !== undefined) {
|
||||
localStorage.setItem(key, payload.localStorage[key]);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
request.onsuccess = () => resolve(request.result);
|
||||
request.onerror = () => reject(request.error);
|
||||
});
|
||||
}
|
||||
|
||||
// 写入数据到 IndexedDB
|
||||
async function writeToIndexedDB(data) {
|
||||
const db = await openIndexedDB();
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const tx = db.transaction(IDB_STORE_NAMES, "readwrite");
|
||||
|
||||
for (const storeName of IDB_STORE_NAMES) {
|
||||
const store = tx.objectStore(storeName);
|
||||
const value = data.indexedDB[storeName];
|
||||
if (value !== undefined) {
|
||||
store.put(value, "value");
|
||||
}
|
||||
}
|
||||
|
||||
tx.oncomplete = () => resolve();
|
||||
tx.onerror = () => reject(tx.error);
|
||||
});
|
||||
}
|
||||
|
||||
// 写入 LocalStorage
|
||||
function writeToLocalStorage(data) {
|
||||
for (const key of LS_KEYS) {
|
||||
if (data.localStorage[key] !== undefined) {
|
||||
localStorage.setItem(key, data.localStorage[key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---- 主逻辑 ----
|
||||
|
||||
async function migrateFromOldSite() {
|
||||
return new Promise((resolve) => {
|
||||
// 创建隐藏 iframe
|
||||
const iframe = document.createElement("iframe");
|
||||
iframe.style.display = "none";
|
||||
iframe.src = `${OLD_ORIGIN}/migrate.html`;
|
||||
document.body.appendChild(iframe);
|
||||
|
||||
// 接收旧域名的数据
|
||||
window.addEventListener("message", async (event) => {
|
||||
if (event.data.type !== "MIGRATE_RESULT") return;
|
||||
const payload = event.data.payload;
|
||||
// 写入 LocalStorage
|
||||
writeToLocalStorage(payload);
|
||||
// 写入 IndexedDB
|
||||
await writeToIndexedDB(payload);
|
||||
// 清理 iframe
|
||||
iframe.remove();
|
||||
(async () => {
|
||||
for (let key of IDB_KEYS) {
|
||||
if (payload.indexedDB[key] !== undefined) {
|
||||
await db.set(key, payload.indexedDB[key]);
|
||||
}
|
||||
}
|
||||
|
||||
resolve(true);
|
||||
});
|
||||
// 清理
|
||||
window.removeEventListener('message', onMessage);
|
||||
iframe.remove();
|
||||
resolve(true);
|
||||
})();
|
||||
}
|
||||
|
||||
iframe.onload = function () {
|
||||
setTimeout(()=>{
|
||||
iframe.contentWindow.postMessage({type: "REQUEST_MIGRATION_DATA"}, OLD_ORIGIN);
|
||||
},3000)
|
||||
window.addEventListener('message', onMessage);
|
||||
|
||||
// 等 iframe 加载完成再发请求
|
||||
iframe.onload = () => {
|
||||
iframe.contentWindow.postMessage({type: 'REQUEST_MIGRATION_DATA'}, OLD_ORIGIN);
|
||||
};
|
||||
|
||||
document.body.appendChild(iframe);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
if (location.href === 'https://typewords.cc/') {
|
||||
window.addEventListener("DOMContentLoaded", () => {
|
||||
// 3️⃣ 自动执行迁移(只执行一次)
|
||||
(async () => {
|
||||
// 避免重复迁移
|
||||
if (localStorage.getItem("__migrated_from_2study_top__")) {
|
||||
console.log("数据已迁移过");
|
||||
return;
|
||||
}
|
||||
if (localStorage.getItem('__migrated_from_2study_top__')) return;
|
||||
|
||||
console.log("开始从旧域名迁移数据…");
|
||||
console.log('开始从旧域名迁移数据…');
|
||||
|
||||
try {
|
||||
await migrateFromOldSite();
|
||||
localStorage.setItem("__migrated_from_2study_top__", "1");
|
||||
console.log("迁移完成");
|
||||
localStorage.setItem('__migrated_from_2study_top__', '1');
|
||||
console.log('迁移完成');
|
||||
} catch (e) {
|
||||
console.error("迁移失败", e);
|
||||
console.error('迁移失败', e);
|
||||
}
|
||||
})();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user