Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 80f8bd72af |
@@ -117,3 +117,7 @@ JSON 导出升级为格式 version 2,包含人物、关系和当前账号有
|
|||||||
### 升级及数据
|
### 升级及数据
|
||||||
|
|
||||||
启动时幂等创建 `family_events` 表,无需修改既有人物、账号、摄像头或录像记录。部署前使用 SQLite backup API 备份数据库。还原备份会回退备份后的数据,旧版本看不到记事表;应保留升级后的数据库备份,不以旧版本界面作为记事恢复工具。源码中不含真实家族记事和人物信息。
|
启动时幂等创建 `family_events` 表,无需修改既有人物、账号、摄像头或录像记录。部署前使用 SQLite backup API 备份数据库。还原备份会回退备份后的数据,旧版本看不到记事表;应保留升级后的数据库备份,不以旧版本界面作为记事恢复工具。源码中不含真实家族记事和人物信息。
|
||||||
|
|
||||||
|
### 代理缓存兼容(v0.1.17)
|
||||||
|
|
||||||
|
入口 HTML 禁止缓存;页面引用的脚本和样式自动带上发布版本号,避免 NPM 等反向代理为旧资源设置长缓存后,新页面混用旧脚本。升级时普通重新载入即可获取与页面一致的资源。
|
||||||
|
|||||||
@@ -794,6 +794,8 @@ class Handler(BaseHTTPRequestHandler):
|
|||||||
raise Problem('页面不存在', 404)
|
raise Problem('页面不存在', 404)
|
||||||
file = ROOT / 'web' / files[path]
|
file = ROOT / 'web' / files[path]
|
||||||
raw = file.read_bytes()
|
raw = file.read_bytes()
|
||||||
|
if path == '/':
|
||||||
|
raw = raw.replace(b'__ASSET_VERSION__', (ROOT / 'VERSION').read_bytes().strip())
|
||||||
self.send_response(200)
|
self.send_response(200)
|
||||||
self.send_header('Content-Type', (mimetypes.guess_type(file.name)[0] or 'application/octet-stream') + '; charset=utf-8')
|
self.send_header('Content-Type', (mimetypes.guess_type(file.name)[0] or 'application/octet-stream') + '; charset=utf-8')
|
||||||
self.send_header('Content-Length', str(len(raw)))
|
self.send_header('Content-Length', str(len(raw)))
|
||||||
@@ -801,7 +803,7 @@ class Handler(BaseHTTPRequestHandler):
|
|||||||
self.send_header('X-Frame-Options', 'DENY')
|
self.send_header('X-Frame-Options', 'DENY')
|
||||||
self.send_header('Referrer-Policy', 'same-origin')
|
self.send_header('Referrer-Policy', 'same-origin')
|
||||||
self.send_header('Content-Security-Policy', "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; media-src 'self' blob:; worker-src 'self' blob:; connect-src 'self'; frame-ancestors 'none'; base-uri 'none'; form-action 'self'")
|
self.send_header('Content-Security-Policy', "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; media-src 'self' blob:; worker-src 'self' blob:; connect-src 'self'; frame-ancestors 'none'; base-uri 'none'; form-action 'self'")
|
||||||
self.send_header('Cache-Control', 'no-cache')
|
self.send_header('Cache-Control', 'no-store' if path == '/' else 'no-cache')
|
||||||
self.end_headers()
|
self.end_headers()
|
||||||
self.wfile.write(raw)
|
self.wfile.write(raw)
|
||||||
except Problem as e:
|
except Problem as e:
|
||||||
|
|||||||
+12
@@ -72,6 +72,18 @@ class WebTests(unittest.TestCase):
|
|||||||
self.assertEqual(re.findall(r'<option value="(.*?)">(.*?)</option>', select),
|
self.assertEqual(re.findall(r'<option value="(.*?)">(.*?)</option>', select),
|
||||||
[('hd', '主码流'), ('compat', '子码流')])
|
[('hd', '主码流'), ('compat', '子码流')])
|
||||||
|
|
||||||
|
def test_asset_urls_follow_release_version(self):
|
||||||
|
code, headers, body = self.request('/', False)
|
||||||
|
self.assertEqual(code, 200)
|
||||||
|
self.assertEqual(headers['Cache-Control'], 'no-store')
|
||||||
|
self.assertNotIn(b'__ASSET_VERSION__', body)
|
||||||
|
assets = re.findall(r'(?:src|href)="(/[^"?]+\.(?:js|css)\?v=[^"]+)"', body.decode())
|
||||||
|
self.assertEqual(len(assets), 13)
|
||||||
|
version = (app.ROOT / 'VERSION').read_text().strip()
|
||||||
|
for asset in assets:
|
||||||
|
self.assertTrue(asset.endswith('?v=' + version))
|
||||||
|
self.assertEqual(self.request(asset, False)[0], 200)
|
||||||
|
|
||||||
def test_origin_rejected(self):
|
def test_origin_rejected(self):
|
||||||
self.assertEqual(self.request('/api/storage', data={}, origin='https://example.org')[0], 403)
|
self.assertEqual(self.request('/api/storage', data={}, origin='https://example.org')[0], 403)
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -2,8 +2,8 @@
|
|||||||
<html lang="zh-CN">
|
<html lang="zh-CN">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
<meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
<title>赵府智家 · 家庭空间</title><link rel="icon" type="image/png" href="/zhao-icon.png"><link rel="apple-touch-icon" href="/zhao-icon.png"><meta name="theme-color" content="#10191d"><link rel="stylesheet" href="/style.css"><link rel="stylesheet" href="/family-map.css">
|
<title>赵府智家 · 家庭空间</title><link rel="icon" type="image/png" href="/zhao-icon.png"><link rel="apple-touch-icon" href="/zhao-icon.png"><meta name="theme-color" content="#10191d"><link rel="stylesheet" href="/style.css?v=__ASSET_VERSION__"><link rel="stylesheet" href="/family-map.css?v=__ASSET_VERSION__">
|
||||||
<script defer src="/vendor/hls.min.js"></script><script defer src="/live-player.js"></script><script defer src="/kinship.js"></script><script defer src="/family-graph.js"></script><script defer src="/calendar.js"></script><script defer src="/app.js"></script><script defer src="/monitor.js"></script><script defer src="/date-fields.js"></script><script defer src="/family-map.js"></script><script defer src="/family.js"></script><script defer src="/family-events.js"></script>
|
<script defer src="/vendor/hls.min.js?v=__ASSET_VERSION__"></script><script defer src="/live-player.js?v=__ASSET_VERSION__"></script><script defer src="/kinship.js?v=__ASSET_VERSION__"></script><script defer src="/family-graph.js?v=__ASSET_VERSION__"></script><script defer src="/calendar.js?v=__ASSET_VERSION__"></script><script defer src="/app.js?v=__ASSET_VERSION__"></script><script defer src="/monitor.js?v=__ASSET_VERSION__"></script><script defer src="/date-fields.js?v=__ASSET_VERSION__"></script><script defer src="/family-map.js?v=__ASSET_VERSION__"></script><script defer src="/family.js?v=__ASSET_VERSION__"></script><script defer src="/family-events.js?v=__ASSET_VERSION__"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<aside class="sidebar">
|
<aside class="sidebar">
|
||||||
|
|||||||
Reference in New Issue
Block a user