fix: 适配新服务器蓝绿部署 — nginx 原生/PostgreSQL Docker 容器名/部署脚本
Some checks failed
Deploy Backend / deploy (push) Has been cancelled
Some checks failed
Deploy Backend / deploy (push) Has been cancelled
This commit is contained in:
@@ -107,7 +107,6 @@ check_service_file() {
|
||||
}
|
||||
|
||||
# ---------- 蓝绿部署:检查目标是否为当前活动环境 ----------
|
||||
NGINX_CONTAINER="1Panel-openresty-ABu5"
|
||||
|
||||
get_proxy_conf() {
|
||||
if [ "${APP_ENV}" = "development" ]; then
|
||||
@@ -122,7 +121,7 @@ check_not_active() {
|
||||
|
||||
local proxy_conf; proxy_conf=$(get_proxy_conf)
|
||||
local active_env
|
||||
active_env=$(remote "docker exec ${NGINX_CONTAINER} cat ${proxy_conf} 2>/dev/null" 2>/dev/null | grep -oP '127\.0\.0\.1:\d+' | head -1 || echo "")
|
||||
active_env=$(remote "sudo cat ${proxy_conf} 2>/dev/null" 2>/dev/null | grep -oP '127\.0\.0\.1:\d+' | head -1 || echo "")
|
||||
|
||||
local current_target
|
||||
case "$active_env" in
|
||||
@@ -271,7 +270,7 @@ backup_database() {
|
||||
local ts; ts=$(date +%Y%m%d_%H%M%S)
|
||||
local bf="${REMOTE_DIR}/backups/${DB_NAME}_${ts}.dump"
|
||||
remote "mkdir -p ${REMOTE_DIR}/backups"
|
||||
if remote "docker exec ${DB_CONTAINER} pg_dump -Fc -U ${DB_USER} ${DB_NAME} > /tmp/backup_${ts}.dump && mv /tmp/backup_${ts}.dump '${bf}'" 2>&1; then
|
||||
if remote "sudo docker exec ${DB_CONTAINER} pg_dump -Fc -U ${DB_USER} ${DB_NAME} > /tmp/backup_${ts}.dump && mv /tmp/backup_${ts}.dump '${bf}'" 2>&1; then
|
||||
log_info "数据库备份成功: ${DB_NAME}_${ts}.dump"
|
||||
cleanup_old_db_backups
|
||||
else
|
||||
@@ -332,16 +331,16 @@ run_migrations() {
|
||||
[ -n "$sql_files" ] && backup_database
|
||||
|
||||
# 创建迁移追踪表
|
||||
remote "docker exec -i ${DB_CONTAINER} psql -U ${DB_USER} -d ${DB_NAME}" <<<'CREATE TABLE IF NOT EXISTS _migrations (name TEXT PRIMARY KEY, applied_at TIMESTAMPTZ DEFAULT NOW());' 2>/dev/null || true
|
||||
remote "sudo docker exec -i ${DB_CONTAINER} psql -U ${DB_USER} -d ${DB_NAME}" <<<'CREATE TABLE IF NOT EXISTS _migrations (name TEXT PRIMARY KEY, applied_at TIMESTAMPTZ DEFAULT NOW());' 2>/dev/null || true
|
||||
|
||||
for f in $sql_files; do
|
||||
local name; name=$(basename "$f")
|
||||
log_info "检查迁移: ${name}"
|
||||
local done; done=$(remote "docker exec ${DB_CONTAINER} psql -U ${DB_USER} -d ${DB_NAME} -tAc \"SELECT COUNT(*) FROM _migrations WHERE name='${name}'\"" 2>/dev/null || echo "0")
|
||||
local done; done=$(remote "sudo docker exec ${DB_CONTAINER} psql -U ${DB_USER} -d ${DB_NAME} -tAc \"SELECT COUNT(*) FROM _migrations WHERE name='${name}'\"" 2>/dev/null || echo "0")
|
||||
[ "$done" != "0" ] && { log_info " 跳过(已执行): ${name}"; continue; }
|
||||
log_info " 执行: ${name}"
|
||||
remote "cat '${f}' | docker exec -i ${DB_CONTAINER} psql -U ${DB_USER} -d ${DB_NAME}" && \
|
||||
remote "docker exec ${DB_CONTAINER} psql -U ${DB_USER} -d ${DB_NAME} -c \"INSERT INTO _migrations (name) VALUES ('${name}')\"" && \
|
||||
remote "cat '${f}' | sudo docker exec -i ${DB_CONTAINER} psql -U ${DB_USER} -d ${DB_NAME}" && \
|
||||
remote "sudo docker exec ${DB_CONTAINER} psql -U ${DB_USER} -d ${DB_NAME} -c \"INSERT INTO _migrations (name) VALUES ('${name}')\"" && \
|
||||
log_info " 完成: ${name}" || log_warn " 迁移 ${name} 可能已执行或失败"
|
||||
done
|
||||
log_info "数据库迁移完成"
|
||||
@@ -459,12 +458,12 @@ check_ssl_expiry() {
|
||||
log_step "检查 SSL 证书到期时间..."
|
||||
[ "$DRY_RUN" = true ] && { log_dry "openssl x509 -checkend 2592000"; return 0; }
|
||||
|
||||
# 从 OpenResty 容器内读取证书
|
||||
local cert_info; cert_info=$(remote "docker exec 1Panel-openresty-ABu5 openssl x509 -in /www/sites/${TEST_DOMAIN#https://}/ssl/fullchain.pem -noout -enddate -checkend 2592000 2>/dev/null" || echo "")
|
||||
# 读取 SSL 证书
|
||||
local cert_info; cert_info=$(remote "sudo openssl x509 -in /www/sites/${TEST_DOMAIN#https://}/ssl/fullchain.pem -noout -enddate -checkend 2592000 2>/dev/null" || echo "")
|
||||
|
||||
if echo "$cert_info" | grep -q "Certificate will expire"; then
|
||||
local expiry; expiry=$(remote "docker exec 1Panel-openresty-ABu5 openssl x509 -in /www/sites/${TEST_DOMAIN#https://}/ssl/fullchain.pem -noout -enddate 2>/dev/null | cut -d= -f2" || echo "未知")
|
||||
local days_left; days_left=$(remote "docker exec 1Panel-openresty-ABu5 openssl x509 -in /www/sites/${TEST_DOMAIN#https://}/ssl/fullchain.pem -noout -checkend 86400 2>/dev/null | grep -c 'will expire'" || echo "0")
|
||||
local expiry; expiry=$(remote "sudo openssl x509 -in /www/sites/${TEST_DOMAIN#https://}/ssl/fullchain.pem -noout -enddate 2>/dev/null | cut -d= -f2" || echo "未知")
|
||||
local days_left; days_left=$(remote "sudo openssl x509 -in /www/sites/${TEST_DOMAIN#https://}/ssl/fullchain.pem -noout -checkend 86400 2>/dev/null | grep -c 'will expire'" || echo "0")
|
||||
log_warn "SSL 证书将于 ${expiry} 到期(不足 30 天)!"
|
||||
[ "$days_left" -gt "0" ] && log_warn " 警告:证书将在 1 天内到期,请立即续期!"
|
||||
else
|
||||
|
||||
52
lib/test.sh
52
lib/test.sh
@@ -9,7 +9,7 @@ BASE_URL="http://127.0.0.1"
|
||||
TEST_USER_ID=""
|
||||
|
||||
setup_test_user() {
|
||||
TEST_USER_ID=$(remote "docker exec ${DB_CONTAINER} psql -U ${DB_USER} -d ${DB_NAME} -tAc \"
|
||||
TEST_USER_ID=$(remote "sudo docker exec ${DB_CONTAINER} psql -U ${DB_USER} -d ${DB_NAME} -tAc \"
|
||||
INSERT INTO users (openid, name, type) VALUES ('test_\$(date +%s)', 'test_user', 2)
|
||||
RETURNING id;
|
||||
\"" 2>/dev/null | tr -d ' ' || echo "")
|
||||
@@ -19,7 +19,7 @@ setup_test_user() {
|
||||
|
||||
cleanup_test_user() {
|
||||
if [ -n "$TEST_USER_ID" ]; then
|
||||
remote "docker exec ${DB_CONTAINER} psql -U ${DB_USER} -d ${DB_NAME} -c \"
|
||||
remote "sudo docker exec ${DB_CONTAINER} psql -U ${DB_USER} -d ${DB_NAME} -c \"
|
||||
DELETE FROM payment_orders WHERE user_id = ${TEST_USER_ID};
|
||||
DELETE FROM users WHERE id = ${TEST_USER_ID};
|
||||
\"" 2>/dev/null || true
|
||||
@@ -74,13 +74,13 @@ test_basic_connectivity() {
|
||||
}
|
||||
|
||||
test_database_connection() {
|
||||
local r; r=$(remote "docker exec ${DB_CONTAINER} pg_isready -U ${DB_USER} 2>/dev/null" || echo "failed")
|
||||
local r; r=$(remote "sudo docker exec ${DB_CONTAINER} pg_isready -U ${DB_USER} 2>/dev/null" || echo "failed")
|
||||
[[ "$r" =~ "accepting connections" ]] && log_info " PostgreSQL 连接正常" || { log_warn " PostgreSQL 连接失败"; return 1; }
|
||||
|
||||
r=$(remote "docker exec ${DB_CONTAINER} psql -U ${DB_USER} -d ${DB_NAME} -tAc 'SELECT 1'" 2>/dev/null || echo "failed")
|
||||
r=$(remote "sudo docker exec ${DB_CONTAINER} psql -U ${DB_USER} -d ${DB_NAME} -tAc 'SELECT 1'" 2>/dev/null || echo "failed")
|
||||
[ "$r" = "1" ] && log_info " 数据库查询正常" || { log_warn " 数据库查询失败"; return 1; }
|
||||
|
||||
r=$(remote "docker exec ${DB_CONTAINER} psql -U ${DB_USER} -d ${DB_NAME} -tAc 'SELECT tablename FROM pg_tables WHERE schemaname='\''public'\'' LIMIT 1'" 2>/dev/null || echo "failed")
|
||||
r=$(remote "sudo docker exec ${DB_CONTAINER} psql -U ${DB_USER} -d ${DB_NAME} -tAc 'SELECT tablename FROM pg_tables WHERE schemaname='\''public'\'' LIMIT 1'" 2>/dev/null || echo "failed")
|
||||
[ -n "$r" ] && log_info " 表存在: ${r}" || { log_warn " 表查询失败"; return 1; }
|
||||
log_info "数据库连接测试通过"; return 0
|
||||
}
|
||||
@@ -173,10 +173,10 @@ test_payment_endpoints() {
|
||||
}
|
||||
|
||||
test_migration_status() {
|
||||
local r; r=$(remote "docker exec ${DB_CONTAINER} psql -U ${DB_USER} -d ${DB_NAME} -tAc 'SELECT COUNT(*) FROM _migrations'" 2>/dev/null || echo "0")
|
||||
local r; r=$(remote "sudo docker exec ${DB_CONTAINER} psql -U ${DB_USER} -d ${DB_NAME} -tAc 'SELECT COUNT(*) FROM _migrations'" 2>/dev/null || echo "0")
|
||||
[ "$r" -gt "0" ] 2>/dev/null && log_info " _migrations 表存在,已执行 ${r} 个迁移 ✅" || { log_warn " _migrations 表不存在或无记录"; return 0; }
|
||||
|
||||
local last; last=$(remote "docker exec ${DB_CONTAINER} psql -U ${DB_USER} -d ${DB_NAME} -tAc 'SELECT name FROM _migrations ORDER BY applied_at DESC LIMIT 1'" 2>/dev/null || echo "")
|
||||
local last; last=$(remote "sudo docker exec ${DB_CONTAINER} psql -U ${DB_USER} -d ${DB_NAME} -tAc 'SELECT name FROM _migrations ORDER BY applied_at DESC LIMIT 1'" 2>/dev/null || echo "")
|
||||
[ -n "$last" ] && log_info " 最新迁移: ${last} ✅"
|
||||
return 0
|
||||
}
|
||||
@@ -212,11 +212,11 @@ test_payment_flow() {
|
||||
# 创建测试订单
|
||||
local sql="INSERT INTO payment_orders (user_id, order_no, package_type, amount, status, expires_at)
|
||||
VALUES (${TEST_USER_ID}, '${test_no}', '${pkg_type}', 100, 'pending', NOW() + INTERVAL '${expected_days} days');"
|
||||
remote "docker exec ${DB_CONTAINER} psql -U ${DB_USER} -d ${DB_NAME} -c \"${sql}\"" 2>/dev/null || true
|
||||
remote "sudo docker exec ${DB_CONTAINER} psql -U ${DB_USER} -d ${DB_NAME} -c \"${sql}\"" 2>/dev/null || true
|
||||
|
||||
# 验证到期时间间隔
|
||||
local interval_days
|
||||
interval_days=$(remote "docker exec ${DB_CONTAINER} psql -U ${DB_USER} -d ${DB_NAME} -tAc \"
|
||||
interval_days=$(remote "sudo docker exec ${DB_CONTAINER} psql -U ${DB_USER} -d ${DB_NAME} -tAc \"
|
||||
SELECT EXTRACT(DAY FROM (expires_at - created_at)) FROM payment_orders WHERE order_no = '${test_no}';
|
||||
\"" 2>/dev/null || echo "0")
|
||||
interval_days=${interval_days%.*}
|
||||
@@ -230,7 +230,7 @@ test_payment_flow() {
|
||||
|
||||
# 清理测试订单
|
||||
local del="DELETE FROM payment_orders WHERE order_no = '${test_no}';"
|
||||
remote "docker exec ${DB_CONTAINER} psql -U ${DB_USER} -d ${DB_NAME} -c \"${del}\"" 2>/dev/null || true
|
||||
remote "sudo docker exec ${DB_CONTAINER} psql -U ${DB_USER} -d ${DB_NAME} -c \"${del}\"" 2>/dev/null || true
|
||||
done
|
||||
|
||||
# 阶段 2:测试提交订单后的会员时长(仅开发环境有 mock 支付)
|
||||
@@ -267,7 +267,7 @@ test_payment_flow() {
|
||||
|
||||
# 5. 验证会员时长
|
||||
local member_expires
|
||||
member_expires=$(remote "docker exec ${DB_CONTAINER} psql -U ${DB_USER} -d ${DB_NAME} -tAc \"
|
||||
member_expires=$(remote "sudo docker exec ${DB_CONTAINER} psql -U ${DB_USER} -d ${DB_NAME} -tAc \"
|
||||
SELECT EXTRACT(DAY FROM (membership_expires_at - NOW()))::int
|
||||
FROM users WHERE id = ${TEST_USER_ID};
|
||||
\"" 2>/dev/null || echo "0")
|
||||
@@ -281,9 +281,9 @@ test_payment_flow() {
|
||||
|
||||
# 6. 清理:取消会员
|
||||
local clean="UPDATE users SET is_member = false, membership_expires_at = NULL WHERE id = ${TEST_USER_ID};"
|
||||
remote "docker exec ${DB_CONTAINER} psql -U ${DB_USER} -d ${DB_NAME} -c \"${clean}\"" 2>/dev/null || true
|
||||
remote "sudo docker exec ${DB_CONTAINER} psql -U ${DB_USER} -d ${DB_NAME} -c \"${clean}\"" 2>/dev/null || true
|
||||
local cancel="UPDATE payment_orders SET status = 'cancelled' WHERE order_no = '${order_no}' AND status = 'paid';"
|
||||
remote "docker exec ${DB_CONTAINER} psql -U ${DB_USER} -d ${DB_NAME} -c \"${cancel}\"" 2>/dev/null || true
|
||||
remote "sudo docker exec ${DB_CONTAINER} psql -U ${DB_USER} -d ${DB_NAME} -c \"${cancel}\"" 2>/dev/null || true
|
||||
else
|
||||
log_warn " 未获取到订单号,跳过 mock 确认测试"
|
||||
fi
|
||||
@@ -323,16 +323,16 @@ test_payment_dedup() {
|
||||
INSERT INTO payment_orders (user_id, order_no, package_type, amount, status, expires_at) VALUES (${user_id}, '${test_no1}', 'monthly', 590, 'pending', NOW() + INTERVAL '30 days');
|
||||
INSERT INTO payment_orders (user_id, order_no, package_type, amount, status, expires_at) VALUES (${user_id}, '${test_no2}', 'monthly', 590, 'pending', NOW() + INTERVAL '30 days');
|
||||
SQLEOF
|
||||
docker exec ${DB_CONTAINER} psql -U ${DB_USER} -d ${DB_NAME} -v ON_ERROR_STOP=1 -f ${sql_file}
|
||||
sudo docker exec ${DB_CONTAINER} psql -U ${DB_USER} -d ${DB_NAME} -v ON_ERROR_STOP=1 -f ${sql_file}
|
||||
rm -f ${sql_file}" 2>&1 || { log_warn " SQL 执行异常"; all_ok=false; }
|
||||
|
||||
# 2. 查询数量
|
||||
local pending_count
|
||||
pending_count=$(remote "docker exec ${DB_CONTAINER} psql -U ${DB_USER} -d ${DB_NAME} -tAc \"SELECT COUNT(*) FROM payment_orders WHERE user_id = ${user_id} AND package_type = 'monthly' AND status = 'pending';\"" 2>/dev/null || echo "0")
|
||||
pending_count=$(remote "sudo docker exec ${DB_CONTAINER} psql -U ${DB_USER} -d ${DB_NAME} -tAc \"SELECT COUNT(*) FROM payment_orders WHERE user_id = ${user_id} AND package_type = 'monthly' AND status = 'pending';\"" 2>/dev/null || echo "0")
|
||||
pending_count=$(echo "$pending_count" | tr -d ' ')
|
||||
|
||||
# 3. 清理
|
||||
remote "docker exec ${DB_CONTAINER} psql -U ${DB_USER} -d ${DB_NAME} -tAc \"DELETE FROM payment_orders WHERE order_no LIKE '${uuid_prefix}%';\"" 2>/dev/null || true
|
||||
remote "sudo docker exec ${DB_CONTAINER} psql -U ${DB_USER} -d ${DB_NAME} -tAc \"DELETE FROM payment_orders WHERE order_no LIKE '${uuid_prefix}%';\"" 2>/dev/null || true
|
||||
|
||||
if [ -n "$pending_count" ] && [ "$pending_count" -ge 2 ] 2>/dev/null; then
|
||||
log_info " 同用户同套餐有 ${pending_count} 个 pending 订单(去重逻辑正常)✅"
|
||||
@@ -391,20 +391,20 @@ test_payment_refund() {
|
||||
# 创建已支付订单(模拟一个月会员)
|
||||
local sql_create="INSERT INTO payment_orders (user_id, order_no, package_type, amount, status, paid_at, expires_at)
|
||||
VALUES (${TEST_USER_ID}, '${test_no}', 'monthly', 590, 'paid', NOW(), NOW() + INTERVAL '30 days');"
|
||||
remote "docker exec ${DB_CONTAINER} psql -U ${DB_USER} -d ${DB_NAME} -c \"${sql_create}\"" 2>/dev/null || true
|
||||
remote "sudo docker exec ${DB_CONTAINER} psql -U ${DB_USER} -d ${DB_NAME} -c \"${sql_create}\"" 2>/dev/null || true
|
||||
|
||||
# 设置用户为会员
|
||||
local sql_member="UPDATE users SET is_member = true, membership_expires_at = NOW() + INTERVAL '30 days' WHERE id = ${TEST_USER_ID};"
|
||||
remote "docker exec ${DB_CONTAINER} psql -U ${DB_USER} -d ${DB_NAME} -c \"${sql_member}\"" 2>/dev/null || true
|
||||
remote "sudo docker exec ${DB_CONTAINER} psql -U ${DB_USER} -d ${DB_NAME} -c \"${sql_member}\"" 2>/dev/null || true
|
||||
|
||||
# 执行退款(直接调用 db 函数等价操作)
|
||||
local sql_refund="UPDATE payment_orders SET status = 'refunded' WHERE order_no = '${test_no}';"
|
||||
remote "docker exec ${DB_CONTAINER} psql -U ${DB_USER} -d ${DB_NAME} -c \"${sql_refund}\"" 2>/dev/null || true
|
||||
remote "sudo docker exec ${DB_CONTAINER} psql -U ${DB_USER} -d ${DB_NAME} -c \"${sql_refund}\"" 2>/dev/null || true
|
||||
|
||||
# 清理:取消会员 + 删除测试订单
|
||||
local clean_member="UPDATE users SET is_member = false, membership_expires_at = NULL WHERE id = ${TEST_USER_ID};"
|
||||
remote "docker exec ${DB_CONTAINER} psql -U ${DB_USER} -d ${DB_NAME} -c \"${clean_member}\"" 2>/dev/null || true
|
||||
remote "docker exec ${DB_CONTAINER} psql -U ${DB_USER} -d ${DB_NAME} -c \"DELETE FROM payment_orders WHERE order_no = '${test_no}';\"" 2>/dev/null || true
|
||||
remote "sudo docker exec ${DB_CONTAINER} psql -U ${DB_USER} -d ${DB_NAME} -c \"${clean_member}\"" 2>/dev/null || true
|
||||
remote "sudo docker exec ${DB_CONTAINER} psql -U ${DB_USER} -d ${DB_NAME} -c \"DELETE FROM payment_orders WHERE order_no = '${test_no}';\"" 2>/dev/null || true
|
||||
|
||||
log_info " 退款 + 会员取消测试完成 ✅"
|
||||
log_info "退款测试通过"
|
||||
@@ -422,15 +422,15 @@ test_payment_verify_detection() {
|
||||
# 构造一个 expiry = paid_at + 1 天的异常订单(模拟 ELSE INTERVAL '0 days')
|
||||
local sql_create="INSERT INTO payment_orders (user_id, order_no, package_type, amount, status, paid_at, expires_at)
|
||||
VALUES (${TEST_USER_ID}, '${test_no}', 'half_year', 3190, 'paid', NOW(), NOW() + INTERVAL '1 day');"
|
||||
remote "docker exec ${DB_CONTAINER} psql -U ${DB_USER} -d ${DB_NAME} -c \"${sql_create}\"" 2>/dev/null || true
|
||||
remote "sudo docker exec ${DB_CONTAINER} psql -U ${DB_USER} -d ${DB_NAME} -c \"${sql_create}\"" 2>/dev/null || true
|
||||
|
||||
# 设置会员为 paid_at + 1 天(模拟 0 天会员时长)
|
||||
local sql_member="UPDATE users SET is_member = true, membership_expires_at = NOW() + INTERVAL '1 day' WHERE id = ${TEST_USER_ID};"
|
||||
remote "docker exec ${DB_CONTAINER} psql -U ${DB_USER} -d ${DB_NAME} -c \"${sql_member}\"" 2>/dev/null || true
|
||||
remote "sudo docker exec ${DB_CONTAINER} psql -U ${DB_USER} -d ${DB_NAME} -c \"${sql_member}\"" 2>/dev/null || true
|
||||
|
||||
# 验证 actual_days < 25 能被检测到
|
||||
local actual_days
|
||||
actual_days=$(remote "docker exec ${DB_CONTAINER} psql -U ${DB_USER} -d ${DB_NAME} -tAc \"
|
||||
actual_days=$(remote "sudo docker exec ${DB_CONTAINER} psql -U ${DB_USER} -d ${DB_NAME} -tAc \"
|
||||
SELECT EXTRACT(DAY FROM (u.membership_expires_at - po.paid_at))
|
||||
FROM payment_orders po JOIN users u ON u.id = po.user_id
|
||||
WHERE po.order_no = '${test_no}';
|
||||
@@ -439,8 +439,8 @@ test_payment_verify_detection() {
|
||||
|
||||
# 清理
|
||||
local clean="UPDATE users SET is_member = false, membership_expires_at = NULL WHERE id = ${TEST_USER_ID};"
|
||||
remote "docker exec ${DB_CONTAINER} psql -U ${DB_USER} -d ${DB_NAME} -c \"${clean}\"" 2>/dev/null || true
|
||||
remote "docker exec ${DB_CONTAINER} psql -U ${DB_USER} -d ${DB_NAME} -c \"DELETE FROM payment_orders WHERE order_no = '${test_no}';\"" 2>/dev/null || true
|
||||
remote "sudo docker exec ${DB_CONTAINER} psql -U ${DB_USER} -d ${DB_NAME} -c \"${clean}\"" 2>/dev/null || true
|
||||
remote "sudo docker exec ${DB_CONTAINER} psql -U ${DB_USER} -d ${DB_NAME} -c \"DELETE FROM payment_orders WHERE order_no = '${test_no}';\"" 2>/dev/null || true
|
||||
|
||||
if [ "$actual_days" -lt 25 ] 2>/dev/null; then
|
||||
log_info " 异常时长 ${actual_days} 天可被 verify_membership_after_payment 检测到(阈值 25 天)✅"
|
||||
|
||||
Reference in New Issue
Block a user