chore: 测试/正式环境端口分离,更新部署配置
- API: 测试:3000, 正式:9000 - TCP: 测试:3001, 正式:9001 - 部署路径: /data/pms (正式), /data/pms_dev (测试) - URL: /pms/ 前缀 - 添加 systemd 服务单元和部署脚本 - 更新 nginx 配置和 AGENTS.md 文档
This commit is contained in:
parent
3cc21b621f
commit
7a04f6da53
202
AGENTS.md
202
AGENTS.md
@ -28,8 +28,13 @@
|
||||
D:\charging-cabinet\
|
||||
├── tasks/ # 任务文件(opencode执行)
|
||||
├── docs/ # 开发文档、协议文档
|
||||
├── deploy/ # 部署配置文件
|
||||
│ ├── nginx/ # Nginx配置
|
||||
│ ├── *.service # systemd服务单元
|
||||
│ └── deploy-*.sh # 部署脚本
|
||||
├── software/
|
||||
│ ├── server/ # Rust后端
|
||||
│ ├── api-server/ # Rust API服务
|
||||
│ ├── device-server/ # Rust TCP设备服务
|
||||
│ └── web/ # React前端
|
||||
├── hardware/ # 硬件资料(原理图/PCB等)
|
||||
└── test/ # 测试用例/报告
|
||||
@ -39,10 +44,22 @@ D:\charging-cabinet\
|
||||
|
||||
### 环境规划
|
||||
|
||||
| 环境 | 域名 | 反代指向 | Nginx配置 |
|
||||
|------|------|---------|----------|
|
||||
| 正式环境 | app.anzhizhichong.com | 127.0.0.1:3000(服务器本地) | /etc/nginx/conf.d/app.anzhizhichong.com.conf |
|
||||
| 测试环境 | test.anzhizhichong.com | 10.8.0.248:3001(本地电脑) | /etc/nginx/conf.d/test.anzhizhichong.com.conf |
|
||||
| 环境 | 域名 | API服务 | TCP服务 | Redis DB | 部署路径 | URL路径 |
|
||||
|------|------|---------|---------|----------|----------|---------|
|
||||
| 正式环境 | app.anzhizhichong.com | :9000 | :9001 | db5 | /data/pms | /pms/ |
|
||||
| 测试环境 | test.anzhizhichong.com | :3000 | :3001 | db6 | /data/pms_dev | /pms/ |
|
||||
|
||||
**服务端口分配:**
|
||||
- 测试环境:API 3000,TCP 3001
|
||||
- 正式环境:API 9000,TCP 9001
|
||||
|
||||
**部署路径:**
|
||||
- 正式环境:/data/pms(api-server、device-server、web)
|
||||
- 测试环境:/data/pms_dev(api-server、device-server、web)
|
||||
|
||||
**Nginx配置:**
|
||||
- 正式: /etc/nginx/conf.d/app.anzhizhichong.com.conf
|
||||
- 测试: /etc/nginx/conf.d/test.anzhizhichong.com.conf
|
||||
|
||||
### 服务器信息
|
||||
|
||||
@ -56,6 +73,11 @@ D:\charging-cabinet\
|
||||
- **不装**: Docker/Node/Rust(本地编译传二进制)
|
||||
- **SSH**: plink连接,首次需 `echo y` 接受host key
|
||||
|
||||
### 正式环境数据库
|
||||
|
||||
- MySQL: `rm-bp1bb7yiv5n8z4uvp.mysql.rds.aliyuncs.com`,user: root,password: Hbhyg731024,database: `pms`
|
||||
- Redis: `r-bp1lsezvgzmgtwhbtg.redis.rds.aliyuncs.com`,password: Hbhyg731024,db: 5
|
||||
|
||||
### 本地开发环境
|
||||
|
||||
**数据库连接(通过ZeroTier):**
|
||||
@ -67,7 +89,7 @@ MySQL: 10.8.0.252:3306
|
||||
|
||||
Redis: 10.8.0.252:6379
|
||||
password: Hbhyg731024@
|
||||
db: 5 (新建,不动db1-4)
|
||||
db: 6 (测试环境用db6,不动db1-5)
|
||||
```
|
||||
|
||||
**代理说明:**
|
||||
@ -128,6 +150,174 @@ Redis: 10.8.0.252:6379
|
||||
2. 跑起来看效果
|
||||
3. 抽查关键逻辑(异常处理、权限校验、协议解析)
|
||||
|
||||
## 部署与维护
|
||||
|
||||
### SSH连接
|
||||
|
||||
```bash
|
||||
# 外网IP(推荐)
|
||||
echo y | plink -ssh root@118.31.119.206 -pw "Hbhyg731024@"
|
||||
|
||||
# ZeroTier内网IP
|
||||
echo y | plink -ssh root@10.8.0.252 -pw "Hbhyg731024@"
|
||||
```
|
||||
|
||||
### 数据库连接
|
||||
|
||||
```bash
|
||||
# MySQL(通过ZeroTier代理)
|
||||
mysql -h 10.8.0.252 -u root -p
|
||||
# 密码:Hbhyg731024@
|
||||
# 测试环境库:pms_dev
|
||||
# 正式环境库:pms
|
||||
|
||||
# Redis(通过ZeroTier代理)
|
||||
redis-cli -h 10.8.0.252 -a "Hbhyg731024@"
|
||||
# 测试环境:db6
|
||||
# 正式环境:db5
|
||||
```
|
||||
|
||||
### Nginx配置
|
||||
|
||||
**正式环境** (`/etc/nginx/conf.d/app.anzhizhichong.com.conf`):
|
||||
```nginx
|
||||
server {
|
||||
listen 80;
|
||||
server_name app.anzhizhichong.com;
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name app.anzhizhichong.com;
|
||||
|
||||
ssl_certificate /etc/nginx/ssl/app.anzhizhichong.com.pem;
|
||||
ssl_certificate_key /etc/nginx/ssl/app.anzhizhichong.com.key;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers HIGH:!aNULL:!MD5;
|
||||
|
||||
root /data/pms;
|
||||
index index.html;
|
||||
|
||||
location /pms/doc/ {
|
||||
proxy_pass http://127.0.0.1:3080/;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
client_max_body_size 500m;
|
||||
}
|
||||
|
||||
location /pms/api/ {
|
||||
proxy_pass http://127.0.0.1:9000/;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
|
||||
location /pms/ {
|
||||
try_files $uri $uri/ /pms/index.html;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**测试环境** (`/etc/nginx/conf.d/test.anzhizhichong.com.conf`):
|
||||
```nginx
|
||||
server {
|
||||
listen 80;
|
||||
server_name test.anzhizhichong.com;
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name test.anzhizhichong.com;
|
||||
|
||||
ssl_certificate /etc/nginx/ssl/test.anzhizhichong.com.pem;
|
||||
ssl_certificate_key /etc/nginx/ssl/test.anzhizhichong.com.key;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers HIGH:!aNULL:!MD5;
|
||||
|
||||
root /data/pms_dev;
|
||||
index index.html;
|
||||
|
||||
location /pms/api/ {
|
||||
proxy_pass http://127.0.0.1:3000/;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
|
||||
location /pms/ {
|
||||
try_files $uri $uri/ /pms/index.html;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> 测试环境SSL证书与正式环境共用(test是app证书的SAN域名),部署时复制pem+key即可。
|
||||
|
||||
### 部署步骤
|
||||
|
||||
**1. 本地编译**
|
||||
```bash
|
||||
# 后端(Rust)
|
||||
cd software/api-server
|
||||
cargo build --release
|
||||
|
||||
cd software/device-server
|
||||
cargo build --release
|
||||
|
||||
# 前端(React)
|
||||
cd software/web
|
||||
npm run build
|
||||
```
|
||||
|
||||
**2. 传输到服务器**
|
||||
```bash
|
||||
# 传输后端二进制(测试环境)
|
||||
scp software/api-server/target/release/api-server root@118.31.119.206:/data/pms_dev/api-server/
|
||||
scp software/device-server/target/release/device-server root@118.31.119.206:/data/pms_dev/device-server/
|
||||
|
||||
# 传输后端二进制(正式环境)
|
||||
scp software/api-server/target/release/api-server root@118.31.119.206:/data/pms/api-server/
|
||||
scp software/device-server/target/release/device-server root@118.31.119.206:/data/pms/device-server/
|
||||
|
||||
# 传输前端静态文件
|
||||
scp -r software/web/dist/* root@118.31.119.206:/data/pms_dev/web/ # 测试环境
|
||||
scp -r software/web/dist/* root@118.31.119.206:/data/pms/web/ # 正式环境
|
||||
```
|
||||
|
||||
**3. 服务器重启服务**
|
||||
```bash
|
||||
# SSH到服务器
|
||||
echo y | plink -ssh root@118.31.119.206 -pw "Hbhyg731024@"
|
||||
|
||||
# 重启测试环境
|
||||
systemctl restart pms-api-test
|
||||
systemctl restart pms-device-test
|
||||
|
||||
# 重启正式环境
|
||||
systemctl restart pms-api-prod
|
||||
systemctl restart pms-device-prod
|
||||
|
||||
# 重启Nginx
|
||||
systemctl restart nginx
|
||||
```
|
||||
|
||||
### 服务管理
|
||||
|
||||
```bash
|
||||
# 查看服务状态
|
||||
systemctl status pms-api
|
||||
systemctl status nginx
|
||||
|
||||
# 查看日志
|
||||
journalctl -u pms-api -f
|
||||
tail -f /var/log/nginx/error.log
|
||||
```
|
||||
|
||||
## 硬件架构
|
||||
|
||||
- 柜控板(ID128)×1 + 仓控板(ID1-N)×N,每仓控板管6个仓体
|
||||
|
||||
10
README.md
10
README.md
@ -29,12 +29,12 @@ charging-cabinet/
|
||||
|
||||
## 部署架构
|
||||
|
||||
| 环境 | 域名 | 反代指向 |
|
||||
|------|------|---------|
|
||||
| 正式环境 | app.anzhizhichong.com | 127.0.0.1:3000(服务器本地) |
|
||||
| 测试环境 | test.anzhizhichong.com | 10.8.0.248:3001(本地电脑) |
|
||||
| 环境 | 域名 | 说明 |
|
||||
|------|------|------|
|
||||
| 正式环境 | app.anzhizhichong.com | 后台管理 + API服务 |
|
||||
| 测试环境 | test.anzhizhichong.com | 测试环境 |
|
||||
|
||||
**服务器**:118.31.119.206(CentOS Stream 9,2核/3.5G/49G)
|
||||
**服务器**:CentOS Stream 9
|
||||
|
||||
**内部服务**:
|
||||
- Gitea: https://app.anzhizhichong.com/doc/
|
||||
|
||||
50
deploy/deploy-prod.sh
Normal file
50
deploy/deploy-prod.sh
Normal file
@ -0,0 +1,50 @@
|
||||
#!/bin/bash
|
||||
# PMS 部署脚本 - 正式环境
|
||||
|
||||
set -e
|
||||
|
||||
echo "=== PMS 正式环境部署 ==="
|
||||
|
||||
# 1. 创建目录
|
||||
echo "1. 创建部署目录..."
|
||||
mkdir -p /data/pms/api-server
|
||||
mkdir -p /data/pms/device-server
|
||||
mkdir -p /data/pms/web
|
||||
|
||||
# 2. 复制文件
|
||||
echo "2. 复制服务文件..."
|
||||
cp software/api-server/target/release/api-server /data/pms/api-server/
|
||||
cp software/api-server/.env.production /data/pms/api-server/.env
|
||||
|
||||
cp software/device-server/target/release/device-server /data/pms/device-server/
|
||||
cp software/device-server/.env.production /data/pms/device-server/.env
|
||||
|
||||
# 3. 复制前端静态文件
|
||||
echo "3. 复制前端文件..."
|
||||
cp -r software/web/dist/* /data/pms/web/
|
||||
|
||||
# 3. 复制systemd服务
|
||||
echo "3. 安装systemd服务..."
|
||||
cp deploy/pms-api-prod.service /etc/systemd/system/
|
||||
cp deploy/pms-device-prod.service /etc/systemd/system/
|
||||
|
||||
# 4. 复制nginx配置
|
||||
echo "4. 安装nginx配置..."
|
||||
cp deploy/nginx/app.anzhizhichong.com.conf /etc/nginx/conf.d/
|
||||
|
||||
# 5. 重启服务
|
||||
echo "5. 重启服务..."
|
||||
systemctl daemon-reload
|
||||
systemctl restart pms-api-prod
|
||||
systemctl restart pms-device-prod
|
||||
systemctl restart nginx
|
||||
|
||||
# 6. 设置开机启动
|
||||
echo "6. 设置开机启动..."
|
||||
systemctl enable pms-api-prod
|
||||
systemctl enable pms-device-prod
|
||||
|
||||
echo "=== 部署完成 ==="
|
||||
echo "正式环境: https://app.anzhizhichong.com"
|
||||
echo "API端口: 9000"
|
||||
echo "TCP端口: 9001"
|
||||
50
deploy/deploy-test.sh
Normal file
50
deploy/deploy-test.sh
Normal file
@ -0,0 +1,50 @@
|
||||
#!/bin/bash
|
||||
# PMS 部署脚本 - 测试环境
|
||||
|
||||
set -e
|
||||
|
||||
echo "=== PMS 测试环境部署 ==="
|
||||
|
||||
# 1. 创建目录
|
||||
echo "1. 创建部署目录..."
|
||||
mkdir -p /data/pms_dev/api-server
|
||||
mkdir -p /data/pms_dev/device-server
|
||||
mkdir -p /data/pms_dev/web
|
||||
|
||||
# 2. 复制文件
|
||||
echo "2. 复制服务文件..."
|
||||
cp software/api-server/target/release/api-server /data/pms_dev/api-server/
|
||||
cp software/api-server/.env.test /data/pms_dev/api-server/.env
|
||||
|
||||
cp software/device-server/target/release/device-server /data/pms_dev/device-server/
|
||||
cp software/device-server/.env.test /data/pms_dev/device-server/.env
|
||||
|
||||
# 3. 复制前端静态文件
|
||||
echo "3. 复制前端文件..."
|
||||
cp -r software/web/dist/* /data/pms_dev/web/
|
||||
|
||||
# 3. 复制systemd服务
|
||||
echo "3. 安装systemd服务..."
|
||||
cp deploy/pms-api-test.service /etc/systemd/system/
|
||||
cp deploy/pms-device-test.service /etc/systemd/system/
|
||||
|
||||
# 4. 复制nginx配置
|
||||
echo "4. 安装nginx配置..."
|
||||
cp deploy/nginx/test.anzhizhichong.com.conf /etc/nginx/conf.d/
|
||||
|
||||
# 5. 重启服务
|
||||
echo "5. 重启服务..."
|
||||
systemctl daemon-reload
|
||||
systemctl restart pms-api-test
|
||||
systemctl restart pms-device-test
|
||||
systemctl restart nginx
|
||||
|
||||
# 6. 设置开机启动
|
||||
echo "6. 设置开机启动..."
|
||||
systemctl enable pms-api-test
|
||||
systemctl enable pms-device-test
|
||||
|
||||
echo "=== 部署完成 ==="
|
||||
echo "测试环境: https://test.anzhizhichong.com"
|
||||
echo "API端口: 3000"
|
||||
echo "TCP端口: 3001"
|
||||
39
deploy/nginx/app.anzhizhichong.com.conf
Normal file
39
deploy/nginx/app.anzhizhichong.com.conf
Normal file
@ -0,0 +1,39 @@
|
||||
server {
|
||||
listen 80;
|
||||
server_name app.anzhizhichong.com;
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name app.anzhizhichong.com;
|
||||
|
||||
ssl_certificate /etc/nginx/ssl/app.anzhizhichong.com.pem;
|
||||
ssl_certificate_key /etc/nginx/ssl/app.anzhizhichong.com.key;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers HIGH:!aNULL:!MD5;
|
||||
|
||||
root /data/pms;
|
||||
index index.html;
|
||||
|
||||
location /pms/doc/ {
|
||||
proxy_pass http://127.0.0.1:3080/;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
client_max_body_size 500m;
|
||||
}
|
||||
|
||||
location /pms/api/ {
|
||||
proxy_pass http://127.0.0.1:9000/;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
|
||||
location /pms/ {
|
||||
try_files $uri $uri/ /pms/index.html;
|
||||
}
|
||||
}
|
||||
30
deploy/nginx/test.anzhizhichong.com.conf
Normal file
30
deploy/nginx/test.anzhizhichong.com.conf
Normal file
@ -0,0 +1,30 @@
|
||||
server {
|
||||
listen 80;
|
||||
server_name test.anzhizhichong.com;
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name test.anzhizhichong.com;
|
||||
|
||||
ssl_certificate /etc/nginx/ssl/test.anzhizhichong.com.pem;
|
||||
ssl_certificate_key /etc/nginx/ssl/test.anzhizhichong.com.key;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers HIGH:!aNULL:!MD5;
|
||||
|
||||
root /data/pms_dev;
|
||||
index index.html;
|
||||
|
||||
location /pms/api/ {
|
||||
proxy_pass http://127.0.0.1:3000/;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
|
||||
location /pms/ {
|
||||
try_files $uri $uri/ /pms/index.html;
|
||||
}
|
||||
}
|
||||
15
deploy/pms-api-prod.service
Normal file
15
deploy/pms-api-prod.service
Normal file
@ -0,0 +1,15 @@
|
||||
[Unit]
|
||||
Description=PMS API Server - Production
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=root
|
||||
WorkingDirectory=/data/pms/api-server
|
||||
ExecStart=/data/pms/api-server/api-server
|
||||
EnvironmentFile=/data/pms/api-server/.env
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
15
deploy/pms-api-test.service
Normal file
15
deploy/pms-api-test.service
Normal file
@ -0,0 +1,15 @@
|
||||
[Unit]
|
||||
Description=PMS API Server - Test Environment
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=root
|
||||
WorkingDirectory=/data/pms_dev/api-server
|
||||
ExecStart=/data/pms_dev/api-server/api-server
|
||||
EnvironmentFile=/data/pms_dev/api-server/.env
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
15
deploy/pms-device-prod.service
Normal file
15
deploy/pms-device-prod.service
Normal file
@ -0,0 +1,15 @@
|
||||
[Unit]
|
||||
Description=PMS Device Server - Production
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=root
|
||||
WorkingDirectory=/data/pms/device-server
|
||||
ExecStart=/data/pms/device-server/device-server
|
||||
EnvironmentFile=/data/pms/device-server/.env
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
15
deploy/pms-device-test.service
Normal file
15
deploy/pms-device-test.service
Normal file
@ -0,0 +1,15 @@
|
||||
[Unit]
|
||||
Description=PMS Device Server - Test Environment
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=root
|
||||
WorkingDirectory=/data/pms_dev/device-server
|
||||
ExecStart=/data/pms_dev/device-server/device-server
|
||||
EnvironmentFile=/data/pms_dev/device-server/.env
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@ -1,12 +1,12 @@
|
||||
# MySQL 连接(pms_dev 库由迁移自动创建)
|
||||
DATABASE_URL=mysql://root:Hbhyg731024%40@10.8.0.252:3306/pms_dev
|
||||
# MySQL 连接(正式环境 pms 库)
|
||||
DATABASE_URL=mysql://root:Hbhyg731024%40@10.8.0.252:3306/pms
|
||||
|
||||
# Redis 连接(生产 db5)
|
||||
REDIS_URL=redis://:Hbhyg731024%40@10.8.0.252:6379/5
|
||||
|
||||
# HTTP 服务
|
||||
HOST=0.0.0.0
|
||||
PORT=3000
|
||||
PORT=9000 # 正式环境API
|
||||
|
||||
# JWT
|
||||
JWT_SECRET=pms-dev-secret-change-me-in-production
|
||||
|
||||
@ -6,7 +6,7 @@ REDIS_URL=redis://:Hbhyg731024%40@10.8.0.252:6379/6
|
||||
|
||||
# HTTP 服务
|
||||
HOST=0.0.0.0
|
||||
PORT=3000
|
||||
PORT=3000 # 测试环境API
|
||||
|
||||
# JWT
|
||||
JWT_SECRET=pms-test-secret
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
REDIS_URL=redis://:Hbhyg731024%40@10.8.0.252:6379/5
|
||||
|
||||
# TCP 设备通讯
|
||||
TCP_PORT=3002
|
||||
TCP_PORT=9001 # 正式环境TCP
|
||||
|
||||
# 节点ID
|
||||
NODE_ID=node-1
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
REDIS_URL=redis://:Hbhyg731024%40@10.8.0.252:6379/6
|
||||
|
||||
# TCP 设备通讯
|
||||
TCP_PORT=3002
|
||||
TCP_PORT=3001 # 测试环境TCP
|
||||
|
||||
# 节点ID
|
||||
NODE_ID=node-1
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user