mdbq 4.0.95__tar.gz → 4.0.97__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.95 → mdbq-4.0.97}/PKG-INFO +1 -1
  2. mdbq-4.0.97/mdbq/__version__.py +1 -0
  3. {mdbq-4.0.95 → mdbq-4.0.97}/mdbq/auth/auth_backend.py +180 -29
  4. {mdbq-4.0.95 → mdbq-4.0.97}/mdbq.egg-info/PKG-INFO +1 -1
  5. mdbq-4.0.95/mdbq/__version__.py +0 -1
  6. {mdbq-4.0.95 → mdbq-4.0.97}/README.txt +0 -0
  7. {mdbq-4.0.95 → mdbq-4.0.97}/mdbq/__init__.py +0 -0
  8. {mdbq-4.0.95 → mdbq-4.0.97}/mdbq/auth/__init__.py +0 -0
  9. {mdbq-4.0.95 → mdbq-4.0.97}/mdbq/auth/rate_limiter.py +0 -0
  10. {mdbq-4.0.95 → mdbq-4.0.97}/mdbq/js/__init__.py +0 -0
  11. {mdbq-4.0.95 → mdbq-4.0.97}/mdbq/js/jc.py +0 -0
  12. {mdbq-4.0.95 → mdbq-4.0.97}/mdbq/log/__init__.py +0 -0
  13. {mdbq-4.0.95 → mdbq-4.0.97}/mdbq/log/mylogger.py +0 -0
  14. {mdbq-4.0.95 → mdbq-4.0.97}/mdbq/myconf/__init__.py +0 -0
  15. {mdbq-4.0.95 → mdbq-4.0.97}/mdbq/myconf/myconf.py +0 -0
  16. {mdbq-4.0.95 → mdbq-4.0.97}/mdbq/mysql/__init__.py +0 -0
  17. {mdbq-4.0.95 → mdbq-4.0.97}/mdbq/mysql/deduplicator.py +0 -0
  18. {mdbq-4.0.95 → mdbq-4.0.97}/mdbq/mysql/mysql.py +0 -0
  19. {mdbq-4.0.95 → mdbq-4.0.97}/mdbq/mysql/s_query.py +0 -0
  20. {mdbq-4.0.95 → mdbq-4.0.97}/mdbq/mysql/unique_.py +0 -0
  21. {mdbq-4.0.95 → mdbq-4.0.97}/mdbq/mysql/uploader.py +0 -0
  22. {mdbq-4.0.95 → mdbq-4.0.97}/mdbq/other/__init__.py +0 -0
  23. {mdbq-4.0.95 → mdbq-4.0.97}/mdbq/other/download_sku_picture.py +0 -0
  24. {mdbq-4.0.95 → mdbq-4.0.97}/mdbq/other/error_handler.py +0 -0
  25. {mdbq-4.0.95 → mdbq-4.0.97}/mdbq/other/otk.py +0 -0
  26. {mdbq-4.0.95 → mdbq-4.0.97}/mdbq/other/pov_city.py +0 -0
  27. {mdbq-4.0.95 → mdbq-4.0.97}/mdbq/other/ua_sj.py +0 -0
  28. {mdbq-4.0.95 → mdbq-4.0.97}/mdbq/pbix/__init__.py +0 -0
  29. {mdbq-4.0.95 → mdbq-4.0.97}/mdbq/pbix/pbix_refresh.py +0 -0
  30. {mdbq-4.0.95 → mdbq-4.0.97}/mdbq/pbix/refresh_all.py +0 -0
  31. {mdbq-4.0.95 → mdbq-4.0.97}/mdbq/redis/__init__.py +0 -0
  32. {mdbq-4.0.95 → mdbq-4.0.97}/mdbq/redis/getredis.py +0 -0
  33. {mdbq-4.0.95 → mdbq-4.0.97}/mdbq/route/__init__.py +0 -0
  34. {mdbq-4.0.95 → mdbq-4.0.97}/mdbq/route/analytics.py +0 -0
  35. {mdbq-4.0.95 → mdbq-4.0.97}/mdbq/route/monitor.py +0 -0
  36. {mdbq-4.0.95 → mdbq-4.0.97}/mdbq/route/routes.py +0 -0
  37. {mdbq-4.0.95 → mdbq-4.0.97}/mdbq/selenium/__init__.py +0 -0
  38. {mdbq-4.0.95 → mdbq-4.0.97}/mdbq/selenium/get_driver.py +0 -0
  39. {mdbq-4.0.95 → mdbq-4.0.97}/mdbq/spider/__init__.py +0 -0
  40. {mdbq-4.0.95 → mdbq-4.0.97}/mdbq.egg-info/SOURCES.txt +0 -0
  41. {mdbq-4.0.95 → mdbq-4.0.97}/mdbq.egg-info/dependency_links.txt +0 -0
  42. {mdbq-4.0.95 → mdbq-4.0.97}/mdbq.egg-info/top_level.txt +0 -0
  43. {mdbq-4.0.95 → mdbq-4.0.97}/setup.cfg +0 -0
  44. {mdbq-4.0.95 → mdbq-4.0.97}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mdbq
3
- Version: 4.0.95
3
+ Version: 4.0.97
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.97'
@@ -221,24 +221,37 @@ class StandaloneAuthManager:
221
221
  CREATE TABLE IF NOT EXISTS device_sessions (
222
222
  id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
223
223
  user_id BIGINT UNSIGNED NOT NULL,
224
- device_id VARCHAR(64) CHARACTER SET ascii NOT NULL,
225
- device_fingerprint VARCHAR(128) CHARACTER SET ascii NOT NULL,
226
- device_name VARCHAR(100) NOT NULL DEFAULT 'Unknown Device',
224
+ device_id VARCHAR(255) CHARACTER SET ascii NOT NULL,
225
+ device_fingerprint VARCHAR(255) CHARACTER SET ascii NOT NULL,
226
+ login_domain VARCHAR(255) NOT NULL DEFAULT '' COMMENT '登录时的域名',
227
+ device_name VARCHAR(255) NOT NULL DEFAULT 'Unknown Device',
228
+ custom_name VARCHAR(255) DEFAULT NULL COMMENT '用户自定义设备名称',
227
229
  device_type ENUM('mobile', 'desktop', 'tablet', 'unknown') NOT NULL DEFAULT 'unknown',
228
230
  platform VARCHAR(50) DEFAULT NULL,
229
231
  browser VARCHAR(50) DEFAULT NULL,
230
- ip_address VARCHAR(45) NOT NULL,
232
+ browser_version VARCHAR(40) DEFAULT NULL,
233
+ screen_resolution VARCHAR(40) DEFAULT NULL COMMENT '屏幕分辨率',
234
+ timezone_offset INT DEFAULT NULL COMMENT '时区偏移(分钟)',
235
+ language VARCHAR(20) DEFAULT NULL COMMENT '浏览器语言',
236
+ hardware_concurrency TINYINT UNSIGNED DEFAULT NULL COMMENT 'CPU核心数',
237
+ current_ip VARCHAR(45) NOT NULL COMMENT '当前IP地址',
238
+ first_ip VARCHAR(45) NOT NULL COMMENT '首次登录IP',
231
239
  user_agent TEXT NOT NULL,
232
240
  last_activity TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
233
241
  created_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
234
242
  is_active TINYINT(1) NOT NULL DEFAULT 1,
243
+ trust_level ENUM('trusted', 'normal', 'suspicious') NOT NULL DEFAULT 'normal' COMMENT '设备信任级别',
235
244
 
236
245
  UNIQUE KEY uk_device_sessions_device_id (device_id),
246
+ UNIQUE KEY uk_device_sessions_user_fingerprint_domain (user_id, device_fingerprint, login_domain),
237
247
  KEY idx_device_sessions_user_id (user_id),
238
248
  KEY idx_device_sessions_user_device (user_id, device_id),
239
249
  KEY idx_device_sessions_last_activity (last_activity),
240
250
  KEY idx_device_sessions_is_active (is_active),
241
251
  KEY idx_device_sessions_fingerprint (device_fingerprint),
252
+ KEY idx_device_sessions_trust_level (trust_level),
253
+ KEY idx_device_sessions_login_domain (login_domain),
254
+ KEY idx_device_sessions_user_domain (user_id, login_domain),
242
255
 
243
256
  CONSTRAINT fk_device_sessions_user_id
244
257
  FOREIGN KEY (user_id)
@@ -611,10 +624,21 @@ class StandaloneAuthManager:
611
624
  except jwt.InvalidTokenError:
612
625
  return None
613
626
 
614
- def create_or_update_device_session(self, user_id, ip_address, user_agent):
627
+ def create_or_update_device_session(self, user_id, ip_address, user_agent, device_info=None, login_domain=''):
615
628
  """创建或更新设备会话"""
616
- device_fingerprint = self._generate_device_fingerprint(user_agent, ip_address)
617
- device_info = self._parse_user_agent(user_agent)
629
+ # 解析用户代理获取基本信息
630
+ parsed_ua = self._parse_user_agent(user_agent)
631
+
632
+ # 合并设备信息
633
+ full_device_info = {
634
+ 'user_agent': user_agent,
635
+ 'platform': parsed_ua.get('platform'),
636
+ **parsed_ua,
637
+ **(device_info or {})
638
+ }
639
+
640
+ # 生成包含域名的设备指纹
641
+ device_fingerprint = self._generate_device_fingerprint(full_device_info, login_domain)
618
642
  device_id = secrets.token_urlsafe(32)
619
643
 
620
644
  conn = self.pool.connection()
@@ -623,11 +647,11 @@ class StandaloneAuthManager:
623
647
  try:
624
648
  current_time_utc = datetime.now(timezone.utc)
625
649
 
626
- # 检查设备是否已存在
650
+ # 检查设备是否已存在(包含域名匹配)
627
651
  cursor.execute('''
628
652
  SELECT id, device_id FROM device_sessions
629
- WHERE user_id = %s AND device_fingerprint = %s AND is_active = 1
630
- ''', (user_id, device_fingerprint))
653
+ WHERE user_id = %s AND device_fingerprint = %s AND login_domain = %s AND is_active = 1
654
+ ''', (user_id, device_fingerprint, login_domain))
631
655
 
632
656
  existing_session = cursor.fetchone()
633
657
 
@@ -638,12 +662,23 @@ class StandaloneAuthManager:
638
662
 
639
663
  cursor.execute('''
640
664
  UPDATE device_sessions
641
- SET ip_address = %s, user_agent = %s, last_activity = %s,
642
- device_name = %s, device_type = %s, platform = %s, browser = %s
665
+ SET current_ip = %s, user_agent = %s, last_activity = %s,
666
+ device_name = %s, device_type = %s, platform = %s, browser = %s,
667
+ browser_version = %s, screen_resolution = %s, timezone_offset = %s,
668
+ language = %s, hardware_concurrency = %s, login_domain = %s
643
669
  WHERE id = %s
644
670
  ''', (ip_address, user_agent, current_time_utc,
645
- device_info['device_name'], device_info['device_type'],
646
- device_info['platform'], device_info['browser'], device_session_id))
671
+ full_device_info.get('device_name', 'Unknown Device'),
672
+ full_device_info.get('device_type', 'unknown'),
673
+ full_device_info.get('platform'),
674
+ full_device_info.get('browser'),
675
+ full_device_info.get('browser_version'),
676
+ full_device_info.get('screen_resolution'),
677
+ full_device_info.get('timezone_offset'),
678
+ full_device_info.get('language'),
679
+ full_device_info.get('hardware_concurrency'),
680
+ login_domain,
681
+ device_session_id))
647
682
  else:
648
683
  # 检查设备数量限制
649
684
  cursor.execute('''
@@ -669,16 +704,25 @@ class StandaloneAuthManager:
669
704
  # 创建新设备会话
670
705
  cursor.execute('''
671
706
  INSERT INTO device_sessions (
672
- user_id, device_id, device_fingerprint, device_name, device_type,
673
- platform, browser, ip_address, user_agent, last_activity
674
- ) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
675
- ''', (user_id, device_id, device_fingerprint, device_info['device_name'],
676
- device_info['device_type'], device_info['platform'], device_info['browser'],
677
- ip_address, user_agent, current_time_utc))
707
+ user_id, device_id, device_fingerprint, login_domain, device_name, device_type,
708
+ platform, browser, browser_version, screen_resolution, timezone_offset,
709
+ language, hardware_concurrency, current_ip, first_ip, user_agent, last_activity
710
+ ) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
711
+ ''', (user_id, device_id, device_fingerprint, login_domain,
712
+ full_device_info.get('device_name', 'Unknown Device'),
713
+ full_device_info.get('device_type', 'unknown'),
714
+ full_device_info.get('platform'),
715
+ full_device_info.get('browser'),
716
+ full_device_info.get('browser_version'),
717
+ full_device_info.get('screen_resolution'),
718
+ full_device_info.get('timezone_offset'),
719
+ full_device_info.get('language'),
720
+ full_device_info.get('hardware_concurrency'),
721
+ ip_address, ip_address, user_agent, current_time_utc))
678
722
 
679
723
  device_session_id = cursor.lastrowid
680
724
 
681
- return device_session_id, device_id, device_info['device_name']
725
+ return device_session_id, device_id, full_device_info.get('device_name', 'Unknown Device')
682
726
 
683
727
  finally:
684
728
  cursor.close()
@@ -1214,10 +1258,36 @@ class StandaloneAuthManager:
1214
1258
  WHERE id = %s
1215
1259
  ''', (device_session_id,))
1216
1260
 
1217
- def _generate_device_fingerprint(self, user_agent, ip_address):
1218
- """生成设备指纹"""
1219
- fingerprint_data = f"{user_agent}:{ip_address}:{datetime.now().strftime('%Y%m%d')}"
1220
- return hashlib.sha256(fingerprint_data.encode()).hexdigest()[:32]
1261
+ def _generate_device_fingerprint(self, device_info, login_domain=''):
1262
+ """
1263
+ 生成稳定的设备指纹(包含登录域名)
1264
+
1265
+ Args:
1266
+ device_info (dict): 设备信息
1267
+ - user_agent: 用户代理
1268
+ - screen_resolution: 屏幕分辨率
1269
+ - timezone_offset: 时区偏移
1270
+ - language: 浏览器语言
1271
+ - hardware_concurrency: CPU核心数
1272
+ - platform: 平台信息
1273
+ login_domain (str): 登录时的域名
1274
+ """
1275
+ # 提取稳定的设备特征(包含登录域名)
1276
+ stable_features = [
1277
+ device_info.get('user_agent', ''),
1278
+ device_info.get('screen_resolution', ''),
1279
+ str(device_info.get('timezone_offset', 0)),
1280
+ device_info.get('language', ''),
1281
+ str(device_info.get('hardware_concurrency', 0)),
1282
+ device_info.get('platform', ''),
1283
+ login_domain or '', # 登录域名作为设备指纹的一部分
1284
+ ]
1285
+
1286
+ # 过滤空值并连接
1287
+ fingerprint_data = ':'.join(filter(None, stable_features))
1288
+
1289
+ # 生成SHA256哈希
1290
+ return hashlib.sha256(fingerprint_data.encode('utf-8')).hexdigest()[:32]
1221
1291
 
1222
1292
  def _parse_user_agent(self, user_agent):
1223
1293
  """解析User-Agent获取设备信息"""
@@ -1320,8 +1390,10 @@ class StandaloneAuthManager:
1320
1390
 
1321
1391
  try:
1322
1392
  cursor.execute('''
1323
- SELECT device_id, device_fingerprint, device_name, device_type, platform, browser,
1324
- ip_address, last_activity, created_at, is_active
1393
+ SELECT device_id, device_fingerprint, login_domain, device_name, custom_name, device_type,
1394
+ platform, browser, browser_version, screen_resolution, timezone_offset,
1395
+ language, hardware_concurrency, current_ip, first_ip,
1396
+ last_activity, created_at, is_active, trust_level
1325
1397
  FROM device_sessions
1326
1398
  WHERE user_id = %s AND is_active = 1
1327
1399
  ORDER BY last_activity DESC
@@ -1336,13 +1408,25 @@ class StandaloneAuthManager:
1336
1408
  if is_current:
1337
1409
  current_device_id = device['device_id']
1338
1410
 
1411
+ # 生成显示名称(优先使用自定义名称)
1412
+ display_name = device['custom_name'] or device['device_name']
1413
+
1339
1414
  devices_list.append({
1340
1415
  'device_id': device['device_id'],
1341
- 'device_name': device['device_name'],
1416
+ 'device_name': display_name,
1417
+ 'custom_name': device['custom_name'],
1418
+ 'login_domain': device['login_domain'],
1342
1419
  'device_type': device['device_type'],
1343
1420
  'platform': device['platform'],
1344
1421
  'browser': device['browser'],
1345
- 'ip_address': device['ip_address'],
1422
+ 'browser_version': device['browser_version'],
1423
+ 'screen_resolution': device['screen_resolution'],
1424
+ 'timezone_offset': device['timezone_offset'],
1425
+ 'language': device['language'],
1426
+ 'hardware_concurrency': device['hardware_concurrency'],
1427
+ 'current_ip': device['current_ip'],
1428
+ 'first_ip': device['first_ip'],
1429
+ 'trust_level': device['trust_level'],
1346
1430
  'last_activity': device['last_activity'].isoformat() if device['last_activity'] else None,
1347
1431
  'created_at': device['created_at'].isoformat() if device['created_at'] else None,
1348
1432
  'is_current': is_current
@@ -1528,6 +1612,73 @@ class StandaloneAuthManager:
1528
1612
  cursor.close()
1529
1613
  conn.close()
1530
1614
 
1615
+ def rename_device(self, user_id, device_id, custom_name):
1616
+ """重命名设备"""
1617
+ conn = self.pool.connection()
1618
+ cursor = conn.cursor()
1619
+
1620
+ try:
1621
+ # 验证设备是否属于该用户
1622
+ cursor.execute('''
1623
+ SELECT id FROM device_sessions
1624
+ WHERE user_id = %s AND device_id = %s AND is_active = 1
1625
+ ''', (user_id, device_id))
1626
+
1627
+ device = cursor.fetchone()
1628
+
1629
+ if not device:
1630
+ return {'success': False, 'message': '设备不存在或已失效'}
1631
+
1632
+ # 更新自定义名称
1633
+ cursor.execute('''
1634
+ UPDATE device_sessions
1635
+ SET custom_name = %s
1636
+ WHERE id = %s
1637
+ ''', (custom_name.strip() if custom_name else None, device['id']))
1638
+
1639
+ return {'success': True, 'message': '设备重命名成功'}
1640
+
1641
+ except Exception as e:
1642
+ return {'success': False, 'message': f'设备重命名失败: {str(e)}'}
1643
+ finally:
1644
+ cursor.close()
1645
+ conn.close()
1646
+
1647
+ def set_device_trust_level(self, user_id, device_id, trust_level):
1648
+ """设置设备信任级别"""
1649
+ if trust_level not in ['trusted', 'normal', 'suspicious']:
1650
+ return {'success': False, 'message': '无效的信任级别'}
1651
+
1652
+ conn = self.pool.connection()
1653
+ cursor = conn.cursor()
1654
+
1655
+ try:
1656
+ # 验证设备是否属于该用户
1657
+ cursor.execute('''
1658
+ SELECT id FROM device_sessions
1659
+ WHERE user_id = %s AND device_id = %s AND is_active = 1
1660
+ ''', (user_id, device_id))
1661
+
1662
+ device = cursor.fetchone()
1663
+
1664
+ if not device:
1665
+ return {'success': False, 'message': '设备不存在或已失效'}
1666
+
1667
+ # 更新信任级别
1668
+ cursor.execute('''
1669
+ UPDATE device_sessions
1670
+ SET trust_level = %s
1671
+ WHERE id = %s
1672
+ ''', (trust_level, device['id']))
1673
+
1674
+ return {'success': True, 'message': '设备信任级别更新成功'}
1675
+
1676
+ except Exception as e:
1677
+ return {'success': False, 'message': f'设备信任级别更新失败: {str(e)}'}
1678
+ finally:
1679
+ cursor.close()
1680
+ conn.close()
1681
+
1531
1682
 
1532
1683
  # Flask集成装饰器
1533
1684
  def require_auth(auth_manager):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mdbq
3
- Version: 4.0.95
3
+ Version: 4.0.97
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.95'
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