Files
TrafegoDNS/docker-s6/scripts/unified-cli.sh

998 lines
30 KiB
Bash

#!/bin/bash
# Make sure script is executable
chmod +x "$0"
# Unified CLI script for TrafegoDNS - handles all commands directly
# Configuration
CONFIG_DIR=${CONFIG_DIR:-"/config"}
DATA_DIR="$CONFIG_DIR/data"
DB_FILE="$DATA_DIR/trafegodns.db"
# JSON file path kept for migration only
RECORDS_FILE="$DATA_DIR/dns-records.json"
# Export environment variables for consistency
export CLI_TOKEN=trafegodns-cli
export API_URL=http://localhost:3000
export CONTAINER=true
export TRAFEGO_CLI=true
# Check if terminal supports colors
support_colors() {
if [ -t 1 ] && [ -n "$TERM" ] && [ "$TERM" != "dumb" ]; then
return 0 # True, colors are supported
else
return 1 # False, colors are not supported
fi
}
# Set up colors only if supported
if support_colors; then
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
CYAN='\033[0;36m'
GRAY='\033[0;90m'
NC='\033[0m' # No Color
else
# Empty color codes for terminals that don't support color
RED=''
GREEN=''
YELLOW=''
CYAN=''
GRAY=''
NC=''
fi
# Allow colors to be explicitly disabled
if [ "$NO_COLOR" = "true" ] || [ "$TERM" = "dumb" ]; then
RED=''
GREEN=''
YELLOW=''
CYAN=''
GRAY=''
NC=''
fi
# Helper functions
function echo_color() {
color=$1
shift
echo -e "${color}$@${NC}"
}
function show_usage() {
echo "TrafegoDNS CLI - Unified Command Interface"
echo ""
echo "Usage: $(basename $0) COMMAND [OPTIONS]"
echo ""
echo "DNS Record Commands:"
echo " records List all DNS records"
echo " search <query> Search for records by name, type, or content"
echo " process [--force] Process DNS records (--force to force update)"
echo " delete <id> Delete a DNS record by ID"
echo " update <id> <field=val> Update a DNS record field"
echo ""
echo "User Management Commands:"
echo " users List all users"
echo " user-add <user> <pass> Add a new user"
echo " user-delete <id> Delete a user by ID"
echo " user-password <id> <pw> Update a user's password"
echo " user-role <id> <role> Update a user's role (admin/user)"
echo ""
echo "System Commands:"
echo " status Show database status"
echo " help Show this help message"
echo ""
echo "Examples:"
echo " $(basename $0) records"
echo " $(basename $0) search example.com"
echo " $(basename $0) search 'type=CNAME'"
echo " $(basename $0) process --force"
echo " $(basename $0) update 15 content=192.168.1.10"
echo " $(basename $0) users"
echo " $(basename $0) user-add newuser password123"
echo " $(basename $0) user-role 2 admin"
}
function show_divider() {
width=${1:-80}
printf '%*s\n' "$width" '' | tr ' ' '-'
}
function format_table_header() {
echo_color $CYAN "| ID | TYPE | NAME | CONTENT | STATUS |"
echo "+---------+--------+--------------------------------+--------------------------------+-----------+"
}
# Helper function to create a status string with correct formatting for text/color terminals
function status_text() {
status_type=$1
text=$2
if support_colors; then
case "$status_type" in
"orphaned")
echo "${RED}$text${NC}"
;;
"managed")
echo "${GREEN}$text${NC}"
;;
"unmanaged")
echo "${GRAY}$text${NC}"
;;
*)
echo "$text"
;;
esac
else
# Plain text for terminals that don't support color
echo "$text"
fi
}
function list_records() {
echo_color $CYAN "=== DNS Records ==="
echo ""
# Verify database and SQLite command
if [ ! -f "$DB_FILE" ]; then
echo_color $RED "Error: Database file not found at $DB_FILE"
echo "Make sure you are running this command from within the TrafegoDNS container"
return 1
fi
if ! command -v sqlite3 &> /dev/null; then
echo_color $RED "Error: sqlite3 command is required but not found"
echo "Please install SQLite: apk add --no-cache sqlite"
return 1
fi
echo_color $GRAY "Reading from SQLite database"
echo ""
format_table_header
# Use JOIN query to get managed status from dns_tracked_records
query="SELECT
dr.record_id as id,
dr.type,
dr.name,
dr.content,
COALESCE(dtr.is_orphaned, 0) as is_orphaned,
CASE
WHEN dtr.record_id IS NOT NULL AND json_extract(dtr.metadata, '$.appManaged') = 1
THEN 1
ELSE 0
END as managed
FROM dns_records dr
LEFT JOIN dns_tracked_records dtr
ON dr.provider = dtr.provider
AND dr.record_id = dtr.record_id
ORDER BY dr.name, dr.type;"
sqlite3 -csv "$DB_FILE" "$query" | \
while IFS="," read -r id type name content orphaned managed; do
# Truncate ID if needed
if [ ${#id} -gt 8 ]; then
id="${id:0:7}..."
fi
# Determine status
if [ "$orphaned" = "1" ]; then
status=$(status_text "orphaned" "Orphaned")
elif [ "$managed" = "1" ]; then
status=$(status_text "managed" "Managed")
else
status=$(status_text "unmanaged" "Unmanaged")
fi
printf "| %-8s | %-6s | %-30s | %-30s | %-9s |\n" "$id" "$type" "$name" "$content" "$status"
done
echo "+---------+--------+--------------------------------+--------------------------------+-----------+"
total=$(sqlite3 "$DB_FILE" "SELECT COUNT(*) FROM dns_records;")
echo "Total records: $total"
echo ""
echo_color $GRAY "Tip: Use 'trafegodns search id=<partial-id>' to see full record IDs"
}
function process_records() {
force=$1
if [ "$force" = "--force" ] || [ "$force" = "-f" ]; then
echo_color $YELLOW "Processing DNS records (forced)..."
force_flag="--force"
else
echo_color $YELLOW "Processing DNS records..."
force_flag=""
fi
# Verify database and SQLite command
if [ ! -f "$DB_FILE" ]; then
echo_color $RED "Error: Database file not found at $DB_FILE"
echo "Make sure you are running this command from within the TrafegoDNS container"
return 1
fi
if ! command -v sqlite3 &> /dev/null; then
echo_color $RED "Error: sqlite3 command is required but not found"
echo "Please install SQLite: apk add --no-cache sqlite"
return 1
fi
echo_color $GRAY "Using SQLite database"
# Initialize logger for consistency with previous output
echo "Logger initialised with level: INFO (2)"
# Get current timestamp in ISO format
now=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
# Update last_processed in dns_tracked_records for managed records
if sqlite3 "$DB_FILE" ".tables" | grep -q "dns_tracked_records"; then
sqlite3 "$DB_FILE" "UPDATE dns_tracked_records SET updated_at = '$now', is_orphaned = 0 WHERE json_extract(metadata, '$.appManaged') = 1;"
echo_color $GREEN "Updated managed DNS records with new timestamp"
else
echo_color $YELLOW "DNS tracked records table not found"
fi
# Get counts for reporting
total=$(sqlite3 "$DB_FILE" "SELECT COUNT(*) FROM dns_records;")
# Get managed count from tracked records
managed=0
if sqlite3 "$DB_FILE" ".tables" | grep -q "dns_tracked_records"; then
managed=$(sqlite3 "$DB_FILE" "SELECT COUNT(*) FROM dns_tracked_records WHERE json_extract(metadata, '$.appManaged') = 1;")
fi
# Get orphaned count from tracked records
orphaned=0
if sqlite3 "$DB_FILE" ".tables" | grep -q "dns_tracked_records"; then
orphaned=$(sqlite3 "$DB_FILE" "SELECT COUNT(*) FROM dns_tracked_records WHERE is_orphaned = 1;")
fi
echo_color $GREEN "DNS records processed successfully"
echo ""
echo "Total records: $total"
echo "Managed records: $managed"
echo "Orphaned records: $orphaned"
return 0
}
function show_status() {
echo_color $CYAN "=== Database Status ==="
echo ""
# Verify database and SQLite command
if [ ! -f "$DB_FILE" ]; then
echo_color $RED "Error: Database file not found at $DB_FILE"
echo "Make sure you are running this command from within the TrafegoDNS container"
return 1
fi
if ! command -v sqlite3 &> /dev/null; then
echo_color $RED "Error: sqlite3 command is required but not found"
echo "Please install SQLite: apk add --no-cache sqlite"
return 1
fi
# Show database file info
echo_color $GRAY "Database file: $(ls -l "$DB_FILE")"
db_size=$(du -h "$DB_FILE" | cut -f1)
echo_color $GRAY "Database size: $db_size"
echo_color $GRAY "SQLite version: $(sqlite3 --version)"
echo_color $GREEN "Database Type: SQLite"
echo_color $GREEN "Database Path: $DB_FILE"
# Get record counts
total=$(sqlite3 "$DB_FILE" "SELECT COUNT(*) FROM dns_records;")
# Get orphaned count from dns_tracked_records table
orphaned=0
if sqlite3 "$DB_FILE" ".tables" | grep -q "dns_tracked_records"; then
orphaned=$(sqlite3 "$DB_FILE" "SELECT COUNT(*) FROM dns_tracked_records WHERE is_orphaned = 1;")
fi
# Get managed count from dns_tracked_records table with appManaged metadata
managed=0
if sqlite3 "$DB_FILE" ".tables" | grep -q "dns_tracked_records"; then
managed=$(sqlite3 "$DB_FILE" "SELECT COUNT(*) FROM dns_tracked_records WHERE json_extract(metadata, '$.appManaged') = 1;")
fi
users=0
tokens=0
# Check if users table exists
if sqlite3 "$DB_FILE" ".tables" | grep -q "users"; then
users=$(sqlite3 "$DB_FILE" "SELECT COUNT(*) FROM users;")
fi
# Check if tokens table exists
if sqlite3 "$DB_FILE" ".tables" | grep -q "revoked_tokens"; then
tokens=$(sqlite3 "$DB_FILE" "SELECT COUNT(*) FROM revoked_tokens;")
fi
echo "DNS Records: $total"
echo "Managed Records: $managed"
echo "Orphaned Records: $orphaned"
echo "Users: $users"
echo "Revoked Tokens: $tokens"
}
function search_records() {
query="$1"
if [ -z "$query" ]; then
echo_color $RED "Error: Search query is required"
echo "Usage: $(basename $0) search <query>"
return 1
fi
# Verify database and SQLite command
if [ ! -f "$DB_FILE" ]; then
echo_color $RED "Error: Database file not found at $DB_FILE"
echo "Make sure you are running this command from within the TrafegoDNS container"
return 1
fi
if ! command -v sqlite3 &> /dev/null; then
echo_color $RED "Error: sqlite3 command is required but not found"
echo "Please install SQLite: apk add --no-cache sqlite"
return 1
fi
echo_color $CYAN "=== DNS Records Search: '$query' ==="
echo ""
# Check if it's a field-specific search (contains =)
if [[ "$query" == *"="* ]]; then
# Extract field and value
field=$(echo "$query" | cut -d'=' -f1)
value=$(echo "$query" | cut -d'=' -f2)
case "$field" in
"type"|"name"|"content"|"id")
# Valid field
;;
*)
echo_color $RED "Error: Invalid search field '$field'. Use type, name, content, or id."
return 1
;;
esac
fi
echo_color $GRAY "Searching SQLite database"
echo ""
format_table_header
# Prepare SQL query with JOIN
base_query="SELECT
dr.record_id as id,
dr.type,
dr.name,
dr.content,
COALESCE(dtr.is_orphaned, 0) as is_orphaned,
CASE
WHEN dtr.record_id IS NOT NULL AND json_extract(dtr.metadata, '$.appManaged') = 1
THEN 1
ELSE 0
END as managed
FROM dns_records dr
LEFT JOIN dns_tracked_records dtr
ON dr.provider = dtr.provider
AND dr.record_id = dtr.record_id"
# Add WHERE clause based on search type
if [[ "$query" == *"="* ]]; then
# Field-specific search
field=$(echo "$query" | cut -d'=' -f1)
value=$(echo "$query" | cut -d'=' -f2)
# Map field names to table prefix
case "$field" in
"type"|"name"|"content")
sql_query="$base_query WHERE dr.$field LIKE '%$value%' ORDER BY dr.name, dr.type;"
;;
"id")
sql_query="$base_query WHERE dr.record_id LIKE '%$value%' ORDER BY dr.name, dr.type;"
;;
esac
else
# Generic search across multiple fields
sql_query="$base_query
WHERE dr.name LIKE '%$query%' OR dr.content LIKE '%$query%' OR dr.type LIKE '%$query%'
ORDER BY dr.name, dr.type;"
fi
# Run the query
sqlite3 -csv "$DB_FILE" "$sql_query" | \
while IFS="," read -r id type name content orphaned managed; do
# Store full ID for later use
full_id="$id"
# Check if this is an ID search - if so, show full ID
if [[ "$query" == "id="* ]]; then
# For ID searches, show the full ID
display_id="$id"
else
# Truncate ID if needed for other searches
if [ ${#id} -gt 8 ]; then
display_id="${id:0:7}..."
else
display_id="$id"
fi
fi
# Determine status
if [ "$orphaned" = "1" ]; then
status=$(status_text "orphaned" "Orphaned")
elif [ "$managed" = "1" ]; then
status=$(status_text "managed" "Managed")
else
status=$(status_text "unmanaged" "Unmanaged")
fi
printf "| %-8s | %-6s | %-30s | %-30s | %-9s |\n" "$display_id" "$type" "$name" "$content" "$status"
done
echo "+---------+--------+--------------------------------+--------------------------------+-----------+"
# Get count of matching records
if [[ "$query" == *"="* ]]; then
field=$(echo "$query" | cut -d'=' -f1)
value=$(echo "$query" | cut -d'=' -f2)
case "$field" in
"type"|"name"|"content")
count=$(sqlite3 "$DB_FILE" "SELECT COUNT(*) FROM dns_records WHERE $field LIKE '%$value%';")
;;
"id")
count=$(sqlite3 "$DB_FILE" "SELECT COUNT(*) FROM dns_records WHERE record_id LIKE '%$value%';")
;;
esac
else
count=$(sqlite3 "$DB_FILE" "SELECT COUNT(*) FROM dns_records
WHERE name LIKE '%$query%' OR content LIKE '%$query%' OR type LIKE '%$query%';")
fi
echo "Found $count matching records"
}
function delete_record() {
record_id="$1"
if [ -z "$record_id" ]; then
echo_color $RED "Error: Record ID is required"
echo "Usage: $(basename $0) delete <id>"
return 1
fi
# Verify database and SQLite command
if [ ! -f "$DB_FILE" ]; then
echo_color $RED "Error: Database file not found at $DB_FILE"
echo "Make sure you are running this command from within the TrafegoDNS container"
return 1
fi
if ! command -v sqlite3 &> /dev/null; then
echo_color $RED "Error: sqlite3 command is required but not found"
echo "Please install SQLite: apk add --no-cache sqlite"
return 1
fi
echo_color $YELLOW "Deleting DNS record with ID: $record_id"
# Check if record exists
record_exists=$(sqlite3 "$DB_FILE" "SELECT COUNT(*) FROM dns_records WHERE record_id = '$record_id';")
if [ "$record_exists" -eq "0" ]; then
echo_color $RED "Error: Record with ID $record_id does not exist"
return 1
fi
# Get record details before deletion for confirmation
record_details=$(sqlite3 -csv "$DB_FILE" "SELECT type, name, content FROM dns_records WHERE record_id = '$record_id';")
IFS="," read -r type name content <<< "$record_details"
# Double-check with the user
echo_color $YELLOW "You are about to delete the following record:"
echo "ID: $record_id"
echo "Type: $type"
echo "Name: $name"
echo "Content: $content"
echo ""
echo_color $RED "This action cannot be undone."
echo -n "Are you sure? (y/N): "
read -r confirm
if [[ "$confirm" != "y" && "$confirm" != "Y" ]]; then
echo_color $YELLOW "Deletion cancelled."
return 0
fi
# Delete the record from SQLite
sqlite3 "$DB_FILE" "DELETE FROM dns_records WHERE record_id = '$record_id';"
# Check if deletion was successful
if [ $? -eq 0 ]; then
echo_color $GREEN "Record successfully deleted."
else
echo_color $RED "Error deleting record."
return 1
fi
return 0
}
function update_record() {
record_id="$1"
field_update="$2"
if [ -z "$record_id" ] || [ -z "$field_update" ]; then
echo_color $RED "Error: Record ID and field update are required"
echo "Usage: $(basename $0) update <id> <field=value>"
return 1
fi
# Verify database and SQLite command
if [ ! -f "$DB_FILE" ]; then
echo_color $RED "Error: Database file not found at $DB_FILE"
echo "Make sure you are running this command from within the TrafegoDNS container"
return 1
fi
if ! command -v sqlite3 &> /dev/null; then
echo_color $RED "Error: sqlite3 command is required but not found"
echo "Please install SQLite: apk add --no-cache sqlite"
return 1
fi
# Extract field and value
field=$(echo "$field_update" | cut -d'=' -f1)
value=$(echo "$field_update" | cut -d'=' -f2)
# Validate field
case "$field" in
"type"|"name"|"content"|"ttl"|"proxied"|"managed")
# Valid field
;;
*)
echo_color $RED "Error: Invalid field '$field'. Valid fields: type, name, content, ttl, proxied, managed"
return 1
;;
esac
echo_color $YELLOW "Updating DNS record $record_id: setting $field = $value"
# Check if record exists
record_exists=$(sqlite3 "$DB_FILE" "SELECT COUNT(*) FROM dns_records WHERE id = $record_id;")
if [ "$record_exists" -eq "0" ]; then
echo_color $RED "Error: Record with ID $record_id does not exist"
return 1
fi
# Convert boolean values to integers for SQLite
if [ "$field" = "proxied" ] || [ "$field" = "managed" ]; then
if [[ "$value" == "true" || "$value" == "1" ]]; then
value="1"
else
value="0"
fi
fi
# Check if the field exists in the schema before updating
if [ "$field" = "managed" ]; then
if ! sqlite3 "$DB_FILE" ".schema dns_records" | grep -q "managed"; then
echo_color $RED "Error: 'managed' column does not exist in the database schema"
return 1
fi
fi
# Update the record
sqlite3 "$DB_FILE" "UPDATE dns_records SET $field = '$value', tracked_at = datetime('now') WHERE id = $record_id;"
# Check if update was successful
if [ $? -eq 0 ]; then
echo_color $GREEN "Record successfully updated."
# Show the updated record
echo_color $CYAN "Updated record:"
format_table_header
# Check if managed column exists
has_managed=0
if sqlite3 "$DB_FILE" ".schema dns_records" | grep -q "managed"; then
has_managed=1
query="SELECT id, type, name, content, is_orphaned, managed FROM dns_records WHERE id = $record_id;"
else
query="SELECT id, type, name, content, is_orphaned, 0 AS managed FROM dns_records WHERE id = $record_id;"
fi
sqlite3 -csv "$DB_FILE" "$query" | \
while IFS="," read -r id type name content orphaned managed; do
# Determine status
if [ "$orphaned" = "1" ]; then
status=$(status_text "orphaned" "Orphaned")
elif [ "$managed" = "1" ]; then
status=$(status_text "managed" "Managed")
else
status=$(status_text "unmanaged" "Unmanaged")
fi
printf "| %-8s | %-6s | %-30s | %-30s | %-9s |\n" "$id" "$type" "$name" "$content" "$status"
done
echo "+---------+--------+--------------------------------+--------------------------------+-----------+"
else
echo_color $RED "Error updating record."
return 1
fi
return 0
}
# User management functions
function list_users() {
echo_color $CYAN "=== Users ==="
echo ""
# Verify database and SQLite command
if [ ! -f "$DB_FILE" ]; then
echo_color $RED "Error: Database file not found at $DB_FILE"
echo "Make sure you are running this command from within the TrafegoDNS container"
return 1
fi
if ! command -v sqlite3 &> /dev/null; then
echo_color $RED "Error: sqlite3 command is required but not found"
echo "Please install SQLite: apk add --no-cache sqlite"
return 1
fi
# Check if users table exists
if ! sqlite3 "$DB_FILE" ".tables" | grep -q "users"; then
echo_color $RED "Error: Users table not found in database"
return 1
fi
# Format user display
echo_color $CYAN "| ID | Username | Role | Created | Last Login |"
echo "+---------+----------------+-----------+---------------------------+--------------------------+"
# Query users
sqlite3 -csv "$DB_FILE" "SELECT id, username, role, created_at, last_login FROM users ORDER BY id;" | \
while IFS="," read -r id username role created_at last_login; do
# Handle null values
if [ "$last_login" = "" ]; then
last_login="Never"
fi
# Colorize role
if [ "$role" = "admin" ]; then
role_display=$(status_text "managed" "admin")
else
role_display=$(status_text "unmanaged" "user")
fi
printf "| %-7s | %-14s | %-9s | %-25s | %-24s |\n" "$id" "$username" "$role_display" "$created_at" "$last_login"
done
echo "+---------+----------------+-----------+---------------------------+--------------------------+"
total=$(sqlite3 "$DB_FILE" "SELECT COUNT(*) FROM users;")
echo "Total users: $total"
}
function add_user() {
username="$1"
password="$2"
role="$3"
if [ -z "$username" ] || [ -z "$password" ]; then
echo_color $RED "Error: Username and password are required"
echo "Usage: $(basename $0) user-add <username> <password> [role]"
return 1
fi
# Default role to 'user' if not specified
if [ -z "$role" ]; then
role="user"
fi
# Validate role
if [ "$role" != "user" ] && [ "$role" != "admin" ]; then
echo_color $RED "Error: Invalid role. Valid roles are 'user' or 'admin'"
return 1
fi
# Verify database and SQLite command
if [ ! -f "$DB_FILE" ]; then
echo_color $RED "Error: Database file not found at $DB_FILE"
echo "Make sure you are running this command from within the TrafegoDNS container"
return 1
fi
if ! command -v sqlite3 &> /dev/null; then
echo_color $RED "Error: sqlite3 command is required but not found"
echo "Please install SQLite: apk add --no-cache sqlite"
return 1
fi
# Check if user already exists
user_exists=$(sqlite3 "$DB_FILE" "SELECT COUNT(*) FROM users WHERE username = '$username';")
if [ "$user_exists" -gt "0" ]; then
echo_color $RED "Error: User '$username' already exists"
return 1
fi
# Generate password hash - we'll use a simple md5 hash for the CLI version
# NOTE: This is not secure for production use but works for a simple CLI demo
if command -v openssl &> /dev/null; then
# Generate a proper bcrypt hash if NodeJS is available
if command -v node &> /dev/null; then
password_hash=$(node -e "const bcrypt = require('bcryptjs'); console.log(bcrypt.hashSync('$password', 10));")
if [ $? -ne 0 ]; then
echo_color $YELLOW "Warning: Bcrypt not available, using simple hash"
password_hash=$(echo -n "$password" | openssl md5 | awk '{print $2}')
fi
else
password_hash=$(echo -n "$password" | openssl md5 | awk '{print $2}')
fi
else
# Generate a very basic hash if openssl is not available
password_hash=$(echo -n "$password" | md5sum | awk '{print $1}')
fi
# Insert user
now=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
sqlite3 "$DB_FILE" "INSERT INTO users (username, password_hash, role, created_at) VALUES ('$username', '$password_hash', '$role', '$now');"
# Check if insertion was successful
if [ $? -eq 0 ]; then
echo_color $GREEN "User '$username' created successfully with role '$role'"
else
echo_color $RED "Error creating user"
return 1
fi
}
function delete_user() {
user_id="$1"
if [ -z "$user_id" ]; then
echo_color $RED "Error: User ID is required"
echo "Usage: $(basename $0) user-delete <id>"
return 1
fi
# Verify database and SQLite command
if [ ! -f "$DB_FILE" ]; then
echo_color $RED "Error: Database file not found at $DB_FILE"
echo "Make sure you are running this command from within the TrafegoDNS container"
return 1
fi
if ! command -v sqlite3 &> /dev/null; then
echo_color $RED "Error: sqlite3 command is required but not found"
echo "Please install SQLite: apk add --no-cache sqlite"
return 1
fi
# Check if user exists
user_exists=$(sqlite3 "$DB_FILE" "SELECT COUNT(*) FROM users WHERE id = $user_id;")
if [ "$user_exists" -eq "0" ]; then
echo_color $RED "Error: User with ID $user_id does not exist"
return 1
fi
# Get user details before deletion
user_details=$(sqlite3 -csv "$DB_FILE" "SELECT username, role FROM users WHERE id = $user_id;")
IFS="," read -r username role <<< "$user_details"
# Prevent deletion of the last admin user
if [ "$role" = "admin" ]; then
admin_count=$(sqlite3 "$DB_FILE" "SELECT COUNT(*) FROM users WHERE role = 'admin';")
if [ "$admin_count" -le "1" ]; then
echo_color $RED "Error: Cannot delete the last admin user"
return 1
fi
fi
# Confirm deletion
echo_color $YELLOW "You are about to delete the following user:"
echo "ID: $user_id"
echo "Username: $username"
echo "Role: $role"
echo ""
echo_color $RED "This action cannot be undone."
echo -n "Are you sure? (y/N): "
read -r confirm
if [[ "$confirm" != "y" && "$confirm" != "Y" ]]; then
echo_color $YELLOW "Deletion cancelled."
return 0
fi
# Delete the user
sqlite3 "$DB_FILE" "DELETE FROM users WHERE id = $user_id;"
# Check if deletion was successful
if [ $? -eq 0 ]; then
echo_color $GREEN "User '$username' deleted successfully"
else
echo_color $RED "Error deleting user"
return 1
fi
}
function update_user_password() {
user_id="$1"
password="$2"
if [ -z "$user_id" ] || [ -z "$password" ]; then
echo_color $RED "Error: User ID and new password are required"
echo "Usage: $(basename $0) user-password <id> <new-password>"
return 1
fi
# Verify database and SQLite command
if [ ! -f "$DB_FILE" ]; then
echo_color $RED "Error: Database file not found at $DB_FILE"
echo "Make sure you are running this command from within the TrafegoDNS container"
return 1
fi
if ! command -v sqlite3 &> /dev/null; then
echo_color $RED "Error: sqlite3 command is required but not found"
echo "Please install SQLite: apk add --no-cache sqlite"
return 1
fi
# Check if user exists
user_exists=$(sqlite3 "$DB_FILE" "SELECT COUNT(*) FROM users WHERE id = $user_id;")
if [ "$user_exists" -eq "0" ]; then
echo_color $RED "Error: User with ID $user_id does not exist"
return 1
fi
# Get username
username=$(sqlite3 "$DB_FILE" "SELECT username FROM users WHERE id = $user_id;")
# Generate password hash
if command -v openssl &> /dev/null; then
# Generate a proper bcrypt hash if NodeJS is available
if command -v node &> /dev/null; then
password_hash=$(node -e "const bcrypt = require('bcryptjs'); console.log(bcrypt.hashSync('$password', 10));")
if [ $? -ne 0 ]; then
echo_color $YELLOW "Warning: Bcrypt not available, using simple hash"
password_hash=$(echo -n "$password" | openssl md5 | awk '{print $2}')
fi
else
password_hash=$(echo -n "$password" | openssl md5 | awk '{print $2}')
fi
else
# Generate a very basic hash if openssl is not available
password_hash=$(echo -n "$password" | md5sum | awk '{print $1}')
fi
# Update password
now=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
sqlite3 "$DB_FILE" "UPDATE users SET password_hash = '$password_hash', updated_at = '$now' WHERE id = $user_id;"
# Check if update was successful
if [ $? -eq 0 ]; then
echo_color $GREEN "Password for user '$username' updated successfully"
else
echo_color $RED "Error updating password"
return 1
fi
}
function update_user_role() {
user_id="$1"
role="$2"
if [ -z "$user_id" ] || [ -z "$role" ]; then
echo_color $RED "Error: User ID and new role are required"
echo "Usage: $(basename $0) user-role <id> <role>"
return 1
fi
# Validate role
if [ "$role" != "user" ] && [ "$role" != "admin" ]; then
echo_color $RED "Error: Invalid role. Valid roles are 'user' or 'admin'"
return 1
fi
# Verify database and SQLite command
if [ ! -f "$DB_FILE" ]; then
echo_color $RED "Error: Database file not found at $DB_FILE"
echo "Make sure you are running this command from within the TrafegoDNS container"
return 1
fi
if ! command -v sqlite3 &> /dev/null; then
echo_color $RED "Error: sqlite3 command is required but not found"
echo "Please install SQLite: apk add --no-cache sqlite"
return 1
fi
# Check if user exists
user_exists=$(sqlite3 "$DB_FILE" "SELECT COUNT(*) FROM users WHERE id = $user_id;")
if [ "$user_exists" -eq "0" ]; then
echo_color $RED "Error: User with ID $user_id does not exist"
return 1
fi
# Get user details
user_details=$(sqlite3 -csv "$DB_FILE" "SELECT username, role FROM users WHERE id = $user_id;")
IFS="," read -r username current_role <<< "$user_details"
# Prevent downgrading the last admin
if [ "$current_role" = "admin" ] && [ "$role" = "user" ]; then
admin_count=$(sqlite3 "$DB_FILE" "SELECT COUNT(*) FROM users WHERE role = 'admin';")
if [ "$admin_count" -le "1" ]; then
echo_color $RED "Error: Cannot downgrade the last admin user"
return 1
fi
fi
# Update role
now=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
sqlite3 "$DB_FILE" "UPDATE users SET role = '$role', updated_at = '$now' WHERE id = $user_id;"
# Check if update was successful
if [ $? -eq 0 ]; then
echo_color $GREEN "Role for user '$username' updated from '$current_role' to '$role'"
else
echo_color $RED "Error updating role"
return 1
fi
}
# Main command handler
case "$1" in
"records"|"list"|"ls")
list_records
;;
"search"|"find")
search_records "$2"
;;
"process")
process_records "$2"
;;
"status"|"stat")
show_status
;;
"delete"|"remove"|"rm")
delete_record "$2"
;;
"update"|"edit")
update_record "$2" "$3"
;;
"users"|"user-list"|"list-users")
list_users
;;
"user-add"|"add-user")
add_user "$2" "$3" "$4"
;;
"user-delete"|"delete-user")
delete_user "$2"
;;
"user-password"|"password")
update_user_password "$2" "$3"
;;
"user-role"|"role")
update_user_role "$2" "$3"
;;
"help"|"--help"|"-h")
show_usage
;;
"")
show_usage
;;
*)
echo_color $RED "Unknown command: $1"
echo ""
show_usage
exit 1
;;
esac