diff --git a/backend/apps/models.py b/backend/apps/models.py index d07481a..8c1551d 100644 --- a/backend/apps/models.py +++ b/backend/apps/models.py @@ -353,6 +353,11 @@ class SecurityConfig(models.Model): max_login_attempts = models.IntegerField(default=5, verbose_name='最大登录尝试次数') lockout_duration = models.IntegerField(default=30, verbose_name='账户锁定时间(分钟)') enable_2fa = models.BooleanField(default=False, verbose_name='启用双因子认证') + # 水印配置 + watermark_enabled = models.BooleanField(default=True, verbose_name='启用水印') + watermark_content = models.TextField(default='胡图图不涂涂', verbose_name='水印内容') + watermark_show_time = models.BooleanField(default=False, verbose_name='显示时间水印') + watermark_show_username = models.BooleanField(default=False, verbose_name='显示用户名水印') update_time = models.DateTimeField(auto_now=True, null=True, verbose_name='更新时间') class Meta: diff --git a/backend/apps/views/security.py b/backend/apps/views/security.py index 919ccb4..a99f690 100644 --- a/backend/apps/views/security.py +++ b/backend/apps/views/security.py @@ -43,6 +43,10 @@ class SecurityConfigView(View): 'max_login_attempts': security_config.max_login_attempts, 'lockout_duration': security_config.lockout_duration, 'enable_2fa': security_config.enable_2fa, + 'watermark_enabled': security_config.watermark_enabled, + 'watermark_content': security_config.watermark_content, + 'watermark_show_time': security_config.watermark_show_time, + 'watermark_show_username': security_config.watermark_show_username, 'update_time': security_config.update_time.strftime('%Y-%m-%d %H:%M:%S') if security_config.update_time else None } }) @@ -67,6 +71,10 @@ class SecurityConfigView(View): max_login_attempts = data.get('max_login_attempts') lockout_duration = data.get('lockout_duration') enable_2fa = data.get('enable_2fa') + watermark_enabled = data.get('watermark_enabled') + watermark_content = data.get('watermark_content') + watermark_show_time = data.get('watermark_show_time') + watermark_show_username = data.get('watermark_show_username') # 验证输入数据 if min_password_length is not None: @@ -111,6 +119,18 @@ class SecurityConfigView(View): 'message': '账户锁定时间必须在5-60分钟之间' }) + if watermark_content is not None: + if not isinstance(watermark_content, str) or len(watermark_content.strip()) == 0: + return JsonResponse({ + 'code': 400, + 'message': '水印内容不能为空' + }) + if len(watermark_content) > 500: + return JsonResponse({ + 'code': 400, + 'message': '水印内容长度不能超过500字符' + }) + # 获取或创建安全配置 security_config, created = SecurityConfig.objects.get_or_create(id=1) @@ -127,6 +147,14 @@ class SecurityConfigView(View): security_config.lockout_duration = lockout_duration if enable_2fa is not None: security_config.enable_2fa = enable_2fa + if watermark_enabled is not None: + security_config.watermark_enabled = watermark_enabled + if watermark_content is not None: + security_config.watermark_content = watermark_content + if watermark_show_time is not None: + security_config.watermark_show_time = watermark_show_time + if watermark_show_username is not None: + security_config.watermark_show_username = watermark_show_username security_config.save() @@ -364,6 +392,73 @@ def cleanup_login_logs(request): except Exception as e: logger.error(f'清理登录日志失败: {str(e)}', exc_info=True) + return JsonResponse({ + 'code': 500, + 'message': f'服务器错误: {str(e)}' + }) + + +@csrf_exempt +@require_http_methods(["GET"]) +def get_watermark_config(request): + """获取水印配置""" + try: + # 获取安全配置 + security_config, created = SecurityConfig.objects.get_or_create( + id=1, + defaults={ + 'min_password_length': 8, + 'password_complexity': ['lowercase', 'number'], + 'session_timeout': 120, + 'max_login_attempts': 5, + 'lockout_duration': 30, + 'enable_2fa': False + } + ) + + return JsonResponse({ + 'code': 200, + 'message': '获取水印配置成功', + 'data': { + 'watermark_enabled': security_config.watermark_enabled, + 'watermark_content': security_config.watermark_content, + 'watermark_show_time': security_config.watermark_show_time, + 'watermark_show_username': security_config.watermark_show_username + } + }) + + except Exception as e: + logger.error(f'获取水印配置失败: {str(e)}', exc_info=True) + return JsonResponse({ + 'code': 500, + 'message': f'服务器错误: {str(e)}' + }) + + +@csrf_exempt +@jwt_auth_required +@require_http_methods(["GET"]) +def get_current_user_info(request): + """获取当前用户信息""" + try: + user = User.objects.get(user_id=request.user_id) + + return JsonResponse({ + 'code': 200, + 'message': '获取用户信息成功', + 'data': { + 'username': user.username, + 'name': user.name or user.username + } + }) + + except User.DoesNotExist: + return JsonResponse({ + 'code': 404, + 'message': '用户不存在' + }) + except Exception as e: + logger.error(f'获取用户信息失败: {str(e)}', exc_info=True) return JsonResponse({ 'code': 500, 'message': f'服务器错误: {str(e)}' diff --git a/backend/backend/urls.py b/backend/backend/urls.py index 64eb911..0704f57 100644 --- a/backend/backend/urls.py +++ b/backend/backend/urls.py @@ -15,7 +15,7 @@ from apps.views.logs import login_logs_list, login_log_detail from apps.views.dashboard import DashboardStatsView, BuildTrendView, BuildDetailView, RecentBuildsView, ProjectDistributionView from apps.views.webhook import GitLabWebhookView -from apps.views.security import SecurityConfigView, get_build_tasks_for_cleanup, cleanup_build_logs, cleanup_login_logs +from apps.views.security import SecurityConfigView, get_build_tasks_for_cleanup, cleanup_build_logs, cleanup_login_logs, get_watermark_config, get_current_user_info urlpatterns = [ path('admin/', admin.site.urls), @@ -71,4 +71,6 @@ urlpatterns = [ path('api/system/security/build-tasks/', get_build_tasks_for_cleanup, name='build-tasks-for-cleanup'), path('api/system/security/cleanup-build-logs/', cleanup_build_logs, name='cleanup-build-logs'), path('api/system/security/cleanup-login-logs/', cleanup_login_logs, name='cleanup-login-logs'), + path('api/system/watermark/', get_watermark_config, name='watermark-config'), + path('api/user/current/', get_current_user_info, name='current-user-info'), ] diff --git a/image/system_basic.png b/image/system_basic.png index 043f108..b6f8be8 100644 Binary files a/image/system_basic.png and b/image/system_basic.png differ diff --git a/liteops_init.sql b/liteops_init.sql index 691ad40..12f13b7 100644 --- a/liteops_init.sql +++ b/liteops_init.sql @@ -262,6 +262,10 @@ CREATE TABLE `security_config` ( `lockout_duration` int NOT NULL, `enable_2fa` tinyint(1) NOT NULL, `update_time` datetime(6) DEFAULT NULL, + `watermark_content` longtext COLLATE utf8mb4_bin NOT NULL, + `watermark_enabled` tinyint(1) NOT NULL, + `watermark_show_time` tinyint(1) NOT NULL, + `watermark_show_username` tinyint(1) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; @@ -356,4 +360,6 @@ INSERT INTO `role` (`id`, `role_id`, `name`, `description`, `permissions`, `crea -- 插入用户角色关联数据 INSERT INTO `user_role` (`id`, `create_time`, `update_time`, `role_id`, `user_id`) VALUES (1, '2025-03-27 14:45:11.269249', '2025-03-27 14:45:11.269261', '333ec25423e04a4e96b4bb238de51cc3', '9bfef5a1ee1d4054be9727934ad112es'); -SET FOREIGN_KEY_CHECKS = 1; +-- 基本设置数据 +INSERT INTO `security_config` (`id`, `min_password_length`, `password_complexity`, `session_timeout`, `max_login_attempts`, `lockout_duration`, `enable_2fa`, `update_time`, `watermark_content`, `watermark_enabled`, `watermark_show_time`, `watermark_show_username`) VALUES (1, 6, '[\"number\", \"lowercase\"]', 120, 7, 5, 0, '2025-07-22 09:47:37.053022', '胡图图不涂涂', 0, 0, 0); +SET FOREIGN_KEY_CHECKS = 1; \ No newline at end of file diff --git a/web/src/App.vue b/web/src/App.vue index 71897ae..931628f 100644 --- a/web/src/App.vue +++ b/web/src/App.vue @@ -1,18 +1,138 @@