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 @@
-
+
+
+
+
+
+