File size: 3,646 Bytes
6dfddfb
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
"""
静态资源版本控制工具
用于给CSS和JS文件添加版本参数,避免浏览器缓存问题
"""

import hashlib
import time
from functools import lru_cache
from pathlib import Path
from typing import Dict

from app.utils.helpers import get_current_version


class StaticVersionManager:
    """静态资源版本管理器"""

    def __init__(self, static_dir: str = "app/static"):
        self.static_dir = Path(static_dir)
        self._version_cache: Dict[str, str] = {}
        self._use_file_hash = True  # 是否使用文件哈希作为版本号

    def get_version_for_file(self, file_path: str) -> str:
        """
        获取文件的版本号

        Args:
            file_path: 相对于static目录的文件路径,如 'css/fonts.css'

        Returns:
            版本号字符串
        """
        if self._use_file_hash:
            return self._get_file_hash_version(file_path)
        else:
            return self._get_app_version()

    def _get_file_hash_version(self, file_path: str) -> str:
        """基于文件内容生成哈希版本号"""
        # 如果已经缓存过,直接返回
        if file_path in self._version_cache:
            return self._version_cache[file_path]

        full_path = self.static_dir / file_path

        if not full_path.exists():
            # 文件不存在,使用应用版本号作为fallback
            version = self._get_app_version()
        else:
            try:
                # 读取文件内容并计算MD5哈希
                with open(full_path, "rb") as f:
                    content = f.read()
                    hash_object = hashlib.md5(content)
                    version = hash_object.hexdigest()[:8]  # 取前8位
            except Exception:
                # 读取失败,使用应用版本号作为fallback
                version = self._get_app_version()

        # 缓存结果
        self._version_cache[file_path] = version
        return version

    def _get_app_version(self) -> str:
        """获取应用程序版本号"""
        try:
            return get_current_version().replace(".", "")
        except Exception:
            # 如果获取版本失败,使用时间戳
            return str(int(time.time()))

    def get_versioned_url(self, file_path: str) -> str:
        """
        获取带版本参数的URL

        Args:
            file_path: 相对于static目录的文件路径

        Returns:
            带版本参数的URL
        """
        version = self.get_version_for_file(file_path)
        return f"/static/{file_path}?v={version}"

    def clear_cache(self):
        """清空版本缓存"""
        self._version_cache.clear()


# 全局实例
_static_version_manager = StaticVersionManager()


def get_static_url(file_path: str) -> str:
    """
    获取静态资源的版本化URL

    Args:
        file_path: 相对于static目录的文件路径

    Returns:
        带版本参数的完整URL

    Example:
        get_static_url('css/fonts.css') -> '/static/css/fonts.css?v=a1b2c3d4'
        get_static_url('js/config_editor.js') -> '/static/js/config_editor.js?v=e5f6g7h8'
    """
    return _static_version_manager.get_versioned_url(file_path)


def clear_static_cache():
    """清空静态资源版本缓存"""
    _static_version_manager.clear_cache()


@lru_cache(maxsize=128)
def get_cached_static_url(file_path: str) -> str:
    """
    获取缓存的静态资源URL(用于开发环境)

    Args:
        file_path: 相对于static目录的文件路径

    Returns:
        带版本参数的完整URL
    """
    return get_static_url(file_path)