python端登录完成
This commit is contained in:
1
app/assets/appearance-patent-CDFDTiWm.css
Normal file
1
app/assets/appearance-patent-CDFDTiWm.css
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
||||
.module-page[data-v-49f8fcb3]{min-height:100vh;background:#1a1a1a}.main-content[data-v-49f8fcb3]{display:flex;height:calc(100vh - 56px);min-height:calc(100vh - 56px)}.left-panel[data-v-49f8fcb3]{width:400px;background:#1e1e1e;padding:20px;overflow-y:auto;border-right:1px solid #2a2a2a}.right-panel[data-v-49f8fcb3]{flex:1;min-width:0;background:#1a1a1a;display:flex;flex-direction:column}.section-title[data-v-49f8fcb3],.subsection-title[data-v-49f8fcb3]{font-size:13px;color:#bbb;margin-bottom:10px}.upload-zone[data-v-49f8fcb3]{border:1px dashed #3a3a3a;border-radius:10px;padding:18px;background:#252525;margin-bottom:18px}.hint[data-v-49f8fcb3],.loading-msg[data-v-49f8fcb3],.files[data-v-49f8fcb3],.muted[data-v-49f8fcb3]{color:#888;font-size:12px;line-height:1.5}.link[data-v-49f8fcb3]{color:#6ea8fe;text-decoration:none}.link[data-v-49f8fcb3]:hover{color:#9fc5ff}.btns[data-v-49f8fcb3],.run-row[data-v-49f8fcb3]{display:flex;gap:10px;flex-wrap:wrap}.opt-btn[data-v-49f8fcb3],.btn-run[data-v-49f8fcb3],.btn-delete[data-v-49f8fcb3],.download[data-v-49f8fcb3]{border:none;cursor:pointer;border-radius:7px}.opt-btn[data-v-49f8fcb3]{padding:8px 14px;color:#ccc;background:#2a2a2a;border:1px solid #3a3a3a}.btn-run[data-v-49f8fcb3]{padding:10px 18px;color:#fff;background:#3498db;font-weight:600}.btn-queue[data-v-49f8fcb3]{background:#27ae60}.btn-run[data-v-49f8fcb3]:disabled{opacity:.55;cursor:not-allowed}.selected-files[data-v-49f8fcb3]{margin-top:14px;color:#999;font-size:12px;word-break:break-all}.selected-files span[data-v-49f8fcb3]{display:block;margin:4px 0}.parse-card[data-v-49f8fcb3],.queue-payload[data-v-49f8fcb3]{margin-top:14px;padding:12px;border:1px solid #2a2a2a;border-radius:8px;background:#202020;color:#b8c1cc;font-size:12px}.queue-payload[data-v-49f8fcb3]{max-height:220px;overflow:auto;color:#8fd3ff;white-space:pre-wrap}.panel-header[data-v-49f8fcb3]{padding:16px 20px;border-bottom:1px solid #2a2a2a;font-size:15px;font-weight:600;color:#ddd}.task-list-wrap[data-v-49f8fcb3]{flex:1;padding:16px 20px;overflow:auto}.clean-result-summary[data-v-49f8fcb3]{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:12px;margin-bottom:16px}.summary-card[data-v-49f8fcb3]{padding:14px 16px;border:1px solid #2a2a2a;border-radius:8px;background:#1e1e1e}.summary-card strong[data-v-49f8fcb3]{display:block;margin-top:8px;color:#eaf4ff;font-size:22px}.summary-label[data-v-49f8fcb3]{color:#8d8d8d;font-size:12px}.result-list-wrap[data-v-49f8fcb3]{border:1px solid #2a2a2a;border-radius:8px;background:#1e1e1e;min-height:180px;margin:0 0 16px}.result-list-header[data-v-49f8fcb3]{display:flex;justify-content:space-between;padding:12px 16px;border-bottom:1px solid #2a2a2a;color:#ddd;font-size:14px}.empty-tasks[data-v-49f8fcb3]{color:#666;font-size:13px;padding:18px;text-align:center}.task-list[data-v-49f8fcb3]{list-style:none;margin:0;padding:12px}.task-item[data-v-49f8fcb3]{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;padding:12px 14px;border:1px solid #2a2a2a;border-radius:8px;margin-bottom:8px;background:#222}.left[data-v-49f8fcb3]{flex:1;min-width:0}.id[data-v-49f8fcb3]{color:#e0e0e0;font-size:13px;font-weight:600}.task-right[data-v-49f8fcb3]{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.status[data-v-49f8fcb3]{padding:4px 10px;border-radius:6px;font-size:12px}.status.success[data-v-49f8fcb3]{background:#2ecc712e;color:#2ecc71}.status.failed[data-v-49f8fcb3]{background:#e74c3c2e;color:#ff6b6b}.status.running[data-v-49f8fcb3]{background:#3498db2e;color:#3498db}.status.pending[data-v-49f8fcb3]{background:#95a5a62e;color:#bdc3c7}.result-hint[data-v-49f8fcb3]{margin-top:6px;color:#e0b96d}.file-progress[data-v-49f8fcb3]{margin-top:8px;max-width:520px}.file-progress-meta[data-v-49f8fcb3]{display:flex;justify-content:space-between;gap:12px;color:#d8c278;font-size:12px}.file-progress-track[data-v-49f8fcb3]{margin-top:5px;height:8px;border-radius:999px;overflow:hidden;background:#303030;border:1px solid #3b3b3b}.file-progress-bar[data-v-49f8fcb3]{height:100%;border-radius:inherit;background:linear-gradient(90deg,#4aa3ff,#f0c75e);transition:width .25s ease}.file-progress-count[data-v-49f8fcb3]{margin-top:4px;color:#858585;font-size:11px}.download[data-v-49f8fcb3]{padding:6px 10px;color:#d6ecff;background:#3498db2e}.btn-delete[data-v-49f8fcb3]{padding:6px 10px;color:#ff8f8f;background:#e74c3c1f}@media(max-width:1100px){.main-content[data-v-49f8fcb3]{flex-direction:column;height:auto}.left-panel[data-v-49f8fcb3]{width:100%;border-right:none;border-bottom:1px solid #2a2a2a}.clean-result-summary[data-v-49f8fcb3]{grid-template-columns:repeat(2,minmax(0,1fr))}}
|
||||
1
app/assets/similar-asin-BgG5p7TZ.css
Normal file
1
app/assets/similar-asin-BgG5p7TZ.css
Normal file
@@ -0,0 +1 @@
|
||||
.module-page[data-v-a27e815e]{min-height:100vh;background:#1a1a1a}.main-content[data-v-a27e815e]{display:flex;height:calc(100vh - 56px);min-height:calc(100vh - 56px)}.left-panel[data-v-a27e815e]{width:400px;background:#1e1e1e;padding:20px;overflow-y:auto;border-right:1px solid #2a2a2a}.right-panel[data-v-a27e815e]{flex:1;min-width:0;background:#1a1a1a;display:flex;flex-direction:column}.section-title[data-v-a27e815e],.subsection-title[data-v-a27e815e]{font-size:13px;color:#bbb;margin-bottom:10px}.upload-zone[data-v-a27e815e]{border:1px dashed #3a3a3a;border-radius:10px;padding:18px;background:#252525;margin-bottom:18px}.hint[data-v-a27e815e],.loading-msg[data-v-a27e815e],.files[data-v-a27e815e],.muted[data-v-a27e815e]{color:#888;font-size:12px;line-height:1.5}.link[data-v-a27e815e]{color:#6ea8fe;text-decoration:none}.link[data-v-a27e815e]:hover{color:#9fc5ff}.btns[data-v-a27e815e],.run-row[data-v-a27e815e]{display:flex;gap:10px;flex-wrap:wrap}.opt-btn[data-v-a27e815e],.btn-run[data-v-a27e815e],.btn-delete[data-v-a27e815e],.download[data-v-a27e815e]{border:none;cursor:pointer;border-radius:7px}.opt-btn[data-v-a27e815e]{padding:8px 14px;color:#ccc;background:#2a2a2a;border:1px solid #3a3a3a}.btn-run[data-v-a27e815e]{padding:10px 18px;color:#fff;background:#3498db;font-weight:600}.btn-queue[data-v-a27e815e]{background:#27ae60}.btn-run[data-v-a27e815e]:disabled{opacity:.55;cursor:not-allowed}.selected-files[data-v-a27e815e]{margin-top:14px;color:#999;font-size:12px;word-break:break-all}.selected-files span[data-v-a27e815e]{display:block;margin:4px 0}.parse-card[data-v-a27e815e],.queue-payload[data-v-a27e815e]{margin-top:14px;padding:12px;border:1px solid #2a2a2a;border-radius:8px;background:#202020;color:#b8c1cc;font-size:12px}.queue-payload[data-v-a27e815e]{max-height:220px;overflow:auto;color:#8fd3ff;white-space:pre-wrap}.panel-header[data-v-a27e815e]{padding:16px 20px;border-bottom:1px solid #2a2a2a;font-size:15px;font-weight:600;color:#ddd}.task-list-wrap[data-v-a27e815e]{flex:1;padding:16px 20px;overflow:auto}.clean-result-summary[data-v-a27e815e]{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:12px;margin-bottom:16px}.summary-card[data-v-a27e815e]{padding:14px 16px;border:1px solid #2a2a2a;border-radius:8px;background:#1e1e1e}.summary-card strong[data-v-a27e815e]{display:block;margin-top:8px;color:#eaf4ff;font-size:22px}.summary-label[data-v-a27e815e]{color:#8d8d8d;font-size:12px}.result-list-wrap[data-v-a27e815e]{border:1px solid #2a2a2a;border-radius:8px;background:#1e1e1e;min-height:180px;margin:0 0 16px}.result-list-header[data-v-a27e815e]{display:flex;justify-content:space-between;padding:12px 16px;border-bottom:1px solid #2a2a2a;color:#ddd;font-size:14px}.empty-tasks[data-v-a27e815e]{color:#666;font-size:13px;padding:18px;text-align:center}.task-list[data-v-a27e815e]{list-style:none;margin:0;padding:12px}.task-item[data-v-a27e815e]{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;padding:12px 14px;border:1px solid #2a2a2a;border-radius:8px;margin-bottom:8px;background:#222}.left[data-v-a27e815e]{flex:1;min-width:0}.id[data-v-a27e815e]{color:#e0e0e0;font-size:13px;font-weight:600}.task-right[data-v-a27e815e]{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.status[data-v-a27e815e]{padding:4px 10px;border-radius:6px;font-size:12px}.status.success[data-v-a27e815e]{background:#2ecc712e;color:#2ecc71}.status.failed[data-v-a27e815e]{background:#e74c3c2e;color:#ff6b6b}.status.running[data-v-a27e815e]{background:#3498db2e;color:#3498db}.status.pending[data-v-a27e815e]{background:#95a5a62e;color:#bdc3c7}.result-hint[data-v-a27e815e]{margin-top:6px;color:#e0b96d}.file-progress[data-v-a27e815e]{margin-top:8px;max-width:520px}.file-progress-meta[data-v-a27e815e]{display:flex;justify-content:space-between;gap:12px;color:#d8c278;font-size:12px}.file-progress-track[data-v-a27e815e]{margin-top:5px;height:8px;border-radius:999px;overflow:hidden;background:#303030;border:1px solid #3b3b3b}.file-progress-bar[data-v-a27e815e]{height:100%;border-radius:inherit;background:linear-gradient(90deg,#4aa3ff,#f0c75e);transition:width .25s ease}.file-progress-count[data-v-a27e815e]{margin-top:4px;color:#858585;font-size:11px}.download[data-v-a27e815e]{padding:6px 10px;color:#d6ecff;background:#3498db2e}.btn-delete[data-v-a27e815e]{padding:6px 10px;color:#ff8f8f;background:#e74c3c1f}@media(max-width:1100px){.main-content[data-v-a27e815e]{flex-direction:column;height:auto}.left-panel[data-v-a27e815e]{width:100%;border-right:none;border-bottom:1px solid #2a2a2a}.clean-result-summary[data-v-a27e815e]{grid-template-columns:repeat(2,minmax(0,1fr))}}
|
||||
File diff suppressed because one or more lines are too long
@@ -4,6 +4,7 @@ JWT 工具:解析 Java 端签发的 token、从请求中提取 token。
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
from typing import Optional, Tuple
|
||||
|
||||
from flask import request
|
||||
@@ -25,6 +26,14 @@ except ImportError as _e:
|
||||
_DEFAULT_SECRET = "please-change-this-secret-please-rotate-at-least-32-bytes"
|
||||
COOKIE_NAME = os.getenv("AIIMAGE_AUTH_COOKIE_NAME", "aiimage_token")
|
||||
|
||||
# 桌面端与 Java 服务器时钟可能漂移(笔记本休眠、用户手动改时间、跨时区等),
|
||||
# 给 JWT exp/nbf 校验留出容差,避免 /api/auth/sync 因为时间不同步而 401。
|
||||
# 默认 5 分钟,可通过 AIIMAGE_JWT_LEEWAY_SECONDS 覆盖。
|
||||
try:
|
||||
_JWT_LEEWAY_SECONDS = int(os.getenv("AIIMAGE_JWT_LEEWAY_SECONDS", "300"))
|
||||
except ValueError:
|
||||
_JWT_LEEWAY_SECONDS = 300
|
||||
|
||||
|
||||
def _signing_key() -> bytes:
|
||||
"""与 Java JwtService.signingKey 保持一致:UTF-8 字节,不足 32 字节右侧补 0。"""
|
||||
@@ -35,6 +44,26 @@ def _signing_key() -> bytes:
|
||||
return key_bytes
|
||||
|
||||
|
||||
def _describe_clock_skew(token: str) -> str:
|
||||
"""解析 token 里的 iat/exp,与本地时钟比较,返回 ' iat=.. exp=.. now=.. skew=..s' 字符串。
|
||||
仅供日志使用,不做安全决策;解析失败返回空串,不影响主流程。"""
|
||||
if pyjwt is None:
|
||||
return ""
|
||||
try:
|
||||
unverified = pyjwt.decode(token, options={"verify_signature": False, "verify_exp": False, "verify_iat": False, "verify_nbf": False})
|
||||
except Exception:
|
||||
return ""
|
||||
iat = unverified.get("iat")
|
||||
exp = unverified.get("exp")
|
||||
now = int(time.time())
|
||||
parts = [f"now={now}"]
|
||||
if isinstance(iat, (int, float)):
|
||||
parts.append(f"iat={int(iat)} skew_iat={now - int(iat)}s")
|
||||
if isinstance(exp, (int, float)):
|
||||
parts.append(f"exp={int(exp)} skew_exp={now - int(exp)}s")
|
||||
return ", " + " ".join(parts)
|
||||
|
||||
|
||||
def parse_token_with_reason(token: str) -> Tuple[Optional[dict], Optional[str]]:
|
||||
"""解析 JWT,返回 (payload, error_reason)。
|
||||
payload 命中时 error_reason 为 None;失败时 payload 为 None,error_reason 描述根因。"""
|
||||
@@ -49,10 +78,17 @@ def parse_token_with_reason(token: str) -> Tuple[Optional[dict], Optional[str]]:
|
||||
return None, f"无法解析 token header: {type(e).__name__}: {e}"
|
||||
alg = header.get("alg") or "?"
|
||||
try:
|
||||
payload = pyjwt.decode(token, _signing_key(), algorithms=["HS256", "HS384", "HS512"])
|
||||
payload = pyjwt.decode(
|
||||
token,
|
||||
_signing_key(),
|
||||
algorithms=["HS256", "HS384", "HS512"],
|
||||
leeway=_JWT_LEEWAY_SECONDS,
|
||||
)
|
||||
except Exception as e:
|
||||
# 常见:ExpiredSignatureError / InvalidSignatureError / DecodeError / InvalidAlgorithmError
|
||||
return None, f"{type(e).__name__}: {e} (token alg={alg})"
|
||||
# 把 token 内 exp/iat 与本地时钟一起打出来,定位"时间漂移"类失败更直接
|
||||
skew_info = _describe_clock_skew(token)
|
||||
return None, f"{type(e).__name__}: {e} (token alg={alg}{skew_info})"
|
||||
sub = payload.get("sub")
|
||||
try:
|
||||
user_id = int(sub) if sub is not None else None
|
||||
|
||||
Reference in New Issue
Block a user