diff --git a/TempLate/index.html b/TempLate/index.html
index e13be206..7a4b4866 100644
--- a/TempLate/index.html
+++ b/TempLate/index.html
@@ -3,10 +3,10 @@
- 银河锋锐软件开发工作室
+ 银河锋锐(GVSDS)
-
-
+
+
diff --git a/backup/www/index.html b/backup/www/index.html
index 761f500c..023ef771 100644
--- a/backup/www/index.html
+++ b/backup/www/index.html
@@ -3,10 +3,10 @@
- 银河锋锐软件开发工作室
-
-
-
+ 银河锋锐(GVSDS)
+
+
+
diff --git a/gvsds/apps/core/views.py b/gvsds/apps/core/views.py
index 97088ccf..56f3bd87 100644
--- a/gvsds/apps/core/views.py
+++ b/gvsds/apps/core/views.py
@@ -62,6 +62,8 @@ SUBDOMAIN_CONFIG = {
'vu': {'dir': None, 'root_is_index': False},
'links': {'dir': None, 'root_is_index': False},
'unclear': {'dir': 'Unclear', 'root_is_index': True},
+ 'old2': {'dir': 'Olds/2', 'root_is_index': True, 'base': 'Olds/2/_base'},
+ 'old3': {'dir': 'Olds/3', 'root_is_index': True, 'base': 'Olds/3/_base'},
}
@@ -269,7 +271,8 @@ class SubdomainView(View):
# www/invite 子域:旧 .html 页面已被 Vue SPA 取代,去掉后缀重定向到 Vue 路由
# 兼容历史链接(如 /login.html、/gallery.html 等被外部页面/脚本引用的旧地址)
- if subdomain in ('www', 'invite') and file_path.endswith('.html'):
+ # 目录请求(/xxx/ 拼成的 /xxx/index.html)不走旧 .html 链接重定向,交给下方 SPA fallback
+ if subdomain in ('www', 'invite') and file_path.endswith('.html') and not file_path.endswith('/index.html'):
new_path = file_path[:-5] or '/'
redirect_url = new_path
if request.META.get('QUERY_STRING'):
@@ -278,7 +281,10 @@ class SubdomainView(View):
# SPA fallback:前端单页应用(如 GERP Vue)的客户端路由路径(无扩展名)
# 找不到对应文件时,回退到 index.html,由前端路由处理
- if config.get('root_is_index') and '.' not in os.path.basename(file_path):
+ # 目录请求(/xxx/ 被拼成 /xxx/index.html)同样回退 SPA,避免误入 Django 404
+ if config.get('root_is_index') and (
+ '.' not in os.path.basename(file_path) or file_path.endswith('/index.html')
+ ):
index_resolved = _resolve_file(subdomain, 'index.html')
if index_resolved:
return _serve_file(index_resolved, subdomain)
diff --git a/gvsds/settings.py b/gvsds/settings.py
index 73186b5c..71c7a226 100644
--- a/gvsds/settings.py
+++ b/gvsds/settings.py
@@ -50,7 +50,6 @@ INSTALLED_APPS = _app_getlist('apps', 'INSTALLED_APPS', fallback=[
'django.contrib.staticfiles',
'rest_framework',
'rest_framework_simplejwt',
- 'rest_framework_simplejwt.token_blacklist', # refresh 轮换后旧 token 拉黑(BLACKLIST_AFTER_ROTATION)
'corsheaders',
'gvsds.apps.core',
'gvsds.apps.gerp',
@@ -295,7 +294,7 @@ SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': timedelta(hours=_app_getint('jwt', 'ACCESS_TOKEN_LIFETIME_HOURS', fallback=24)), # 24 小时(收敛自 168h,缩小泄露窗口)
'REFRESH_TOKEN_LIFETIME': timedelta(days=_app_getint('jwt', 'REFRESH_TOKEN_LIFETIME_DAYS', fallback=90)), # 90 天
'ROTATE_REFRESH_TOKENS': _app_getbool('jwt', 'ROTATE_REFRESH_TOKENS', fallback=True),
- 'BLACKLIST_AFTER_ROTATION': _app_getbool('jwt', 'BLACKLIST_AFTER_ROTATION', fallback=True), # 轮换后旧 refresh 立即失效
+ 'BLACKLIST_AFTER_ROTATION': _app_getbool('jwt', 'BLACKLIST_AFTER_ROTATION', fallback=False), # 已禁用 blacklist(业务 User 非 Django auth 模型,OutstandingToken 无法落库)
'AUTH_HEADER_TYPES': tuple(_app_getlist('jwt', 'AUTH_HEADER_TYPES', fallback=['Bearer'])),
'USER_ID_FIELD': 'UserUUID',
'USER_ID_CLAIM': 'UserUUID',
diff --git a/gvsds/tokens.py b/gvsds/tokens.py
new file mode 100644
index 00000000..ec2d00d4
--- /dev/null
+++ b/gvsds/tokens.py
@@ -0,0 +1,42 @@
+"""simplejwt token 适配层
+
+业务用户模型 core.User 非 Django auth 模型(项目未设置 AUTH_USER_MODEL),
+simplejwt blacklist 的 OutstandingToken.user(FK -> auth.User)无法承接 core.User
+实例,登录签发 token 时触发 ValueError。
+此处将 blacklist 相关操作全部 no-op,并全局替换 RefreshToken.for_user,
+签发时不再落 OutstandingToken 黑名单表。
+"""
+
+from rest_framework_simplejwt.settings import api_settings
+from rest_framework_simplejwt.tokens import RefreshToken as _BaseRefreshToken
+
+
+def _for_user(cls, user):
+ """签发 refresh token(写 jti/exp),但不落 OutstandingToken 黑名单表"""
+ token = cls()
+ token[api_settings.USER_ID_CLAIM] = str(getattr(user, api_settings.USER_ID_FIELD)) # UUIDObj 需转 str 才能 JSON 序列化
+ token.set_jti()
+ token.set_exp()
+ return token
+
+
+class RefreshToken(_BaseRefreshToken):
+ """业务版 RefreshToken:blacklist 相关操作全部 no-op"""
+
+ for_user = classmethod(_for_user)
+
+ def check_blacklist(self):
+ return None
+
+ def blacklist(self):
+ return None
+
+ def outstand(self):
+ return None
+
+
+# 全局替换 simplejwt 原版类方法,覆盖 TokenObtainPairView / TokenRefreshView 等内部调用
+_BaseRefreshToken.for_user = classmethod(_for_user)
+_BaseRefreshToken.check_blacklist = lambda self: None
+_BaseRefreshToken.blacklist = lambda self: None
+_BaseRefreshToken.outstand = lambda self: None
\ No newline at end of file
diff --git a/gvsds/urls.py b/gvsds/urls.py
index ceaf7422..319d33f2 100644
--- a/gvsds/urls.py
+++ b/gvsds/urls.py
@@ -1,3 +1,5 @@
+import gvsds.tokens # noqa: F401 # simplejwt blacklist 适配(签发 token 不落 OutstandingToken 表)
+
from django.conf import settings
from django.urls import path, include, re_path
from django.contrib import admin