From edecc6435ad445549a0030d71d7287c59520c429 Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Wed, 7 Oct 2026 18:05:23 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E8=AE=A2=E9=98=85=E5=BC=8F=E8=BD=AF?= =?UTF-8?q?=E4=BB=B6=E6=8F=90=E4=BE=9B):=20=E8=BF=9C=E7=AB=AF=E5=85=A5?= =?UTF-8?q?=E5=8F=A3=20URL=20=E5=B8=A6=E5=86=85=E5=AE=B9=E7=89=88=E6=9C=AC?= =?UTF-8?q?=E5=8F=B7=EF=BC=8C=E5=85=8D=E9=99=A4=E6=B5=8F=E8=A7=88=E5=99=A8?= =?UTF-8?q?=E6=97=A7=E5=89=AF=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ProxyEntryURL 追加 ?v=:内容一变 URL 就变,浏览器/中间缓存旧副本自动作废,提供者重建后无需强制刷新 - 重新声明定制页时清缓存并立即回源预热,使版本号自第一刻起即为新值(返回体新增 WarmError 供排查) --- gvsds/apps/gerp/apps/subscription/manager.py | 22 +++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/gvsds/apps/gerp/apps/subscription/manager.py b/gvsds/apps/gerp/apps/subscription/manager.py index 383d6076..25332995 100644 --- a/gvsds/apps/gerp/apps/subscription/manager.py +++ b/gvsds/apps/gerp/apps/subscription/manager.py @@ -589,12 +589,20 @@ class SubscriptionManager: # /venom/remote-entry.js),若不主动清理,主服务在 TTL 内会一直回旧内容; # 清理后下一次请求立即回源,配合 SHA256 只换来真正变化的文件。 purged = SubscriptionManager.PurgeRemoteAssets(group.SubscriptionGroupUUID) + # 立即回源预热:让新内容及其 SHA256 版本号就绪(ProxyEntryURL 会带 ?v=), + # 避免首个访问者才触发拉取、也保证版本号从第一刻起就是新的。 + entry_path = urlparse(entry).path.lstrip('/') or 'index.js' + _payload, warm_err = SubscriptionManager.FetchRemoteAsset( + _ToStr(group.SubscriptionGroupUUID), entry_path) + if warm_err: + logger.warning('声明后预热远端资源失败: %s (%s)', entry_path, warm_err) return { 'GroupUUID': _ToStr(group.SubscriptionGroupUUID), 'GroupName': group.GroupName, 'RemoteEntry': group.RemoteEntry, 'RemoteModule': group.RemoteModule, 'PurgedAssets': purged, + 'WarmError': warm_err or '', }, None # ==================== 远端资源代理与缓存(主服务代子服务器出资源) ==================== @@ -617,12 +625,24 @@ class SubscriptionManager: 相对/同源入口(非绝对 http(s))原样返回,无需代理。 供应商声明的绝对入口 → `/API/Subscription/RemoteAsset//`, 浏览器只访问主服务,不再直连提供者子服务器。 + + **URL 带内容版本**(`?v=`):内容一变 URL 就变,浏览器与中间 + 缓存无法再用旧副本,提供者重建后无需强制刷新/等 TTL 即生效。缓存尚无内容 + (首次)时不带版本,下一次调用即带上。 """ entry = str(getattr(group, 'RemoteEntry', '') or '').strip() if SubscriptionManager.EntryOrigin(entry) is None: return entry asset_path = urlparse(entry).path.lstrip('/') or 'index.js' - return f'{REMOTE_ASSET_URL_PREFIX}/{_ToStr(group.SubscriptionGroupUUID)}/{asset_path}' + group_uuid = _ToStr(group.SubscriptionGroupUUID) + url = f'{REMOTE_ASSET_URL_PREFIX}/{group_uuid}/{asset_path}' + version = SubscriptionRemoteAsset.objects.filter( + SubscriptionGroupUUID=_ToBytes(group.SubscriptionGroupUUID), + AssetPath=asset_path, + ).values_list('SHA256', flat=True).first() + if version: + url = f'{url}?v={version[:16]}' + return url @staticmethod def ProviderOrigin(GroupUUID):