NextChat icon indicating copy to clipboard operation
NextChat copied to clipboard

What happens to app.nextchat.dev? My data are still there, don't have backup

Open gatiewski opened this issue 7 months ago • 6 comments

What happens to app.nextchat.dev? My data are still there, don't have backup. Why hasn't the site announced that it will be shut down soon? How to move localstorage data from app.nextchat.dev to nextchat.club in firefox?

gatiewski avatar Jun 24 '25 15:06 gatiewski

Exporting all data from keyval-store was done in google chrome Run in the console on the old domain:

allow pasting // If necessary, type this first
// Copy all the code below and paste it into the console:
(async () => {
  try {
    const dbName = "keyval-store"; 
    const storeName = "keyval";   
        

    const request = indexedDB.open(dbName);
    const db = await new Promise((resolve, reject) => {
      request.onsuccess = () => resolve(request.result);
      request.onerror = (e) => reject(`Opening error: ${e.target.error}`);
    });
    
    const tx = db.transaction(storeName, "readonly");
    const store = tx.objectStore(storeName);
    const allData = await new Promise((resolve) => {
      const data = {};
      const cursorReq = store.openCursor();
      
      cursorReq.onsuccess = (e) => {
        const cursor = e.target.result;
        if (cursor) {
          data[cursor.key] = cursor.value;
          cursor.continue();
        } else {
          resolve(data);
        }
      };
    });
    
   
    const json = JSON.stringify(allData, null, 2);
    const blob = new Blob([json], {type: "application/json"});
    const url = URL.createObjectURL(blob);
    const a = document.createElement("a");
    a.href = url;
    a.download = `${dbName}-${new Date().toISOString().slice(0,10)}.json`;
    document.body.appendChild(a);
    a.click();
    
    setTimeout(() => {
      document.body.removeChild(a);
      URL.revokeObjectURL(url);
      console.log("✅ Records exported:", Object.keys(allData).length);
    }, 500);
    
    return allData;
  } catch (error) {
    console.error("🚫 Error:", error);
    return null;
  }
})();

  1. import on the new domain To restore data on the new domain:
allow pasting  // If necessary, type this first

(async () => {
  try {
    const dbName = "keyval-store";
    const storeName = "keyval";
    
    const input = document.createElement("input");
    input.type = "file";
    input.accept = ".json";
    
    input.onchange = async (e) => {
      const file = e.target.files[0];
      if (!file) return;
      
      const reader = new FileReader();
      reader.onload = async (event) => {
        try {
          const data = JSON.parse(event.target.result);
          
          const request = indexedDB.open(dbName, 1);
          
          request.onupgradeneeded = (e) => {
            const db = e.target.result;
            if (!db.objectStoreNames.contains(storeName)) {
              db.createObjectStore(storeName);
            }
          };
          
          const db = await new Promise(resolve => {
            request.onsuccess = () => resolve(request.result);
          });
          

          const tx = db.transaction(storeName, "readwrite");
          const store = tx.objectStore(storeName);
          
          for (const [key, value] of Object.entries(data)) {
            store.put(value, key); 
          }
          
          console.log("✅ Imported records:", Object.keys(data).length);
          alert("(Ctrl+R)");
        } catch (error) {
          console.error("🚫 Parsing error:", error);
        }
      };
      reader.readAsText(file);
    };
    
    document.body.appendChild(input);
    input.click();
    setTimeout(() => input.remove(), 10000);
  } catch (error) {
    console.error("🚫 Error:", error);
  }
})();

nikilokitokinoki avatar Jun 24 '25 21:06 nikilokitokinoki

but still the new domain does not work as it should, tried both with vpn and proxy, and even without the key I get this error { "error": { "code": "unsupported_country_region_territory", "message": "Country, region, or territory not supported", "param": null, "type": "request_forbidden" } }

nikilokitokinoki avatar Jun 24 '25 21:06 nikilokitokinoki

我的数据也没有备份,并且在Android设备的浏览器上我无法在新旧域名上打开控制台

Aober1 avatar Jun 25 '25 05:06 Aober1

我的数据也没有备份,并且在Android设备的浏览器上我无法在新旧域名上打开控制台

I think many people were using app.nextchat.dev, and they just shut it down without considering the consequences for users or their data, some had a lot of valuable information stored there.

gatiewski avatar Jun 25 '25 07:06 gatiewski

Yeah, I’ve got some data that I didn’t export either. Unfortunately, they didn’t give us any advance notice before shutting down the site.

myunco avatar Jun 25 '25 13:06 myunco

我的数据也没有备份,并且在Android设备的浏览器上我无法在新旧域名上打开控制台

对于移动端浏览器没有控制台,但能够运行油猴插件的浏览器(edge 移动端浏览器、X 浏览器、via……)可以使用以下脚本。 For mobile browsers that do not have a console but can run the Greasemonkey plugin (edge Mobile, X browser, via...) The following scripts can be used. PC设备同样可以使用。 PC devices can also be used.

我使用 flutter 制作了一个 app.nextchat.dev 的安卓临时代替品 编译后的apk:NextChatTempAPP 其他开源作者的flutter代码(我在其基础上进行了依赖更新、添加logo等,由于修改非常简单,应该不太需要公开源码) ChatGPT-Next-APP

I used flutter to create a temporary Android alternative for app.nextchat.dev The compiled apk: NextChatTempAPP flutter code from other open-source authors (I made dependency updates, added logos, etc. based on it. Since the modifications were very simple, Should not need to open source) ChatGPT-Next-APP

// ==UserScript==
// @name         IndexedDB KeyVal Store导出、导入为JSON
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  将IndexedDB keyval-store存储桶中的全部数据导出下载为JSON,从JSON文件导入数据到IndexedDB keyval-store
// @author       WorldDev
// @match        *://*/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    // 延迟一秒执行
    setTimeout(() => {
        // 创建一个容器来放置按钮
        const buttonContainer = document.createElement('div');
        buttonContainer.style.position = 'fixed';
        buttonContainer.style.top = '100px';
        buttonContainer.style.left = '100px';
        buttonContainer.style.zIndex = '9999';
        buttonContainer.style.display = 'flex';
        buttonContainer.style.flexDirection = 'column';
        buttonContainer.style.gap = '0'; // 设置按钮无间隔
        document.body.appendChild(buttonContainer);

        // 创建导出按钮
        const exportButton = document.createElement('button');
        exportButton.id = 'exportButton';
        exportButton.textContent = '导出数据为JSON';
        buttonContainer.appendChild(exportButton);

        // 创建导入按钮
        const importButton = document.createElement('button');
        importButton.id = 'importButton';
        importButton.textContent = '从JSON导入数据';
        buttonContainer.appendChild(importButton);

        // 创建隐藏的文件输入元素用于导入
        const fileInput = document.createElement('input');
        fileInput.type = 'file';
        fileInput.accept = '.json';
        fileInput.style.display = 'none';
        document.body.appendChild(fileInput);

        // 绑定事件监听器
        document.getElementById('exportButton').addEventListener('click', exportDataToJson);
        document.getElementById('importButton').addEventListener('click', () => fileInput.click());
        fileInput.addEventListener('change', handleFileSelect);

        function openDatabase() {
            return new Promise((resolve, reject) => {
                const request = indexedDB.open('keyval-store');
                request.onerror = event => reject(event.target.error);
                request.onsuccess = event => resolve(event.target.result);
                request.onupgradeneeded = event => {
                    const db = event.target.result;
                    if (!db.objectStoreNames.contains('keyval')) {
                        db.createObjectStore('keyval');
                    }
                };
            });
        }

        async function getAllData(db) {
            const transaction = db.transaction(['keyval'], 'readonly');
            const objectStore = transaction.objectStore('keyval');
            const data = await getAllFromObjectStore(objectStore);
            return data;
        }

        function getAllFromObjectStore(objectStore) {
            return new Promise((resolve, reject) => {
                const allItems = [];
                const request = objectStore.openCursor();
                request.onsuccess = event => {
                    const cursor = event.target.result;
                    if (cursor) {
                        allItems.push({ key: cursor.key, value: cursor.value });
                        cursor.continue();
                    } else {
                        resolve(allItems);
                    }
                };
                request.onerror = event => reject(event.target.error);
            });
        }

        async function exportDataToJson() {
            try {
                const db = await openDatabase();
                const data = await getAllData(db);
                const jsonContent = JSON.stringify(data, null, 2);
                downloadJson(jsonContent, 'indexeddb_data.json');
            } catch (error) {
                console.error('导出数据时出错:', error);
                alert('导出数据失败,请检查控制台错误信息。');
            }
        }

        function downloadJson(content, filename) {
            const blob = new Blob([content], { type: 'application/json' });
            const url = URL.createObjectURL(blob);
            const a = document.createElement('a');
            a.href = url;
            a.download = filename;
            document.body.appendChild(a);
            a.click();
            document.body.removeChild(a);
            URL.revokeObjectURL(url);
        }

        function handleFileSelect(event) {
            const file = event.target.files[0];
            if (file) {
                const reader = new FileReader();
                reader.readAsText(file);
                reader.onload = async e => {
                    try {
                        const jsonData = JSON.parse(e.target.result);
                        const db = await openDatabase();
                        await importDataToDb(db, jsonData);
                        alert('数据导入成功!');
                    } catch (error) {
                        console.error('导入数据时出错:', error);
                        alert('导入数据失败,请检查控制台错误信息。');
                    }
                };
            }
        }

        async function importDataToDb(db, data) {
            const transaction = db.transaction(['keyval'], 'readwrite');
            const objectStore = transaction.objectStore('keyval');

            for (const item of data) {
                objectStore.put(item.value, item.key);
            }

            return new Promise((resolve, reject) => {
                transaction.oncomplete = () => resolve();
                transaction.onerror = event => reject(event.target.error);
            });
        }
    }, 1000); // 延迟1秒执行
})();

gitgitTrue avatar Jun 27 '25 14:06 gitgitTrue