dev #2
@@ -5,6 +5,15 @@ on:
|
||||
tags:
|
||||
- 'v*'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tag:
|
||||
description: 'Docker image tag (e.g., v1.0.0, latest)'
|
||||
required: false
|
||||
default: 'latest'
|
||||
push_to_registry:
|
||||
description: 'Push to registry?'
|
||||
required: false
|
||||
default: 'true'
|
||||
|
||||
jobs:
|
||||
build-and-push:
|
||||
|
||||
@@ -5,6 +5,15 @@ on:
|
||||
branches:
|
||||
- dev
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
skip_health_check:
|
||||
description: 'Skip health check?'
|
||||
required: false
|
||||
default: 'false'
|
||||
run_migrations:
|
||||
description: 'Run database migrations?'
|
||||
required: false
|
||||
default: 'true'
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
@@ -47,17 +56,8 @@ jobs:
|
||||
cp docker-compose.yml \$DEPLOY_DIR/
|
||||
cd \$DEPLOY_DIR
|
||||
|
||||
cat > .env <<'ENVEOF'
|
||||
APP_ENV=staging
|
||||
APP_DEBUG=false
|
||||
APP_URL=https://dev.merchbay.app
|
||||
DB_HOST=$DEV_DB_HOST
|
||||
DB_PORT=${DEV_DB_PORT:-3306}
|
||||
DB_DATABASE=$DEV_DB_DATABASE
|
||||
DB_USERNAME=$DEV_DB_USERNAME
|
||||
DB_PASSWORD=$DEV_DB_PASSWORD
|
||||
DOMAIN=dev.merchbay.app
|
||||
ENVEOF
|
||||
# .env file should already exist on server with all secrets
|
||||
# If it doesn't exist, deployment will fail (this is intentional for security)
|
||||
|
||||
docker compose down || true
|
||||
docker image prune -f
|
||||
@@ -78,11 +78,6 @@ jobs:
|
||||
DEPLOY_SSH_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
|
||||
DEPLOY_USER: ${{ secrets.DEPLOY_USER }}
|
||||
DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }}
|
||||
DEV_DB_HOST: ${{ secrets.DEV_DB_HOST }}
|
||||
DEV_DB_PORT: ${{ secrets.DEV_DB_PORT }}
|
||||
DEV_DB_DATABASE: ${{ secrets.DEV_DB_DATABASE }}
|
||||
DEV_DB_USERNAME: ${{ secrets.DEV_DB_USERNAME }}
|
||||
DEV_DB_PASSWORD: ${{ secrets.DEV_DB_PASSWORD }}
|
||||
|
||||
- name: Health Check
|
||||
shell: sh
|
||||
|
||||
@@ -48,17 +48,8 @@ jobs:
|
||||
cp docker-compose.yml \$DEPLOY_DIR/
|
||||
cd \$DEPLOY_DIR
|
||||
|
||||
cat > .env <<'ENVEOF'
|
||||
APP_ENV=production
|
||||
APP_DEBUG=false
|
||||
APP_URL=https://merchbay.com
|
||||
DB_HOST=$PROD_DB_HOST
|
||||
DB_PORT=${PROD_DB_PORT:-3306}
|
||||
DB_DATABASE=$PROD_DB_DATABASE
|
||||
DB_USERNAME=$PROD_DB_USERNAME
|
||||
DB_PASSWORD=$PROD_DB_PASSWORD
|
||||
DOMAIN=merchbay.com
|
||||
ENVEOF
|
||||
# .env file should already exist on server with all secrets
|
||||
# If it doesn't exist, deployment will fail (this is intentional for security)
|
||||
|
||||
docker compose down || true
|
||||
docker image prune -f
|
||||
@@ -79,11 +70,6 @@ jobs:
|
||||
PROD_DEPLOY_SSH_KEY: ${{ secrets.PROD_DEPLOY_SSH_KEY }}
|
||||
PROD_DEPLOY_USER: ${{ secrets.PROD_DEPLOY_USER }}
|
||||
PROD_DEPLOY_HOST: ${{ secrets.PROD_DEPLOY_HOST }}
|
||||
PROD_DB_HOST: ${{ secrets.PROD_DB_HOST }}
|
||||
PROD_DB_PORT: ${{ secrets.PROD_DB_PORT }}
|
||||
PROD_DB_DATABASE: ${{ secrets.PROD_DB_DATABASE }}
|
||||
PROD_DB_USERNAME: ${{ secrets.PROD_DB_USERNAME }}
|
||||
PROD_DB_PASSWORD: ${{ secrets.PROD_DB_PASSWORD }}
|
||||
|
||||
- name: Health Check
|
||||
shell: sh
|
||||
|
||||
187
DEPLOYMENT-SETUP.md
Normal file
187
DEPLOYMENT-SETUP.md
Normal file
@@ -0,0 +1,187 @@
|
||||
# Deployment Setup Guide
|
||||
|
||||
This guide will help you set up your deployment infrastructure for the MerchBay Admin application.
|
||||
|
||||
## Quick Start
|
||||
|
||||
We've created two helper scripts to simplify the setup process:
|
||||
|
||||
### 1. Setup SSH Keys (`setup-ssh-keys.sh`)
|
||||
|
||||
Generate and configure SSH keys for Gitea deployment.
|
||||
|
||||
```bash
|
||||
./setup-ssh-keys.sh
|
||||
```
|
||||
|
||||
**What it does:**
|
||||
- Generates an SSH key pair for deployment
|
||||
- Shows you the private key to add to Gitea secrets
|
||||
- Optionally deploys the public key to your server
|
||||
- Tests the SSH connection
|
||||
|
||||
### 2. Setup Server Environment (`setup-server-env.sh`)
|
||||
|
||||
Configure `.env` files on your deployment servers.
|
||||
|
||||
```bash
|
||||
./setup-server-env.sh
|
||||
```
|
||||
|
||||
**What it does:**
|
||||
- Guides you through environment configuration
|
||||
- Creates `.env` file on your server
|
||||
- Shows you which Gitea secrets are needed
|
||||
- Supports both production and development environments
|
||||
|
||||
## Manual Setup (Alternative)
|
||||
|
||||
If you prefer manual setup, follow these steps:
|
||||
|
||||
### Step 1: Generate SSH Keys
|
||||
|
||||
```bash
|
||||
# Generate SSH key
|
||||
ssh-keygen -t ed25519 -C "gitea-deploy-key" -f ~/.ssh/gitea_deploy_key -N ""
|
||||
|
||||
# View private key (for Gitea)
|
||||
cat ~/.ssh/gitea_deploy_key
|
||||
|
||||
# View public key (for server)
|
||||
cat ~/.ssh/gitea_deploy_key.pub
|
||||
```
|
||||
|
||||
### Step 2: Add Public Key to Server
|
||||
|
||||
```bash
|
||||
# SSH to your server
|
||||
ssh user@your-server
|
||||
|
||||
# Add public key
|
||||
mkdir -p ~/.ssh
|
||||
echo "your-public-key-here" >> ~/.ssh/authorized_keys
|
||||
chmod 600 ~/.ssh/authorized_keys
|
||||
```
|
||||
|
||||
### Step 3: Create .env Files on Server
|
||||
|
||||
**Production Server:**
|
||||
```bash
|
||||
ssh user@prod-server
|
||||
|
||||
mkdir -p /var/www/merchbay_admin
|
||||
cat > /var/www/merchbay_admin/.env << 'EOF'
|
||||
APP_ENV=production
|
||||
APP_DEBUG=false
|
||||
APP_URL=https://merchbay.com
|
||||
DB_HOST=localhost
|
||||
DB_PORT=3306
|
||||
DB_DATABASE=merchbay_prod
|
||||
DB_USERNAME=merchbay_user
|
||||
DB_PASSWORD=your_secure_password
|
||||
DOMAIN=merchbay.com
|
||||
EOF
|
||||
|
||||
chmod 600 /var/www/merchbay_admin/.env
|
||||
```
|
||||
|
||||
**Development Server:**
|
||||
```bash
|
||||
ssh user@dev-server
|
||||
|
||||
mkdir -p /var/www/merchbay_admin_dev
|
||||
cat > /var/www/merchbay_admin_dev/.env << 'EOF'
|
||||
APP_ENV=staging
|
||||
APP_DEBUG=false
|
||||
APP_URL=https://dev.merchbay.app
|
||||
DB_HOST=localhost
|
||||
DB_PORT=3306
|
||||
DB_DATABASE=merchbay_dev
|
||||
DB_USERNAME=merchbay_user
|
||||
DB_PASSWORD=your_dev_password
|
||||
DOMAIN=dev.merchbay.app
|
||||
EOF
|
||||
|
||||
chmod 600 /var/www/merchbay_admin_dev/.env
|
||||
```
|
||||
|
||||
### Step 4: Add Secrets to Gitea
|
||||
|
||||
Go to your Gitea repository → Settings → Secrets
|
||||
|
||||
**For Production (deploy.yml):**
|
||||
- `PROD_DEPLOY_SSH_KEY` - Your private SSH key content
|
||||
- `PROD_DEPLOY_USER` - SSH username (e.g., `root`)
|
||||
- `PROD_DEPLOY_HOST` - Server IP/hostname
|
||||
|
||||
**For Development (deploy-dev.yml):**
|
||||
- `DEPLOY_SSH_KEY` - Your private SSH key content
|
||||
- `DEPLOY_USER` - SSH username (e.g., `root`)
|
||||
- `DEPLOY_HOST` - Server IP/hostname
|
||||
|
||||
**For Docker Registry (build-push.yml):**
|
||||
- `DOCKER_REGISTRY_URL` - Your registry URL
|
||||
- `DOCKER_USERNAME` - Registry username
|
||||
- `DOCKER_PASSWORD` - Registry password
|
||||
|
||||
## Benefits of This Approach
|
||||
|
||||
✅ **Fewer Secrets** - Only 3 secrets per environment instead of 8+
|
||||
✅ **Centralized** - All database/app secrets stay on the server
|
||||
✅ **Reusable** - Same SSH credentials work for all apps
|
||||
✅ **Secure** - Secrets never appear in CI/CD logs
|
||||
✅ **Easy Updates** - Edit `.env` files directly on server
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### SSH Connection Issues
|
||||
|
||||
```bash
|
||||
# Test SSH connection
|
||||
ssh -i ~/.ssh/gitea_deploy_key user@server
|
||||
|
||||
# Check SSH key permissions
|
||||
chmod 600 ~/.ssh/gitea_deploy_key
|
||||
chmod 644 ~/.ssh/gitea_deploy_key.pub
|
||||
```
|
||||
|
||||
### Workflow Fails with "Could not resolve hostname"
|
||||
|
||||
- Make sure all secrets are added to Gitea
|
||||
- Verify `DEPLOY_HOST` / `PROD_DEPLOY_HOST` is correct
|
||||
- Check `DEPLOY_USER` / `PROD_DEPLOY_USER` is set
|
||||
|
||||
### .env File Not Found
|
||||
|
||||
- Run `./setup-server-env.sh` to create it
|
||||
- Or manually create `.env` file on server at:
|
||||
- Production: `/var/www/merchbay_admin/.env`
|
||||
- Development: `/var/www/merchbay_admin_dev/.env`
|
||||
|
||||
## Multiple Applications
|
||||
|
||||
To deploy multiple applications using the same setup:
|
||||
|
||||
1. **Use the same SSH keys** - No need to generate new ones
|
||||
2. **Create separate .env files** - One per app on the server
|
||||
3. **Only 3 Gitea secrets total** - Reuse across all apps!
|
||||
|
||||
Example for another app:
|
||||
```bash
|
||||
# Same SSH key works!
|
||||
# Just create new .env file
|
||||
ssh user@server
|
||||
mkdir -p /var/www/another_app
|
||||
cat > /var/www/another_app/.env << 'EOF'
|
||||
# App-specific configuration
|
||||
EOF
|
||||
```
|
||||
|
||||
## Security Best Practices
|
||||
|
||||
- ✅ Never commit `.env` files to git
|
||||
- ✅ Keep private keys secure
|
||||
- ✅ Use strong database passwords
|
||||
- ✅ Restrict SSH key permissions (600)
|
||||
- ✅ Use different passwords for prod/dev
|
||||
- ✅ Regularly rotate credentials
|
||||
153
setup-server-env.sh
Executable file
153
setup-server-env.sh
Executable file
@@ -0,0 +1,153 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Server Environment Setup Script
|
||||
# This script helps you set up .env files on your deployment servers
|
||||
|
||||
set -e
|
||||
|
||||
echo "================================================"
|
||||
echo "Server Environment Setup for MerchBay Admin"
|
||||
echo "================================================"
|
||||
echo ""
|
||||
|
||||
# Color codes for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Function to prompt for input with default value
|
||||
prompt_input() {
|
||||
local prompt="$1"
|
||||
local default="$2"
|
||||
local value
|
||||
|
||||
if [ -n "$default" ]; then
|
||||
read -p "$prompt [$default]: " value
|
||||
echo "${value:-$default}"
|
||||
else
|
||||
read -p "$prompt: " value
|
||||
echo "$value"
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to prompt for password (hidden input)
|
||||
prompt_password() {
|
||||
local prompt="$1"
|
||||
local value
|
||||
|
||||
read -s -p "$prompt: " value
|
||||
echo ""
|
||||
echo "$value"
|
||||
}
|
||||
|
||||
# Choose environment
|
||||
echo "Which environment are you setting up?"
|
||||
echo "1) Production"
|
||||
echo "2) Development"
|
||||
read -p "Enter choice [1-2]: " env_choice
|
||||
|
||||
case $env_choice in
|
||||
1)
|
||||
ENV_TYPE="production"
|
||||
DEPLOY_DIR="/var/www/merchbay_admin"
|
||||
DEFAULT_DOMAIN="merchbay.com"
|
||||
DEFAULT_APP_URL="https://merchbay.com"
|
||||
DEFAULT_APP_ENV="production"
|
||||
DEFAULT_DB_NAME="merchbay_prod"
|
||||
;;
|
||||
2)
|
||||
ENV_TYPE="development"
|
||||
DEPLOY_DIR="/var/www/merchbay_admin_dev"
|
||||
DEFAULT_DOMAIN="dev.merchbay.app"
|
||||
DEFAULT_APP_URL="https://dev.merchbay.app"
|
||||
DEFAULT_APP_ENV="staging"
|
||||
DEFAULT_DB_NAME="merchbay_dev"
|
||||
;;
|
||||
*)
|
||||
echo -e "${RED}Invalid choice${NC}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
echo -e "\n${GREEN}Setting up $ENV_TYPE environment${NC}\n"
|
||||
|
||||
# Collect information
|
||||
echo "=== Application Settings ==="
|
||||
APP_ENV=$(prompt_input "APP_ENV" "$DEFAULT_APP_ENV")
|
||||
APP_DEBUG=$(prompt_input "APP_DEBUG (true/false)" "false")
|
||||
APP_URL=$(prompt_input "APP_URL" "$DEFAULT_APP_URL")
|
||||
DOMAIN=$(prompt_input "DOMAIN" "$DEFAULT_DOMAIN")
|
||||
|
||||
echo -e "\n=== Database Settings ==="
|
||||
DB_HOST=$(prompt_input "DB_HOST" "localhost")
|
||||
DB_PORT=$(prompt_input "DB_PORT" "3306")
|
||||
DB_DATABASE=$(prompt_input "DB_DATABASE" "$DEFAULT_DB_NAME")
|
||||
DB_USERNAME=$(prompt_input "DB_USERNAME" "merchbay_user")
|
||||
DB_PASSWORD=$(prompt_password "DB_PASSWORD")
|
||||
|
||||
echo -e "\n=== Server Connection ==="
|
||||
SERVER_USER=$(prompt_input "SSH Username" "root")
|
||||
SERVER_HOST=$(prompt_input "Server IP/Hostname" "")
|
||||
|
||||
if [ -z "$SERVER_HOST" ]; then
|
||||
echo -e "${RED}Error: Server hostname is required${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Generate .env content
|
||||
ENV_CONTENT="APP_ENV=$APP_ENV
|
||||
APP_DEBUG=$APP_DEBUG
|
||||
APP_URL=$APP_URL
|
||||
DB_HOST=$DB_HOST
|
||||
DB_PORT=$DB_PORT
|
||||
DB_DATABASE=$DB_DATABASE
|
||||
DB_USERNAME=$DB_USERNAME
|
||||
DB_PASSWORD=$DB_PASSWORD
|
||||
DOMAIN=$DOMAIN"
|
||||
|
||||
# Show summary
|
||||
echo -e "\n${YELLOW}=== Configuration Summary ===${NC}"
|
||||
echo "Environment: $ENV_TYPE"
|
||||
echo "Deploy Directory: $DEPLOY_DIR"
|
||||
echo "Server: $SERVER_USER@$SERVER_HOST"
|
||||
echo "Domain: $DOMAIN"
|
||||
echo "Database: $DB_DATABASE"
|
||||
echo ""
|
||||
|
||||
# Ask for confirmation
|
||||
read -p "Deploy this configuration to the server? (y/n): " confirm
|
||||
|
||||
if [ "$confirm" != "y" ]; then
|
||||
echo "Aborted."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Deploy to server
|
||||
echo -e "\n${GREEN}Deploying configuration to server...${NC}"
|
||||
|
||||
# Create deployment directory and .env file
|
||||
ssh "$SERVER_USER@$SERVER_HOST" "mkdir -p $DEPLOY_DIR"
|
||||
|
||||
# Upload .env file
|
||||
echo "$ENV_CONTENT" | ssh "$SERVER_USER@$SERVER_HOST" "cat > $DEPLOY_DIR/.env && chmod 600 $DEPLOY_DIR/.env"
|
||||
|
||||
echo -e "\n${GREEN}✓ Configuration deployed successfully!${NC}"
|
||||
echo -e "\nThe .env file has been created at: ${YELLOW}$DEPLOY_DIR/.env${NC}"
|
||||
echo ""
|
||||
|
||||
# Display Gitea secrets needed
|
||||
echo -e "${YELLOW}=== Gitea Secrets Required ===${NC}"
|
||||
if [ "$ENV_TYPE" = "production" ]; then
|
||||
echo "Add these secrets to your Gitea repository:"
|
||||
echo " • PROD_DEPLOY_SSH_KEY: Your SSH private key"
|
||||
echo " • PROD_DEPLOY_USER: $SERVER_USER"
|
||||
echo " • PROD_DEPLOY_HOST: $SERVER_HOST"
|
||||
else
|
||||
echo "Add these secrets to your Gitea repository:"
|
||||
echo " • DEPLOY_SSH_KEY: Your SSH private key"
|
||||
echo " • DEPLOY_USER: $SERVER_USER"
|
||||
echo " • DEPLOY_HOST: $SERVER_HOST"
|
||||
fi
|
||||
|
||||
echo -e "\n${GREEN}Setup complete!${NC}"
|
||||
90
setup-ssh-keys.sh
Executable file
90
setup-ssh-keys.sh
Executable file
@@ -0,0 +1,90 @@
|
||||
#!/bin/bash
|
||||
|
||||
# SSH Key Generation Script for Gitea Deployment
|
||||
# This script generates SSH keys and helps you set them up
|
||||
|
||||
set -e
|
||||
|
||||
echo "================================================"
|
||||
echo "SSH Key Setup for Gitea Deployment"
|
||||
echo "================================================"
|
||||
echo ""
|
||||
|
||||
# Color codes
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
SSH_KEY_PATH="$HOME/.ssh/gitea_deploy_key"
|
||||
|
||||
# Check if key already exists
|
||||
if [ -f "$SSH_KEY_PATH" ]; then
|
||||
echo -e "${YELLOW}Warning: SSH key already exists at $SSH_KEY_PATH${NC}"
|
||||
read -p "Do you want to overwrite it? (y/n): " overwrite
|
||||
if [ "$overwrite" != "y" ]; then
|
||||
echo "Using existing key."
|
||||
else
|
||||
rm -f "$SSH_KEY_PATH" "$SSH_KEY_PATH.pub"
|
||||
echo "Generating new SSH key..."
|
||||
ssh-keygen -t ed25519 -C "gitea-deploy-key" -f "$SSH_KEY_PATH" -N ""
|
||||
fi
|
||||
else
|
||||
echo "Generating new SSH key..."
|
||||
ssh-keygen -t ed25519 -C "gitea-deploy-key" -f "$SSH_KEY_PATH" -N ""
|
||||
fi
|
||||
|
||||
echo -e "\n${GREEN}✓ SSH key generated successfully!${NC}\n"
|
||||
|
||||
# Display private key for Gitea
|
||||
echo -e "${YELLOW}=== PRIVATE KEY (for Gitea Secrets) ===${NC}"
|
||||
echo -e "${BLUE}Copy this ENTIRE content for your Gitea secret:${NC}\n"
|
||||
cat "$SSH_KEY_PATH"
|
||||
echo ""
|
||||
|
||||
# Display public key for server
|
||||
echo -e "\n${YELLOW}=== PUBLIC KEY (for Server) ===${NC}"
|
||||
echo -e "${BLUE}Copy this content to add to your server's ~/.ssh/authorized_keys:${NC}\n"
|
||||
cat "$SSH_KEY_PATH.pub"
|
||||
echo ""
|
||||
|
||||
# Ask if user wants to deploy to server now
|
||||
echo -e "\n${YELLOW}=== Deploy Public Key to Server ===${NC}"
|
||||
read -p "Do you want to add the public key to a server now? (y/n): " deploy_now
|
||||
|
||||
if [ "$deploy_now" = "y" ]; then
|
||||
read -p "Enter SSH username: " ssh_user
|
||||
read -p "Enter server IP/hostname: " ssh_host
|
||||
|
||||
echo -e "\nAdding public key to $ssh_user@$ssh_host..."
|
||||
|
||||
# Copy public key to server
|
||||
ssh-copy-id -i "$SSH_KEY_PATH.pub" "$ssh_user@$ssh_host" 2>/dev/null || \
|
||||
ssh "$ssh_user@$ssh_host" "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys" < "$SSH_KEY_PATH.pub"
|
||||
|
||||
echo -e "\n${GREEN}✓ Public key added to server!${NC}"
|
||||
|
||||
# Test connection
|
||||
echo -e "\nTesting SSH connection..."
|
||||
if ssh -i "$SSH_KEY_PATH" -o StrictHostKeyChecking=no "$ssh_user@$ssh_host" "echo 'Connection successful!'" 2>/dev/null; then
|
||||
echo -e "${GREEN}✓ SSH connection test successful!${NC}"
|
||||
else
|
||||
echo -e "${YELLOW}⚠ SSH connection test failed. Please check your server configuration.${NC}"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Summary
|
||||
echo -e "\n${GREEN}=== Setup Complete! ===${NC}"
|
||||
echo -e "\n${YELLOW}Next Steps:${NC}"
|
||||
echo "1. Copy the PRIVATE KEY above and add it to Gitea Secrets as:"
|
||||
echo " • DEPLOY_SSH_KEY (for dev)"
|
||||
echo " • PROD_DEPLOY_SSH_KEY (for production)"
|
||||
echo ""
|
||||
echo "2. If you didn't deploy the public key yet, manually add it to your server:"
|
||||
echo " ssh user@server"
|
||||
echo " echo '$(cat "$SSH_KEY_PATH.pub")' >> ~/.ssh/authorized_keys"
|
||||
echo ""
|
||||
echo "3. The key files are saved at:"
|
||||
echo " Private: $SSH_KEY_PATH"
|
||||
echo " Public: $SSH_KEY_PATH.pub"
|
||||
echo ""
|
||||
Reference in New Issue
Block a user