feat: add cockpit index page + DDL reference for SDLC module
This commit is contained in:
parent
eb62d12c23
commit
289ccb1656
255
mysql.ddl.sql
Normal file
255
mysql.ddl.sql
Normal file
@ -0,0 +1,255 @@
|
||||
|
||||
-- models/sd_deploy_envs.json
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
-- 建库时请用以下语句,支持emoji字符
|
||||
-- CREATE DATABASE mydb CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
drop table if exists sd_deploy_envs;
|
||||
CREATE TABLE sd_deploy_envs
|
||||
(
|
||||
|
||||
`id` VARCHAR(32) NOT NULL comment '主键ID',
|
||||
`project_id` VARCHAR(32) NOT NULL comment '项目ID',
|
||||
`env_type` VARCHAR(20) NOT NULL comment '环境类型',
|
||||
`host` VARCHAR(200) NOT NULL comment 'SSH主机地址',
|
||||
`port` int NOT NULL DEFAULT '22' comment 'SSH端口',
|
||||
`user` VARCHAR(100) NOT NULL comment 'SSH用户',
|
||||
`ssh_key_path` VARCHAR(500) comment 'SSH密钥路径',
|
||||
`sudo_enabled` VARCHAR(1) NOT NULL DEFAULT ''N'' comment '免密Sudo',
|
||||
`deploy_path` VARCHAR(500) NOT NULL comment '部署目录',
|
||||
`python_path` VARCHAR(500) comment 'Python路径',
|
||||
`db_host` VARCHAR(200) comment '数据库地址',
|
||||
`db_port` int DEFAULT '3306' comment '数据库端口',
|
||||
`db_name` VARCHAR(100) comment '数据库名',
|
||||
`db_user` VARCHAR(100) comment '数据库用户',
|
||||
`db_password` VARCHAR(500) comment '数据库密码(加密)',
|
||||
`status` VARCHAR(20) NOT NULL DEFAULT ''configured'' comment '环境状态',
|
||||
`verified_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP comment '最近验证时间',
|
||||
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL comment '创建时间',
|
||||
`updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP comment '更新时间'
|
||||
|
||||
|
||||
,primary key(id)
|
||||
|
||||
|
||||
)
|
||||
CHARACTER SET utf8mb4
|
||||
COLLATE utf8mb4_unicode_ci
|
||||
engine=innodb
|
||||
comment '部署环境表'
|
||||
;
|
||||
|
||||
CREATE INDEX sd_deploy_envs_idx_sd_deploy_envs_project ON sd_deploy_envs(project_id);
|
||||
CREATE INDEX sd_deploy_envs_idx_sd_deploy_envs_type ON sd_deploy_envs(env_type);
|
||||
CREATE UNIQUE INDEX sd_deploy_envs_idx_sd_deploy_envs_unique ON sd_deploy_envs(project_id,env_type);
|
||||
|
||||
-- models/sd_test_plans.json
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
-- 建库时请用以下语句,支持emoji字符
|
||||
-- CREATE DATABASE mydb CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
drop table if exists sd_test_plans;
|
||||
CREATE TABLE sd_test_plans
|
||||
(
|
||||
|
||||
`id` VARCHAR(32) NOT NULL comment '主键ID',
|
||||
`iteration_id` VARCHAR(32) NOT NULL comment '迭代ID',
|
||||
`plan_name` VARCHAR(200) NOT NULL comment '方案名称',
|
||||
`plan_type` VARCHAR(20) NOT NULL comment '方案类型',
|
||||
`scope` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci comment '测试范围',
|
||||
`environment` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci comment '测试环境要求(JSON)',
|
||||
`entry_criteria` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci comment '准入条件',
|
||||
`exit_criteria` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci comment '准出条件',
|
||||
`status` VARCHAR(20) NOT NULL DEFAULT ''draft'' comment '方案状态',
|
||||
`created_by` VARCHAR(32) comment '创建人',
|
||||
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL comment '创建时间',
|
||||
`updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP comment '更新时间'
|
||||
|
||||
|
||||
,primary key(id)
|
||||
|
||||
|
||||
)
|
||||
CHARACTER SET utf8mb4
|
||||
COLLATE utf8mb4_unicode_ci
|
||||
engine=innodb
|
||||
comment '测试方案表'
|
||||
;
|
||||
|
||||
CREATE INDEX sd_test_plans_idx_sd_test_plans_iteration ON sd_test_plans(iteration_id);
|
||||
CREATE INDEX sd_test_plans_idx_sd_test_plans_status ON sd_test_plans(status);
|
||||
|
||||
-- models/sd_test_cases.json
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
-- 建库时请用以下语句,支持emoji字符
|
||||
-- CREATE DATABASE mydb CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
drop table if exists sd_test_cases;
|
||||
CREATE TABLE sd_test_cases
|
||||
(
|
||||
|
||||
`id` VARCHAR(32) NOT NULL comment '主键ID',
|
||||
`plan_id` VARCHAR(32) NOT NULL comment '方案ID',
|
||||
`case_name` VARCHAR(200) NOT NULL comment '用例名称',
|
||||
`case_type` VARCHAR(20) NOT NULL comment '用例类型',
|
||||
`priority` VARCHAR(10) NOT NULL DEFAULT ''P2'' comment '优先级',
|
||||
`precondition` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci comment '前置条件',
|
||||
`steps` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci comment '测试步骤(JSON数组)',
|
||||
`expected_result` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci comment '预期结果',
|
||||
`actual_result` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci comment '实际结果',
|
||||
`status` VARCHAR(20) NOT NULL DEFAULT ''pending'' comment '用例状态',
|
||||
`executed_by` VARCHAR(32) comment '执行人',
|
||||
`executed_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP comment '执行时间',
|
||||
`duration_ms` int comment '执行耗时(毫秒)',
|
||||
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL comment '创建时间'
|
||||
|
||||
|
||||
,primary key(id)
|
||||
|
||||
|
||||
)
|
||||
CHARACTER SET utf8mb4
|
||||
COLLATE utf8mb4_unicode_ci
|
||||
engine=innodb
|
||||
comment '测试用例表'
|
||||
;
|
||||
|
||||
CREATE INDEX sd_test_cases_idx_sd_test_cases_plan ON sd_test_cases(plan_id);
|
||||
CREATE INDEX sd_test_cases_idx_sd_test_cases_status ON sd_test_cases(status);
|
||||
CREATE INDEX sd_test_cases_idx_sd_test_cases_type ON sd_test_cases(case_type);
|
||||
|
||||
-- models/sd_iterations.json
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
-- 建库时请用以下语句,支持emoji字符
|
||||
-- CREATE DATABASE mydb CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
drop table if exists sd_iterations;
|
||||
CREATE TABLE sd_iterations
|
||||
(
|
||||
|
||||
`id` VARCHAR(32) NOT NULL comment '主键ID',
|
||||
`project_id` VARCHAR(32) NOT NULL comment '项目ID',
|
||||
`iteration_name` VARCHAR(200) NOT NULL comment '迭代名称',
|
||||
`iteration_type` VARCHAR(20) NOT NULL DEFAULT ''new_feature'' comment '迭代类型',
|
||||
`scope` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci comment '迭代范围(JSON)',
|
||||
`task_id` VARCHAR(32) comment '关联Pipeline任务ID',
|
||||
`status` VARCHAR(20) NOT NULL DEFAULT ''planning'' comment '迭代状态',
|
||||
`priority` int NOT NULL DEFAULT '5' comment '优先级',
|
||||
`started_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP comment '开始时间',
|
||||
`completed_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP comment '完成时间',
|
||||
`created_by` VARCHAR(32) comment '创建人',
|
||||
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL comment '创建时间',
|
||||
`updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP comment '更新时间'
|
||||
|
||||
|
||||
,primary key(id)
|
||||
|
||||
|
||||
)
|
||||
CHARACTER SET utf8mb4
|
||||
COLLATE utf8mb4_unicode_ci
|
||||
engine=innodb
|
||||
comment 'SDLC迭代表'
|
||||
;
|
||||
|
||||
CREATE INDEX sd_iterations_idx_sd_iterations_project ON sd_iterations(project_id);
|
||||
CREATE INDEX sd_iterations_idx_sd_iterations_status ON sd_iterations(status);
|
||||
|
||||
-- models/sd_bugs.json
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
-- 建库时请用以下语句,支持emoji字符
|
||||
-- CREATE DATABASE mydb CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
drop table if exists sd_bugs;
|
||||
CREATE TABLE sd_bugs
|
||||
(
|
||||
|
||||
`id` VARCHAR(32) NOT NULL comment '主键ID',
|
||||
`iteration_id` VARCHAR(32) NOT NULL comment '迭代ID',
|
||||
`case_id` VARCHAR(32) comment '关联用例ID',
|
||||
`step_name` VARCHAR(100) comment '发现步骤名',
|
||||
`title` VARCHAR(300) NOT NULL comment 'Bug标题',
|
||||
`description` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci comment '详细描述',
|
||||
`severity` VARCHAR(10) NOT NULL DEFAULT ''major'' comment '严重程度',
|
||||
`priority` VARCHAR(10) NOT NULL DEFAULT ''P1'' comment '优先级',
|
||||
`status` VARCHAR(20) NOT NULL DEFAULT ''open'' comment 'Bug状态',
|
||||
`reporter_type` VARCHAR(10) NOT NULL comment '提交人类型',
|
||||
`reporter_id` VARCHAR(32) NOT NULL comment '提交人ID',
|
||||
`assignee_id` VARCHAR(32) comment '处理人ID',
|
||||
`fix_description` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci comment '修复说明',
|
||||
`fix_commit` VARCHAR(100) comment '修复Commit',
|
||||
`verified_by` VARCHAR(32) comment '验证人',
|
||||
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL comment '创建时间',
|
||||
`updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP comment '更新时间',
|
||||
`closed_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP comment '关闭时间'
|
||||
|
||||
|
||||
,primary key(id)
|
||||
|
||||
|
||||
)
|
||||
CHARACTER SET utf8mb4
|
||||
COLLATE utf8mb4_unicode_ci
|
||||
engine=innodb
|
||||
comment 'Bug管理表'
|
||||
;
|
||||
|
||||
CREATE INDEX sd_bugs_idx_sd_bugs_iteration ON sd_bugs(iteration_id);
|
||||
CREATE INDEX sd_bugs_idx_sd_bugs_status ON sd_bugs(status);
|
||||
CREATE INDEX sd_bugs_idx_sd_bugs_severity ON sd_bugs(severity);
|
||||
CREATE INDEX sd_bugs_idx_sd_bugs_assignee ON sd_bugs(assignee_id);
|
||||
|
||||
-- models/sd_projects.json
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
-- 建库时请用以下语句,支持emoji字符
|
||||
-- CREATE DATABASE mydb CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
drop table if exists sd_projects;
|
||||
CREATE TABLE sd_projects
|
||||
(
|
||||
|
||||
`id` VARCHAR(32) NOT NULL comment '主键ID',
|
||||
`name` VARCHAR(200) NOT NULL comment '项目名称',
|
||||
`description` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci comment '项目描述',
|
||||
`project_type` VARCHAR(50) NOT NULL DEFAULT ''web_app'' comment '项目类型',
|
||||
`tech_stack` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci comment '技术栈配置(JSON)',
|
||||
`repo_url` VARCHAR(500) comment '代码仓库地址',
|
||||
`pipeline_id` VARCHAR(32) comment '关联Pipeline定义ID',
|
||||
`status` VARCHAR(20) NOT NULL DEFAULT ''draft'' comment '项目状态',
|
||||
`org_id` VARCHAR(32) NOT NULL DEFAULT ''0'' comment '组织ID',
|
||||
`created_by` VARCHAR(32) comment '创建人',
|
||||
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL comment '创建时间',
|
||||
`updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP comment '更新时间'
|
||||
|
||||
|
||||
,primary key(id)
|
||||
|
||||
|
||||
)
|
||||
CHARACTER SET utf8mb4
|
||||
COLLATE utf8mb4_unicode_ci
|
||||
engine=innodb
|
||||
comment 'SDLC项目表'
|
||||
;
|
||||
|
||||
CREATE INDEX sd_projects_idx_sd_projects_status ON sd_projects(status);
|
||||
CREATE INDEX sd_projects_idx_sd_projects_org ON sd_projects(org_id);
|
||||
|
||||
203
wwwroot/index_cockpit.ui
Normal file
203
wwwroot/index_cockpit.ui
Normal file
@ -0,0 +1,203 @@
|
||||
{
|
||||
"widgettype": "VBox",
|
||||
"options": {
|
||||
"width": "100%",
|
||||
"height": "100%",
|
||||
"padding": "0"
|
||||
},
|
||||
"subwidgets": [
|
||||
{
|
||||
"widgettype": "HBox",
|
||||
"options": {
|
||||
"width": "100%",
|
||||
"alignItems": "center",
|
||||
"padding": "16px 24px 8px 24px",
|
||||
"cheight": 6,
|
||||
"gap": "12px"
|
||||
},
|
||||
"subwidgets": [
|
||||
{
|
||||
"widgettype": "Title2",
|
||||
"options": {
|
||||
"text": "开发产线驾驶舱"
|
||||
}
|
||||
},
|
||||
{
|
||||
"widgettype": "Text",
|
||||
"options": {
|
||||
"text": "AI驱动的对话式开发",
|
||||
"cfontsize": 0.9,
|
||||
"color": "#94a3b8"
|
||||
}
|
||||
},
|
||||
{
|
||||
"widgettype": "Filler"
|
||||
},
|
||||
{
|
||||
"widgettype": "Form",
|
||||
"id": "model_selector",
|
||||
"options": {
|
||||
"name": "model_selector",
|
||||
"cols": 1,
|
||||
"fields": [
|
||||
{
|
||||
"name": "model_id",
|
||||
"uitype": "code",
|
||||
"label": "",
|
||||
"placeholder": "选择模型",
|
||||
"cwidth": 12,
|
||||
"dataurl": "/pipeline-sdlc/api/cockpit_model_options.dspy",
|
||||
"valueField": "model_id",
|
||||
"textField": "model_id_text",
|
||||
"params": {
|
||||
"valueField": "model_id",
|
||||
"textField": "model_id_text"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"widgettype": "Button",
|
||||
"options": {
|
||||
"name": "model_config",
|
||||
"label": "模型配置",
|
||||
"css": "small"
|
||||
},
|
||||
"binds": [
|
||||
{
|
||||
"wid": "self",
|
||||
"event": "click",
|
||||
"actiontype": "script",
|
||||
"target": "self",
|
||||
"script": "var pw=new bricks.PopupWindow({title:'模型配置',cwidth:36,cheight:26,auto_open:true});bricks.widgetBuild({widgettype:'urlwidget',options:{url:'/pipeline_core/llm/index.ui',method:'GET'}},pw.content_w).then(function(w){if(w)pw.content_w.add_widget(w);});"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"widgettype": "HBox",
|
||||
"options": {
|
||||
"width": "100%",
|
||||
"padding": "8px 24px 8px 24px",
|
||||
"cheight": 4,
|
||||
"gap": "16px"
|
||||
},
|
||||
"subwidgets": [
|
||||
{
|
||||
"widgettype": "urlwidget",
|
||||
"id": "stats_row",
|
||||
"options": {
|
||||
"url": "{{entire_url('/pipeline-sdlc/api/cockpit_stats.dspy')}}",
|
||||
"method": "GET"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"widgettype": "HBox",
|
||||
"id": "context_bar",
|
||||
"options": {
|
||||
"width": "100%",
|
||||
"padding": "4px 24px 4px 24px",
|
||||
"cheight": 2.5,
|
||||
"gap": "12px",
|
||||
"alignItems": "center",
|
||||
"bgcolor": "#e8f0fe"
|
||||
},
|
||||
"subwidgets": [
|
||||
{
|
||||
"widgettype": "Text",
|
||||
"options": {
|
||||
"text": "📁 当前项目:--",
|
||||
"cfontsize": 0.85,
|
||||
"color": "#1e40af"
|
||||
}
|
||||
},
|
||||
{
|
||||
"widgettype": "Text",
|
||||
"options": {
|
||||
"text": "🔄 当前迭代:--",
|
||||
"cfontsize": 0.85,
|
||||
"color": "#1e40af"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"widgettype": "VBox",
|
||||
"options": {
|
||||
"css": "filler",
|
||||
"width": "100%",
|
||||
"height": "100%",
|
||||
"padding": "0 24px 0 24px",
|
||||
"gap": "0"
|
||||
},
|
||||
"subwidgets": [
|
||||
{
|
||||
"widgettype": "VScrollPanel",
|
||||
"id": "chat_scroll",
|
||||
"options": {
|
||||
"css": "filler",
|
||||
"width": "100%",
|
||||
"height": "100%",
|
||||
"bgcolor": "#f8fafc",
|
||||
"border": "1px solid #e2e8f0",
|
||||
"borderBottom": "none",
|
||||
"borderRadius": "8px 8px 0 0",
|
||||
"padding": "16px"
|
||||
},
|
||||
"subwidgets": [
|
||||
{
|
||||
"widgettype": "VBox",
|
||||
"options": {
|
||||
"alignItems": "center",
|
||||
"padding": "40px 0",
|
||||
"gap": "12px"
|
||||
},
|
||||
"subwidgets": [
|
||||
{
|
||||
"widgettype": "Text",
|
||||
"options": {
|
||||
"text": "选择一个迭代后,在下方输入你的需求。",
|
||||
"cfontsize": 1,
|
||||
"color": "#64748b"
|
||||
}
|
||||
},
|
||||
{
|
||||
"widgettype": "Text",
|
||||
"options": {
|
||||
"text": "Agent 将自动分析需求、生成设计、驱动产线执行。",
|
||||
"cfontsize": 0.85,
|
||||
"color": "#94a3b8"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"widgettype": "TextFiles",
|
||||
"id": "chat_input",
|
||||
"options": {
|
||||
"bgcolor": "#fff",
|
||||
"border": "1px solid #e2e8f0",
|
||||
"borderTop": "none",
|
||||
"borderRadius": "0 0 8px 8px",
|
||||
"padding": "10px 14px 10px 14px"
|
||||
},
|
||||
"binds": [
|
||||
{
|
||||
"wid": "self",
|
||||
"event": "inputed",
|
||||
"actiontype": "script",
|
||||
"target": "self",
|
||||
"script": "var p=params.prompt;var files=params.add_files||[];var iid='';try{var iter=bricks.getWidgetById('current_iteration_id',bricks.app);if(iter)iid=iter.options.value||'';}catch(e){}var mid='';try{var el=document.querySelector('#model_selector select')||document.querySelector('select');if(el)mid=el.value||'';}catch(e){}var chat=bricks.getWidgetById('chat_scroll',bricks.app);var at=null;if(chat){var ub=new bricks.HBox({width:'100%'});var um=new bricks.VBox({width:'85%',alignSelf:'flex-end',bgcolor:'#dbeafe',borderRadius:'12px',padding:'12px 16px',marginBottom:'10px',gap:'4px'});um.add_widget(new bricks.Text({text:'\\u4f60',cfontsize:0.75,color:'#2563eb',fontWeight:'bold'}));um.add_widget(new bricks.Text({text:p,cfontsize:0.95,color:'#1e293b',whiteSpace:'pre-wrap'}));ub.add_widget(new bricks.VBox({css:'filler'}));ub.add_widget(um);ub.add_widget(new bricks.Svg({rate:2,url:bricks_resource('imgs/chat-user.svg')}));chat.add_widget(ub);var ab=new bricks.HBox({width:'100%'});var am=new bricks.VBox({width:'85%',alignSelf:'flex-start',bgcolor:'#e8f0fe',borderRadius:'12px',padding:'12px 16px',marginBottom:'10px',gap:'4px'});am.add_widget(new bricks.Text({text:'Agent',cfontsize:0.75,color:'#3b82f6',fontWeight:'bold'}));at=new bricks.Text({text:'\\u6b63\\u5728\\u5206\\u6790\\u9700\\u6c42...',cfontsize:0.85,color:'#64748b'});am.add_widget(at);ab.add_widget(new bricks.Svg({rate:2,url:bricks_resource('imgs/llm.svg')}));ab.add_widget(am);ab.add_widget(new bricks.VBox({css:'filler'}));chat.add_widget(ab);}var body='message_text='+encodeURIComponent(p)+'&iteration_id='+encodeURIComponent(iid)+'&model_id='+encodeURIComponent(mid)+'&action=send_message&file_paths='+encodeURIComponent(JSON.stringify(files.map(function(f){return f.name||f})));fetch('/pipeline-sdlc/api/cockpit_chat.dspy',{method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded'},body:body}).then(function(r){return r.json()}).then(function(r){if(r.success){if(at)at.set_text(r.agent_reply||'\\u5df2\\u5904\\u7406');var s=bricks.getWidgetById('stats_row',bricks.app);if(s)s.render({});}else{if(at)at.set_text('\\u9519\\u8bef: '+(r.error||'\\u672a\\u77e5\\u9519\\u8bef'));}});"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user