mdbq 4.0.96__tar.gz → 4.0.98__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of mdbq might be problematic. Click here for more details.

Files changed (44) hide show
  1. {mdbq-4.0.96 → mdbq-4.0.98}/PKG-INFO +1 -1
  2. mdbq-4.0.98/mdbq/__version__.py +1 -0
  3. {mdbq-4.0.96 → mdbq-4.0.98}/mdbq/auth/auth_backend.py +143 -75
  4. {mdbq-4.0.96 → mdbq-4.0.98}/mdbq.egg-info/PKG-INFO +1 -1
  5. mdbq-4.0.96/mdbq/__version__.py +0 -1
  6. {mdbq-4.0.96 → mdbq-4.0.98}/README.txt +0 -0
  7. {mdbq-4.0.96 → mdbq-4.0.98}/mdbq/__init__.py +0 -0
  8. {mdbq-4.0.96 → mdbq-4.0.98}/mdbq/auth/__init__.py +0 -0
  9. {mdbq-4.0.96 → mdbq-4.0.98}/mdbq/auth/rate_limiter.py +0 -0
  10. {mdbq-4.0.96 → mdbq-4.0.98}/mdbq/js/__init__.py +0 -0
  11. {mdbq-4.0.96 → mdbq-4.0.98}/mdbq/js/jc.py +0 -0
  12. {mdbq-4.0.96 → mdbq-4.0.98}/mdbq/log/__init__.py +0 -0
  13. {mdbq-4.0.96 → mdbq-4.0.98}/mdbq/log/mylogger.py +0 -0
  14. {mdbq-4.0.96 → mdbq-4.0.98}/mdbq/myconf/__init__.py +0 -0
  15. {mdbq-4.0.96 → mdbq-4.0.98}/mdbq/myconf/myconf.py +0 -0
  16. {mdbq-4.0.96 → mdbq-4.0.98}/mdbq/mysql/__init__.py +0 -0
  17. {mdbq-4.0.96 → mdbq-4.0.98}/mdbq/mysql/deduplicator.py +0 -0
  18. {mdbq-4.0.96 → mdbq-4.0.98}/mdbq/mysql/mysql.py +0 -0
  19. {mdbq-4.0.96 → mdbq-4.0.98}/mdbq/mysql/s_query.py +0 -0
  20. {mdbq-4.0.96 → mdbq-4.0.98}/mdbq/mysql/unique_.py +0 -0
  21. {mdbq-4.0.96 → mdbq-4.0.98}/mdbq/mysql/uploader.py +0 -0
  22. {mdbq-4.0.96 → mdbq-4.0.98}/mdbq/other/__init__.py +0 -0
  23. {mdbq-4.0.96 → mdbq-4.0.98}/mdbq/other/download_sku_picture.py +0 -0
  24. {mdbq-4.0.96 → mdbq-4.0.98}/mdbq/other/error_handler.py +0 -0
  25. {mdbq-4.0.96 → mdbq-4.0.98}/mdbq/other/otk.py +0 -0
  26. {mdbq-4.0.96 → mdbq-4.0.98}/mdbq/other/pov_city.py +0 -0
  27. {mdbq-4.0.96 → mdbq-4.0.98}/mdbq/other/ua_sj.py +0 -0
  28. {mdbq-4.0.96 → mdbq-4.0.98}/mdbq/pbix/__init__.py +0 -0
  29. {mdbq-4.0.96 → mdbq-4.0.98}/mdbq/pbix/pbix_refresh.py +0 -0
  30. {mdbq-4.0.96 → mdbq-4.0.98}/mdbq/pbix/refresh_all.py +0 -0
  31. {mdbq-4.0.96 → mdbq-4.0.98}/mdbq/redis/__init__.py +0 -0
  32. {mdbq-4.0.96 → mdbq-4.0.98}/mdbq/redis/getredis.py +0 -0
  33. {mdbq-4.0.96 → mdbq-4.0.98}/mdbq/route/__init__.py +0 -0
  34. {mdbq-4.0.96 → mdbq-4.0.98}/mdbq/route/analytics.py +0 -0
  35. {mdbq-4.0.96 → mdbq-4.0.98}/mdbq/route/monitor.py +0 -0
  36. {mdbq-4.0.96 → mdbq-4.0.98}/mdbq/route/routes.py +0 -0
  37. {mdbq-4.0.96 → mdbq-4.0.98}/mdbq/selenium/__init__.py +0 -0
  38. {mdbq-4.0.96 → mdbq-4.0.98}/mdbq/selenium/get_driver.py +0 -0
  39. {mdbq-4.0.96 → mdbq-4.0.98}/mdbq/spider/__init__.py +0 -0
  40. {mdbq-4.0.96 → mdbq-4.0.98}/mdbq.egg-info/SOURCES.txt +0 -0
  41. {mdbq-4.0.96 → mdbq-4.0.98}/mdbq.egg-info/dependency_links.txt +0 -0
  42. {mdbq-4.0.96 → mdbq-4.0.98}/mdbq.egg-info/top_level.txt +0 -0
  43. {mdbq-4.0.96 → mdbq-4.0.98}/setup.cfg +0 -0
  44. {mdbq-4.0.96 → mdbq-4.0.98}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mdbq
3
- Version: 4.0.96
3
+ Version: 4.0.98
4
4
  Home-page: https://pypi.org/project/mdbq
5
5
  Author: xigua,
6
6
  Author-email: 2587125111@qq.com
@@ -0,0 +1 @@
1
+ VERSION = '4.0.98'
@@ -223,6 +223,8 @@ class StandaloneAuthManager:
223
223
  user_id BIGINT UNSIGNED NOT NULL,
224
224
  device_id VARCHAR(255) CHARACTER SET ascii NOT NULL,
225
225
  device_fingerprint VARCHAR(255) CHARACTER SET ascii NOT NULL,
226
+ login_domain VARCHAR(255) NOT NULL DEFAULT '' COMMENT '登录时的域名',
227
+ session_version INT UNSIGNED NOT NULL DEFAULT 1 COMMENT '会话版本号',
226
228
  device_name VARCHAR(255) NOT NULL DEFAULT 'Unknown Device',
227
229
  custom_name VARCHAR(255) DEFAULT NULL COMMENT '用户自定义设备名称',
228
230
  device_type ENUM('mobile', 'desktop', 'tablet', 'unknown') NOT NULL DEFAULT 'unknown',
@@ -242,13 +244,15 @@ class StandaloneAuthManager:
242
244
  trust_level ENUM('trusted', 'normal', 'suspicious') NOT NULL DEFAULT 'normal' COMMENT '设备信任级别',
243
245
 
244
246
  UNIQUE KEY uk_device_sessions_device_id (device_id),
245
- UNIQUE KEY uk_device_sessions_user_fingerprint (user_id, device_fingerprint),
247
+ UNIQUE KEY uk_device_sessions_user_fingerprint_domain_version (user_id, device_fingerprint, login_domain, session_version),
246
248
  KEY idx_device_sessions_user_id (user_id),
247
249
  KEY idx_device_sessions_user_device (user_id, device_id),
248
250
  KEY idx_device_sessions_last_activity (last_activity),
249
251
  KEY idx_device_sessions_is_active (is_active),
250
252
  KEY idx_device_sessions_fingerprint (device_fingerprint),
251
253
  KEY idx_device_sessions_trust_level (trust_level),
254
+ KEY idx_device_sessions_login_domain (login_domain),
255
+ KEY idx_device_sessions_user_domain (user_id, login_domain),
252
256
 
253
257
  CONSTRAINT fk_device_sessions_user_id
254
258
  FOREIGN KEY (user_id)
@@ -621,7 +625,7 @@ class StandaloneAuthManager:
621
625
  except jwt.InvalidTokenError:
622
626
  return None
623
627
 
624
- def create_or_update_device_session(self, user_id, ip_address, user_agent, device_info=None):
628
+ def create_or_update_device_session(self, user_id, ip_address, user_agent, device_info=None, login_domain=''):
625
629
  """创建或更新设备会话"""
626
630
  # 解析用户代理获取基本信息
627
631
  parsed_ua = self._parse_user_agent(user_agent)
@@ -634,7 +638,8 @@ class StandaloneAuthManager:
634
638
  **(device_info or {})
635
639
  }
636
640
 
637
- device_fingerprint = self._generate_device_fingerprint(full_device_info)
641
+ # 生成包含域名的设备指纹
642
+ device_fingerprint = self._generate_device_fingerprint(full_device_info, login_domain)
638
643
  device_id = secrets.token_urlsafe(32)
639
644
 
640
645
  conn = self.pool.connection()
@@ -643,79 +648,63 @@ class StandaloneAuthManager:
643
648
  try:
644
649
  current_time_utc = datetime.now(timezone.utc)
645
650
 
646
- # 检查设备是否已存在
651
+ # 软删除:将该用户在该域名下的相同设备指纹的所有活跃会话标记为非活跃
647
652
  cursor.execute('''
648
- SELECT id, device_id FROM device_sessions
649
- WHERE user_id = %s AND device_fingerprint = %s AND is_active = 1
650
- ''', (user_id, device_fingerprint))
653
+ UPDATE device_sessions
654
+ SET is_active = 0
655
+ WHERE user_id = %s AND device_fingerprint = %s AND login_domain = %s AND is_active = 1
656
+ ''', (user_id, device_fingerprint, login_domain))
651
657
 
652
- existing_session = cursor.fetchone()
658
+ # 获取下一个版本号
659
+ cursor.execute('''
660
+ SELECT COALESCE(MAX(session_version), 0) + 1 as next_version
661
+ FROM device_sessions
662
+ WHERE user_id = %s AND device_fingerprint = %s AND login_domain = %s
663
+ ''', (user_id, device_fingerprint, login_domain))
653
664
 
654
- if existing_session:
655
- # 更新现有设备会话
656
- device_session_id = existing_session['id']
657
- device_id = existing_session['device_id']
658
-
659
- cursor.execute('''
660
- UPDATE device_sessions
661
- SET current_ip = %s, user_agent = %s, last_activity = %s,
662
- device_name = %s, device_type = %s, platform = %s, browser = %s,
663
- browser_version = %s, screen_resolution = %s, timezone_offset = %s,
664
- language = %s, hardware_concurrency = %s
665
- WHERE id = %s
666
- ''', (ip_address, user_agent, current_time_utc,
667
- full_device_info.get('device_name', 'Unknown Device'),
668
- full_device_info.get('device_type', 'unknown'),
669
- full_device_info.get('platform'),
670
- full_device_info.get('browser'),
671
- full_device_info.get('browser_version'),
672
- full_device_info.get('screen_resolution'),
673
- full_device_info.get('timezone_offset'),
674
- full_device_info.get('language'),
675
- full_device_info.get('hardware_concurrency'),
676
- device_session_id))
677
- else:
678
- # 检查设备数量限制
679
- cursor.execute('''
680
- SELECT COUNT(*) as active_count FROM device_sessions
681
- WHERE user_id = %s AND is_active = 1
682
- ''', (user_id,))
683
-
684
- active_count = cursor.fetchone()['active_count']
685
-
686
- if active_count >= self.auth_config['max_concurrent_devices']:
687
- # 踢出最旧的设备
688
- cursor.execute('''
689
- SELECT id FROM device_sessions
690
- WHERE user_id = %s AND is_active = 1
691
- ORDER BY last_activity ASC
692
- LIMIT %s
693
- ''', (user_id, active_count - self.auth_config['max_concurrent_devices'] + 1))
694
-
695
- old_sessions = cursor.fetchall()
696
- for old_session in old_sessions:
697
- self._revoke_device_session(cursor, old_session['id'], 'device_limit')
698
-
699
- # 创建新设备会话
665
+ next_version = cursor.fetchone()['next_version']
666
+
667
+ # 检查设备数量限制
668
+ cursor.execute('''
669
+ SELECT COUNT(*) as active_count FROM device_sessions
670
+ WHERE user_id = %s AND is_active = 1
671
+ ''', (user_id,))
672
+
673
+ active_count = cursor.fetchone()['active_count']
674
+
675
+ if active_count >= self.auth_config['max_concurrent_devices']:
676
+ # 踢出最旧的设备
700
677
  cursor.execute('''
701
- INSERT INTO device_sessions (
702
- user_id, device_id, device_fingerprint, device_name, device_type,
703
- platform, browser, browser_version, screen_resolution, timezone_offset,
704
- language, hardware_concurrency, current_ip, first_ip, user_agent, last_activity
705
- ) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
706
- ''', (user_id, device_id, device_fingerprint,
707
- full_device_info.get('device_name', 'Unknown Device'),
708
- full_device_info.get('device_type', 'unknown'),
709
- full_device_info.get('platform'),
710
- full_device_info.get('browser'),
711
- full_device_info.get('browser_version'),
712
- full_device_info.get('screen_resolution'),
713
- full_device_info.get('timezone_offset'),
714
- full_device_info.get('language'),
715
- full_device_info.get('hardware_concurrency'),
716
- ip_address, ip_address, user_agent, current_time_utc))
678
+ SELECT id FROM device_sessions
679
+ WHERE user_id = %s AND is_active = 1
680
+ ORDER BY last_activity ASC
681
+ LIMIT %s
682
+ ''', (user_id, active_count - self.auth_config['max_concurrent_devices'] + 1))
717
683
 
718
- device_session_id = cursor.lastrowid
684
+ old_sessions = cursor.fetchall()
685
+ for old_session in old_sessions:
686
+ self._revoke_device_session(cursor, old_session['id'], 'device_limit')
687
+
688
+ # 创建新设备会话(带版本号)
689
+ cursor.execute('''
690
+ INSERT INTO device_sessions (
691
+ user_id, device_id, device_fingerprint, login_domain, session_version, device_name, device_type,
692
+ platform, browser, browser_version, screen_resolution, timezone_offset,
693
+ language, hardware_concurrency, current_ip, first_ip, user_agent, last_activity
694
+ ) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
695
+ ''', (user_id, device_id, device_fingerprint, login_domain, next_version,
696
+ full_device_info.get('device_name', 'Unknown Device'),
697
+ full_device_info.get('device_type', 'unknown'),
698
+ full_device_info.get('platform'),
699
+ full_device_info.get('browser'),
700
+ full_device_info.get('browser_version'),
701
+ full_device_info.get('screen_resolution'),
702
+ full_device_info.get('timezone_offset'),
703
+ full_device_info.get('language'),
704
+ full_device_info.get('hardware_concurrency'),
705
+ ip_address, ip_address, user_agent, current_time_utc))
706
+
707
+ device_session_id = cursor.lastrowid
719
708
 
720
709
  return device_session_id, device_id, full_device_info.get('device_name', 'Unknown Device')
721
710
 
@@ -1253,9 +1242,9 @@ class StandaloneAuthManager:
1253
1242
  WHERE id = %s
1254
1243
  ''', (device_session_id,))
1255
1244
 
1256
- def _generate_device_fingerprint(self, device_info):
1245
+ def _generate_device_fingerprint(self, device_info, login_domain=''):
1257
1246
  """
1258
- 生成稳定的设备指纹
1247
+ 生成稳定的设备指纹(包含登录域名)
1259
1248
 
1260
1249
  Args:
1261
1250
  device_info (dict): 设备信息
@@ -1265,8 +1254,9 @@ class StandaloneAuthManager:
1265
1254
  - language: 浏览器语言
1266
1255
  - hardware_concurrency: CPU核心数
1267
1256
  - platform: 平台信息
1257
+ login_domain (str): 登录时的域名
1268
1258
  """
1269
- # 提取稳定的设备特征
1259
+ # 提取稳定的设备特征(包含登录域名)
1270
1260
  stable_features = [
1271
1261
  device_info.get('user_agent', ''),
1272
1262
  device_info.get('screen_resolution', ''),
@@ -1274,6 +1264,7 @@ class StandaloneAuthManager:
1274
1264
  device_info.get('language', ''),
1275
1265
  str(device_info.get('hardware_concurrency', 0)),
1276
1266
  device_info.get('platform', ''),
1267
+ login_domain or '', # 登录域名作为设备指纹的一部分
1277
1268
  ]
1278
1269
 
1279
1270
  # 过滤空值并连接
@@ -1383,7 +1374,7 @@ class StandaloneAuthManager:
1383
1374
 
1384
1375
  try:
1385
1376
  cursor.execute('''
1386
- SELECT device_id, device_fingerprint, device_name, custom_name, device_type,
1377
+ SELECT device_id, device_fingerprint, login_domain, device_name, custom_name, device_type,
1387
1378
  platform, browser, browser_version, screen_resolution, timezone_offset,
1388
1379
  language, hardware_concurrency, current_ip, first_ip,
1389
1380
  last_activity, created_at, is_active, trust_level
@@ -1408,6 +1399,7 @@ class StandaloneAuthManager:
1408
1399
  'device_id': device['device_id'],
1409
1400
  'device_name': display_name,
1410
1401
  'custom_name': device['custom_name'],
1402
+ 'login_domain': device['login_domain'],
1411
1403
  'device_type': device['device_type'],
1412
1404
  'platform': device['platform'],
1413
1405
  'browser': device['browser'],
@@ -1671,6 +1663,82 @@ class StandaloneAuthManager:
1671
1663
  cursor.close()
1672
1664
  conn.close()
1673
1665
 
1666
+ def get_device_session_history(self, user_id, device_fingerprint, login_domain, limit=10):
1667
+ """获取设备会话历史记录"""
1668
+ conn = self.pool.connection()
1669
+ cursor = conn.cursor()
1670
+
1671
+ try:
1672
+ cursor.execute('''
1673
+ SELECT device_id, session_version, device_name, current_ip, first_ip,
1674
+ last_activity, created_at, is_active, trust_level
1675
+ FROM device_sessions
1676
+ WHERE user_id = %s AND device_fingerprint = %s AND login_domain = %s
1677
+ ORDER BY session_version DESC
1678
+ LIMIT %s
1679
+ ''', (user_id, device_fingerprint, login_domain, limit))
1680
+
1681
+ sessions = cursor.fetchall()
1682
+
1683
+ return [
1684
+ {
1685
+ 'device_id': session['device_id'],
1686
+ 'session_version': session['session_version'],
1687
+ 'device_name': session['device_name'],
1688
+ 'current_ip': session['current_ip'],
1689
+ 'first_ip': session['first_ip'],
1690
+ 'last_activity': session['last_activity'].isoformat() if session['last_activity'] else None,
1691
+ 'created_at': session['created_at'].isoformat() if session['created_at'] else None,
1692
+ 'is_active': session['is_active'],
1693
+ 'trust_level': session['trust_level']
1694
+ }
1695
+ for session in sessions
1696
+ ]
1697
+
1698
+ finally:
1699
+ cursor.close()
1700
+ conn.close()
1701
+
1702
+ def cleanup_old_device_sessions(self, user_id=None, days_threshold=90):
1703
+ """清理旧的非活跃设备会话记录"""
1704
+ conn = self.pool.connection()
1705
+ cursor = conn.cursor()
1706
+
1707
+ try:
1708
+ current_time_utc = datetime.now(timezone.utc)
1709
+ threshold_time = current_time_utc - timedelta(days=days_threshold)
1710
+
1711
+ if user_id:
1712
+ # 清理特定用户的旧记录
1713
+ cursor.execute('''
1714
+ DELETE FROM device_sessions
1715
+ WHERE user_id = %s AND is_active = 0 AND last_activity < %s
1716
+ ''', (user_id, threshold_time))
1717
+ else:
1718
+ # 清理所有用户的旧记录
1719
+ cursor.execute('''
1720
+ DELETE FROM device_sessions
1721
+ WHERE is_active = 0 AND last_activity < %s
1722
+ ''', (threshold_time,))
1723
+
1724
+ cleaned_count = cursor.rowcount
1725
+
1726
+ return {
1727
+ 'success': True,
1728
+ 'cleaned_count': cleaned_count,
1729
+ 'message': f'成功清理 {cleaned_count} 条旧设备会话记录'
1730
+ }
1731
+
1732
+ except Exception as e:
1733
+ return {
1734
+ 'success': False,
1735
+ 'cleaned_count': 0,
1736
+ 'message': f'清理旧设备会话记录失败: {str(e)}'
1737
+ }
1738
+ finally:
1739
+ cursor.close()
1740
+ conn.close()
1741
+
1674
1742
 
1675
1743
  # Flask集成装饰器
1676
1744
  def require_auth(auth_manager):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mdbq
3
- Version: 4.0.96
3
+ Version: 4.0.98
4
4
  Home-page: https://pypi.org/project/mdbq
5
5
  Author: xigua,
6
6
  Author-email: 2587125111@qq.com
@@ -1 +0,0 @@
1
- VERSION = '4.0.96'
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes