@@ -11,7 +11,7 @@ set -eo pipefail
1111export LC_NUMERIC=C
1212
1313# ── Section 1: Initialization ────────────────────────────────
14- VERSION=" 1.0.0 "
14+ VERSION=" 1.0.1 "
1515SCRIPT_NAME=" mtproxymax"
1616INSTALL_DIR=" /opt/mtproxymax"
1717CONFIG_DIR=" ${INSTALL_DIR} /mtproxy"
@@ -1278,7 +1278,7 @@ get_user_stats() {
12781278
12791279# Add a new secret
12801280secret_add () {
1281- local label=" $1 " custom_secret=" ${2:- } "
1281+ local label=" $1 " custom_secret=" ${2:- } " no_restart= " ${3 :- false} "
12821282
12831283 # Validate label
12841284 if [ -z " $label " ]; then
@@ -1330,7 +1330,7 @@ secret_add() {
13301330 save_secrets
13311331
13321332 # Restart if running (run_proxy_container regenerates config)
1333- if is_proxy_running; then
1333+ if [ " $no_restart " != " true " ] && is_proxy_running; then
13341334 restart_proxy_container
13351335 fi
13361336
@@ -1351,7 +1351,7 @@ secret_add() {
13511351
13521352# Remove a secret
13531353secret_remove () {
1354- local label=" $1 " force=" ${2:- false} "
1354+ local label=" $1 " force=" ${2:- false} " no_restart= " ${3 :- false} "
13551355
13561356 local idx=-1
13571357 local i
@@ -1411,13 +1411,93 @@ secret_remove() {
14111411
14121412 save_secrets
14131413
1414- if is_proxy_running; then
1414+ if [ " $no_restart " != " true " ] && is_proxy_running; then
14151415 restart_proxy_container
14161416 fi
14171417
14181418 log_success " Secret '${label} ' removed"
14191419}
14201420
1421+ # Batch add multiple secrets (single restart)
1422+ secret_add_batch () {
1423+ local labels=(" $@ " )
1424+
1425+ if [ ${# labels[@]} -eq 0 ]; then
1426+ log_error " Usage: mtproxymax secret add-batch <label1> <label2> ..."
1427+ return 1
1428+ fi
1429+
1430+ local added=0 failed=0
1431+ for label in " ${labels[@]} " ; do
1432+ if secret_add " $label " " " " true" ; then
1433+ added=$(( added + 1 ))
1434+ else
1435+ failed=$(( failed + 1 ))
1436+ fi
1437+ done
1438+
1439+ # Single restart after all additions
1440+ if [ $added -gt 0 ] && is_proxy_running; then
1441+ restart_proxy_container
1442+ fi
1443+
1444+ echo " "
1445+ log_success " Batch complete: ${added} added, ${failed} failed"
1446+ }
1447+
1448+ # Batch remove multiple secrets (single restart)
1449+ secret_remove_batch () {
1450+ local force=" ${1:- false} "
1451+ shift 2> /dev/null || true
1452+ local labels=(" $@ " )
1453+
1454+ if [ ${# labels[@]} -eq 0 ]; then
1455+ log_error " Usage: mtproxymax secret remove-batch <label1> <label2> ..."
1456+ return 1
1457+ fi
1458+
1459+ # Count how many of the requested labels actually exist
1460+ local match_count=0
1461+ local l i
1462+ for l in " ${labels[@]} " ; do
1463+ for i in " ${! SECRETS_LABELS[@]} " ; do
1464+ [ " ${SECRETS_LABELS[$i]} " = " $l " ] && { match_count=$(( match_count + 1 )) ; break ; }
1465+ done
1466+ done
1467+
1468+ if [ $match_count -ge ${# SECRETS_LABELS[@]} ]; then
1469+ log_error " Cannot remove all secrets — proxy needs at least one"
1470+ return 1
1471+ fi
1472+
1473+ # Confirm unless forced
1474+ if [ " $force " != " true" ] && [ -t 0 ]; then
1475+ echo -e " ${YELLOW} Remove ${# labels[@]} secrets? Users with these keys will be disconnected.${NC} "
1476+ echo -e " ${DIM} Labels: ${labels[*]}${NC} "
1477+ echo -en " ${BOLD} Type 'yes' to confirm:${NC} "
1478+ local confirm
1479+ read -r confirm
1480+ [ " $confirm " != " yes" ] && { log_info " Cancelled" ; return 0; }
1481+ fi
1482+
1483+ local removed=0 failed=0
1484+ for l in " ${labels[@]} " ; do
1485+ if secret_remove " $l " " true" " true" ; then
1486+ removed=$(( removed + 1 ))
1487+ else
1488+ failed=$(( failed + 1 ))
1489+ fi
1490+ done
1491+
1492+ # Single restart after all removals
1493+ if [ $removed -gt 0 ] && is_proxy_running; then
1494+ restart_proxy_container
1495+ fi
1496+
1497+ echo " "
1498+ log_success " Batch complete: ${removed} removed, ${failed} failed"
1499+ }
1500+
14211501# List all secrets
14221502secret_list () {
14231503 load_secrets
@@ -3906,7 +3986,9 @@ show_cli_help() {
39063986 echo " "
39073987 echo -e " ${BOLD} Secret Management:${NC} "
39083988 echo -e " ${GREEN} secret add${NC} <label> Add a new secret"
3989+ echo -e " ${GREEN} secret add-batch${NC} <l1> <l2> ... Add multiple secrets (single restart)"
39093990 echo -e " ${GREEN} secret remove${NC} <label> Remove a secret"
3991+ echo -e " ${GREEN} secret remove-batch${NC} <l1> <l2> ... Remove multiple secrets (single restart)"
39103992 echo -e " ${GREEN} secret list${NC} List all secrets"
39113993 echo -e " ${GREEN} secret rotate${NC} <label> Rotate a secret"
39123994 echo -e " ${GREEN} secret link${NC} [label] Show proxy link"
@@ -3916,6 +3998,7 @@ show_cli_help() {
39163998 echo -e " ${GREEN} secret limits${NC} [label] Show user limits"
39173999 echo -e " ${GREEN} secret setlimit${NC} <label> conns|ips|quota|expires <value>"
39184000 echo -e " ${GREEN} secret setlimits${NC} <label> <conns> <ips> <quota> [expires]"
4001+ echo -e " ${DIM} Tip: add/remove support --no-restart flag for scripting${NC} "
39194002 echo " "
39204003 echo -e " ${BOLD} Upstream Routing:${NC} "
39214004 echo -e " ${GREEN} upstream list${NC} List upstreams"
@@ -4058,8 +4141,26 @@ cli_main() {
40584141 local subcmd=" ${1:- list} "
40594142 shift 2> /dev/null || true
40604143 case " $subcmd " in
4061- add) check_root; secret_add " $1 " " ${2:- } " ;;
4062- remove) check_root; secret_remove " $1 " ;;
4144+ add)
4145+ check_root
4146+ local _no_restart=" false"
4147+ [[ " ${* } " == * " --no-restart" * ]] && _no_restart=" true"
4148+ local _args=()
4149+ for _a in " $@ " ; do [[ " $_a " != " --no-restart" ]] && _args+=(" $_a " ); done
4150+ secret_add " ${_args[0]:- } " " ${_args[1]:- } " " $_no_restart "
4151+ ;;
4152+ add-batch)
4153+ check_root; secret_add_batch " $@ " ;;
4154+ remove)
4155+ check_root
4156+ local _no_restart=" false"
4157+ [[ " ${* } " == * " --no-restart" * ]] && _no_restart=" true"
4158+ local _args=()
4159+ for _a in " $@ " ; do [[ " $_a " != " --no-restart" ]] && _args+=(" $_a " ); done
4160+ secret_remove " ${_args[0]:- } " " false" " $_no_restart "
4161+ ;;
4162+ remove-batch)
4163+ check_root; secret_remove_batch " false" " $@ " ;;
40634164 list) secret_list ;;
40644165 rotate) check_root; secret_rotate " $1 " ;;
40654166 link) get_proxy_link_https " ${1:- } " ; echo " " ;;
@@ -4726,6 +4827,8 @@ show_secrets_menu() {
47264827 echo -e " ${DIM} [3]${NC} Rotate a secret"
47274828 echo -e " ${DIM} [4]${NC} Enable/disable a secret"
47284829 echo -e " ${DIM} [5]${NC} Set user limits"
4830+ echo -e " ${DIM} [6]${NC} Batch add secrets"
4831+ echo -e " ${DIM} [7]${NC} Batch remove secrets"
47294832 echo -e " ${DIM} [0]${NC} Back"
47304833
47314834 local choice
@@ -4778,6 +4881,28 @@ show_secrets_menu() {
47784881 fi
47794882 press_any_key
47804883 ;;
4884+ 6)
4885+ echo -e " ${DIM} Enter labels separated by spaces${NC} "
4886+ echo -en " ${BOLD} Labels:${NC} "
4887+ local batch_labels
4888+ read -r batch_labels
4889+ if [ -n " $batch_labels " ]; then
4890+ # shellcheck disable=SC2086
4891+ secret_add_batch $batch_labels || true
4892+ fi
4893+ press_any_key
4894+ ;;
4895+ 7)
4896+ echo -e " ${DIM} Enter labels separated by spaces${NC} "
4897+ echo -en " ${BOLD} Labels to remove:${NC} "
4898+ local batch_labels
4899+ read -r batch_labels
4900+ if [ -n " $batch_labels " ]; then
4901+ # shellcheck disable=SC2086
4902+ secret_remove_batch " false" $batch_labels || true
4903+ fi
4904+ press_any_key
4905+ ;;
47814906 0|" " ) return ;;
47824907 * ) ;;
47834908 esac
@@ -5239,7 +5364,9 @@ show_info_multisecret() {
52395364 echo " "
52405365 echo -e " ${BOLD} Commands:${NC} "
52415366 echo -e " ${GREEN} mtproxymax secret add <label>${NC} Create a new secret"
5367+ echo -e " ${GREEN} mtproxymax secret add-batch <l1> <l2> ...${NC} Add multiple (single restart)"
52425368 echo -e " ${GREEN} mtproxymax secret remove <label>${NC} Delete a secret"
5369+ echo -e " ${GREEN} mtproxymax secret remove-batch <l1> <l2> ...${NC} Remove multiple (single restart)"
52435370 echo -e " ${GREEN} mtproxymax secret rotate <label>${NC} Replace key, keep label"
52445371 echo -e " ${GREEN} mtproxymax secret enable <label>${NC} Re-enable a disabled secret"
52455372 echo -e " ${GREEN} mtproxymax secret disable <label>${NC} Temporarily disable access"
0 commit comments