feat: webapp/card delivery server example
This commit is contained in:
@@ -1,2 +1,67 @@
|
||||
# Hearth_Server
|
||||
# Hearth Server 示例
|
||||
|
||||
Hearth 启动器的 webAPP / 卡片下发服务端示例。**本质是纯静态文件**,无需后端逻辑。
|
||||
|
||||
## 目录结构
|
||||
|
||||
```
|
||||
Hearth_Server/
|
||||
├── manifest.json # webAPP 清单(客户端启动时拉取)
|
||||
├── apps/<id>/index.html # 各 webAPP 入口页
|
||||
├── icons/<id>.svg # 图标
|
||||
├── offlines/<id>.zip # 可选离线包(整站资源打成 zip)
|
||||
└── cards/
|
||||
├── catalog.json # 首页卡片目录
|
||||
└── <id>/card.html # 单个卡片
|
||||
```
|
||||
|
||||
## 快速启动(测试用)
|
||||
|
||||
```bash
|
||||
cd ~/Project/Hearth_Server
|
||||
python3 -m http.server 8080
|
||||
```
|
||||
|
||||
然后在 Hearth 设置页 →「webAPP 服务器地址」填 `http://<本机IP>:8080`。
|
||||
|
||||
## 正式部署
|
||||
|
||||
- nginx:`location / { root /path/to/Hearth_Server; }`
|
||||
- 或传云对象存储(OSS/COS)+ CDN,整个目录原样上传
|
||||
|
||||
## 更新流程
|
||||
|
||||
1. 改 webAPP 资源(`apps/` 下的文件)
|
||||
2. 改 `manifest.json` 的 `version`(+1)
|
||||
3. 客户端「检查更新」或重启即可拉取
|
||||
|
||||
## manifest.json 字段
|
||||
|
||||
| 字段 | 说明 |
|
||||
|---|---|
|
||||
| `version` | 清单版本号,客户端据此判断更新 |
|
||||
| `apps[].id` | 唯一 id |
|
||||
| `apps[].name` | 显示名 |
|
||||
| `apps[].icon` | 图标地址(建议 SVG) |
|
||||
| `apps[].url` | 入口页地址(相对地址即可) |
|
||||
| `apps[].offline.package` | 可选离线包 zip 路径 |
|
||||
| `apps[].offline.version` | 离线包版本 |
|
||||
|
||||
## 离线包
|
||||
|
||||
- 把某个 webAPP 的**全部资源**(`apps/<id>/` 下所有文件,含子目录)打成 zip,放 `offlines/<id>.zip`
|
||||
- zip 内需**包含 index.html**(作为离线入口)
|
||||
- 生成示例:`cd apps/demo && zip -r ../../offlines/demo.zip .`
|
||||
|
||||
## 离线包使用与版本管理
|
||||
|
||||
- **下载时机**:客户端拉取清单时**只同步列表,不自动下载程序包**;只有首次点开某 webAPP 时才下载(显示进度覆盖层)。
|
||||
- **本地优先**:下载后解压到本地目录,之后点开直接本地加载;**服务端删除该离线包不影响本地运行**。
|
||||
- **手动导入**:长按 webAPP 卡片 →「导入离线包」→ 选择本地 zip(适用于无服务器或离线分发场景)。
|
||||
- **版本更新**:
|
||||
- `manifest.json` 里把 `offline.version` 升版本后,客户端列表该 webAPP 显示「**可更新**」标签(**不强制**,点开仍运行本地旧版)。
|
||||
- 长按卡片弹出菜单:
|
||||
- `更新离线包`(删除本地并重新下载,显示版本 x → y)
|
||||
- `删除本地包`
|
||||
- `属性`(查看本地 / 云端版本号)
|
||||
- 云端版本号 ≤ 本地版本号时不提示更新。
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>示例应用</title>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body {
|
||||
font-family: -apple-system, "MiSans", "PingFang SC", sans-serif;
|
||||
background: #f5f5f7;
|
||||
color: #1a1a1a;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 16px;
|
||||
padding: 24px;
|
||||
}
|
||||
.icon {
|
||||
width: 72px; height: 72px; border-radius: 20px;
|
||||
background: linear-gradient(135deg, #ff6900, #ff9d00);
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
font-size: 34px; color: #fff;
|
||||
}
|
||||
h1 { font-size: 24px; font-weight: 600; }
|
||||
.card {
|
||||
background: #fff; border-radius: 16px; padding: 16px 20px;
|
||||
width: 100%; max-width: 360px;
|
||||
box-shadow: 0 2px 12px rgba(0,0,0,.06);
|
||||
}
|
||||
.row { display: flex; justify-content: space-between; padding: 8px 0; font-size: 14px; }
|
||||
.row .k { color: #8a8a90; }
|
||||
.note { font-size: 12px; color: #8a8a90; text-align: center; line-height: 1.6; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="icon">🎨</div>
|
||||
<h1>示例应用</h1>
|
||||
<div class="card">
|
||||
<div class="row"><span class="k">来源</span><span>远程服务器</span></div>
|
||||
<div class="row"><span class="k">当前时间</span><span id="clock">--:--:--</span></div>
|
||||
</div>
|
||||
<p class="note">这是 Hearth 服务端的示例 webapp<br>用于演示远程下发 / 加载进度 / 离线包</p>
|
||||
<script>
|
||||
function tick() {
|
||||
var now = new Date();
|
||||
var p = function (n) { return String(n).padStart(2, '0'); };
|
||||
document.getElementById('clock').textContent =
|
||||
p(now.getHours()) + ':' + p(now.getMinutes()) + ':' + p(now.getSeconds());
|
||||
}
|
||||
tick();
|
||||
setInterval(tick, 1000);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"cards": [
|
||||
{
|
||||
"id": "weather",
|
||||
"name": "天气",
|
||||
"priority": 2,
|
||||
"entry": "cards/weather/card.html"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<style>
|
||||
/* 卡片是 HTML 片段,Hearth 会把它塞进 .card 容器 */
|
||||
/* 卡片内容:天气示例 */
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
.w { display: flex; align-items: center; gap: 10px; font-family: sans-serif; }
|
||||
.w .ic { font-size: 28px; }
|
||||
.w .t { font-size: 20px; font-weight: 600; color: inherit; }
|
||||
.w .s { font-size: 12px; color: #8a8a90; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="w">
|
||||
<div class="ic">☀</div>
|
||||
<div>
|
||||
<div class="t">26°</div>
|
||||
<div class="s">晴</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<rect width="24" height="24" rx="6" fill="#c62f2f"/>
|
||||
<path d="M9 18V6l10-2v11" fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<circle cx="6.5" cy="18" r="2.2" fill="#fff"/>
|
||||
<circle cx="16.5" cy="15" r="2.2" fill="#fff"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 342 B |
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<rect width="24" height="24" rx="6" fill="#ff6900"/>
|
||||
<text x="12" y="17" font-size="13" text-anchor="middle" fill="white" font-family="sans-serif">H</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 228 B |
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"version": 1,
|
||||
"updatedAt": 1723785600,
|
||||
"apps": [
|
||||
{
|
||||
"id": "demo",
|
||||
"name": "示例应用",
|
||||
"icon": "icons/demo.svg",
|
||||
"url": "apps/demo/index.html",
|
||||
"offline": {
|
||||
"package": "offlines/demo.zip",
|
||||
"version": "1.0.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "cloudmusic",
|
||||
"name": "网易云音乐",
|
||||
"icon": "icons/cloudmusic.svg",
|
||||
"url": "https://music.163.com/st/webplayer"
|
||||
}
|
||||
]
|
||||
}
|
||||
Binary file not shown.
Reference in New Issue
Block a user