网页端:平台桥与部署修复(宝塔/Nginx)
- lib/platform:网页 Electron 统一 bridge/web/electron,子目录 API 基路径、返回首页跳站点根、路径末尾斜杠兼容 - index/design*.web.html:网页入口,web.js 加缓存参数避免旧脚本缓存 - api:PHP 读写 soon;文档说明目录权限与 Nginx - lib/design*、lib/index:与 platformBridge 对接及网页侧逻辑 - 公共资源 JsBarcode/jr-qrcode;文档与 package/README/.gitignore 等同步更新 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
# SoonDesign 网页端服务器文件保存 API
|
||||
|
||||
## 功能说明
|
||||
|
||||
网页端默认将文件保存在浏览器缓存(localStorage/sessionStorage)中。通过部署这些 API 接口,可以实现将文件保存到服务器,实现真正的文件持久化。
|
||||
|
||||
## 部署步骤
|
||||
|
||||
### 1. 上传 API 文件
|
||||
|
||||
将 `api` 目录下的 PHP 文件上传到服务器的 `/api/` 目录(相对于网站根目录)。
|
||||
|
||||
例如,如果网站根目录是 `/www/wwwroot/soonWebsite/`,则:
|
||||
- `save_file.php` → `/www/wwwroot/soonWebsite/api/save_file.php`
|
||||
- `read_file.php` → `/www/wwwroot/soonWebsite/api/read_file.php`
|
||||
|
||||
### 2. 创建文件存储目录
|
||||
|
||||
创建用于存储 `.soon` 文件的目录:
|
||||
|
||||
```bash
|
||||
mkdir -p /www/wwwroot/soonWebsite/design/files
|
||||
chmod 755 /www/wwwroot/soonWebsite/design/files
|
||||
```
|
||||
|
||||
### 3. 配置 Nginx(如果需要)
|
||||
|
||||
确保 Nginx 配置允许访问 `/api/` 和 `/design/files/` 目录:
|
||||
|
||||
```nginx
|
||||
location /api/ {
|
||||
try_files $uri $uri/ =404;
|
||||
}
|
||||
|
||||
location /design/files/ {
|
||||
alias /www/wwwroot/soonWebsite/design/files/;
|
||||
# 允许列出文件(可选)
|
||||
autoindex off;
|
||||
}
|
||||
```
|
||||
|
||||
### 4. 测试 API
|
||||
|
||||
访问以下 URL 测试 API 是否正常工作:
|
||||
|
||||
- 保存文件:`http://114.55.133.123:8888/api/save_file.php`
|
||||
- 读取文件:`http://114.55.133.123:8888/api/read_file.php?fileName=test.soon`
|
||||
|
||||
## 工作原理
|
||||
|
||||
1. **自动检测服务器环境**:网页端会自动检测是否在服务器环境(非 localhost/127.0.0.1)
|
||||
2. **保存文件**:当用户保存文件时,如果是服务器环境,会自动调用 `save_file.php` 将文件上传到服务器
|
||||
3. **读取文件**:当打开文件时,如果是服务器文件路径,会自动调用 `read_file.php` 从服务器读取
|
||||
4. **降级处理**:如果服务器保存失败,会自动降级为浏览器下载
|
||||
|
||||
## 文件存储位置
|
||||
|
||||
所有保存的 `.soon` 文件存储在:
|
||||
```
|
||||
/design/files/
|
||||
```
|
||||
|
||||
文件访问路径为:
|
||||
```
|
||||
http://114.55.133.123:8888/design/files/文件名.soon
|
||||
```
|
||||
|
||||
## 安全说明
|
||||
|
||||
- API 接口已包含文件名清理,防止路径遍历攻击
|
||||
- 建议在生产环境中添加身份验证(如 Token 验证)
|
||||
- 建议限制文件大小和类型
|
||||
- 建议定期清理旧文件
|
||||
|
||||
## 故障排除
|
||||
|
||||
### 问题:保存时提示"保存失败"
|
||||
|
||||
**可能原因:**
|
||||
1. API 文件路径不正确
|
||||
2. 文件存储目录权限不足
|
||||
3. PHP 配置问题
|
||||
|
||||
**解决方法:**
|
||||
1. 检查 API 文件是否在正确位置
|
||||
2. 检查目录权限:`chmod 755 /www/wwwroot/soonWebsite/design/files`
|
||||
3. 检查 PHP 错误日志
|
||||
|
||||
### 问题:文件保存后找不到
|
||||
|
||||
**可能原因:**
|
||||
1. 文件保存路径配置错误
|
||||
2. Nginx 配置问题
|
||||
|
||||
**解决方法:**
|
||||
1. 检查 `save_file.php` 中的 `$saveDir` 路径
|
||||
2. 检查 Nginx 配置是否正确指向文件目录
|
||||
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
/**
|
||||
* SoonDesign 网页端文件读取接口
|
||||
* 从服务器读取 .soon 文件
|
||||
*/
|
||||
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
header('Access-Control-Allow-Origin: *');
|
||||
header('Access-Control-Allow-Methods: GET, OPTIONS');
|
||||
header('Access-Control-Allow-Headers: Content-Type');
|
||||
|
||||
// 处理 OPTIONS 预检请求
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
|
||||
http_response_code(200);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 只接受 GET 请求
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'GET') {
|
||||
http_response_code(405);
|
||||
echo json_encode(['success' => false, 'error' => 'Method not allowed']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 获取文件名
|
||||
$fileName = isset($_GET['fileName']) ? $_GET['fileName'] : '';
|
||||
|
||||
// 验证文件名
|
||||
if (empty($fileName)) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['success' => false, 'error' => '文件名不能为空']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 清理文件名,防止路径遍历攻击
|
||||
$fileName = basename($fileName);
|
||||
if (strpos($fileName, '..') !== false) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['success' => false, 'error' => '无效的文件名']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 获取文件路径
|
||||
$saveDir = dirname(__DIR__) . '/design/files/';
|
||||
$filePath = $saveDir . $fileName;
|
||||
|
||||
// 检查文件是否存在
|
||||
if (!file_exists($filePath)) {
|
||||
http_response_code(404);
|
||||
echo json_encode(['success' => false, 'error' => '文件不存在']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 读取文件内容
|
||||
try {
|
||||
$content = file_get_contents($filePath);
|
||||
if ($content === false) {
|
||||
throw new Exception('文件读取失败');
|
||||
}
|
||||
|
||||
// 尝试解析 JSON 验证格式
|
||||
$json = json_decode($content, true);
|
||||
if (json_last_error() !== JSON_ERROR_NONE) {
|
||||
throw new Exception('文件格式错误: ' . json_last_error_msg());
|
||||
}
|
||||
|
||||
echo $content;
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'error' => '读取失败: ' . $e->getMessage()
|
||||
]);
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
/**
|
||||
* SoonDesign 网页端文件保存接口
|
||||
* 接收 POST 请求,保存 .soon 文件到服务器
|
||||
*/
|
||||
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
header('Access-Control-Allow-Origin: *');
|
||||
header('Access-Control-Allow-Methods: POST, OPTIONS');
|
||||
header('Access-Control-Allow-Headers: Content-Type');
|
||||
|
||||
// 处理 OPTIONS 预检请求
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
|
||||
http_response_code(200);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 只接受 POST 请求
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
http_response_code(405);
|
||||
echo json_encode(['success' => false, 'error' => 'Method not allowed']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 获取保存目录(相对于网站根目录)
|
||||
// 如果 API 在 /api/ 目录,文件存储在 /design/files/
|
||||
$saveDir = dirname(__DIR__) . '/design/files/';
|
||||
// 如果上述路径不正确,请根据实际部署情况修改,例如:
|
||||
// $saveDir = '/www/wwwroot/soonWebsite/design/files/';
|
||||
|
||||
// 确保目录存在
|
||||
if (!is_dir($saveDir)) {
|
||||
mkdir($saveDir, 0755, true);
|
||||
}
|
||||
|
||||
// 获取文件名和内容
|
||||
$fileName = isset($_POST['fileName']) ? $_POST['fileName'] : '';
|
||||
$fileContent = isset($_POST['fileContent']) ? $_POST['fileContent'] : '';
|
||||
|
||||
// 验证文件名
|
||||
if (empty($fileName)) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['success' => false, 'error' => '文件名不能为空']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 清理文件名,防止路径遍历攻击
|
||||
$fileName = basename($fileName);
|
||||
if (strpos($fileName, '..') !== false) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['success' => false, 'error' => '无效的文件名']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 确保文件名以 .soon 结尾
|
||||
if (substr($fileName, -5) !== '.soon') {
|
||||
$fileName = preg_replace('/\.soon$/i', '', $fileName) . '.soon';
|
||||
}
|
||||
|
||||
// 验证内容
|
||||
if (empty($fileContent)) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['success' => false, 'error' => '文件内容不能为空']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 保存文件
|
||||
$filePath = $saveDir . $fileName;
|
||||
try {
|
||||
$result = file_put_contents($filePath, $fileContent);
|
||||
if ($result === false) {
|
||||
throw new Exception('文件写入失败');
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'message' => '文件保存成功',
|
||||
'filePath' => '/design/files/' . $fileName,
|
||||
'fileName' => $fileName
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'error' => '保存失败: ' . $e->getMessage()
|
||||
]);
|
||||
}
|
||||
?>
|
||||
Reference in New Issue
Block a user