7.8 KiB
7.8 KiB
Workdizhi 数据持久化修复方案
问题分析
当前问题:
- 数据存储在浏览器的
localStorage中 docker-compose.yml中没有数据卷挂载- 在新电脑打开时,浏览器的 localStorage 是空的,导致数据丢失
根本原因:
localStorage是浏览器本地存储,不是服务器端存储- 容器中没有数据持久化配置
- 数据没有保存到服务器文件系统
解决方案
方案 1: 添加后端 API + 文件存储(推荐)
这是最彻底的解决方案,需要修改代码添加后端 API。
步骤 1: 创建简单的 Node.js 后端
创建 server.js:
const express = require('express');
const fs = require('fs').promises;
const path = require('path');
const cors = require('cors');
const app = express();
const DATA_FILE = path.join(__dirname, 'data', 'urls.json');
app.use(cors());
app.use(express.json());
app.use(express.static('public'));
// 确保数据目录存在
async function ensureDataDir() {
const dataDir = path.dirname(DATA_FILE);
await fs.mkdir(dataDir, { recursive: true });
}
// 读取数据
app.get('/api/urls', async (req, res) => {
try {
await ensureDataDir();
const data = await fs.readFile(DATA_FILE, 'utf8');
res.json(JSON.parse(data));
} catch (error) {
if (error.code === 'ENOENT') {
res.json({ urls: [], categories: [] });
} else {
res.status(500).json({ error: error.message });
}
}
});
// 保存数据
app.post('/api/urls', async (req, res) => {
try {
await ensureDataDir();
await fs.writeFile(DATA_FILE, JSON.stringify(req.body, null, 2));
res.json({ success: true });
} catch (error) {
res.status(500).json({ error: error.message });
}
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
步骤 2: 修改前端代码
修改 script.js 中的数据加载和保存方法:
// 修改 loadData 方法
async loadData() {
try {
const response = await fetch('/api/urls');
const data = await response.json();
this.urls = data.urls || this.getDefaultUrls();
this.categories = data.categories || this.getDefaultCategories();
} catch (error) {
console.error('从服务器加载数据失败,使用默认数据:', error);
// 降级到 localStorage
const savedUrls = localStorage.getItem('urlManager_urls');
const savedCategories = localStorage.getItem('urlManager_categories');
if (savedUrls) {
this.urls = JSON.parse(savedUrls);
} else {
this.urls = this.getDefaultUrls();
}
if (savedCategories) {
this.categories = JSON.parse(savedCategories);
} else {
this.categories = this.getDefaultCategories();
}
}
}
// 修改 saveData 方法
async saveData() {
try {
// 先保存到服务器
const response = await fetch('/api/urls', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
urls: this.urls,
categories: this.categories
})
});
if (!response.ok) throw new Error('服务器保存失败');
} catch (error) {
console.error('保存到服务器失败,降级到 localStorage:', error);
// 降级到 localStorage
try {
localStorage.setItem('urlManager_urls', JSON.stringify(this.urls));
localStorage.setItem('urlManager_categories', JSON.stringify(this.categories));
} catch (e) {
console.error('保存到 localStorage 也失败:', e);
}
}
}
步骤 3: 更新 Dockerfile
FROM node:18-alpine
WORKDIR /app
# 安装依赖
COPY package.json .
RUN npm install
# 复制文件
COPY server.js .
COPY index.html public/
COPY script.js public/
COPY style.css public/
# 创建数据目录
RUN mkdir -p data
EXPOSE 3000
CMD ["node", "server.js"]
步骤 4: 更新 docker-compose.yml
version: '3.8'
services:
web:
build: .
container_name: workdizhi-web
ports:
- "3006:3000"
restart: unless-stopped
volumes:
# 挂载数据目录,持久化存储
- ./data:/app/data
networks:
- workdizhi-network
networks:
workdizhi-network:
driver: bridge
方案 2: 使用 Nginx + 文件存储(简单方案)
如果不想添加后端,可以使用 Nginx 的静态文件服务 + JavaScript 文件操作。
步骤 1: 修改 script.js 使用 fetch API 保存到文件
// 添加文件保存方法
async saveToFile() {
try {
const data = {
urls: this.urls,
categories: this.categories,
timestamp: new Date().toISOString()
};
// 使用 Blob 和下载方式保存(需要用户手动操作)
const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'workdizhi_backup.json';
a.click();
URL.revokeObjectURL(url);
} catch (error) {
console.error('保存文件失败:', error);
}
}
// 添加文件加载方法
async loadFromFile(file) {
try {
const text = await file.text();
const data = JSON.parse(text);
this.urls = data.urls || this.getDefaultUrls();
this.categories = data.categories || this.getDefaultCategories();
this.saveData();
this.render();
} catch (error) {
console.error('加载文件失败:', error);
alert('文件格式错误或损坏');
}
}
方案 3: 导出/导入功能(临时方案)
添加导出和导入功能,让用户可以手动备份和恢复数据。
修改 script.js 添加导出/导入功能
// 导出数据
exportData() {
const data = {
urls: this.urls,
categories: this.categories,
exportDate: new Date().toISOString()
};
const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `workdizhi_backup_${new Date().toISOString().split('T')[0]}.json`;
a.click();
URL.revokeObjectURL(url);
}
// 导入数据
importData(file) {
const reader = new FileReader();
reader.onload = (e) => {
try {
const data = JSON.parse(e.target.result);
this.urls = data.urls || [];
this.categories = data.categories || [];
this.saveData();
this.render();
alert('数据导入成功!');
} catch (error) {
alert('文件格式错误,导入失败');
}
};
reader.readAsText(file);
}
推荐实施步骤
快速修复(方案 3 - 导出/导入)
- 添加导出/导入按钮到界面
- 用户可以定期导出数据备份
- 在新电脑上导入备份文件
长期方案(方案 1 - 后端 API)
- 创建 Node.js 后端服务
- 修改前端代码使用 API
- 添加数据卷挂载
- 重新构建和部署
当前数据位置
- 浏览器 localStorage:
urlManager_urls,urlManager_categories - 服务器: 无持久化存储
修复后的数据位置
- 服务器文件:
/home/renjianbo/devops/workdizhi/workdizhi/data/urls.json - 数据卷: 挂载到容器内的
/app/data或/usr/share/nginx/html/data
注意事项
- 备份现有数据: 在修改前,先导出当前 localStorage 中的数据
- 测试: 修改后充分测试数据保存和加载功能
- 迁移: 如果已有数据,需要提供迁移脚本