diff --git a/apps/openlitespeed/1.7.17-lsphp74/.env.sample b/apps/openlitespeed/1.7.17-lsphp74/.env.sample
new file mode 100644
index 00000000..0902e645
--- /dev/null
+++ b/apps/openlitespeed/1.7.17-lsphp74/.env.sample
@@ -0,0 +1,6 @@
+CONTAINER_NAME="openlitespeed"
+DATA_PATH="./data"
+PANEL_APP_PORT_CONSOLE="40113"
+PANEL_APP_PORT_HTTP="80"
+PANEL_APP_PORT_HTTPS="443"
+TIME_ZONE="Asia/Shanghai"
diff --git a/apps/openlitespeed/1.7.17-lsphp74/data.yml b/apps/openlitespeed/1.7.17-lsphp74/data.yml
new file mode 100644
index 00000000..2c19e306
--- /dev/null
+++ b/apps/openlitespeed/1.7.17-lsphp74/data.yml
@@ -0,0 +1,40 @@
+additionalProperties:
+ formFields:
+ - default: 80
+ edit: true
+ envKey: PANEL_APP_PORT_HTTP
+ labelEn: HTTP Port
+ labelZh: HTTP端口
+ required: true
+ rule: paramPort
+ type: number
+ - default: 443
+ edit: true
+ envKey: PANEL_APP_PORT_HTTPS
+ labelEn: HTTPS Port
+ labelZh: HTTPS端口
+ required: true
+ rule: paramPort
+ type: number
+ - default: 40113
+ edit: true
+ envKey: PANEL_APP_PORT_CONSOLE
+ labelEn: Console Port
+ labelZh: 控制台端口
+ required: true
+ rule: paramPort
+ type: number
+ - default: ./data
+ disabled: true
+ envKey: DATA_PATH
+ labelEn: Data folder path
+ labelZh: 数据文件夹路径
+ required: true
+ type: text
+ - default: Asia/Shanghai
+ edit: true
+ envKey: TIME_ZONE
+ labelEn: Time zone
+ labelZh: 时区
+ required: true
+ type: text
diff --git a/apps/openlitespeed/1.7.17-lsphp74/data/bin/acme.sh b/apps/openlitespeed/1.7.17-lsphp74/data/bin/acme.sh
new file mode 100644
index 00000000..2804e217
--- /dev/null
+++ b/apps/openlitespeed/1.7.17-lsphp74/data/bin/acme.sh
@@ -0,0 +1,293 @@
+#!/usr/bin/env bash
+EMAIL=''
+NO_EMAIL=''
+DOMAIN=''
+INSTALL=''
+UNINSTALL=''
+TYPE=0
+CONT_NAME='litespeed'
+ACME_SRC='https://raw.githubusercontent.com/Neilpang/acme.sh/master/acme.sh'
+EPACE=' '
+RENEW=''
+RENEW_ALL=''
+FORCE=''
+REVOKE=''
+REMOVE=''
+
+echow(){
+ FLAG=${1}
+ shift
+ echo -e "\033[1m${EPACE}${FLAG}\033[0m${@}"
+}
+
+help_message(){
+ case ${1} in
+ "1")
+ echo 'You will need to install acme script at the first time.'
+ echo 'Please run acme.sh --install --email example@example.com'
+ ;;
+ "2")
+ echo -e "\033[1mOPTIONS\033[0m"
+ echow '-D, --domain [DOMAIN_NAME]'
+ echo "${EPACE}${EPACE}Example: acme.sh --domain example.com"
+ echo "${EPACE}${EPACE}will auto detect and apply for both example.com and www.example.com domains."
+ echow '-H, --help'
+ echo "${EPACE}${EPACE}Display help and exit."
+ echo -e "\033[1m Only for the First time\033[0m"
+ echow '--install --email [EMAIL_ADDR]'
+ echo "${EPACE}${EPACE}Will install ACME with the Email provided"
+ echow '-r, --renew'
+ echo "${EPACE}${EPACE}Renew a specific domain with -D or --domain parameter if posibile. To force renew, use -f parameter."
+ echow '-R, --renew-all'
+ echo "${EPACE}${EPACE}Renew all domains if possible. To force renew, use -f parameter."
+ echow '-f, -F, --force'
+ echo "${EPACE}${EPACE}Force renew for a specific domain or all domains."
+ echow '-v, --revoke'
+ echo "${EPACE}${EPACE}Revoke a domain."
+ echow '-V, --remove'
+ echo "${EPACE}${EPACE}Remove a domain."
+ exit 0
+ ;;
+ "3")
+ echo 'Please run acme.sh --domain [DOMAIN_NAME] to apply certificate'
+ exit 0
+ ;;
+ esac
+}
+
+check_input(){
+ if [ -z "${1}" ]; then
+ help_message 2
+ fi
+}
+
+domain_filter(){
+ if [ -z "${1}" ]; then
+ help_message 3
+ fi
+ DOMAIN="${1}"
+ DOMAIN="${DOMAIN#http://}"
+ DOMAIN="${DOMAIN#https://}"
+ DOMAIN="${DOMAIN#ftp://}"
+ DOMAIN="${DOMAIN#scp://}"
+ DOMAIN="${DOMAIN#scp://}"
+ DOMAIN="${DOMAIN#sftp://}"
+ DOMAIN=${DOMAIN%%/*}
+}
+
+email_filter(){
+ CKREG="^[a-z0-9!#\$%&'*+/=?^_\`{|}~-]+(\.[a-z0-9!#$%&'*+/=?^_\`{|}~-]+)*@([a-z0-9]([a-z0-9-]*[a-z0-9])?\.)+[a-z0-9]([a-z0-9-]*[a-z0-9])?\$"
+ if [[ "${1}" =~ ${CKREG} ]] ; then
+ echo -e "[O] The E-mail \033[32m${1}\033[0m is valid."
+ else
+ echo -e "[X] The E-mail \e[31m${1}\e[39m is invalid"
+ exit 1
+ fi
+}
+
+cert_hook(){
+ echo '[Start] Adding ACME hook'
+ docker compose exec ${CONT_NAME} su -s /bin/bash -c "certhookctl.sh"
+ echo '[End] Adding ACME hook'
+}
+
+www_domain(){
+ CHECK_WWW=$(echo ${1} | cut -c1-4)
+ if [[ ${CHECK_WWW} == www. ]] ; then
+ DOMAIN=$(echo ${1} | cut -c 5-)
+ else
+ DOMAIN=${1}
+ fi
+ WWW_DOMAIN="www.${DOMAIN}"
+}
+
+domain_verify(){
+ curl -Is http://${DOMAIN}/ | grep -i LiteSpeed > /dev/null 2>&1
+ if [ ${?} = 0 ]; then
+ echo -e "[O] The domain name \033[32m${DOMAIN}\033[0m is accessible."
+ TYPE=1
+ curl -Is http://${WWW_DOMAIN}/ | grep -i LiteSpeed > /dev/null 2>&1
+ if [ ${?} = 0 ]; then
+ echo -e "[O] The domain name \033[32m${WWW_DOMAIN}\033[0m is accessible."
+ TYPE=2
+ else
+ echo -e "[!] The domain name ${WWW_DOMAIN} is inaccessible."
+ fi
+ else
+ echo -e "[X] The domain name \e[31m${DOMAIN}\e[39m is inaccessible, please verify."
+ exit 1
+ fi
+}
+
+install_acme(){
+ echo '[Start] Install ACME'
+ if [ "${1}" = 'true' ]; then
+ docker compose exec litespeed su -c "cd; wget ${ACME_SRC}; chmod 755 acme.sh; \
+ ./acme.sh --install --cert-home ~/.acme.sh/certs; \
+ rm ~/acme.sh"
+ elif [ "${2}" != '' ]; then
+ email_filter "${2}"
+ docker compose exec litespeed su -c "cd; wget ${ACME_SRC}; chmod 755 acme.sh; \
+ ./acme.sh --install --cert-home ~/.acme.sh/certs --accountemail ${2}; \
+ rm ~/acme.sh"
+ else
+ help_message 1
+ exit 1
+ fi
+ echo '[End] Install ACME'
+}
+
+uninstall_acme(){
+ echo '[Start] Uninstall ACME'
+ docker compose exec ${CONT_NAME} su -c "~/.acme.sh/acme.sh --uninstall"
+ echo '[End] Uninstall ACME'
+ exit 0
+}
+
+check_acme(){
+ echo '[Start] Checking ACME'
+ docker compose exec ${CONT_NAME} su -c "test -f /root/.acme.sh/acme.sh"
+ if [ ${?} != 0 ]; then
+ install_acme "${NO_EMAIL}" "${EMAIL}"
+ cert_hook
+ help_message 3
+ fi
+ echo '[End] Checking ACME'
+}
+
+lsws_restart(){
+ docker compose exec ${CONT_NAME} su -c '/usr/local/lsws/bin/lswsctrl restart >/dev/null'
+}
+
+doc_root_verify(){
+ if [ "${DOC_ROOT}" = '' ]; then
+ DOC_PATH="/var/www/vhosts/${1}/html"
+ else
+ DOC_PATH="${DOC_ROOT}"
+ fi
+ docker compose exec ${CONT_NAME} su -c "[ -e ${DOC_PATH} ]"
+ if [ ${?} -eq 0 ]; then
+ echo -e "[O] The document root folder \033[32m${DOC_PATH}\033[0m does exist."
+ else
+ echo -e "[X] The document root folder \e[31m${DOC_PATH}\e[39m does not exist!"
+ exit 1
+ fi
+}
+
+install_cert(){
+ echo '[Start] Apply Lets Encrypt Certificate'
+ if [ ${TYPE} = 1 ]; then
+ docker compose exec ${CONT_NAME} su -c "/root/.acme.sh/acme.sh --issue -d ${1} -w ${DOC_PATH}"
+ elif [ ${TYPE} = 2 ]; then
+ docker compose exec ${CONT_NAME} su -c "/root/.acme.sh/acme.sh --issue -d ${1} -d www.${1} -w ${DOC_PATH}"
+ else
+ echo 'unknown Type!'
+ exit 2
+ fi
+ echo '[End] Apply Lets Encrypt Certificate'
+}
+
+renew_acme(){
+ echo '[Start] Renew ACME'
+ if [ "${FORCE}" = 'true' ]; then
+ docker compose exec ${CONT_NAME} su -c "~/.acme.sh/acme.sh --renew --domain ${1} --force"
+ else
+ docker compose exec ${CONT_NAME} su -c "~/.acme.sh/acme.sh --renew --domain ${1}"
+ fi
+ echo '[End] Renew ACME'
+ lsws_restart
+}
+
+renew_all_acme(){
+ echo '[Start] Renew all ACME'
+ if [ "${FORCE}" = 'true' ]; then
+ docker compose exec ${CONT_NAME} su -c "~/.acme.sh/acme.sh --renew-all --force"
+ else
+ docker compose exec ${CONT_NAME} su -c "~/.acme.sh/acme.sh --renew-all"
+ fi
+ echo '[End] Renew all ACME'
+ lsws_restart
+}
+
+revoke(){
+ echo '[Start] Revoke a domain'
+ docker compose exec ${CONT_NAME} su -c "~/.acme.sh/acme.sh --revoke --domain ${1}"
+ echo '[End] Revoke a domain'
+ lsws_restart
+}
+
+remove(){
+ echo '[Start] Remove a domain'
+ docker compose exec ${CONT_NAME} su -c "~/.acme.sh/acme.sh --remove --domain ${1}"
+ echo '[End] Remove a domain'
+ lsws_restart
+}
+
+main(){
+ if [ "${RENEW_ALL}" = 'true' ]; then
+ renew_all_acme
+ exit 0
+ elif [ "${RENEW}" = 'true' ]; then
+ renew_acme ${DOMAIN}
+ exit 0
+ elif [ "${REVOKE}" = 'true' ]; then
+ revoke ${DOMAIN}
+ exit 0
+ elif [ "${REMOVE}" = 'true' ]; then
+ remove ${DOMAIN}
+ exit 0
+ fi
+
+ check_acme
+ domain_filter ${DOMAIN}
+ www_domain ${DOMAIN}
+ domain_verify
+ doc_root_verify ${DOMAIN}
+ install_cert ${DOMAIN}
+ lsws_restart
+}
+
+check_input ${1}
+while [ ! -z "${1}" ]; do
+ case ${1} in
+ -[hH] | -help | --help)
+ help_message 2
+ ;;
+ -[dD] | -domain | --domain) shift
+ check_input "${1}"
+ DOMAIN="${1}"
+ ;;
+ -[iI] | --install )
+ INSTALL=true
+ ;;
+ -[uU] | --uninstall )
+ UNINSTALL=true
+ uninstall_acme
+ ;;
+ -[fF] | --force )
+ FORCE=true
+ ;;
+ -[r] | --renew )
+ RENEW=true
+ ;;
+ -[R] | --renew-all )
+ RENEW_ALL=true
+ ;;
+ -[v] | --revoke )
+ REVOKE=true
+ ;;
+ -[V] | --remove )
+ REMOVE=true
+ ;;
+ -[eE] | --email ) shift
+ check_input "${1}"
+ EMAIL="${1}"
+ ;;
+ *)
+ help_message 2
+ ;;
+ esac
+ shift
+done
+
+main
\ No newline at end of file
diff --git a/apps/openlitespeed/1.7.17-lsphp74/data/bin/appinstall.sh b/apps/openlitespeed/1.7.17-lsphp74/data/bin/appinstall.sh
new file mode 100644
index 00000000..ba67d52d
--- /dev/null
+++ b/apps/openlitespeed/1.7.17-lsphp74/data/bin/appinstall.sh
@@ -0,0 +1,60 @@
+#!/usr/bin/env bash
+APP_NAME=''
+DOMAIN=''
+EPACE=' '
+
+echow(){
+ FLAG=${1}
+ shift
+ echo -e "\033[1m${EPACE}${FLAG}\033[0m${@}"
+}
+
+help_message(){
+ echo -e "\033[1mOPTIONS\033[0m"
+ echow '-A, --app [app_name] -D, --domain [DOMAIN_NAME]'
+ echo "${EPACE}${EPACE}Example: appinstall.sh -A wordpress -D example.com"
+ echo "${EPACE}${EPACE}Will install WordPress CMS under the example.com domain"
+ echow '-H, --help'
+ echo "${EPACE}${EPACE}Display help and exit."
+ exit 0
+}
+
+check_input(){
+ if [ -z "${1}" ]; then
+ help_message
+ exit 1
+ fi
+}
+
+app_download(){
+ docker compose exec litespeed su -c "appinstallctl.sh --app ${1} --domain ${2}"
+ bash bin/webadmin.sh -r
+ exit 0
+}
+
+main(){
+ app_download ${APP_NAME} ${DOMAIN}
+}
+
+check_input ${1}
+while [ ! -z "${1}" ]; do
+ case ${1} in
+ -[hH] | -help | --help)
+ help_message
+ ;;
+ -[aA] | -app | --app) shift
+ check_input "${1}"
+ APP_NAME="${1}"
+ ;;
+ -[dD] | -domain | --domain) shift
+ check_input "${1}"
+ DOMAIN="${1}"
+ ;;
+ *)
+ help_message
+ ;;
+ esac
+ shift
+done
+
+main
\ No newline at end of file
diff --git a/apps/openlitespeed/1.7.17-lsphp74/data/bin/container/appinstallctl.sh b/apps/openlitespeed/1.7.17-lsphp74/data/bin/container/appinstallctl.sh
new file mode 100644
index 00000000..d79c63f1
--- /dev/null
+++ b/apps/openlitespeed/1.7.17-lsphp74/data/bin/container/appinstallctl.sh
@@ -0,0 +1,660 @@
+#!/bin/bash
+DEFAULT_VH_ROOT='/var/www/vhosts'
+VH_DOC_ROOT=''
+VHNAME=''
+APP_NAME=''
+DOMAIN=''
+WWW_UID=''
+WWW_GID=''
+WP_CONST_CONF=''
+PUB_IP=$(curl -s http://checkip.amazonaws.com)
+DB_HOST='mysql'
+PLUGINLIST="litespeed-cache.zip"
+THEME='twentytwenty'
+EPACE=' '
+
+echow(){
+ FLAG=${1}
+ shift
+ echo -e "\033[1m${EPACE}${FLAG}\033[0m${@}"
+}
+
+help_message(){
+ echo -e "\033[1mOPTIONS\033[0m"
+ echow '-A, -app [wordpress] -D, --domain [DOMAIN_NAME]'
+ echo "${EPACE}${EPACE}Example: appinstallctl.sh --app wordpress --domain example.com"
+ echow '-H, --help'
+ echo "${EPACE}${EPACE}Display help and exit."
+ exit 0
+}
+
+check_input(){
+ if [ -z "${1}" ]; then
+ help_message
+ exit 1
+ fi
+}
+
+linechange(){
+ LINENUM=$(grep -n "${1}" ${2} | cut -d: -f 1)
+ if [ -n "${LINENUM}" ] && [ "${LINENUM}" -eq "${LINENUM}" ] 2>/dev/null; then
+ sed -i "${LINENUM}d" ${2}
+ sed -i "${LINENUM}i${3}" ${2}
+ fi
+}
+
+ck_ed(){
+ if [ ! -f /bin/ed ]; then
+ echo "Install ed package.."
+ apt-get install ed -y > /dev/null 2>&1
+ fi
+}
+
+ck_unzip(){
+ if [ ! -f /usr/bin/unzip ]; then
+ echo "Install unzip package.."
+ apt-get install unzip -y > /dev/null 2>&1
+ fi
+}
+
+get_owner(){
+ WWW_UID=$(stat -c "%u" ${DEFAULT_VH_ROOT})
+ WWW_GID=$(stat -c "%g" ${DEFAULT_VH_ROOT})
+ if [ ${WWW_UID} -eq 0 ] || [ ${WWW_GID} -eq 0 ]; then
+ WWW_UID=1000
+ WWW_GID=1000
+ echo "Set owner to ${WWW_UID}"
+ fi
+}
+
+get_db_pass(){
+ if [ -f ${DEFAULT_VH_ROOT}/${1}/.db_pass ]; then
+ SQL_DB=$(grep -i Database ${VH_ROOT}/.db_pass | awk -F ':' '{print $2}' | tr -d '"')
+ SQL_USER=$(grep -i Username ${VH_ROOT}/.db_pass | awk -F ':' '{print $2}' | tr -d '"')
+ SQL_PASS=$(grep -i Password ${VH_ROOT}/.db_pass | awk -F ':' '{print $2}' | tr -d '"')
+ else
+ echo 'db pass file can not locate, skip wp-config pre-config.'
+ fi
+}
+
+set_vh_docroot(){
+ if [ "${VHNAME}" != '' ]; then
+ VH_ROOT="${DEFAULT_VH_ROOT}/${VHNAME}"
+ VH_DOC_ROOT="${DEFAULT_VH_ROOT}/${VHNAME}/html"
+ WP_CONST_CONF="${VH_DOC_ROOT}/wp-content/plugins/litespeed-cache/data/const.default.ini"
+ elif [ -d ${DEFAULT_VH_ROOT}/${1}/html ]; then
+ VH_ROOT="${DEFAULT_VH_ROOT}/${1}"
+ VH_DOC_ROOT="${DEFAULT_VH_ROOT}/${1}/html"
+ WP_CONST_CONF="${VH_DOC_ROOT}/wp-content/plugins/litespeed-cache/data/const.default.ini"
+ else
+ echo "${DEFAULT_VH_ROOT}/${1}/html does not exist, please add domain first! Abort!"
+ exit 1
+ fi
+}
+
+check_sql_native(){
+ local COUNTER=0
+ local LIMIT_NUM=100
+ until [ "$(curl -v mysql:3306 2>&1 | grep -i 'native\|Connected')" ]; do
+ echo "Counter: ${COUNTER}/${LIMIT_NUM}"
+ COUNTER=$((COUNTER+1))
+ if [ ${COUNTER} = 10 ]; then
+ echo '--- MySQL is starting, please wait... ---'
+ elif [ ${COUNTER} = ${LIMIT_NUM} ]; then
+ echo '--- MySQL is timeout, exit! ---'
+ exit 1
+ fi
+ sleep 1
+ done
+}
+
+install_wp_plugin(){
+ for PLUGIN in ${PLUGINLIST}; do
+ wget -q -P ${VH_DOC_ROOT}/wp-content/plugins/ https://downloads.wordpress.org/plugin/${PLUGIN}
+ if [ ${?} = 0 ]; then
+ ck_unzip
+ unzip -qq -o ${VH_DOC_ROOT}/wp-content/plugins/${PLUGIN} -d ${VH_DOC_ROOT}/wp-content/plugins/
+ else
+ echo "${PLUGINLIST} FAILED to download"
+ fi
+ done
+ rm -f ${VH_DOC_ROOT}/wp-content/plugins/*.zip
+}
+
+set_htaccess(){
+ if [ ! -f ${VH_DOC_ROOT}/.htaccess ]; then
+ touch ${VH_DOC_ROOT}/.htaccess
+ fi
+ cat << EOM > ${VH_DOC_ROOT}/.htaccess
+# BEGIN WordPress
+
+RewriteEngine On
+RewriteBase /
+RewriteRule ^index\.php$ - [L]
+RewriteCond %{REQUEST_FILENAME} !-f
+RewriteCond %{REQUEST_FILENAME} !-d
+RewriteRule . /index.php [L]
+
+# END WordPress
+EOM
+}
+
+get_theme_name(){
+ THEME_NAME=$(grep WP_DEFAULT_THEME ${VH_DOC_ROOT}/wp-includes/default-constants.php | grep -v '!' | awk -F "'" '{print $4}')
+ echo "${THEME_NAME}" | grep 'twenty' >/dev/null 2>&1
+ if [ ${?} = 0 ]; then
+ THEME="${THEME_NAME}"
+ fi
+}
+
+set_lscache(){
+ cat << EOM > "${WP_CONST_CONF}"
+;
+; This is the predefined default LSCWP configuration file
+;
+; All the keys and values please refer \`src/const.cls.php\`
+;
+; Comments start with \`;\`
+;
+;; -------------------------------------------------- ;;
+;; -------------- General ----------------- ;;
+;; -------------------------------------------------- ;;
+; O_AUTO_UPGRADE
+auto_upgrade = false
+; O_API_KEY
+api_key = ''
+; O_SERVER_IP
+server_ip = ''
+; O_NEWS
+news = false
+;; -------------------------------------------------- ;;
+;; -------------- Cache ----------------- ;;
+;; -------------------------------------------------- ;;
+cache-priv = true
+cache-commenter = true
+cache-rest = true
+cache-page_login = true
+cache-favicon = true
+cache-resources = true
+cache-browser = false
+cache-mobile = false
+cache-mobile_rules = 'Mobile
+Android
+Silk/
+Kindle
+BlackBerry
+Opera Mini
+Opera Mobi'
+cache-exc_useragents = ''
+cache-exc_cookies = ''
+cache-exc_qs = ''
+cache-exc_cat = ''
+cache-exc_tag = ''
+cache-force_uri = ''
+cache-force_pub_uri = ''
+cache-priv_uri = ''
+cache-exc = ''
+cache-exc_roles = ''
+cache-drop_qs = 'fbclid
+gclid
+utm*
+_ga'
+cache-ttl_pub = 604800
+cache-ttl_priv = 1800
+cache-ttl_frontpage = 604800
+cache-ttl_feed = 604800
+; O_CACHE_TTL_REST
+cache-ttl_rest = 604800
+cache-ttl_browser = 31557600
+cache-login_cookie = ''
+cache-vary_group = ''
+cache-ttl_status = '403 3600
+404 3600
+500 3600'
+;; -------------------------------------------------- ;;
+;; -------------- Purge ----------------- ;;
+;; -------------------------------------------------- ;;
+; O_PURGE_ON_UPGRADE
+purge-upgrade = true
+; O_PURGE_STALE
+purge-stale = true
+purge-post_all = false
+purge-post_f = true
+purge-post_h = true
+purge-post_p = true
+purge-post_pwrp = true
+purge-post_a = true
+purge-post_y = false
+purge-post_m = true
+purge-post_d = false
+purge-post_t = true
+purge-post_pt = true
+purge-timed_urls = ''
+purge-timed_urls_time = ''
+purge-hook_all = 'switch_theme
+wp_create_nav_menu
+wp_update_nav_menu
+wp_delete_nav_menu
+create_term
+edit_terms
+delete_term
+add_link
+edit_link
+delete_link'
+;; -------------------------------------------------- ;;
+;; -------------- ESI ----------------- ;;
+;; -------------------------------------------------- ;;
+; O_ESI
+esi = false
+; O_ESI_CACHE_ADMBAR
+esi-cache_admbar = true
+; O_ESI_CACHE_COMMFORM
+esi-cache_commform = true
+; O_ESI_NONCE
+esi-nonce = 'stats_nonce
+subscribe_nonce'
+;; -------------------------------------------------- ;;
+;; -------------- Utilities ----------------- ;;
+;; -------------------------------------------------- ;;
+util-heartbeat = true
+util-instant_click = false
+util-check_advcache = true
+util-no_https_vary = false
+;; -------------------------------------------------- ;;
+;; -------------- Debug ----------------- ;;
+;; -------------------------------------------------- ;;
+; O_DEBUG_DISABLE_ALL
+debug-disable_all = false
+; O_DEBUG
+debug = false
+; O_DEBUG_IPS
+debug-ips = '127.0.0.1'
+; O_DEBUG_LEVEL
+debug-level = false
+; O_DEBUG_FILESIZE
+debug-filesize = 3
+; O_DEBUG_COOKIE
+debug-cookie = false
+; O_DEBUG_COLLAPS_QS
+debug-collaps_qs = false
+; O_DEBUG_INC
+debug-inc = ''
+; O_DEBUG_EXC
+debug-exc = ''
+;; -------------------------------------------------- ;;
+;; -------------- DB Optm ----------------- ;;
+;; -------------------------------------------------- ;;
+; O_DB_OPTM_REVISIONS_MAX
+db_optm-revisions_max = 0
+; O_DB_OPTM_REVISIONS_AGE
+db_optm-revisions_age = 0
+;; -------------------------------------------------- ;;
+;; -------------- HTML Optm ----------------- ;;
+;; -------------------------------------------------- ;;
+; O_OPTM_CSS_MIN
+optm-css_min = false
+optm-css_inline_min = false
+; O_OPTM_CSS_COMB
+optm-css_comb = false
+optm-css_comb_priority = false
+; O_OPTM_CSS_HTTP2
+optm-css_http2 = false
+optm-css_exc = ''
+; O_OPTM_JS_MIN
+optm-js_min = false
+optm-js_inline_min = false
+; O_OPTM_JS_COMB
+optm-js_comb = false
+optm-js_comb_priority = false
+; O_OPTM_JS_HTTP2
+optm-js_http2 = false
+; O_OPTM_EXC_JQ
+optm-js_exc = ''
+optm-ttl = 604800
+optm-html_min = false
+optm-qs_rm = false
+optm-ggfonts_rm = false
+; O_OPTM_CSS_ASYNC
+optm-css_async = false
+; O_OPTM_CCSS_GEN
+optm-ccss_gen = true
+; O_OPTM_CCSS_ASYNC
+optm-ccss_async = true
+; O_OPTM_CSS_ASYNC_INLINE
+optm-css_async_inline = true
+; O_OPTM_CSS_FONT_DISPLAY
+optm-css_font_display = false
+; O_OPTM_JS_DEFER
+optm-js_defer = false
+; O_OPTM_JS_INLINE_DEFER
+optm-js_inline_defer = false
+optm-emoji_rm = false
+optm-exc_jq = true
+optm-ggfonts_async = false
+optm-max_size = 2
+optm-rm_comment = false
+optm-exc_roles = ''
+optm-ccss_con = ''
+optm-js_defer_exc = ''
+; O_OPTM_DNS_PREFETCH
+optm-dns_prefetch = ''
+; O_OPTM_DNS_PREFETCH_CTRL
+optm-dns_prefetch_ctrl = false
+optm-exc = ''
+; O_OPTM_CCSS_SEP_POSTTYPE
+optm-ccss_sep_posttype = ''
+; O_OPTM_CCSS_SEP_URI
+optm-ccss_sep_uri = ''
+;; -------------------------------------------------- ;;
+;; -------------- Object Cache ----------------- ;;
+;; -------------------------------------------------- ;;
+object = true
+object-kind = false
+;object-host = 'localhost'
+object-host = '/var/www/memcached.sock'
+;object-port = 11211
+cache_object_port = ''
+object-life = 360
+object-persistent = true
+object-admin = true
+object-transients = true
+object-db_id = 0
+object-user = ''
+object-pswd = ''
+object-global_groups = 'users
+userlogins
+usermeta
+user_meta
+site-transient
+site-options
+site-lookup
+blog-lookup
+blog-details
+rss
+global-posts
+blog-id-cache'
+object-non_persistent_groups = 'comment
+counts
+plugins
+wc_session_id'
+;; -------------------------------------------------- ;;
+;; -------------- Discussion ----------------- ;;
+;; -------------------------------------------------- ;;
+; O_DISCUSS_AVATAR_CACHE
+discuss-avatar_cache = false
+; O_DISCUSS_AVATAR_CRON
+discuss-avatar_cron = false
+; O_DISCUSS_AVATAR_CACHE_TTL
+discuss-avatar_cache_ttl = 604800
+;; -------------------------------------------------- ;;
+;; -------------- Media ----------------- ;;
+;; -------------------------------------------------- ;;
+; O_MEDIA_LAZY
+media-lazy = false
+; O_MEDIA_LAZY_PLACEHOLDER
+media-lazy_placeholder = ''
+; O_MEDIA_PLACEHOLDER_RESP
+media-placeholder_resp = false
+; O_MEDIA_PLACEHOLDER_RESP_COLOR
+media-placeholder_resp_color = '#cfd4db'
+; O_MEDIA_PLACEHOLDER_RESP_GENERATOR
+media-placeholder_resp_generator = false
+; O_MEDIA_PLACEHOLDER_RESP_SVG
+media-placeholder_resp_svg = ''
+; O_MEDIA_PLACEHOLDER_LQIP
+media-placeholder_lqip = false
+; O_MEDIA_PLACEHOLDER_LQIP_QUAL
+media-placeholder_lqip_qual = 4
+; O_MEDIA_PLACEHOLDER_RESP_ASYNC
+media-placeholder_resp_async = true
+; O_MEDIA_IFRAME_LAZY
+media-iframe_lazy = false
+; O_MEDIA_LAZYJS_INLINE
+media-lazyjs_inline = false
+; O_MEDIA_LAZY_EXC
+media-lazy_exc = ''
+; O_MEDIA_LAZY_CLS_EXC
+media-lazy_cls_exc = ''
+; O_MEDIA_LAZY_PARENT_CLS_EXC
+media-lazy_parent_cls_exc = ''
+; O_MEDIA_IFRAME_LAZY_CLS_EXC
+media-iframe_lazy_cls_exc = ''
+; O_MEDIA_IFRAME_LAZY_PARENT_CLS_EXC
+media-iframe_lazy_parent_cls_exc = ''
+; O_MEDIA_LAZY_URI_EXC
+media-lazy_uri_exc = ''
+;; -------------------------------------------------- ;;
+;; -------------- Image Optm ----------------- ;;
+;; -------------------------------------------------- ;;
+img_optm-auto = false
+img_optm-cron = true
+img_optm-ori = true
+img_optm-rm_bkup = false
+img_optm-webp = false
+img_optm-lossless = false
+img_optm-exif = false
+img_optm-webp_replace = false
+img_optm-webp_attr = 'img.src
+div.data-thumb
+img.data-src
+div.data-large_image
+img.retina_logo_url
+div.data-parallax-image
+video.poster'
+img_optm-webp_replace_srcset = false
+img_optm-jpg_quality = 82
+;; -------------------------------------------------- ;;
+;; -------------- Crawler ----------------- ;;
+;; -------------------------------------------------- ;;
+crawler = false
+crawler-inc_posts = true
+crawler-inc_pages = true
+crawler-inc_cats = true
+crawler-inc_tags = true
+crawler-exc_cpt = ''
+crawler-order_links = 'date_desc'
+crawler-usleep = 500
+crawler-run_duration = 400
+crawler-run_interval = 600
+crawler-crawl_interval = 302400
+crawler-threads = 3
+crawler-timeout = 30
+crawler-load_limit = 1
+; O_CRAWLER_SITEMAP
+crawler-sitemap = ''
+; O_CRAWLER_DROP_DOMAIN
+crawler-drop_domain = true
+crawler-roles = ''
+crawler-cookies = ''
+;; -------------------------------------------------- ;;
+;; -------------- Misc ----------------- ;;
+;; -------------------------------------------------- ;;
+; O_MISC_HTACCESS_FRONT
+misc-htaccess_front = ''
+; O_MISC_HTACCESS_BACK
+misc-htaccess_back = ''
+; O_MISC_HEARTBEAT_FRONT
+misc-heartbeat_front = false
+; O_MISC_HEARTBEAT_FRONT_TTL
+misc-heartbeat_front_ttl = 60
+; O_MISC_HEARTBEAT_BACK
+misc-heartbeat_back = false
+; O_MISC_HEARTBEAT_BACK_TTL
+misc-heartbeat_back_ttl = 60
+; O_MISC_HEARTBEAT_EDITOR
+misc-heartbeat_editor = false
+; O_MISC_HEARTBEAT_EDITOR_TTL
+misc-heartbeat_editor_ttl = 15
+;; -------------------------------------------------- ;;
+;; -------------- CDN ----------------- ;;
+;; -------------------------------------------------- ;;
+cdn = false
+cdn-ori = ''
+cdn-ori_dir = ''
+cdn-exc = ''
+cdn-remote_jq = false
+cdn-quic = false
+cdn-quic_email = ''
+cdn-quic_key = ''
+cdn-cloudflare = false
+cdn-cloudflare_email = ''
+cdn-cloudflare_key = ''
+cdn-cloudflare_name = ''
+cdn-cloudflare_zone = ''
+; \`cdn-mapping\` needs to be put in the end with a section tag
+;; -------------------------------------------------- ;;
+;; -------------- CDN 2 ----------------- ;;
+;; -------------------------------------------------- ;;
+; <------------ CDN Mapping Example BEGIN -------------------->
+; Need to keep the section tag \`[cdn-mapping]\` before list.
+;
+; NOTE 1) Need to set all child options to make all resources to be replaced without missing.
+; NOTE 2) \`url[n]\` option must have to enable the row setting of \`n\`.
+; NOTE 3) This section needs to be put in the end of this .ini file
+;
+; To enable the 2nd mapping record by default, please remove the \`;;\` in the related lines.
+[cdn-mapping]
+url[0] = ''
+inc_js[0] = true
+inc_css[0] = true
+inc_img[0] = true
+filetype[0] = '.aac
+.css
+.eot
+.gif
+.jpeg
+.js
+.jpg
+.less
+.mp3
+.mp4
+.ogg
+.otf
+.pdf
+.png
+.svg
+.ttf
+.woff'
+;;url[1] = 'https://2nd_CDN_url.com/'
+;;filetype[1] = '.webm'
+; <------------ CDN Mapping Example END ------------------>
+EOM
+
+ THEME_PATH="${VH_DOC_ROOT}/wp-content/themes/${THEME}"
+ if [ ! -f ${THEME_PATH}/functions.php ]; then
+ cat >> "${THEME_PATH}/functions.php" <>/dev/null 2>&1
+2i
+require_once( WP_CONTENT_DIR.'/../wp-admin/includes/plugin.php' );
+\$path = 'litespeed-cache/litespeed-cache.php' ;
+if (!is_plugin_active( \$path )) {
+ activate_plugin( \$path ) ;
+ rename( __FILE__ . '.bk', __FILE__ );
+}
+.
+w
+q
+END
+ fi
+}
+
+preinstall_wordpress(){
+ if [ "${VHNAME}" != '' ]; then
+ get_db_pass ${VHNAME}
+ else
+ get_db_pass ${DOMAIN}
+ fi
+ if [ ! -f ${VH_DOC_ROOT}/wp-config.php ] && [ -f ${VH_DOC_ROOT}/wp-config-sample.php ]; then
+ cp ${VH_DOC_ROOT}/wp-config-sample.php ${VH_DOC_ROOT}/wp-config.php
+ NEWDBPWD="define('DB_PASSWORD', '${SQL_PASS}');"
+ linechange 'DB_PASSWORD' ${VH_DOC_ROOT}/wp-config.php "${NEWDBPWD}"
+ NEWDBPWD="define('DB_USER', '${SQL_USER}');"
+ linechange 'DB_USER' ${VH_DOC_ROOT}/wp-config.php "${NEWDBPWD}"
+ NEWDBPWD="define('DB_NAME', '${SQL_DB}');"
+ linechange 'DB_NAME' ${VH_DOC_ROOT}/wp-config.php "${NEWDBPWD}"
+ #NEWDBPWD="define('DB_HOST', '${PUB_IP}');"
+ NEWDBPWD="define('DB_HOST', '${DB_HOST}');"
+ linechange 'DB_HOST' ${VH_DOC_ROOT}/wp-config.php "${NEWDBPWD}"
+ elif [ -f ${VH_DOC_ROOT}/wp-config.php ]; then
+ echo "${VH_DOC_ROOT}/wp-config.php already exist, exit !"
+ exit 1
+ else
+ echo 'Skip!'
+ exit 2
+ fi
+}
+
+app_wordpress_dl(){
+ if [ ! -f "${VH_DOC_ROOT}/wp-config.php" ] && [ ! -f "${VH_DOC_ROOT}/wp-config-sample.php" ]; then
+ wp core download \
+ --allow-root \
+ --quiet
+ else
+ echo 'wordpress already exist, abort!'
+ exit 1
+ fi
+}
+
+change_owner(){
+ if [ "${VHNAME}" != '' ]; then
+ chown -R ${WWW_UID}:${WWW_GID} ${DEFAULT_VH_ROOT}/${VHNAME}
+ else
+ chown -R ${WWW_UID}:${WWW_GID} ${DEFAULT_VH_ROOT}/${DOMAIN}
+ fi
+}
+
+main(){
+ set_vh_docroot ${DOMAIN}
+ get_owner
+ cd ${VH_DOC_ROOT}
+ if [ "${APP_NAME}" = 'wordpress' ] || [ "${APP_NAME}" = 'wp' ]; then
+ check_sql_native
+ app_wordpress_dl
+ preinstall_wordpress
+ install_wp_plugin
+ set_htaccess
+ get_theme_name
+ set_lscache
+ change_owner
+ exit 0
+ else
+ echo "APP: ${APP_NAME} not support, exit!"
+ exit 1
+ fi
+}
+
+check_input ${1}
+while [ ! -z "${1}" ]; do
+ case ${1} in
+ -[hH] | -help | --help)
+ help_message
+ ;;
+ -[aA] | -app | --app) shift
+ check_input "${1}"
+ APP_NAME="${1}"
+ ;;
+ -[dD] | -domain | --domain) shift
+ check_input "${1}"
+ DOMAIN="${1}"
+ ;;
+ -vhname | --vhname) shift
+ VHNAME="${1}"
+ ;;
+ *)
+ help_message
+ ;;
+ esac
+ shift
+done
+main
diff --git a/apps/openlitespeed/1.7.17-lsphp74/data/bin/container/certhookctl.sh b/apps/openlitespeed/1.7.17-lsphp74/data/bin/container/certhookctl.sh
new file mode 100644
index 00000000..18be0965
--- /dev/null
+++ b/apps/openlitespeed/1.7.17-lsphp74/data/bin/container/certhookctl.sh
@@ -0,0 +1,18 @@
+#!/bin/bash
+BOTCRON='/var/spool/cron/crontabs/root'
+
+cert_hook(){
+ grep 'acme' ${BOTCRON} >/dev/null
+ if [ ${?} = 0 ]; then
+ grep 'lswsctrl' ${BOTCRON} >/dev/null
+ if [ ${?} = 0 ]; then
+ echo 'Hook already exist, skip!'
+ else
+ sed -i 's/--cron/--cron --renew-hook "\/usr\/local\/lsws\/bin\/lswsctrl restart"/g' ${BOTCRON}
+ fi
+ else
+ echo "[X] ${BOTCRON} does not exist, please check it later!"
+ fi
+}
+
+cert_hook
\ No newline at end of file
diff --git a/apps/openlitespeed/1.7.17-lsphp74/data/bin/container/domainctl.sh b/apps/openlitespeed/1.7.17-lsphp74/data/bin/container/domainctl.sh
new file mode 100644
index 00000000..75539ef0
--- /dev/null
+++ b/apps/openlitespeed/1.7.17-lsphp74/data/bin/container/domainctl.sh
@@ -0,0 +1,160 @@
+#!/usr/bin/env bash
+CK_RESULT=''
+LSDIR='/usr/local/lsws'
+LS_HTTPD_CONF="${LSDIR}/conf/httpd_config.xml"
+OLS_HTTPD_CONF="${LSDIR}/conf/httpd_config.conf"
+EPACE=' '
+
+echow(){
+ FLAG=${1}
+ shift
+ echo -e "\033[1m${EPACE}${FLAG}\033[0m${@}"
+}
+
+help_message(){
+ echo -e "\033[1mOPTIONS\033[0m"
+ echow '-A, --add [DOMAIN_NAME]'
+ echo "${EPACE}${EPACE}Will add domain to listener and creat a virtual host from template"
+ echow '-D, --del [DOMAIN_NAME]'
+ echo "${EPACE}${EPACE}Will delete domain from listener"
+ echow '-H, --help'
+ echo "${EPACE}${EPACE}Display help."
+}
+
+check_lsv(){
+ if [ -f ${LSDIR}/bin/openlitespeed ]; then
+ LSV='openlitespeed'
+ elif [ -f ${LSDIR}/bin/litespeed ]; then
+ LSV='lsws'
+ else
+ echo 'Version not exist, abort!'
+ exit 1
+ fi
+}
+
+dot_escape(){
+ ESCAPE=$(echo ${1} | sed 's/\./\\./g')
+}
+
+check_duplicate(){
+ CK_RESULT=$(grep -E "${1}" ${2})
+}
+
+fst_match_line(){
+ FIRST_LINE_NUM=$(grep -n -m 1 ${1} ${2} | awk -F ':' '{print $1}')
+}
+fst_match_after(){
+ FIRST_NUM_AFTER=$(tail -n +${1} ${2} | grep -n -m 1 ${3} | awk -F ':' '{print $1}')
+}
+lst_match_line(){
+ fst_match_after ${1} ${2} ${3}
+ LAST_LINE_NUM=$((${FIRST_LINE_NUM}+${FIRST_NUM_AFTER}-1))
+}
+
+check_input(){
+ if [ -z "${1}" ]; then
+ help_message
+ exit 1
+ fi
+}
+
+check_www(){
+ CHECK_WWW=$(echo ${1} | cut -c1-4)
+ if [[ ${CHECK_WWW} == www. ]] ; then
+ echo 'www domain shoudnt be passed!'
+ exit 1
+ fi
+}
+
+www_domain(){
+ check_www ${1}
+ WWW_DOMAIN=$(echo www.${1})
+}
+
+add_ls_domain(){
+ fst_match_line 'docker.xml' ${LS_HTTPD_CONF}
+ NEWNUM=$((FIRST_LINE_NUM+2))
+ sed -i "${NEWNUM}i \ \ \ \ \ \ \n \ \ \ \ \ \ \ ${DOMAIN}\n \ \ \ \ \ \ \ ${DOMAIN},${WWW_DOMAIN}\n \ \ \ \ \ \ " ${LS_HTTPD_CONF}
+}
+
+add_ols_domain(){
+ perl -0777 -p -i -e 's/(vhTemplate docker \{[^}]+)\}*(^.*listeners.*$)/\1$2
+ member '${DOMAIN}' {
+ vhDomain '${DOMAIN},${WWW_DOMAIN}'
+ }/gmi' ${OLS_HTTPD_CONF}
+}
+
+add_domain(){
+ check_lsv
+ dot_escape ${1}
+ DOMAIN=${ESCAPE}
+ www_domain ${1}
+ if [ "${LSV}" = 'lsws' ]; then
+ check_duplicate "vhDomain.*${DOMAIN}" ${LS_HTTPD_CONF}
+ if [ "${CK_RESULT}" != '' ]; then
+ echo "# It appears the domain already exist! Check the ${LS_HTTPD_CONF} if you believe this is a mistake!"
+ exit 1
+ fi
+ elif [ "${LSV}" = 'openlitespeed' ]; then
+ check_duplicate "member.*${DOMAIN}" ${OLS_HTTPD_CONF}
+ if [ "${CK_RESULT}" != '' ]; then
+ echo "# It appears the domain already exist! Check the ${OLS_HTTPD_CONF} if you believe this is a mistake!"
+ exit 1
+ fi
+ fi
+ add_ls_domain
+ add_ols_domain
+}
+
+del_ls_domain(){
+ fst_match_line "*${1}" ${LS_HTTPD_CONF}
+ FIRST_LINE_NUM=$((FIRST_LINE_NUM-1))
+ lst_match_line ${FIRST_LINE_NUM} ${LS_HTTPD_CONF} ''
+ sed -i "${FIRST_LINE_NUM},${LAST_LINE_NUM}d" ${LS_HTTPD_CONF}
+}
+
+del_ols_domain(){
+ fst_match_line ${1} ${OLS_HTTPD_CONF}
+ lst_match_line ${FIRST_LINE_NUM} ${OLS_HTTPD_CONF} '}'
+ sed -i "${FIRST_LINE_NUM},${LAST_LINE_NUM}d" ${OLS_HTTPD_CONF}
+}
+
+del_domain(){
+ check_lsv
+ dot_escape ${1}
+ DOMAIN=${ESCAPE}
+ if [ "${LSV}" = 'lsws' ]; then
+ check_duplicate "vhDomain.*${DOMAIN}" ${LS_HTTPD_CONF}
+ if [ "${CK_RESULT}" = '' ]; then
+ echo "# Domain non-exist! Check the ${LS_HTTPD_CONF} if you believe this is a mistake!"
+ exit 1
+ fi
+ elif [ "${LSV}" = 'openlitespeed' ]; then
+ check_duplicate "member.*${DOMAIN}" ${OLS_HTTPD_CONF}
+ if [ "${CK_RESULT}" = '' ]; then
+ echo "# Domain non-exist! Check the ${OLS_HTTPD_CONF} if you believe this is a mistake!"
+ exit 1
+ fi
+ fi
+ del_ls_domain ${1}
+ del_ols_domain ${1}
+}
+
+check_input ${1}
+while [ ! -z "${1}" ]; do
+ case ${1} in
+ -[hH] | -help | --help)
+ help_message
+ ;;
+ -[aA] | -add | --add) shift
+ add_domain ${1}
+ ;;
+ -[dD] | -del | --del | --delete) shift
+ del_domain ${1}
+ ;;
+ *)
+ help_message
+ ;;
+ esac
+ shift
+done
\ No newline at end of file
diff --git a/apps/openlitespeed/1.7.17-lsphp74/data/bin/container/owaspctl.sh b/apps/openlitespeed/1.7.17-lsphp74/data/bin/container/owaspctl.sh
new file mode 100644
index 00000000..72fd8e59
--- /dev/null
+++ b/apps/openlitespeed/1.7.17-lsphp74/data/bin/container/owaspctl.sh
@@ -0,0 +1,236 @@
+#!/bin/bash
+LSDIR='/usr/local/lsws'
+OWASP_DIR="${LSDIR}/conf/owasp"
+RULE_FILE='modsec_includes.conf'
+LS_HTTPD_CONF="${LSDIR}/conf/httpd_config.xml"
+OLS_HTTPD_CONF="${LSDIR}/conf/httpd_config.conf"
+EPACE=' '
+OWASP_V='3.3.4'
+
+echow(){
+ FLAG=${1}
+ shift
+ echo -e "\033[1m${EPACE}${FLAG}\033[0m${@}"
+}
+
+help_message(){
+ echo -e "\033[1mOPTIONS\033[0m"
+ echow '-E, --enable'
+ echo "${EPACE}${EPACE}Will Enable mod_secure module with latest OWASP version of rules"
+ echow '-D, --disable'
+ echo "${EPACE}${EPACE}Will Disable mod_secure module with latest OWASP version of rules"
+ echow '-H, --help'
+ echo "${EPACE}${EPACE}Display help and exit."
+ exit 0
+}
+
+check_lsv(){
+ if [ -f ${LSDIR}/bin/openlitespeed ]; then
+ LSV='openlitespeed'
+ elif [ -f ${LSDIR}/bin/litespeed ]; then
+ LSV='lsws'
+ else
+ echo 'Version not exist, abort!'
+ exit 1
+ fi
+}
+
+check_input(){
+ if [ -z "${1}" ]; then
+ help_message
+ exit 1
+ fi
+}
+
+mk_owasp_dir(){
+ if [ -d ${OWASP_DIR} ] ; then
+ rm -rf ${OWASP_DIR}
+ fi
+ mkdir -p ${OWASP_DIR}
+ if [ ${?} -ne 0 ] ; then
+ echo "Unable to create directory: ${OWASP_DIR}, exit!"
+ exit 1
+ fi
+}
+
+fst_match_line(){
+ FIRST_LINE_NUM=$(grep -n -m 1 "${1}" ${2} | awk -F ':' '{print $1}')
+}
+fst_match_after(){
+ FIRST_NUM_AFTER=$(tail -n +${1} ${2} | grep -n -m 1 ${3} | awk -F ':' '{print $1}')
+}
+lst_match_line(){
+ fst_match_after ${1} ${2} ${3}
+ LAST_LINE_NUM=$((${FIRST_LINE_NUM}+${FIRST_NUM_AFTER}-1))
+}
+
+enable_ols_modsec(){
+ grep 'module mod_security {' ${OLS_HTTPD_CONF} >/dev/null 2>&1
+ if [ ${?} -eq 0 ] ; then
+ echo "Already configured for modsecurity."
+ else
+ echo 'Enable modsecurity'
+ sed -i "s=module cache=module mod_security {\nmodsecurity on\
+ \nmodsecurity_rules \`\nSecRuleEngine On\n\`\nmodsecurity_rules_file \
+ ${OWASP_DIR}/${RULE_FILE}\n ls_enabled 1\n}\
+ \n\nmodule cache=" ${OLS_HTTPD_CONF}
+ fi
+}
+
+enable_ls_modsec(){
+ grep '1' ${LS_HTTPD_CONF} >/dev/null 2>&1
+ if [ ${?} -eq 0 ] ; then
+ echo "LSWS already configured for modsecurity"
+ else
+ echo 'Enable modsecurity'
+ sed -i \
+ "s=0=1=" ${LS_HTTPD_CONF}
+ sed -i \
+ "s==\n\
+ \n\
+ ModSec\n\
+ 1\n\
+ include ${OWASP_DIR}/modsec_includes.conf\n\
+ =" ${LS_HTTPD_CONF}
+ fi
+}
+
+enable_modsec(){
+ if [ "${LSV}" = 'lsws' ]; then
+ enable_ls_modsec
+ elif [ "${LSV}" = 'openlitespeed' ]; then
+ enable_ols_modsec
+ fi
+}
+
+disable_ols_modesec(){
+ grep 'module mod_security {' ${OLS_HTTPD_CONF} >/dev/null 2>&1
+ if [ ${?} -eq 0 ] ; then
+ echo 'Disable modsecurity'
+ fst_match_line 'module mod_security' ${OLS_HTTPD_CONF}
+ lst_match_line ${FIRST_LINE_NUM} ${OLS_HTTPD_CONF} '}'
+ sed -i "${FIRST_LINE_NUM},${LAST_LINE_NUM}d" ${OLS_HTTPD_CONF}
+ else
+ echo 'Already disabled for modsecurity'
+ fi
+}
+
+disable_ls_modesec(){
+ grep '0' ${LS_HTTPD_CONF}
+ if [ ${?} -eq 0 ] ; then
+ echo 'Already disabled for modsecurity'
+ else
+ echo 'Disable modsecurity'
+ sed -i \
+ "s=1=0=" ${LS_HTTPD_CONF}
+ fst_match_line 'censorshipRuleSet' ${LS_HTTPD_CONF}
+ lst_match_line ${FIRST_LINE_NUM} ${LS_HTTPD_CONF} '/censorshipRuleSet'
+ sed -i "${FIRST_LINE_NUM},${LAST_LINE_NUM}d" ${LS_HTTPD_CONF}
+ fi
+}
+
+disable_modsec(){
+ check_lsv
+ if [ "${LSV}" = 'lsws' ]; then
+ disable_ls_modesec
+ elif [ "${LSV}" = 'openlitespeed' ]; then
+ disable_ols_modesec
+ fi
+}
+
+install_unzip(){
+ if [ ! -f /usr/bin/unzip ]; then
+ echo 'Install Unzip'
+ apt update >/dev/null 2>&1
+ apt-get install unzip -y >/dev/null 2>&1
+ fi
+}
+
+install_owasp(){
+ cd ${OWASP_DIR}
+ echo 'Download OWASP rules'
+ wget -q https://github.com/coreruleset/coreruleset/archive/refs/tags/v${OWASP_V}.zip
+ unzip -qq v${OWASP_V}.zip
+ rm -f v${OWASP_V}.zip
+ mv coreruleset-* owasp-modsecurity-crs
+}
+
+configure_owasp(){
+ echo 'Config OWASP rules.'
+ cd ${OWASP_DIR}
+ echo "include modsecurity.conf
+include owasp-modsecurity-crs/crs-setup.conf
+include owasp-modsecurity-crs/rules/REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf
+include owasp-modsecurity-crs/rules/REQUEST-901-INITIALIZATION.conf
+include owasp-modsecurity-crs/rules/REQUEST-903.9001-DRUPAL-EXCLUSION-RULES.conf
+include owasp-modsecurity-crs/rules/REQUEST-903.9002-WORDPRESS-EXCLUSION-RULES.conf
+include owasp-modsecurity-crs/rules/REQUEST-903.9003-NEXTCLOUD-EXCLUSION-RULES.conf
+include owasp-modsecurity-crs/rules/REQUEST-903.9004-DOKUWIKI-EXCLUSION-RULES.conf
+include owasp-modsecurity-crs/rules/REQUEST-903.9005-CPANEL-EXCLUSION-RULES.conf
+include owasp-modsecurity-crs/rules/REQUEST-903.9006-XENFORO-EXCLUSION-RULES.conf
+include owasp-modsecurity-crs/rules/REQUEST-905-COMMON-EXCEPTIONS.conf
+include owasp-modsecurity-crs/rules/REQUEST-910-IP-REPUTATION.conf
+include owasp-modsecurity-crs/rules/REQUEST-911-METHOD-ENFORCEMENT.conf
+include owasp-modsecurity-crs/rules/REQUEST-912-DOS-PROTECTION.conf
+include owasp-modsecurity-crs/rules/REQUEST-913-SCANNER-DETECTION.conf
+include owasp-modsecurity-crs/rules/REQUEST-920-PROTOCOL-ENFORCEMENT.conf
+include owasp-modsecurity-crs/rules/REQUEST-921-PROTOCOL-ATTACK.conf
+include owasp-modsecurity-crs/rules/REQUEST-930-APPLICATION-ATTACK-LFI.conf
+include owasp-modsecurity-crs/rules/REQUEST-931-APPLICATION-ATTACK-RFI.conf
+include owasp-modsecurity-crs/rules/REQUEST-932-APPLICATION-ATTACK-RCE.conf
+include owasp-modsecurity-crs/rules/REQUEST-933-APPLICATION-ATTACK-PHP.conf
+include owasp-modsecurity-crs/rules/REQUEST-934-APPLICATION-ATTACK-NODEJS.conf
+include owasp-modsecurity-crs/rules/REQUEST-941-APPLICATION-ATTACK-XSS.conf
+include owasp-modsecurity-crs/rules/REQUEST-942-APPLICATION-ATTACK-SQLI.conf
+include owasp-modsecurity-crs/rules/REQUEST-943-APPLICATION-ATTACK-SESSION-FIXATION.conf
+include owasp-modsecurity-crs/rules/REQUEST-944-APPLICATION-ATTACK-JAVA.conf
+include owasp-modsecurity-crs/rules/REQUEST-949-BLOCKING-EVALUATION.conf
+include owasp-modsecurity-crs/rules/RESPONSE-950-DATA-LEAKAGES.conf
+include owasp-modsecurity-crs/rules/RESPONSE-951-DATA-LEAKAGES-SQL.conf
+include owasp-modsecurity-crs/rules/RESPONSE-952-DATA-LEAKAGES-JAVA.conf
+include owasp-modsecurity-crs/rules/RESPONSE-953-DATA-LEAKAGES-PHP.conf
+include owasp-modsecurity-crs/rules/RESPONSE-954-DATA-LEAKAGES-IIS.conf
+include owasp-modsecurity-crs/rules/RESPONSE-959-BLOCKING-EVALUATION.conf
+include owasp-modsecurity-crs/rules/RESPONSE-980-CORRELATION.conf
+include owasp-modsecurity-crs/rules/RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf">modsec_includes.conf
+ echo "SecRuleEngine On">modsecurity.conf
+ cd ${OWASP_DIR}/owasp-modsecurity-crs
+ if [ -f crs-setup.conf.example ]; then
+ mv crs-setup.conf.example crs-setup.conf
+ fi
+ cd ${OWASP_DIR}/owasp-modsecurity-crs/rules
+ if [ -f REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf.example ]; then
+ mv REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf.example REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf
+ fi
+ if [ -f RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf.example ]; then
+ mv RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf.example RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf
+ fi
+}
+
+main_owasp(){
+ mk_owasp_dir
+ install_unzip
+ install_owasp
+ configure_owasp
+ check_lsv
+ enable_modsec
+}
+
+check_input ${1}
+while [ ! -z "${1}" ]; do
+ case ${1} in
+ -[hH] | -help | --help)
+ help_message
+ ;;
+ -[eE] | -enable | --enable)
+ main_owasp
+ ;;
+ -[dD] | -disable | --disable)
+ disable_modsec
+ ;;
+ *)
+ help_message
+ ;;
+ esac
+ shift
+done
\ No newline at end of file
diff --git a/apps/openlitespeed/1.7.17-lsphp74/data/bin/container/serialctl.sh b/apps/openlitespeed/1.7.17-lsphp74/data/bin/container/serialctl.sh
new file mode 100644
index 00000000..42e312dc
--- /dev/null
+++ b/apps/openlitespeed/1.7.17-lsphp74/data/bin/container/serialctl.sh
@@ -0,0 +1,84 @@
+#!/bin/bash
+LSDIR='/usr/local/lsws'
+EPACE=' '
+
+echow(){
+ FLAG=${1}
+ shift
+ echo -e "\033[1m${EPACE}${FLAG}\033[0m${@}"
+}
+
+help_message(){
+ echo -e "\033[1mOPTIONS\033[0m"
+ echow '-S, --serial [YOUR_SERIAL|TRIAL]'
+ echo "${EPACE}${EPACE}Will apply and register the serial to LSWS."
+ echow '-H, --help'
+ echo "${EPACE}${EPACE}Display help and exit."
+ exit 0
+}
+
+check_input(){
+ if [ -z "${1}" ]; then
+ help_message
+ exit 1
+ fi
+}
+
+backup_old(){
+ if [ -f ${1} ] && [ ! -f ${1}_old ]; then
+ mv ${1} ${1}_old
+ fi
+}
+
+detect_ols(){
+ if [ -e ${LSDIR}/bin/openlitespeed ]; then
+ echo '[X] Detect OpenLiteSpeed, abort!'
+ exit 1
+ fi
+}
+
+apply_serial(){
+ detect_ols
+ check_input ${1}
+ echo ${1} | grep -i 'trial' >/dev/null
+ if [ ${?} = 0 ]; then
+ echo 'Apply Trial License'
+ if [ ! -e ${LSDIR}/conf/serial.no ] && [ ! -e ${LSDIR}/conf/license.key ]; then
+ rm -f ${LSDIR}/conf/trial.key*
+ wget -P ${LSDIR}/conf -q http://license.litespeedtech.com/reseller/trial.key
+ echo 'Apply trial finished'
+ else
+ echo "Please backup and remove your existing license, apply abort!"
+ exit 1
+ fi
+ else
+ echo "Apply Serial number: ${1}"
+ backup_old ${LSDIR}/conf/serial.no
+ backup_old ${LSDIR}/conf/license.key
+ backup_old ${LSDIR}/conf/trial.key
+ echo "${1}" > ${LSDIR}/conf/serial.no
+ ${LSDIR}/bin/lshttpd -r
+ if [ -f ${LSDIR}/conf/license.key ]; then
+ echo '[O] Apply success'
+ else
+ echo '[X] Apply failed, please check!'
+ exit 1
+ fi
+ fi
+}
+
+check_input ${1}
+while [ ! -z "${1}" ]; do
+ case ${1} in
+ -[hH] | -help | --help)
+ help_message
+ ;;
+ -[sS] | -serial | --serial) shift
+ apply_serial "${1}"
+ ;;
+ *)
+ help_message
+ ;;
+ esac
+ shift
+done
\ No newline at end of file
diff --git a/apps/openlitespeed/1.7.17-lsphp74/data/bin/database.sh b/apps/openlitespeed/1.7.17-lsphp74/data/bin/database.sh
new file mode 100644
index 00000000..ec1462d4
--- /dev/null
+++ b/apps/openlitespeed/1.7.17-lsphp74/data/bin/database.sh
@@ -0,0 +1,158 @@
+#!/usr/bin/env bash
+source .env
+
+DOMAIN=''
+SQL_DB=''
+SQL_USER=''
+SQL_PASS=''
+ANY="'%'"
+SET_OK=0
+EPACE=' '
+
+echow(){
+ FLAG=${1}
+ shift
+ echo -e "\033[1m${EPACE}${FLAG}\033[0m${@}"
+}
+
+help_message(){
+ echo -e "\033[1mOPTIONS\033[0m"
+ echow '-D, --domain [DOMAIN_NAME]'
+ echo "${EPACE}${EPACE}Example: database.sh -D example.com"
+ echo "${EPACE}${EPACE}Will auto generate Database/username/password for the domain"
+ echow '-D, --domain [DOMAIN_NAME] -U, --user [xxx] -P, --password [xxx] -DB, --database [xxx]'
+ echo "${EPACE}${EPACE}Example: database.sh -D example.com -U USERNAME -P PASSWORD -DB DATABASENAME"
+ echo "${EPACE}${EPACE}Will create Database/username/password by given"
+ echow '-H, --help'
+ echo "${EPACE}${EPACE}Display help and exit."
+ exit 0
+}
+
+check_input(){
+ if [ -z "${1}" ]; then
+ help_message
+ exit 1
+ fi
+}
+
+specify_name(){
+ check_input ${SQL_USER}
+ check_input ${SQL_PASS}
+ check_input ${SQL_DB}
+}
+
+auto_name(){
+ SQL_DB="${TRANSNAME}"
+ SQL_USER="${TRANSNAME}"
+ SQL_PASS="'${RANDOM_PASS}'"
+}
+
+gen_pass(){
+ RANDOM_PASS="$(openssl rand -base64 12)"
+}
+
+trans_name(){
+ TRANSNAME=$(echo ${1} | tr -d '.&&-')
+}
+
+display_credential(){
+ if [ ${SET_OK} = 0 ]; then
+ echo "Database: ${SQL_DB}"
+ echo "Username: ${SQL_USER}"
+ echo "Password: $(echo ${SQL_PASS} | tr -d "'")"
+ fi
+}
+
+store_credential(){
+ if [ -d "./sites/${1}" ]; then
+ if [ -f ./sites/${1}/.db_pass ]; then
+ mv ./sites/${1}/.db_pass ./sites/${1}/.db_pass.bk
+ fi
+ cat > "./sites/${1}/.db_pass" << EOT
+"Database":"${SQL_DB}"
+"Username":"${SQL_USER}"
+"Password":"$(echo ${SQL_PASS} | tr -d "'")"
+EOT
+ else
+ echo "./sites/${1} not found, abort credential store!"
+ fi
+}
+
+check_db_access(){
+ docker compose exec -T mysql su -c "mysql -uroot -p${MYSQL_ROOT_PASSWORD} -e 'status'" >/dev/null 2>&1
+ if [ ${?} != 0 ]; then
+ echo '[X] DB access failed, please check!'
+ exit 1
+ fi
+}
+
+check_db_exist(){
+ docker compose exec -T mysql su -c "test -e /var/lib/mysql/${1}"
+ if [ ${?} = 0 ]; then
+ echo "Database ${1} already exist, skip DB creation!"
+ exit 0
+ fi
+}
+
+db_setup(){
+ docker compose exec -T mysql su -c 'mysql -uroot -p${MYSQL_ROOT_PASSWORD} \
+ -e "CREATE DATABASE '${SQL_DB}';" \
+ -e "GRANT ALL PRIVILEGES ON '${SQL_DB}'.* TO '${SQL_USER}'@'${ANY}' IDENTIFIED BY '${SQL_PASS}';" \
+ -e "FLUSH PRIVILEGES;"'
+ SET_OK=${?}
+}
+
+auto_setup_main(){
+ check_input ${DOMAIN}
+ gen_pass
+ trans_name ${DOMAIN}
+ auto_name
+ check_db_exist ${SQL_DB}
+ check_db_access
+ db_setup
+ display_credential
+ store_credential ${DOMAIN}
+}
+
+specify_setup_main(){
+ specify_name
+ check_db_exist ${SQL_DB}
+ check_db_access
+ db_setup
+ display_credential
+ store_credential ${DOMAIN}
+}
+
+main(){
+ if [ "${SQL_USER}" != '' ] && [ "${SQL_PASS}" != '' ] && [ "${SQL_DB}" != '' ]; then
+ specify_setup_main
+ else
+ auto_setup_main
+ fi
+}
+
+check_input ${1}
+while [ ! -z "${1}" ]; do
+ case ${1} in
+ -[hH] | -help | --help)
+ help_message
+ ;;
+ -[dD] | -domain| --domain) shift
+ DOMAIN="${1}"
+ ;;
+ -[uU] | -user | --user) shift
+ SQL_USER="${1}"
+ ;;
+ -[pP] | -password| --password) shift
+ SQL_PASS="'${1}'"
+ ;;
+ -db | -DB | -database| --database) shift
+ SQL_DB="${1}"
+ ;;
+ *)
+ help_message
+ ;;
+ esac
+ shift
+done
+main
diff --git a/apps/openlitespeed/1.7.17-lsphp74/data/bin/demosite.sh b/apps/openlitespeed/1.7.17-lsphp74/data/bin/demosite.sh
new file mode 100644
index 00000000..91474647
--- /dev/null
+++ b/apps/openlitespeed/1.7.17-lsphp74/data/bin/demosite.sh
@@ -0,0 +1,102 @@
+#!/usr/bin/env bash
+source .env
+APP_NAME='wordpress'
+CONT_NAME='litespeed'
+DOC_FD=''
+
+echow(){
+ FLAG=${1}
+ shift
+ echo -e "\033[1m${EPACE}${FLAG}\033[0m${@}"
+}
+
+help_message(){
+ case ${1} in
+ "1")
+ echow "Script will get 'DOMAIN' and 'database' info from .env file, then auto setup virtual host and the wordpress site for you."
+ exit 0
+ ;;
+ "2")
+ echow 'Service finished, enjoy your accelarated LiteSpeed server!'
+ ;;
+ esac
+}
+
+domain_filter(){
+ if [ ! -n "${DOMAIN}" ]; then
+ echo "Parameters not supplied, please check!"
+ exit 1
+ fi
+ DOMAIN="${1}"
+ DOMAIN="${DOMAIN#http://}"
+ DOMAIN="${DOMAIN#https://}"
+ DOMAIN="${DOMAIN#ftp://}"
+ DOMAIN="${DOMAIN#scp://}"
+ DOMAIN="${DOMAIN#scp://}"
+ DOMAIN="${DOMAIN#sftp://}"
+ DOMAIN=${DOMAIN%%/*}
+}
+
+gen_root_fd(){
+ DOC_FD="./sites/${1}/"
+ if [ -d "./sites/${1}" ]; then
+ echo -e "[O] The root folder \033[32m${DOC_FD}\033[0m exist."
+ else
+ echo "Creating - document root."
+ bash bin/domain.sh -add ${1}
+ echo "Finished - document root."
+ fi
+}
+
+create_db(){
+ if [ ! -n "${MYSQL_DATABASE}" ] || [ ! -n "${MYSQL_USER}" ] || [ ! -n "${MYSQL_PASSWORD}" ]; then
+ echo "Parameters not supplied, please check!"
+ exit 1
+ else
+ bash bin/database.sh -D ${1} -U ${MYSQL_USER} -P ${MYSQL_PASSWORD} -DB ${MYSQL_DATABASE}
+ fi
+}
+
+store_credential(){
+ if [ -f ${DOC_FD}/.db_pass ]; then
+ echo '[O] db file exist!'
+ else
+ echo 'Storing database parameter'
+ cat > "${DOC_FD}/.db_pass" << EOT
+"Database":"${MYSQL_DATABASE}"
+"Username":"${MYSQL_USER}"
+"Password":"$(echo ${MYSQL_PASSWORD} | tr -d "'")"
+EOT
+ fi
+}
+
+app_download(){
+ docker compose exec -T ${CONT_NAME} su -c "appinstallctl.sh --app ${1} --domain ${2}"
+}
+
+lsws_restart(){
+ bash bin/webadmin.sh -r
+}
+
+main(){
+ domain_filter ${DOMAIN}
+ gen_root_fd ${DOMAIN}
+ create_db ${DOMAIN}
+ store_credential
+ app_download ${APP_NAME} ${DOMAIN}
+ lsws_restart
+ help_message 2
+}
+
+while [ ! -z "${1}" ]; do
+ case ${1} in
+ -[hH] | -help | --help)
+ help_message 1
+ ;;
+ *)
+ help_message 1
+ ;;
+ esac
+ shift
+done
+main
\ No newline at end of file
diff --git a/apps/openlitespeed/1.7.17-lsphp74/data/bin/dev/list-flagged-files.sh b/apps/openlitespeed/1.7.17-lsphp74/data/bin/dev/list-flagged-files.sh
new file mode 100644
index 00000000..bde4f25f
--- /dev/null
+++ b/apps/openlitespeed/1.7.17-lsphp74/data/bin/dev/list-flagged-files.sh
@@ -0,0 +1,2 @@
+#!/usr/bin/env bash
+git ls-files -v|grep '^S'
diff --git a/apps/openlitespeed/1.7.17-lsphp74/data/bin/dev/no-skip-worktree-conf.sh b/apps/openlitespeed/1.7.17-lsphp74/data/bin/dev/no-skip-worktree-conf.sh
new file mode 100644
index 00000000..9bf83e7c
--- /dev/null
+++ b/apps/openlitespeed/1.7.17-lsphp74/data/bin/dev/no-skip-worktree-conf.sh
@@ -0,0 +1,2 @@
+#!/usr/bin/env bash
+find conf -maxdepth 1 -type d \( ! -name . \) -exec bash -c "cd '{}' && pwd && git ls-files -z ${pwd} | xargs -0 git update-index --no-skip-worktree" \;
diff --git a/apps/openlitespeed/1.7.17-lsphp74/data/bin/dev/skip-worktree-conf.sh b/apps/openlitespeed/1.7.17-lsphp74/data/bin/dev/skip-worktree-conf.sh
new file mode 100644
index 00000000..18b16184
--- /dev/null
+++ b/apps/openlitespeed/1.7.17-lsphp74/data/bin/dev/skip-worktree-conf.sh
@@ -0,0 +1,3 @@
+#!/usr/bin/env bash
+find conf -maxdepth 1 -type d \( ! -name . \) -exec bash -c "cd '{}' && pwd && git ls-files -z ${pwd} | xargs -0 git update-index --skip-worktree" \;
+
diff --git a/apps/openlitespeed/1.7.17-lsphp74/data/bin/domain.sh b/apps/openlitespeed/1.7.17-lsphp74/data/bin/domain.sh
new file mode 100644
index 00000000..d99dca40
--- /dev/null
+++ b/apps/openlitespeed/1.7.17-lsphp74/data/bin/domain.sh
@@ -0,0 +1,61 @@
+#!/usr/bin/env bash
+CONT_NAME='litespeed'
+EPACE=' '
+
+echow(){
+ FLAG=${1}
+ shift
+ echo -e "\033[1m${EPACE}${FLAG}\033[0m${@}"
+}
+
+help_message(){
+ echo -e "\033[1mOPTIONS\033[0m"
+ echow "-A, --add [domain_name]"
+ echo "${EPACE}${EPACE}Example: domain.sh -A example.com, will add the domain to Listener and auto create a new virtual host."
+ echow "-D, --del [domain_name]"
+ echo "${EPACE}${EPACE}Example: domain.sh -D example.com, will delete the domain from Listener."
+ echow '-H, --help'
+ echo "${EPACE}${EPACE}Display help and exit."
+}
+
+check_input(){
+ if [ -z "${1}" ]; then
+ help_message
+ exit 1
+ fi
+}
+
+add_domain(){
+ check_input ${1}
+ docker compose exec ${CONT_NAME} su -s /bin/bash lsadm -c "cd /usr/local/lsws/conf && domainctl.sh --add ${1}"
+ if [ ! -d "./sites/${1}" ]; then
+ mkdir -p ./sites/${1}/{html,logs,certs}
+ fi
+ bash bin/webadmin.sh -r
+}
+
+del_domain(){
+ check_input ${1}
+ docker compose exec ${CONT_NAME} su -s /bin/bash lsadm -c "cd /usr/local/lsws/conf && domainctl.sh --del ${1}"
+ bash bin/webadmin.sh -r
+}
+
+check_input ${1}
+while [ ! -z "${1}" ]; do
+ case ${1} in
+ -[hH] | -help | --help)
+ help_message
+ ;;
+ -[aA] | -add | --add) shift
+ add_domain ${1}
+ ;;
+ -[dD] | -del | --del | --delete) shift
+ del_domain ${1}
+ ;;
+ *)
+ help_message
+ ;;
+ esac
+ shift
+done
+
\ No newline at end of file
diff --git a/apps/openlitespeed/1.7.17-lsphp74/data/bin/webadmin.sh b/apps/openlitespeed/1.7.17-lsphp74/data/bin/webadmin.sh
new file mode 100644
index 00000000..18e108ec
--- /dev/null
+++ b/apps/openlitespeed/1.7.17-lsphp74/data/bin/webadmin.sh
@@ -0,0 +1,98 @@
+#!/usr/bin/env bash
+CONT_NAME='litespeed'
+EPACE=' '
+
+echow(){
+ FLAG=${1}
+ shift
+ echo -e "\033[1m${EPACE}${FLAG}\033[0m${@}"
+}
+
+help_message(){
+ echo -e "\033[1mOPTIONS\033[0m"
+ echow '[Enter Your PASSWORD]'
+ echo "${EPACE}${EPACE}Example: webadmin.sh MY_SECURE_PASS, to update web admin password immediatly."
+ echow '-R, --restart'
+ echo "${EPACE}${EPACE}Will gracefully restart LiteSpeed Web Server."
+ echow '-M, --mod-secure [enable|disable]'
+ echo "${EPACE}${EPACE}Example: webadmin.sh -M enable, will enable and apply Mod_Secure OWASP rules on server"
+ echow '-U, --upgrade'
+ echo "${EPACE}${EPACE}Will upgrade web server to latest stable version"
+ echow '-S, --serial [YOUR_SERIAL|TRIAL]'
+ echo "${EPACE}${EPACE}Will apply your serial number to LiteSpeed Web Server."
+ echow '-H, --help'
+ echo "${EPACE}${EPACE}Display help and exit."
+ exit 0
+}
+
+check_input(){
+ if [ -z "${1}" ]; then
+ help_message
+ exit 1
+ fi
+}
+
+lsws_restart(){
+ docker compose exec -T ${CONT_NAME} su -c '/usr/local/lsws/bin/lswsctrl restart >/dev/null'
+}
+
+apply_serial(){
+ docker compose exec ${CONT_NAME} su -c "serialctl.sh --serial ${1}"
+ lsws_restart
+}
+
+mod_secure(){
+ if [ "${1}" = 'enable' ] || [ "${1}" = 'Enable' ]; then
+ docker compose exec ${CONT_NAME} su -s /bin/bash root -c "owaspctl.sh --enable"
+ lsws_restart
+ elif [ "${1}" = 'disable' ] || [ "${1}" = 'Disable' ]; then
+ docker compose exec ${CONT_NAME} su -s /bin/bash root -c "owaspctl.sh --disable"
+ lsws_restart
+ else
+ help_message
+ fi
+}
+
+ls_upgrade(){
+ echo 'Upgrade web server to latest stable version.'
+ docker compose exec ${CONT_NAME} su -c '/usr/local/lsws/admin/misc/lsup.sh 2>/dev/null'
+}
+
+set_web_admin(){
+ echo 'Update web admin password.'
+ local LSADPATH='/usr/local/lsws/admin'
+ docker compose exec ${CONT_NAME} su -s /bin/bash lsadm -c \
+ 'if [ -e /usr/local/lsws/admin/fcgi-bin/admin_php ]; then \
+ echo "admin:$('${LSADPATH}'/fcgi-bin/admin_php -q '${LSADPATH}'/misc/htpasswd.php '${1}')" > '${LSADPATH}'/conf/htpasswd; \
+ else echo "admin:$('${LSADPATH}'/fcgi-bin/admin_php5 -q '${LSADPATH}'/misc/htpasswd.php '${1}')" > '${LSADPATH}'/conf/htpasswd; \
+ fi';
+}
+
+main(){
+ set_web_admin ${1}
+}
+
+check_input ${1}
+while [ ! -z "${1}" ]; do
+ case ${1} in
+ -[hH] | -help | --help)
+ help_message
+ ;;
+ -[rR] | -restart | --restart)
+ lsws_restart
+ ;;
+ -M | -mode-secure | --mod-secure) shift
+ mod_secure ${1}
+ ;;
+ -lsup | --lsup | --upgrade | -U) shift
+ ls_upgrade
+ ;;
+ -[sS] | -serial | --serial) shift
+ apply_serial ${1}
+ ;;
+ *)
+ main ${1}
+ ;;
+ esac
+ shift
+done
\ No newline at end of file
diff --git a/apps/openlitespeed/1.7.17-lsphp74/data/sites/.gitignore b/apps/openlitespeed/1.7.17-lsphp74/data/sites/.gitignore
new file mode 100644
index 00000000..d6b7ef32
--- /dev/null
+++ b/apps/openlitespeed/1.7.17-lsphp74/data/sites/.gitignore
@@ -0,0 +1,2 @@
+*
+!.gitignore
diff --git a/apps/openlitespeed/1.7.17-lsphp74/docker-compose.yml b/apps/openlitespeed/1.7.17-lsphp74/docker-compose.yml
new file mode 100644
index 00000000..0a5bef3c
--- /dev/null
+++ b/apps/openlitespeed/1.7.17-lsphp74/docker-compose.yml
@@ -0,0 +1,30 @@
+version: '3'
+services:
+ litespeed:
+ container_name: ${CONTAINER_NAME}
+ restart: always
+ networks:
+ - 1panel-network
+ logging:
+ driver: none
+ volumes:
+ - ${DATA_PATH}/lsws/conf:/usr/local/lsws/conf
+ - ${DATA_PATH}/lsws/admin-conf:/usr/local/lsws/admin/conf
+ - ${DATA_PATH}/bin/container:/usr/local/bin
+ - ${DATA_PATH}/sites:/var/www/vhosts/
+ - ${DATA_PATH}/acme:/root/.acme.sh/
+ - ${DATA_PATH}/logs:/usr/local/lsws/logs/
+ ports:
+ - "${PANEL_APP_PORT_HTTP}:80"
+ - "${PANEL_APP_PORT_HTTPS}:443"
+ - "${PANEL_APP_PORT_HTTPS}:443/udp"
+ - "${PANEL_APP_PORT_CONSOLE}:7080"
+ environment:
+ - TZ=${TIME_ZONE}
+ image: litespeedtech/openlitespeed:1.7.17-lsphp74
+ labels:
+ createdBy: "Apps"
+
+networks:
+ 1panel-network:
+ external: true
diff --git a/apps/openlitespeed/1.7.17-lsphp81/.env.sample b/apps/openlitespeed/1.7.17-lsphp81/.env.sample
new file mode 100644
index 00000000..0902e645
--- /dev/null
+++ b/apps/openlitespeed/1.7.17-lsphp81/.env.sample
@@ -0,0 +1,6 @@
+CONTAINER_NAME="openlitespeed"
+DATA_PATH="./data"
+PANEL_APP_PORT_CONSOLE="40113"
+PANEL_APP_PORT_HTTP="80"
+PANEL_APP_PORT_HTTPS="443"
+TIME_ZONE="Asia/Shanghai"
diff --git a/apps/openlitespeed/1.7.17-lsphp81/data.yml b/apps/openlitespeed/1.7.17-lsphp81/data.yml
new file mode 100644
index 00000000..2c19e306
--- /dev/null
+++ b/apps/openlitespeed/1.7.17-lsphp81/data.yml
@@ -0,0 +1,40 @@
+additionalProperties:
+ formFields:
+ - default: 80
+ edit: true
+ envKey: PANEL_APP_PORT_HTTP
+ labelEn: HTTP Port
+ labelZh: HTTP端口
+ required: true
+ rule: paramPort
+ type: number
+ - default: 443
+ edit: true
+ envKey: PANEL_APP_PORT_HTTPS
+ labelEn: HTTPS Port
+ labelZh: HTTPS端口
+ required: true
+ rule: paramPort
+ type: number
+ - default: 40113
+ edit: true
+ envKey: PANEL_APP_PORT_CONSOLE
+ labelEn: Console Port
+ labelZh: 控制台端口
+ required: true
+ rule: paramPort
+ type: number
+ - default: ./data
+ disabled: true
+ envKey: DATA_PATH
+ labelEn: Data folder path
+ labelZh: 数据文件夹路径
+ required: true
+ type: text
+ - default: Asia/Shanghai
+ edit: true
+ envKey: TIME_ZONE
+ labelEn: Time zone
+ labelZh: 时区
+ required: true
+ type: text
diff --git a/apps/openlitespeed/1.7.17-lsphp81/data/bin/acme.sh b/apps/openlitespeed/1.7.17-lsphp81/data/bin/acme.sh
new file mode 100644
index 00000000..2804e217
--- /dev/null
+++ b/apps/openlitespeed/1.7.17-lsphp81/data/bin/acme.sh
@@ -0,0 +1,293 @@
+#!/usr/bin/env bash
+EMAIL=''
+NO_EMAIL=''
+DOMAIN=''
+INSTALL=''
+UNINSTALL=''
+TYPE=0
+CONT_NAME='litespeed'
+ACME_SRC='https://raw.githubusercontent.com/Neilpang/acme.sh/master/acme.sh'
+EPACE=' '
+RENEW=''
+RENEW_ALL=''
+FORCE=''
+REVOKE=''
+REMOVE=''
+
+echow(){
+ FLAG=${1}
+ shift
+ echo -e "\033[1m${EPACE}${FLAG}\033[0m${@}"
+}
+
+help_message(){
+ case ${1} in
+ "1")
+ echo 'You will need to install acme script at the first time.'
+ echo 'Please run acme.sh --install --email example@example.com'
+ ;;
+ "2")
+ echo -e "\033[1mOPTIONS\033[0m"
+ echow '-D, --domain [DOMAIN_NAME]'
+ echo "${EPACE}${EPACE}Example: acme.sh --domain example.com"
+ echo "${EPACE}${EPACE}will auto detect and apply for both example.com and www.example.com domains."
+ echow '-H, --help'
+ echo "${EPACE}${EPACE}Display help and exit."
+ echo -e "\033[1m Only for the First time\033[0m"
+ echow '--install --email [EMAIL_ADDR]'
+ echo "${EPACE}${EPACE}Will install ACME with the Email provided"
+ echow '-r, --renew'
+ echo "${EPACE}${EPACE}Renew a specific domain with -D or --domain parameter if posibile. To force renew, use -f parameter."
+ echow '-R, --renew-all'
+ echo "${EPACE}${EPACE}Renew all domains if possible. To force renew, use -f parameter."
+ echow '-f, -F, --force'
+ echo "${EPACE}${EPACE}Force renew for a specific domain or all domains."
+ echow '-v, --revoke'
+ echo "${EPACE}${EPACE}Revoke a domain."
+ echow '-V, --remove'
+ echo "${EPACE}${EPACE}Remove a domain."
+ exit 0
+ ;;
+ "3")
+ echo 'Please run acme.sh --domain [DOMAIN_NAME] to apply certificate'
+ exit 0
+ ;;
+ esac
+}
+
+check_input(){
+ if [ -z "${1}" ]; then
+ help_message 2
+ fi
+}
+
+domain_filter(){
+ if [ -z "${1}" ]; then
+ help_message 3
+ fi
+ DOMAIN="${1}"
+ DOMAIN="${DOMAIN#http://}"
+ DOMAIN="${DOMAIN#https://}"
+ DOMAIN="${DOMAIN#ftp://}"
+ DOMAIN="${DOMAIN#scp://}"
+ DOMAIN="${DOMAIN#scp://}"
+ DOMAIN="${DOMAIN#sftp://}"
+ DOMAIN=${DOMAIN%%/*}
+}
+
+email_filter(){
+ CKREG="^[a-z0-9!#\$%&'*+/=?^_\`{|}~-]+(\.[a-z0-9!#$%&'*+/=?^_\`{|}~-]+)*@([a-z0-9]([a-z0-9-]*[a-z0-9])?\.)+[a-z0-9]([a-z0-9-]*[a-z0-9])?\$"
+ if [[ "${1}" =~ ${CKREG} ]] ; then
+ echo -e "[O] The E-mail \033[32m${1}\033[0m is valid."
+ else
+ echo -e "[X] The E-mail \e[31m${1}\e[39m is invalid"
+ exit 1
+ fi
+}
+
+cert_hook(){
+ echo '[Start] Adding ACME hook'
+ docker compose exec ${CONT_NAME} su -s /bin/bash -c "certhookctl.sh"
+ echo '[End] Adding ACME hook'
+}
+
+www_domain(){
+ CHECK_WWW=$(echo ${1} | cut -c1-4)
+ if [[ ${CHECK_WWW} == www. ]] ; then
+ DOMAIN=$(echo ${1} | cut -c 5-)
+ else
+ DOMAIN=${1}
+ fi
+ WWW_DOMAIN="www.${DOMAIN}"
+}
+
+domain_verify(){
+ curl -Is http://${DOMAIN}/ | grep -i LiteSpeed > /dev/null 2>&1
+ if [ ${?} = 0 ]; then
+ echo -e "[O] The domain name \033[32m${DOMAIN}\033[0m is accessible."
+ TYPE=1
+ curl -Is http://${WWW_DOMAIN}/ | grep -i LiteSpeed > /dev/null 2>&1
+ if [ ${?} = 0 ]; then
+ echo -e "[O] The domain name \033[32m${WWW_DOMAIN}\033[0m is accessible."
+ TYPE=2
+ else
+ echo -e "[!] The domain name ${WWW_DOMAIN} is inaccessible."
+ fi
+ else
+ echo -e "[X] The domain name \e[31m${DOMAIN}\e[39m is inaccessible, please verify."
+ exit 1
+ fi
+}
+
+install_acme(){
+ echo '[Start] Install ACME'
+ if [ "${1}" = 'true' ]; then
+ docker compose exec litespeed su -c "cd; wget ${ACME_SRC}; chmod 755 acme.sh; \
+ ./acme.sh --install --cert-home ~/.acme.sh/certs; \
+ rm ~/acme.sh"
+ elif [ "${2}" != '' ]; then
+ email_filter "${2}"
+ docker compose exec litespeed su -c "cd; wget ${ACME_SRC}; chmod 755 acme.sh; \
+ ./acme.sh --install --cert-home ~/.acme.sh/certs --accountemail ${2}; \
+ rm ~/acme.sh"
+ else
+ help_message 1
+ exit 1
+ fi
+ echo '[End] Install ACME'
+}
+
+uninstall_acme(){
+ echo '[Start] Uninstall ACME'
+ docker compose exec ${CONT_NAME} su -c "~/.acme.sh/acme.sh --uninstall"
+ echo '[End] Uninstall ACME'
+ exit 0
+}
+
+check_acme(){
+ echo '[Start] Checking ACME'
+ docker compose exec ${CONT_NAME} su -c "test -f /root/.acme.sh/acme.sh"
+ if [ ${?} != 0 ]; then
+ install_acme "${NO_EMAIL}" "${EMAIL}"
+ cert_hook
+ help_message 3
+ fi
+ echo '[End] Checking ACME'
+}
+
+lsws_restart(){
+ docker compose exec ${CONT_NAME} su -c '/usr/local/lsws/bin/lswsctrl restart >/dev/null'
+}
+
+doc_root_verify(){
+ if [ "${DOC_ROOT}" = '' ]; then
+ DOC_PATH="/var/www/vhosts/${1}/html"
+ else
+ DOC_PATH="${DOC_ROOT}"
+ fi
+ docker compose exec ${CONT_NAME} su -c "[ -e ${DOC_PATH} ]"
+ if [ ${?} -eq 0 ]; then
+ echo -e "[O] The document root folder \033[32m${DOC_PATH}\033[0m does exist."
+ else
+ echo -e "[X] The document root folder \e[31m${DOC_PATH}\e[39m does not exist!"
+ exit 1
+ fi
+}
+
+install_cert(){
+ echo '[Start] Apply Lets Encrypt Certificate'
+ if [ ${TYPE} = 1 ]; then
+ docker compose exec ${CONT_NAME} su -c "/root/.acme.sh/acme.sh --issue -d ${1} -w ${DOC_PATH}"
+ elif [ ${TYPE} = 2 ]; then
+ docker compose exec ${CONT_NAME} su -c "/root/.acme.sh/acme.sh --issue -d ${1} -d www.${1} -w ${DOC_PATH}"
+ else
+ echo 'unknown Type!'
+ exit 2
+ fi
+ echo '[End] Apply Lets Encrypt Certificate'
+}
+
+renew_acme(){
+ echo '[Start] Renew ACME'
+ if [ "${FORCE}" = 'true' ]; then
+ docker compose exec ${CONT_NAME} su -c "~/.acme.sh/acme.sh --renew --domain ${1} --force"
+ else
+ docker compose exec ${CONT_NAME} su -c "~/.acme.sh/acme.sh --renew --domain ${1}"
+ fi
+ echo '[End] Renew ACME'
+ lsws_restart
+}
+
+renew_all_acme(){
+ echo '[Start] Renew all ACME'
+ if [ "${FORCE}" = 'true' ]; then
+ docker compose exec ${CONT_NAME} su -c "~/.acme.sh/acme.sh --renew-all --force"
+ else
+ docker compose exec ${CONT_NAME} su -c "~/.acme.sh/acme.sh --renew-all"
+ fi
+ echo '[End] Renew all ACME'
+ lsws_restart
+}
+
+revoke(){
+ echo '[Start] Revoke a domain'
+ docker compose exec ${CONT_NAME} su -c "~/.acme.sh/acme.sh --revoke --domain ${1}"
+ echo '[End] Revoke a domain'
+ lsws_restart
+}
+
+remove(){
+ echo '[Start] Remove a domain'
+ docker compose exec ${CONT_NAME} su -c "~/.acme.sh/acme.sh --remove --domain ${1}"
+ echo '[End] Remove a domain'
+ lsws_restart
+}
+
+main(){
+ if [ "${RENEW_ALL}" = 'true' ]; then
+ renew_all_acme
+ exit 0
+ elif [ "${RENEW}" = 'true' ]; then
+ renew_acme ${DOMAIN}
+ exit 0
+ elif [ "${REVOKE}" = 'true' ]; then
+ revoke ${DOMAIN}
+ exit 0
+ elif [ "${REMOVE}" = 'true' ]; then
+ remove ${DOMAIN}
+ exit 0
+ fi
+
+ check_acme
+ domain_filter ${DOMAIN}
+ www_domain ${DOMAIN}
+ domain_verify
+ doc_root_verify ${DOMAIN}
+ install_cert ${DOMAIN}
+ lsws_restart
+}
+
+check_input ${1}
+while [ ! -z "${1}" ]; do
+ case ${1} in
+ -[hH] | -help | --help)
+ help_message 2
+ ;;
+ -[dD] | -domain | --domain) shift
+ check_input "${1}"
+ DOMAIN="${1}"
+ ;;
+ -[iI] | --install )
+ INSTALL=true
+ ;;
+ -[uU] | --uninstall )
+ UNINSTALL=true
+ uninstall_acme
+ ;;
+ -[fF] | --force )
+ FORCE=true
+ ;;
+ -[r] | --renew )
+ RENEW=true
+ ;;
+ -[R] | --renew-all )
+ RENEW_ALL=true
+ ;;
+ -[v] | --revoke )
+ REVOKE=true
+ ;;
+ -[V] | --remove )
+ REMOVE=true
+ ;;
+ -[eE] | --email ) shift
+ check_input "${1}"
+ EMAIL="${1}"
+ ;;
+ *)
+ help_message 2
+ ;;
+ esac
+ shift
+done
+
+main
\ No newline at end of file
diff --git a/apps/openlitespeed/1.7.17-lsphp81/data/bin/appinstall.sh b/apps/openlitespeed/1.7.17-lsphp81/data/bin/appinstall.sh
new file mode 100644
index 00000000..ba67d52d
--- /dev/null
+++ b/apps/openlitespeed/1.7.17-lsphp81/data/bin/appinstall.sh
@@ -0,0 +1,60 @@
+#!/usr/bin/env bash
+APP_NAME=''
+DOMAIN=''
+EPACE=' '
+
+echow(){
+ FLAG=${1}
+ shift
+ echo -e "\033[1m${EPACE}${FLAG}\033[0m${@}"
+}
+
+help_message(){
+ echo -e "\033[1mOPTIONS\033[0m"
+ echow '-A, --app [app_name] -D, --domain [DOMAIN_NAME]'
+ echo "${EPACE}${EPACE}Example: appinstall.sh -A wordpress -D example.com"
+ echo "${EPACE}${EPACE}Will install WordPress CMS under the example.com domain"
+ echow '-H, --help'
+ echo "${EPACE}${EPACE}Display help and exit."
+ exit 0
+}
+
+check_input(){
+ if [ -z "${1}" ]; then
+ help_message
+ exit 1
+ fi
+}
+
+app_download(){
+ docker compose exec litespeed su -c "appinstallctl.sh --app ${1} --domain ${2}"
+ bash bin/webadmin.sh -r
+ exit 0
+}
+
+main(){
+ app_download ${APP_NAME} ${DOMAIN}
+}
+
+check_input ${1}
+while [ ! -z "${1}" ]; do
+ case ${1} in
+ -[hH] | -help | --help)
+ help_message
+ ;;
+ -[aA] | -app | --app) shift
+ check_input "${1}"
+ APP_NAME="${1}"
+ ;;
+ -[dD] | -domain | --domain) shift
+ check_input "${1}"
+ DOMAIN="${1}"
+ ;;
+ *)
+ help_message
+ ;;
+ esac
+ shift
+done
+
+main
\ No newline at end of file
diff --git a/apps/openlitespeed/1.7.17-lsphp81/data/bin/container/appinstallctl.sh b/apps/openlitespeed/1.7.17-lsphp81/data/bin/container/appinstallctl.sh
new file mode 100644
index 00000000..d79c63f1
--- /dev/null
+++ b/apps/openlitespeed/1.7.17-lsphp81/data/bin/container/appinstallctl.sh
@@ -0,0 +1,660 @@
+#!/bin/bash
+DEFAULT_VH_ROOT='/var/www/vhosts'
+VH_DOC_ROOT=''
+VHNAME=''
+APP_NAME=''
+DOMAIN=''
+WWW_UID=''
+WWW_GID=''
+WP_CONST_CONF=''
+PUB_IP=$(curl -s http://checkip.amazonaws.com)
+DB_HOST='mysql'
+PLUGINLIST="litespeed-cache.zip"
+THEME='twentytwenty'
+EPACE=' '
+
+echow(){
+ FLAG=${1}
+ shift
+ echo -e "\033[1m${EPACE}${FLAG}\033[0m${@}"
+}
+
+help_message(){
+ echo -e "\033[1mOPTIONS\033[0m"
+ echow '-A, -app [wordpress] -D, --domain [DOMAIN_NAME]'
+ echo "${EPACE}${EPACE}Example: appinstallctl.sh --app wordpress --domain example.com"
+ echow '-H, --help'
+ echo "${EPACE}${EPACE}Display help and exit."
+ exit 0
+}
+
+check_input(){
+ if [ -z "${1}" ]; then
+ help_message
+ exit 1
+ fi
+}
+
+linechange(){
+ LINENUM=$(grep -n "${1}" ${2} | cut -d: -f 1)
+ if [ -n "${LINENUM}" ] && [ "${LINENUM}" -eq "${LINENUM}" ] 2>/dev/null; then
+ sed -i "${LINENUM}d" ${2}
+ sed -i "${LINENUM}i${3}" ${2}
+ fi
+}
+
+ck_ed(){
+ if [ ! -f /bin/ed ]; then
+ echo "Install ed package.."
+ apt-get install ed -y > /dev/null 2>&1
+ fi
+}
+
+ck_unzip(){
+ if [ ! -f /usr/bin/unzip ]; then
+ echo "Install unzip package.."
+ apt-get install unzip -y > /dev/null 2>&1
+ fi
+}
+
+get_owner(){
+ WWW_UID=$(stat -c "%u" ${DEFAULT_VH_ROOT})
+ WWW_GID=$(stat -c "%g" ${DEFAULT_VH_ROOT})
+ if [ ${WWW_UID} -eq 0 ] || [ ${WWW_GID} -eq 0 ]; then
+ WWW_UID=1000
+ WWW_GID=1000
+ echo "Set owner to ${WWW_UID}"
+ fi
+}
+
+get_db_pass(){
+ if [ -f ${DEFAULT_VH_ROOT}/${1}/.db_pass ]; then
+ SQL_DB=$(grep -i Database ${VH_ROOT}/.db_pass | awk -F ':' '{print $2}' | tr -d '"')
+ SQL_USER=$(grep -i Username ${VH_ROOT}/.db_pass | awk -F ':' '{print $2}' | tr -d '"')
+ SQL_PASS=$(grep -i Password ${VH_ROOT}/.db_pass | awk -F ':' '{print $2}' | tr -d '"')
+ else
+ echo 'db pass file can not locate, skip wp-config pre-config.'
+ fi
+}
+
+set_vh_docroot(){
+ if [ "${VHNAME}" != '' ]; then
+ VH_ROOT="${DEFAULT_VH_ROOT}/${VHNAME}"
+ VH_DOC_ROOT="${DEFAULT_VH_ROOT}/${VHNAME}/html"
+ WP_CONST_CONF="${VH_DOC_ROOT}/wp-content/plugins/litespeed-cache/data/const.default.ini"
+ elif [ -d ${DEFAULT_VH_ROOT}/${1}/html ]; then
+ VH_ROOT="${DEFAULT_VH_ROOT}/${1}"
+ VH_DOC_ROOT="${DEFAULT_VH_ROOT}/${1}/html"
+ WP_CONST_CONF="${VH_DOC_ROOT}/wp-content/plugins/litespeed-cache/data/const.default.ini"
+ else
+ echo "${DEFAULT_VH_ROOT}/${1}/html does not exist, please add domain first! Abort!"
+ exit 1
+ fi
+}
+
+check_sql_native(){
+ local COUNTER=0
+ local LIMIT_NUM=100
+ until [ "$(curl -v mysql:3306 2>&1 | grep -i 'native\|Connected')" ]; do
+ echo "Counter: ${COUNTER}/${LIMIT_NUM}"
+ COUNTER=$((COUNTER+1))
+ if [ ${COUNTER} = 10 ]; then
+ echo '--- MySQL is starting, please wait... ---'
+ elif [ ${COUNTER} = ${LIMIT_NUM} ]; then
+ echo '--- MySQL is timeout, exit! ---'
+ exit 1
+ fi
+ sleep 1
+ done
+}
+
+install_wp_plugin(){
+ for PLUGIN in ${PLUGINLIST}; do
+ wget -q -P ${VH_DOC_ROOT}/wp-content/plugins/ https://downloads.wordpress.org/plugin/${PLUGIN}
+ if [ ${?} = 0 ]; then
+ ck_unzip
+ unzip -qq -o ${VH_DOC_ROOT}/wp-content/plugins/${PLUGIN} -d ${VH_DOC_ROOT}/wp-content/plugins/
+ else
+ echo "${PLUGINLIST} FAILED to download"
+ fi
+ done
+ rm -f ${VH_DOC_ROOT}/wp-content/plugins/*.zip
+}
+
+set_htaccess(){
+ if [ ! -f ${VH_DOC_ROOT}/.htaccess ]; then
+ touch ${VH_DOC_ROOT}/.htaccess
+ fi
+ cat << EOM > ${VH_DOC_ROOT}/.htaccess
+# BEGIN WordPress
+
+RewriteEngine On
+RewriteBase /
+RewriteRule ^index\.php$ - [L]
+RewriteCond %{REQUEST_FILENAME} !-f
+RewriteCond %{REQUEST_FILENAME} !-d
+RewriteRule . /index.php [L]
+
+# END WordPress
+EOM
+}
+
+get_theme_name(){
+ THEME_NAME=$(grep WP_DEFAULT_THEME ${VH_DOC_ROOT}/wp-includes/default-constants.php | grep -v '!' | awk -F "'" '{print $4}')
+ echo "${THEME_NAME}" | grep 'twenty' >/dev/null 2>&1
+ if [ ${?} = 0 ]; then
+ THEME="${THEME_NAME}"
+ fi
+}
+
+set_lscache(){
+ cat << EOM > "${WP_CONST_CONF}"
+;
+; This is the predefined default LSCWP configuration file
+;
+; All the keys and values please refer \`src/const.cls.php\`
+;
+; Comments start with \`;\`
+;
+;; -------------------------------------------------- ;;
+;; -------------- General ----------------- ;;
+;; -------------------------------------------------- ;;
+; O_AUTO_UPGRADE
+auto_upgrade = false
+; O_API_KEY
+api_key = ''
+; O_SERVER_IP
+server_ip = ''
+; O_NEWS
+news = false
+;; -------------------------------------------------- ;;
+;; -------------- Cache ----------------- ;;
+;; -------------------------------------------------- ;;
+cache-priv = true
+cache-commenter = true
+cache-rest = true
+cache-page_login = true
+cache-favicon = true
+cache-resources = true
+cache-browser = false
+cache-mobile = false
+cache-mobile_rules = 'Mobile
+Android
+Silk/
+Kindle
+BlackBerry
+Opera Mini
+Opera Mobi'
+cache-exc_useragents = ''
+cache-exc_cookies = ''
+cache-exc_qs = ''
+cache-exc_cat = ''
+cache-exc_tag = ''
+cache-force_uri = ''
+cache-force_pub_uri = ''
+cache-priv_uri = ''
+cache-exc = ''
+cache-exc_roles = ''
+cache-drop_qs = 'fbclid
+gclid
+utm*
+_ga'
+cache-ttl_pub = 604800
+cache-ttl_priv = 1800
+cache-ttl_frontpage = 604800
+cache-ttl_feed = 604800
+; O_CACHE_TTL_REST
+cache-ttl_rest = 604800
+cache-ttl_browser = 31557600
+cache-login_cookie = ''
+cache-vary_group = ''
+cache-ttl_status = '403 3600
+404 3600
+500 3600'
+;; -------------------------------------------------- ;;
+;; -------------- Purge ----------------- ;;
+;; -------------------------------------------------- ;;
+; O_PURGE_ON_UPGRADE
+purge-upgrade = true
+; O_PURGE_STALE
+purge-stale = true
+purge-post_all = false
+purge-post_f = true
+purge-post_h = true
+purge-post_p = true
+purge-post_pwrp = true
+purge-post_a = true
+purge-post_y = false
+purge-post_m = true
+purge-post_d = false
+purge-post_t = true
+purge-post_pt = true
+purge-timed_urls = ''
+purge-timed_urls_time = ''
+purge-hook_all = 'switch_theme
+wp_create_nav_menu
+wp_update_nav_menu
+wp_delete_nav_menu
+create_term
+edit_terms
+delete_term
+add_link
+edit_link
+delete_link'
+;; -------------------------------------------------- ;;
+;; -------------- ESI ----------------- ;;
+;; -------------------------------------------------- ;;
+; O_ESI
+esi = false
+; O_ESI_CACHE_ADMBAR
+esi-cache_admbar = true
+; O_ESI_CACHE_COMMFORM
+esi-cache_commform = true
+; O_ESI_NONCE
+esi-nonce = 'stats_nonce
+subscribe_nonce'
+;; -------------------------------------------------- ;;
+;; -------------- Utilities ----------------- ;;
+;; -------------------------------------------------- ;;
+util-heartbeat = true
+util-instant_click = false
+util-check_advcache = true
+util-no_https_vary = false
+;; -------------------------------------------------- ;;
+;; -------------- Debug ----------------- ;;
+;; -------------------------------------------------- ;;
+; O_DEBUG_DISABLE_ALL
+debug-disable_all = false
+; O_DEBUG
+debug = false
+; O_DEBUG_IPS
+debug-ips = '127.0.0.1'
+; O_DEBUG_LEVEL
+debug-level = false
+; O_DEBUG_FILESIZE
+debug-filesize = 3
+; O_DEBUG_COOKIE
+debug-cookie = false
+; O_DEBUG_COLLAPS_QS
+debug-collaps_qs = false
+; O_DEBUG_INC
+debug-inc = ''
+; O_DEBUG_EXC
+debug-exc = ''
+;; -------------------------------------------------- ;;
+;; -------------- DB Optm ----------------- ;;
+;; -------------------------------------------------- ;;
+; O_DB_OPTM_REVISIONS_MAX
+db_optm-revisions_max = 0
+; O_DB_OPTM_REVISIONS_AGE
+db_optm-revisions_age = 0
+;; -------------------------------------------------- ;;
+;; -------------- HTML Optm ----------------- ;;
+;; -------------------------------------------------- ;;
+; O_OPTM_CSS_MIN
+optm-css_min = false
+optm-css_inline_min = false
+; O_OPTM_CSS_COMB
+optm-css_comb = false
+optm-css_comb_priority = false
+; O_OPTM_CSS_HTTP2
+optm-css_http2 = false
+optm-css_exc = ''
+; O_OPTM_JS_MIN
+optm-js_min = false
+optm-js_inline_min = false
+; O_OPTM_JS_COMB
+optm-js_comb = false
+optm-js_comb_priority = false
+; O_OPTM_JS_HTTP2
+optm-js_http2 = false
+; O_OPTM_EXC_JQ
+optm-js_exc = ''
+optm-ttl = 604800
+optm-html_min = false
+optm-qs_rm = false
+optm-ggfonts_rm = false
+; O_OPTM_CSS_ASYNC
+optm-css_async = false
+; O_OPTM_CCSS_GEN
+optm-ccss_gen = true
+; O_OPTM_CCSS_ASYNC
+optm-ccss_async = true
+; O_OPTM_CSS_ASYNC_INLINE
+optm-css_async_inline = true
+; O_OPTM_CSS_FONT_DISPLAY
+optm-css_font_display = false
+; O_OPTM_JS_DEFER
+optm-js_defer = false
+; O_OPTM_JS_INLINE_DEFER
+optm-js_inline_defer = false
+optm-emoji_rm = false
+optm-exc_jq = true
+optm-ggfonts_async = false
+optm-max_size = 2
+optm-rm_comment = false
+optm-exc_roles = ''
+optm-ccss_con = ''
+optm-js_defer_exc = ''
+; O_OPTM_DNS_PREFETCH
+optm-dns_prefetch = ''
+; O_OPTM_DNS_PREFETCH_CTRL
+optm-dns_prefetch_ctrl = false
+optm-exc = ''
+; O_OPTM_CCSS_SEP_POSTTYPE
+optm-ccss_sep_posttype = ''
+; O_OPTM_CCSS_SEP_URI
+optm-ccss_sep_uri = ''
+;; -------------------------------------------------- ;;
+;; -------------- Object Cache ----------------- ;;
+;; -------------------------------------------------- ;;
+object = true
+object-kind = false
+;object-host = 'localhost'
+object-host = '/var/www/memcached.sock'
+;object-port = 11211
+cache_object_port = ''
+object-life = 360
+object-persistent = true
+object-admin = true
+object-transients = true
+object-db_id = 0
+object-user = ''
+object-pswd = ''
+object-global_groups = 'users
+userlogins
+usermeta
+user_meta
+site-transient
+site-options
+site-lookup
+blog-lookup
+blog-details
+rss
+global-posts
+blog-id-cache'
+object-non_persistent_groups = 'comment
+counts
+plugins
+wc_session_id'
+;; -------------------------------------------------- ;;
+;; -------------- Discussion ----------------- ;;
+;; -------------------------------------------------- ;;
+; O_DISCUSS_AVATAR_CACHE
+discuss-avatar_cache = false
+; O_DISCUSS_AVATAR_CRON
+discuss-avatar_cron = false
+; O_DISCUSS_AVATAR_CACHE_TTL
+discuss-avatar_cache_ttl = 604800
+;; -------------------------------------------------- ;;
+;; -------------- Media ----------------- ;;
+;; -------------------------------------------------- ;;
+; O_MEDIA_LAZY
+media-lazy = false
+; O_MEDIA_LAZY_PLACEHOLDER
+media-lazy_placeholder = ''
+; O_MEDIA_PLACEHOLDER_RESP
+media-placeholder_resp = false
+; O_MEDIA_PLACEHOLDER_RESP_COLOR
+media-placeholder_resp_color = '#cfd4db'
+; O_MEDIA_PLACEHOLDER_RESP_GENERATOR
+media-placeholder_resp_generator = false
+; O_MEDIA_PLACEHOLDER_RESP_SVG
+media-placeholder_resp_svg = ''
+; O_MEDIA_PLACEHOLDER_LQIP
+media-placeholder_lqip = false
+; O_MEDIA_PLACEHOLDER_LQIP_QUAL
+media-placeholder_lqip_qual = 4
+; O_MEDIA_PLACEHOLDER_RESP_ASYNC
+media-placeholder_resp_async = true
+; O_MEDIA_IFRAME_LAZY
+media-iframe_lazy = false
+; O_MEDIA_LAZYJS_INLINE
+media-lazyjs_inline = false
+; O_MEDIA_LAZY_EXC
+media-lazy_exc = ''
+; O_MEDIA_LAZY_CLS_EXC
+media-lazy_cls_exc = ''
+; O_MEDIA_LAZY_PARENT_CLS_EXC
+media-lazy_parent_cls_exc = ''
+; O_MEDIA_IFRAME_LAZY_CLS_EXC
+media-iframe_lazy_cls_exc = ''
+; O_MEDIA_IFRAME_LAZY_PARENT_CLS_EXC
+media-iframe_lazy_parent_cls_exc = ''
+; O_MEDIA_LAZY_URI_EXC
+media-lazy_uri_exc = ''
+;; -------------------------------------------------- ;;
+;; -------------- Image Optm ----------------- ;;
+;; -------------------------------------------------- ;;
+img_optm-auto = false
+img_optm-cron = true
+img_optm-ori = true
+img_optm-rm_bkup = false
+img_optm-webp = false
+img_optm-lossless = false
+img_optm-exif = false
+img_optm-webp_replace = false
+img_optm-webp_attr = 'img.src
+div.data-thumb
+img.data-src
+div.data-large_image
+img.retina_logo_url
+div.data-parallax-image
+video.poster'
+img_optm-webp_replace_srcset = false
+img_optm-jpg_quality = 82
+;; -------------------------------------------------- ;;
+;; -------------- Crawler ----------------- ;;
+;; -------------------------------------------------- ;;
+crawler = false
+crawler-inc_posts = true
+crawler-inc_pages = true
+crawler-inc_cats = true
+crawler-inc_tags = true
+crawler-exc_cpt = ''
+crawler-order_links = 'date_desc'
+crawler-usleep = 500
+crawler-run_duration = 400
+crawler-run_interval = 600
+crawler-crawl_interval = 302400
+crawler-threads = 3
+crawler-timeout = 30
+crawler-load_limit = 1
+; O_CRAWLER_SITEMAP
+crawler-sitemap = ''
+; O_CRAWLER_DROP_DOMAIN
+crawler-drop_domain = true
+crawler-roles = ''
+crawler-cookies = ''
+;; -------------------------------------------------- ;;
+;; -------------- Misc ----------------- ;;
+;; -------------------------------------------------- ;;
+; O_MISC_HTACCESS_FRONT
+misc-htaccess_front = ''
+; O_MISC_HTACCESS_BACK
+misc-htaccess_back = ''
+; O_MISC_HEARTBEAT_FRONT
+misc-heartbeat_front = false
+; O_MISC_HEARTBEAT_FRONT_TTL
+misc-heartbeat_front_ttl = 60
+; O_MISC_HEARTBEAT_BACK
+misc-heartbeat_back = false
+; O_MISC_HEARTBEAT_BACK_TTL
+misc-heartbeat_back_ttl = 60
+; O_MISC_HEARTBEAT_EDITOR
+misc-heartbeat_editor = false
+; O_MISC_HEARTBEAT_EDITOR_TTL
+misc-heartbeat_editor_ttl = 15
+;; -------------------------------------------------- ;;
+;; -------------- CDN ----------------- ;;
+;; -------------------------------------------------- ;;
+cdn = false
+cdn-ori = ''
+cdn-ori_dir = ''
+cdn-exc = ''
+cdn-remote_jq = false
+cdn-quic = false
+cdn-quic_email = ''
+cdn-quic_key = ''
+cdn-cloudflare = false
+cdn-cloudflare_email = ''
+cdn-cloudflare_key = ''
+cdn-cloudflare_name = ''
+cdn-cloudflare_zone = ''
+; \`cdn-mapping\` needs to be put in the end with a section tag
+;; -------------------------------------------------- ;;
+;; -------------- CDN 2 ----------------- ;;
+;; -------------------------------------------------- ;;
+; <------------ CDN Mapping Example BEGIN -------------------->
+; Need to keep the section tag \`[cdn-mapping]\` before list.
+;
+; NOTE 1) Need to set all child options to make all resources to be replaced without missing.
+; NOTE 2) \`url[n]\` option must have to enable the row setting of \`n\`.
+; NOTE 3) This section needs to be put in the end of this .ini file
+;
+; To enable the 2nd mapping record by default, please remove the \`;;\` in the related lines.
+[cdn-mapping]
+url[0] = ''
+inc_js[0] = true
+inc_css[0] = true
+inc_img[0] = true
+filetype[0] = '.aac
+.css
+.eot
+.gif
+.jpeg
+.js
+.jpg
+.less
+.mp3
+.mp4
+.ogg
+.otf
+.pdf
+.png
+.svg
+.ttf
+.woff'
+;;url[1] = 'https://2nd_CDN_url.com/'
+;;filetype[1] = '.webm'
+; <------------ CDN Mapping Example END ------------------>
+EOM
+
+ THEME_PATH="${VH_DOC_ROOT}/wp-content/themes/${THEME}"
+ if [ ! -f ${THEME_PATH}/functions.php ]; then
+ cat >> "${THEME_PATH}/functions.php" <>/dev/null 2>&1
+2i
+require_once( WP_CONTENT_DIR.'/../wp-admin/includes/plugin.php' );
+\$path = 'litespeed-cache/litespeed-cache.php' ;
+if (!is_plugin_active( \$path )) {
+ activate_plugin( \$path ) ;
+ rename( __FILE__ . '.bk', __FILE__ );
+}
+.
+w
+q
+END
+ fi
+}
+
+preinstall_wordpress(){
+ if [ "${VHNAME}" != '' ]; then
+ get_db_pass ${VHNAME}
+ else
+ get_db_pass ${DOMAIN}
+ fi
+ if [ ! -f ${VH_DOC_ROOT}/wp-config.php ] && [ -f ${VH_DOC_ROOT}/wp-config-sample.php ]; then
+ cp ${VH_DOC_ROOT}/wp-config-sample.php ${VH_DOC_ROOT}/wp-config.php
+ NEWDBPWD="define('DB_PASSWORD', '${SQL_PASS}');"
+ linechange 'DB_PASSWORD' ${VH_DOC_ROOT}/wp-config.php "${NEWDBPWD}"
+ NEWDBPWD="define('DB_USER', '${SQL_USER}');"
+ linechange 'DB_USER' ${VH_DOC_ROOT}/wp-config.php "${NEWDBPWD}"
+ NEWDBPWD="define('DB_NAME', '${SQL_DB}');"
+ linechange 'DB_NAME' ${VH_DOC_ROOT}/wp-config.php "${NEWDBPWD}"
+ #NEWDBPWD="define('DB_HOST', '${PUB_IP}');"
+ NEWDBPWD="define('DB_HOST', '${DB_HOST}');"
+ linechange 'DB_HOST' ${VH_DOC_ROOT}/wp-config.php "${NEWDBPWD}"
+ elif [ -f ${VH_DOC_ROOT}/wp-config.php ]; then
+ echo "${VH_DOC_ROOT}/wp-config.php already exist, exit !"
+ exit 1
+ else
+ echo 'Skip!'
+ exit 2
+ fi
+}
+
+app_wordpress_dl(){
+ if [ ! -f "${VH_DOC_ROOT}/wp-config.php" ] && [ ! -f "${VH_DOC_ROOT}/wp-config-sample.php" ]; then
+ wp core download \
+ --allow-root \
+ --quiet
+ else
+ echo 'wordpress already exist, abort!'
+ exit 1
+ fi
+}
+
+change_owner(){
+ if [ "${VHNAME}" != '' ]; then
+ chown -R ${WWW_UID}:${WWW_GID} ${DEFAULT_VH_ROOT}/${VHNAME}
+ else
+ chown -R ${WWW_UID}:${WWW_GID} ${DEFAULT_VH_ROOT}/${DOMAIN}
+ fi
+}
+
+main(){
+ set_vh_docroot ${DOMAIN}
+ get_owner
+ cd ${VH_DOC_ROOT}
+ if [ "${APP_NAME}" = 'wordpress' ] || [ "${APP_NAME}" = 'wp' ]; then
+ check_sql_native
+ app_wordpress_dl
+ preinstall_wordpress
+ install_wp_plugin
+ set_htaccess
+ get_theme_name
+ set_lscache
+ change_owner
+ exit 0
+ else
+ echo "APP: ${APP_NAME} not support, exit!"
+ exit 1
+ fi
+}
+
+check_input ${1}
+while [ ! -z "${1}" ]; do
+ case ${1} in
+ -[hH] | -help | --help)
+ help_message
+ ;;
+ -[aA] | -app | --app) shift
+ check_input "${1}"
+ APP_NAME="${1}"
+ ;;
+ -[dD] | -domain | --domain) shift
+ check_input "${1}"
+ DOMAIN="${1}"
+ ;;
+ -vhname | --vhname) shift
+ VHNAME="${1}"
+ ;;
+ *)
+ help_message
+ ;;
+ esac
+ shift
+done
+main
diff --git a/apps/openlitespeed/1.7.17-lsphp81/data/bin/container/certhookctl.sh b/apps/openlitespeed/1.7.17-lsphp81/data/bin/container/certhookctl.sh
new file mode 100644
index 00000000..18be0965
--- /dev/null
+++ b/apps/openlitespeed/1.7.17-lsphp81/data/bin/container/certhookctl.sh
@@ -0,0 +1,18 @@
+#!/bin/bash
+BOTCRON='/var/spool/cron/crontabs/root'
+
+cert_hook(){
+ grep 'acme' ${BOTCRON} >/dev/null
+ if [ ${?} = 0 ]; then
+ grep 'lswsctrl' ${BOTCRON} >/dev/null
+ if [ ${?} = 0 ]; then
+ echo 'Hook already exist, skip!'
+ else
+ sed -i 's/--cron/--cron --renew-hook "\/usr\/local\/lsws\/bin\/lswsctrl restart"/g' ${BOTCRON}
+ fi
+ else
+ echo "[X] ${BOTCRON} does not exist, please check it later!"
+ fi
+}
+
+cert_hook
\ No newline at end of file
diff --git a/apps/openlitespeed/1.7.17-lsphp81/data/bin/container/domainctl.sh b/apps/openlitespeed/1.7.17-lsphp81/data/bin/container/domainctl.sh
new file mode 100644
index 00000000..75539ef0
--- /dev/null
+++ b/apps/openlitespeed/1.7.17-lsphp81/data/bin/container/domainctl.sh
@@ -0,0 +1,160 @@
+#!/usr/bin/env bash
+CK_RESULT=''
+LSDIR='/usr/local/lsws'
+LS_HTTPD_CONF="${LSDIR}/conf/httpd_config.xml"
+OLS_HTTPD_CONF="${LSDIR}/conf/httpd_config.conf"
+EPACE=' '
+
+echow(){
+ FLAG=${1}
+ shift
+ echo -e "\033[1m${EPACE}${FLAG}\033[0m${@}"
+}
+
+help_message(){
+ echo -e "\033[1mOPTIONS\033[0m"
+ echow '-A, --add [DOMAIN_NAME]'
+ echo "${EPACE}${EPACE}Will add domain to listener and creat a virtual host from template"
+ echow '-D, --del [DOMAIN_NAME]'
+ echo "${EPACE}${EPACE}Will delete domain from listener"
+ echow '-H, --help'
+ echo "${EPACE}${EPACE}Display help."
+}
+
+check_lsv(){
+ if [ -f ${LSDIR}/bin/openlitespeed ]; then
+ LSV='openlitespeed'
+ elif [ -f ${LSDIR}/bin/litespeed ]; then
+ LSV='lsws'
+ else
+ echo 'Version not exist, abort!'
+ exit 1
+ fi
+}
+
+dot_escape(){
+ ESCAPE=$(echo ${1} | sed 's/\./\\./g')
+}
+
+check_duplicate(){
+ CK_RESULT=$(grep -E "${1}" ${2})
+}
+
+fst_match_line(){
+ FIRST_LINE_NUM=$(grep -n -m 1 ${1} ${2} | awk -F ':' '{print $1}')
+}
+fst_match_after(){
+ FIRST_NUM_AFTER=$(tail -n +${1} ${2} | grep -n -m 1 ${3} | awk -F ':' '{print $1}')
+}
+lst_match_line(){
+ fst_match_after ${1} ${2} ${3}
+ LAST_LINE_NUM=$((${FIRST_LINE_NUM}+${FIRST_NUM_AFTER}-1))
+}
+
+check_input(){
+ if [ -z "${1}" ]; then
+ help_message
+ exit 1
+ fi
+}
+
+check_www(){
+ CHECK_WWW=$(echo ${1} | cut -c1-4)
+ if [[ ${CHECK_WWW} == www. ]] ; then
+ echo 'www domain shoudnt be passed!'
+ exit 1
+ fi
+}
+
+www_domain(){
+ check_www ${1}
+ WWW_DOMAIN=$(echo www.${1})
+}
+
+add_ls_domain(){
+ fst_match_line 'docker.xml' ${LS_HTTPD_CONF}
+ NEWNUM=$((FIRST_LINE_NUM+2))
+ sed -i "${NEWNUM}i \ \ \ \ \ \ \n \ \ \ \ \ \ \ ${DOMAIN}\n \ \ \ \ \ \ \ ${DOMAIN},${WWW_DOMAIN}\n \ \ \ \ \ \ " ${LS_HTTPD_CONF}
+}
+
+add_ols_domain(){
+ perl -0777 -p -i -e 's/(vhTemplate docker \{[^}]+)\}*(^.*listeners.*$)/\1$2
+ member '${DOMAIN}' {
+ vhDomain '${DOMAIN},${WWW_DOMAIN}'
+ }/gmi' ${OLS_HTTPD_CONF}
+}
+
+add_domain(){
+ check_lsv
+ dot_escape ${1}
+ DOMAIN=${ESCAPE}
+ www_domain ${1}
+ if [ "${LSV}" = 'lsws' ]; then
+ check_duplicate "vhDomain.*${DOMAIN}" ${LS_HTTPD_CONF}
+ if [ "${CK_RESULT}" != '' ]; then
+ echo "# It appears the domain already exist! Check the ${LS_HTTPD_CONF} if you believe this is a mistake!"
+ exit 1
+ fi
+ elif [ "${LSV}" = 'openlitespeed' ]; then
+ check_duplicate "member.*${DOMAIN}" ${OLS_HTTPD_CONF}
+ if [ "${CK_RESULT}" != '' ]; then
+ echo "# It appears the domain already exist! Check the ${OLS_HTTPD_CONF} if you believe this is a mistake!"
+ exit 1
+ fi
+ fi
+ add_ls_domain
+ add_ols_domain
+}
+
+del_ls_domain(){
+ fst_match_line "*${1}" ${LS_HTTPD_CONF}
+ FIRST_LINE_NUM=$((FIRST_LINE_NUM-1))
+ lst_match_line ${FIRST_LINE_NUM} ${LS_HTTPD_CONF} ''
+ sed -i "${FIRST_LINE_NUM},${LAST_LINE_NUM}d" ${LS_HTTPD_CONF}
+}
+
+del_ols_domain(){
+ fst_match_line ${1} ${OLS_HTTPD_CONF}
+ lst_match_line ${FIRST_LINE_NUM} ${OLS_HTTPD_CONF} '}'
+ sed -i "${FIRST_LINE_NUM},${LAST_LINE_NUM}d" ${OLS_HTTPD_CONF}
+}
+
+del_domain(){
+ check_lsv
+ dot_escape ${1}
+ DOMAIN=${ESCAPE}
+ if [ "${LSV}" = 'lsws' ]; then
+ check_duplicate "vhDomain.*${DOMAIN}" ${LS_HTTPD_CONF}
+ if [ "${CK_RESULT}" = '' ]; then
+ echo "# Domain non-exist! Check the ${LS_HTTPD_CONF} if you believe this is a mistake!"
+ exit 1
+ fi
+ elif [ "${LSV}" = 'openlitespeed' ]; then
+ check_duplicate "member.*${DOMAIN}" ${OLS_HTTPD_CONF}
+ if [ "${CK_RESULT}" = '' ]; then
+ echo "# Domain non-exist! Check the ${OLS_HTTPD_CONF} if you believe this is a mistake!"
+ exit 1
+ fi
+ fi
+ del_ls_domain ${1}
+ del_ols_domain ${1}
+}
+
+check_input ${1}
+while [ ! -z "${1}" ]; do
+ case ${1} in
+ -[hH] | -help | --help)
+ help_message
+ ;;
+ -[aA] | -add | --add) shift
+ add_domain ${1}
+ ;;
+ -[dD] | -del | --del | --delete) shift
+ del_domain ${1}
+ ;;
+ *)
+ help_message
+ ;;
+ esac
+ shift
+done
\ No newline at end of file
diff --git a/apps/openlitespeed/1.7.17-lsphp81/data/bin/container/owaspctl.sh b/apps/openlitespeed/1.7.17-lsphp81/data/bin/container/owaspctl.sh
new file mode 100644
index 00000000..72fd8e59
--- /dev/null
+++ b/apps/openlitespeed/1.7.17-lsphp81/data/bin/container/owaspctl.sh
@@ -0,0 +1,236 @@
+#!/bin/bash
+LSDIR='/usr/local/lsws'
+OWASP_DIR="${LSDIR}/conf/owasp"
+RULE_FILE='modsec_includes.conf'
+LS_HTTPD_CONF="${LSDIR}/conf/httpd_config.xml"
+OLS_HTTPD_CONF="${LSDIR}/conf/httpd_config.conf"
+EPACE=' '
+OWASP_V='3.3.4'
+
+echow(){
+ FLAG=${1}
+ shift
+ echo -e "\033[1m${EPACE}${FLAG}\033[0m${@}"
+}
+
+help_message(){
+ echo -e "\033[1mOPTIONS\033[0m"
+ echow '-E, --enable'
+ echo "${EPACE}${EPACE}Will Enable mod_secure module with latest OWASP version of rules"
+ echow '-D, --disable'
+ echo "${EPACE}${EPACE}Will Disable mod_secure module with latest OWASP version of rules"
+ echow '-H, --help'
+ echo "${EPACE}${EPACE}Display help and exit."
+ exit 0
+}
+
+check_lsv(){
+ if [ -f ${LSDIR}/bin/openlitespeed ]; then
+ LSV='openlitespeed'
+ elif [ -f ${LSDIR}/bin/litespeed ]; then
+ LSV='lsws'
+ else
+ echo 'Version not exist, abort!'
+ exit 1
+ fi
+}
+
+check_input(){
+ if [ -z "${1}" ]; then
+ help_message
+ exit 1
+ fi
+}
+
+mk_owasp_dir(){
+ if [ -d ${OWASP_DIR} ] ; then
+ rm -rf ${OWASP_DIR}
+ fi
+ mkdir -p ${OWASP_DIR}
+ if [ ${?} -ne 0 ] ; then
+ echo "Unable to create directory: ${OWASP_DIR}, exit!"
+ exit 1
+ fi
+}
+
+fst_match_line(){
+ FIRST_LINE_NUM=$(grep -n -m 1 "${1}" ${2} | awk -F ':' '{print $1}')
+}
+fst_match_after(){
+ FIRST_NUM_AFTER=$(tail -n +${1} ${2} | grep -n -m 1 ${3} | awk -F ':' '{print $1}')
+}
+lst_match_line(){
+ fst_match_after ${1} ${2} ${3}
+ LAST_LINE_NUM=$((${FIRST_LINE_NUM}+${FIRST_NUM_AFTER}-1))
+}
+
+enable_ols_modsec(){
+ grep 'module mod_security {' ${OLS_HTTPD_CONF} >/dev/null 2>&1
+ if [ ${?} -eq 0 ] ; then
+ echo "Already configured for modsecurity."
+ else
+ echo 'Enable modsecurity'
+ sed -i "s=module cache=module mod_security {\nmodsecurity on\
+ \nmodsecurity_rules \`\nSecRuleEngine On\n\`\nmodsecurity_rules_file \
+ ${OWASP_DIR}/${RULE_FILE}\n ls_enabled 1\n}\
+ \n\nmodule cache=" ${OLS_HTTPD_CONF}
+ fi
+}
+
+enable_ls_modsec(){
+ grep '1' ${LS_HTTPD_CONF} >/dev/null 2>&1
+ if [ ${?} -eq 0 ] ; then
+ echo "LSWS already configured for modsecurity"
+ else
+ echo 'Enable modsecurity'
+ sed -i \
+ "s=0=1=" ${LS_HTTPD_CONF}
+ sed -i \
+ "s==\n\
+ \n\
+ ModSec\n\
+ 1\n\
+ include ${OWASP_DIR}/modsec_includes.conf\n\
+ =" ${LS_HTTPD_CONF}
+ fi
+}
+
+enable_modsec(){
+ if [ "${LSV}" = 'lsws' ]; then
+ enable_ls_modsec
+ elif [ "${LSV}" = 'openlitespeed' ]; then
+ enable_ols_modsec
+ fi
+}
+
+disable_ols_modesec(){
+ grep 'module mod_security {' ${OLS_HTTPD_CONF} >/dev/null 2>&1
+ if [ ${?} -eq 0 ] ; then
+ echo 'Disable modsecurity'
+ fst_match_line 'module mod_security' ${OLS_HTTPD_CONF}
+ lst_match_line ${FIRST_LINE_NUM} ${OLS_HTTPD_CONF} '}'
+ sed -i "${FIRST_LINE_NUM},${LAST_LINE_NUM}d" ${OLS_HTTPD_CONF}
+ else
+ echo 'Already disabled for modsecurity'
+ fi
+}
+
+disable_ls_modesec(){
+ grep '0' ${LS_HTTPD_CONF}
+ if [ ${?} -eq 0 ] ; then
+ echo 'Already disabled for modsecurity'
+ else
+ echo 'Disable modsecurity'
+ sed -i \
+ "s=1=0=" ${LS_HTTPD_CONF}
+ fst_match_line 'censorshipRuleSet' ${LS_HTTPD_CONF}
+ lst_match_line ${FIRST_LINE_NUM} ${LS_HTTPD_CONF} '/censorshipRuleSet'
+ sed -i "${FIRST_LINE_NUM},${LAST_LINE_NUM}d" ${LS_HTTPD_CONF}
+ fi
+}
+
+disable_modsec(){
+ check_lsv
+ if [ "${LSV}" = 'lsws' ]; then
+ disable_ls_modesec
+ elif [ "${LSV}" = 'openlitespeed' ]; then
+ disable_ols_modesec
+ fi
+}
+
+install_unzip(){
+ if [ ! -f /usr/bin/unzip ]; then
+ echo 'Install Unzip'
+ apt update >/dev/null 2>&1
+ apt-get install unzip -y >/dev/null 2>&1
+ fi
+}
+
+install_owasp(){
+ cd ${OWASP_DIR}
+ echo 'Download OWASP rules'
+ wget -q https://github.com/coreruleset/coreruleset/archive/refs/tags/v${OWASP_V}.zip
+ unzip -qq v${OWASP_V}.zip
+ rm -f v${OWASP_V}.zip
+ mv coreruleset-* owasp-modsecurity-crs
+}
+
+configure_owasp(){
+ echo 'Config OWASP rules.'
+ cd ${OWASP_DIR}
+ echo "include modsecurity.conf
+include owasp-modsecurity-crs/crs-setup.conf
+include owasp-modsecurity-crs/rules/REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf
+include owasp-modsecurity-crs/rules/REQUEST-901-INITIALIZATION.conf
+include owasp-modsecurity-crs/rules/REQUEST-903.9001-DRUPAL-EXCLUSION-RULES.conf
+include owasp-modsecurity-crs/rules/REQUEST-903.9002-WORDPRESS-EXCLUSION-RULES.conf
+include owasp-modsecurity-crs/rules/REQUEST-903.9003-NEXTCLOUD-EXCLUSION-RULES.conf
+include owasp-modsecurity-crs/rules/REQUEST-903.9004-DOKUWIKI-EXCLUSION-RULES.conf
+include owasp-modsecurity-crs/rules/REQUEST-903.9005-CPANEL-EXCLUSION-RULES.conf
+include owasp-modsecurity-crs/rules/REQUEST-903.9006-XENFORO-EXCLUSION-RULES.conf
+include owasp-modsecurity-crs/rules/REQUEST-905-COMMON-EXCEPTIONS.conf
+include owasp-modsecurity-crs/rules/REQUEST-910-IP-REPUTATION.conf
+include owasp-modsecurity-crs/rules/REQUEST-911-METHOD-ENFORCEMENT.conf
+include owasp-modsecurity-crs/rules/REQUEST-912-DOS-PROTECTION.conf
+include owasp-modsecurity-crs/rules/REQUEST-913-SCANNER-DETECTION.conf
+include owasp-modsecurity-crs/rules/REQUEST-920-PROTOCOL-ENFORCEMENT.conf
+include owasp-modsecurity-crs/rules/REQUEST-921-PROTOCOL-ATTACK.conf
+include owasp-modsecurity-crs/rules/REQUEST-930-APPLICATION-ATTACK-LFI.conf
+include owasp-modsecurity-crs/rules/REQUEST-931-APPLICATION-ATTACK-RFI.conf
+include owasp-modsecurity-crs/rules/REQUEST-932-APPLICATION-ATTACK-RCE.conf
+include owasp-modsecurity-crs/rules/REQUEST-933-APPLICATION-ATTACK-PHP.conf
+include owasp-modsecurity-crs/rules/REQUEST-934-APPLICATION-ATTACK-NODEJS.conf
+include owasp-modsecurity-crs/rules/REQUEST-941-APPLICATION-ATTACK-XSS.conf
+include owasp-modsecurity-crs/rules/REQUEST-942-APPLICATION-ATTACK-SQLI.conf
+include owasp-modsecurity-crs/rules/REQUEST-943-APPLICATION-ATTACK-SESSION-FIXATION.conf
+include owasp-modsecurity-crs/rules/REQUEST-944-APPLICATION-ATTACK-JAVA.conf
+include owasp-modsecurity-crs/rules/REQUEST-949-BLOCKING-EVALUATION.conf
+include owasp-modsecurity-crs/rules/RESPONSE-950-DATA-LEAKAGES.conf
+include owasp-modsecurity-crs/rules/RESPONSE-951-DATA-LEAKAGES-SQL.conf
+include owasp-modsecurity-crs/rules/RESPONSE-952-DATA-LEAKAGES-JAVA.conf
+include owasp-modsecurity-crs/rules/RESPONSE-953-DATA-LEAKAGES-PHP.conf
+include owasp-modsecurity-crs/rules/RESPONSE-954-DATA-LEAKAGES-IIS.conf
+include owasp-modsecurity-crs/rules/RESPONSE-959-BLOCKING-EVALUATION.conf
+include owasp-modsecurity-crs/rules/RESPONSE-980-CORRELATION.conf
+include owasp-modsecurity-crs/rules/RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf">modsec_includes.conf
+ echo "SecRuleEngine On">modsecurity.conf
+ cd ${OWASP_DIR}/owasp-modsecurity-crs
+ if [ -f crs-setup.conf.example ]; then
+ mv crs-setup.conf.example crs-setup.conf
+ fi
+ cd ${OWASP_DIR}/owasp-modsecurity-crs/rules
+ if [ -f REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf.example ]; then
+ mv REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf.example REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf
+ fi
+ if [ -f RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf.example ]; then
+ mv RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf.example RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf
+ fi
+}
+
+main_owasp(){
+ mk_owasp_dir
+ install_unzip
+ install_owasp
+ configure_owasp
+ check_lsv
+ enable_modsec
+}
+
+check_input ${1}
+while [ ! -z "${1}" ]; do
+ case ${1} in
+ -[hH] | -help | --help)
+ help_message
+ ;;
+ -[eE] | -enable | --enable)
+ main_owasp
+ ;;
+ -[dD] | -disable | --disable)
+ disable_modsec
+ ;;
+ *)
+ help_message
+ ;;
+ esac
+ shift
+done
\ No newline at end of file
diff --git a/apps/openlitespeed/1.7.17-lsphp81/data/bin/container/serialctl.sh b/apps/openlitespeed/1.7.17-lsphp81/data/bin/container/serialctl.sh
new file mode 100644
index 00000000..42e312dc
--- /dev/null
+++ b/apps/openlitespeed/1.7.17-lsphp81/data/bin/container/serialctl.sh
@@ -0,0 +1,84 @@
+#!/bin/bash
+LSDIR='/usr/local/lsws'
+EPACE=' '
+
+echow(){
+ FLAG=${1}
+ shift
+ echo -e "\033[1m${EPACE}${FLAG}\033[0m${@}"
+}
+
+help_message(){
+ echo -e "\033[1mOPTIONS\033[0m"
+ echow '-S, --serial [YOUR_SERIAL|TRIAL]'
+ echo "${EPACE}${EPACE}Will apply and register the serial to LSWS."
+ echow '-H, --help'
+ echo "${EPACE}${EPACE}Display help and exit."
+ exit 0
+}
+
+check_input(){
+ if [ -z "${1}" ]; then
+ help_message
+ exit 1
+ fi
+}
+
+backup_old(){
+ if [ -f ${1} ] && [ ! -f ${1}_old ]; then
+ mv ${1} ${1}_old
+ fi
+}
+
+detect_ols(){
+ if [ -e ${LSDIR}/bin/openlitespeed ]; then
+ echo '[X] Detect OpenLiteSpeed, abort!'
+ exit 1
+ fi
+}
+
+apply_serial(){
+ detect_ols
+ check_input ${1}
+ echo ${1} | grep -i 'trial' >/dev/null
+ if [ ${?} = 0 ]; then
+ echo 'Apply Trial License'
+ if [ ! -e ${LSDIR}/conf/serial.no ] && [ ! -e ${LSDIR}/conf/license.key ]; then
+ rm -f ${LSDIR}/conf/trial.key*
+ wget -P ${LSDIR}/conf -q http://license.litespeedtech.com/reseller/trial.key
+ echo 'Apply trial finished'
+ else
+ echo "Please backup and remove your existing license, apply abort!"
+ exit 1
+ fi
+ else
+ echo "Apply Serial number: ${1}"
+ backup_old ${LSDIR}/conf/serial.no
+ backup_old ${LSDIR}/conf/license.key
+ backup_old ${LSDIR}/conf/trial.key
+ echo "${1}" > ${LSDIR}/conf/serial.no
+ ${LSDIR}/bin/lshttpd -r
+ if [ -f ${LSDIR}/conf/license.key ]; then
+ echo '[O] Apply success'
+ else
+ echo '[X] Apply failed, please check!'
+ exit 1
+ fi
+ fi
+}
+
+check_input ${1}
+while [ ! -z "${1}" ]; do
+ case ${1} in
+ -[hH] | -help | --help)
+ help_message
+ ;;
+ -[sS] | -serial | --serial) shift
+ apply_serial "${1}"
+ ;;
+ *)
+ help_message
+ ;;
+ esac
+ shift
+done
\ No newline at end of file
diff --git a/apps/openlitespeed/1.7.17-lsphp81/data/bin/database.sh b/apps/openlitespeed/1.7.17-lsphp81/data/bin/database.sh
new file mode 100644
index 00000000..ec1462d4
--- /dev/null
+++ b/apps/openlitespeed/1.7.17-lsphp81/data/bin/database.sh
@@ -0,0 +1,158 @@
+#!/usr/bin/env bash
+source .env
+
+DOMAIN=''
+SQL_DB=''
+SQL_USER=''
+SQL_PASS=''
+ANY="'%'"
+SET_OK=0
+EPACE=' '
+
+echow(){
+ FLAG=${1}
+ shift
+ echo -e "\033[1m${EPACE}${FLAG}\033[0m${@}"
+}
+
+help_message(){
+ echo -e "\033[1mOPTIONS\033[0m"
+ echow '-D, --domain [DOMAIN_NAME]'
+ echo "${EPACE}${EPACE}Example: database.sh -D example.com"
+ echo "${EPACE}${EPACE}Will auto generate Database/username/password for the domain"
+ echow '-D, --domain [DOMAIN_NAME] -U, --user [xxx] -P, --password [xxx] -DB, --database [xxx]'
+ echo "${EPACE}${EPACE}Example: database.sh -D example.com -U USERNAME -P PASSWORD -DB DATABASENAME"
+ echo "${EPACE}${EPACE}Will create Database/username/password by given"
+ echow '-H, --help'
+ echo "${EPACE}${EPACE}Display help and exit."
+ exit 0
+}
+
+check_input(){
+ if [ -z "${1}" ]; then
+ help_message
+ exit 1
+ fi
+}
+
+specify_name(){
+ check_input ${SQL_USER}
+ check_input ${SQL_PASS}
+ check_input ${SQL_DB}
+}
+
+auto_name(){
+ SQL_DB="${TRANSNAME}"
+ SQL_USER="${TRANSNAME}"
+ SQL_PASS="'${RANDOM_PASS}'"
+}
+
+gen_pass(){
+ RANDOM_PASS="$(openssl rand -base64 12)"
+}
+
+trans_name(){
+ TRANSNAME=$(echo ${1} | tr -d '.&&-')
+}
+
+display_credential(){
+ if [ ${SET_OK} = 0 ]; then
+ echo "Database: ${SQL_DB}"
+ echo "Username: ${SQL_USER}"
+ echo "Password: $(echo ${SQL_PASS} | tr -d "'")"
+ fi
+}
+
+store_credential(){
+ if [ -d "./sites/${1}" ]; then
+ if [ -f ./sites/${1}/.db_pass ]; then
+ mv ./sites/${1}/.db_pass ./sites/${1}/.db_pass.bk
+ fi
+ cat > "./sites/${1}/.db_pass" << EOT
+"Database":"${SQL_DB}"
+"Username":"${SQL_USER}"
+"Password":"$(echo ${SQL_PASS} | tr -d "'")"
+EOT
+ else
+ echo "./sites/${1} not found, abort credential store!"
+ fi
+}
+
+check_db_access(){
+ docker compose exec -T mysql su -c "mysql -uroot -p${MYSQL_ROOT_PASSWORD} -e 'status'" >/dev/null 2>&1
+ if [ ${?} != 0 ]; then
+ echo '[X] DB access failed, please check!'
+ exit 1
+ fi
+}
+
+check_db_exist(){
+ docker compose exec -T mysql su -c "test -e /var/lib/mysql/${1}"
+ if [ ${?} = 0 ]; then
+ echo "Database ${1} already exist, skip DB creation!"
+ exit 0
+ fi
+}
+
+db_setup(){
+ docker compose exec -T mysql su -c 'mysql -uroot -p${MYSQL_ROOT_PASSWORD} \
+ -e "CREATE DATABASE '${SQL_DB}';" \
+ -e "GRANT ALL PRIVILEGES ON '${SQL_DB}'.* TO '${SQL_USER}'@'${ANY}' IDENTIFIED BY '${SQL_PASS}';" \
+ -e "FLUSH PRIVILEGES;"'
+ SET_OK=${?}
+}
+
+auto_setup_main(){
+ check_input ${DOMAIN}
+ gen_pass
+ trans_name ${DOMAIN}
+ auto_name
+ check_db_exist ${SQL_DB}
+ check_db_access
+ db_setup
+ display_credential
+ store_credential ${DOMAIN}
+}
+
+specify_setup_main(){
+ specify_name
+ check_db_exist ${SQL_DB}
+ check_db_access
+ db_setup
+ display_credential
+ store_credential ${DOMAIN}
+}
+
+main(){
+ if [ "${SQL_USER}" != '' ] && [ "${SQL_PASS}" != '' ] && [ "${SQL_DB}" != '' ]; then
+ specify_setup_main
+ else
+ auto_setup_main
+ fi
+}
+
+check_input ${1}
+while [ ! -z "${1}" ]; do
+ case ${1} in
+ -[hH] | -help | --help)
+ help_message
+ ;;
+ -[dD] | -domain| --domain) shift
+ DOMAIN="${1}"
+ ;;
+ -[uU] | -user | --user) shift
+ SQL_USER="${1}"
+ ;;
+ -[pP] | -password| --password) shift
+ SQL_PASS="'${1}'"
+ ;;
+ -db | -DB | -database| --database) shift
+ SQL_DB="${1}"
+ ;;
+ *)
+ help_message
+ ;;
+ esac
+ shift
+done
+main
diff --git a/apps/openlitespeed/1.7.17-lsphp81/data/bin/demosite.sh b/apps/openlitespeed/1.7.17-lsphp81/data/bin/demosite.sh
new file mode 100644
index 00000000..91474647
--- /dev/null
+++ b/apps/openlitespeed/1.7.17-lsphp81/data/bin/demosite.sh
@@ -0,0 +1,102 @@
+#!/usr/bin/env bash
+source .env
+APP_NAME='wordpress'
+CONT_NAME='litespeed'
+DOC_FD=''
+
+echow(){
+ FLAG=${1}
+ shift
+ echo -e "\033[1m${EPACE}${FLAG}\033[0m${@}"
+}
+
+help_message(){
+ case ${1} in
+ "1")
+ echow "Script will get 'DOMAIN' and 'database' info from .env file, then auto setup virtual host and the wordpress site for you."
+ exit 0
+ ;;
+ "2")
+ echow 'Service finished, enjoy your accelarated LiteSpeed server!'
+ ;;
+ esac
+}
+
+domain_filter(){
+ if [ ! -n "${DOMAIN}" ]; then
+ echo "Parameters not supplied, please check!"
+ exit 1
+ fi
+ DOMAIN="${1}"
+ DOMAIN="${DOMAIN#http://}"
+ DOMAIN="${DOMAIN#https://}"
+ DOMAIN="${DOMAIN#ftp://}"
+ DOMAIN="${DOMAIN#scp://}"
+ DOMAIN="${DOMAIN#scp://}"
+ DOMAIN="${DOMAIN#sftp://}"
+ DOMAIN=${DOMAIN%%/*}
+}
+
+gen_root_fd(){
+ DOC_FD="./sites/${1}/"
+ if [ -d "./sites/${1}" ]; then
+ echo -e "[O] The root folder \033[32m${DOC_FD}\033[0m exist."
+ else
+ echo "Creating - document root."
+ bash bin/domain.sh -add ${1}
+ echo "Finished - document root."
+ fi
+}
+
+create_db(){
+ if [ ! -n "${MYSQL_DATABASE}" ] || [ ! -n "${MYSQL_USER}" ] || [ ! -n "${MYSQL_PASSWORD}" ]; then
+ echo "Parameters not supplied, please check!"
+ exit 1
+ else
+ bash bin/database.sh -D ${1} -U ${MYSQL_USER} -P ${MYSQL_PASSWORD} -DB ${MYSQL_DATABASE}
+ fi
+}
+
+store_credential(){
+ if [ -f ${DOC_FD}/.db_pass ]; then
+ echo '[O] db file exist!'
+ else
+ echo 'Storing database parameter'
+ cat > "${DOC_FD}/.db_pass" << EOT
+"Database":"${MYSQL_DATABASE}"
+"Username":"${MYSQL_USER}"
+"Password":"$(echo ${MYSQL_PASSWORD} | tr -d "'")"
+EOT
+ fi
+}
+
+app_download(){
+ docker compose exec -T ${CONT_NAME} su -c "appinstallctl.sh --app ${1} --domain ${2}"
+}
+
+lsws_restart(){
+ bash bin/webadmin.sh -r
+}
+
+main(){
+ domain_filter ${DOMAIN}
+ gen_root_fd ${DOMAIN}
+ create_db ${DOMAIN}
+ store_credential
+ app_download ${APP_NAME} ${DOMAIN}
+ lsws_restart
+ help_message 2
+}
+
+while [ ! -z "${1}" ]; do
+ case ${1} in
+ -[hH] | -help | --help)
+ help_message 1
+ ;;
+ *)
+ help_message 1
+ ;;
+ esac
+ shift
+done
+main
\ No newline at end of file
diff --git a/apps/openlitespeed/1.7.17-lsphp81/data/bin/dev/list-flagged-files.sh b/apps/openlitespeed/1.7.17-lsphp81/data/bin/dev/list-flagged-files.sh
new file mode 100644
index 00000000..bde4f25f
--- /dev/null
+++ b/apps/openlitespeed/1.7.17-lsphp81/data/bin/dev/list-flagged-files.sh
@@ -0,0 +1,2 @@
+#!/usr/bin/env bash
+git ls-files -v|grep '^S'
diff --git a/apps/openlitespeed/1.7.17-lsphp81/data/bin/dev/no-skip-worktree-conf.sh b/apps/openlitespeed/1.7.17-lsphp81/data/bin/dev/no-skip-worktree-conf.sh
new file mode 100644
index 00000000..9bf83e7c
--- /dev/null
+++ b/apps/openlitespeed/1.7.17-lsphp81/data/bin/dev/no-skip-worktree-conf.sh
@@ -0,0 +1,2 @@
+#!/usr/bin/env bash
+find conf -maxdepth 1 -type d \( ! -name . \) -exec bash -c "cd '{}' && pwd && git ls-files -z ${pwd} | xargs -0 git update-index --no-skip-worktree" \;
diff --git a/apps/openlitespeed/1.7.17-lsphp81/data/bin/dev/skip-worktree-conf.sh b/apps/openlitespeed/1.7.17-lsphp81/data/bin/dev/skip-worktree-conf.sh
new file mode 100644
index 00000000..18b16184
--- /dev/null
+++ b/apps/openlitespeed/1.7.17-lsphp81/data/bin/dev/skip-worktree-conf.sh
@@ -0,0 +1,3 @@
+#!/usr/bin/env bash
+find conf -maxdepth 1 -type d \( ! -name . \) -exec bash -c "cd '{}' && pwd && git ls-files -z ${pwd} | xargs -0 git update-index --skip-worktree" \;
+
diff --git a/apps/openlitespeed/1.7.17-lsphp81/data/bin/domain.sh b/apps/openlitespeed/1.7.17-lsphp81/data/bin/domain.sh
new file mode 100644
index 00000000..d99dca40
--- /dev/null
+++ b/apps/openlitespeed/1.7.17-lsphp81/data/bin/domain.sh
@@ -0,0 +1,61 @@
+#!/usr/bin/env bash
+CONT_NAME='litespeed'
+EPACE=' '
+
+echow(){
+ FLAG=${1}
+ shift
+ echo -e "\033[1m${EPACE}${FLAG}\033[0m${@}"
+}
+
+help_message(){
+ echo -e "\033[1mOPTIONS\033[0m"
+ echow "-A, --add [domain_name]"
+ echo "${EPACE}${EPACE}Example: domain.sh -A example.com, will add the domain to Listener and auto create a new virtual host."
+ echow "-D, --del [domain_name]"
+ echo "${EPACE}${EPACE}Example: domain.sh -D example.com, will delete the domain from Listener."
+ echow '-H, --help'
+ echo "${EPACE}${EPACE}Display help and exit."
+}
+
+check_input(){
+ if [ -z "${1}" ]; then
+ help_message
+ exit 1
+ fi
+}
+
+add_domain(){
+ check_input ${1}
+ docker compose exec ${CONT_NAME} su -s /bin/bash lsadm -c "cd /usr/local/lsws/conf && domainctl.sh --add ${1}"
+ if [ ! -d "./sites/${1}" ]; then
+ mkdir -p ./sites/${1}/{html,logs,certs}
+ fi
+ bash bin/webadmin.sh -r
+}
+
+del_domain(){
+ check_input ${1}
+ docker compose exec ${CONT_NAME} su -s /bin/bash lsadm -c "cd /usr/local/lsws/conf && domainctl.sh --del ${1}"
+ bash bin/webadmin.sh -r
+}
+
+check_input ${1}
+while [ ! -z "${1}" ]; do
+ case ${1} in
+ -[hH] | -help | --help)
+ help_message
+ ;;
+ -[aA] | -add | --add) shift
+ add_domain ${1}
+ ;;
+ -[dD] | -del | --del | --delete) shift
+ del_domain ${1}
+ ;;
+ *)
+ help_message
+ ;;
+ esac
+ shift
+done
+
\ No newline at end of file
diff --git a/apps/openlitespeed/1.7.17-lsphp81/data/bin/webadmin.sh b/apps/openlitespeed/1.7.17-lsphp81/data/bin/webadmin.sh
new file mode 100644
index 00000000..18e108ec
--- /dev/null
+++ b/apps/openlitespeed/1.7.17-lsphp81/data/bin/webadmin.sh
@@ -0,0 +1,98 @@
+#!/usr/bin/env bash
+CONT_NAME='litespeed'
+EPACE=' '
+
+echow(){
+ FLAG=${1}
+ shift
+ echo -e "\033[1m${EPACE}${FLAG}\033[0m${@}"
+}
+
+help_message(){
+ echo -e "\033[1mOPTIONS\033[0m"
+ echow '[Enter Your PASSWORD]'
+ echo "${EPACE}${EPACE}Example: webadmin.sh MY_SECURE_PASS, to update web admin password immediatly."
+ echow '-R, --restart'
+ echo "${EPACE}${EPACE}Will gracefully restart LiteSpeed Web Server."
+ echow '-M, --mod-secure [enable|disable]'
+ echo "${EPACE}${EPACE}Example: webadmin.sh -M enable, will enable and apply Mod_Secure OWASP rules on server"
+ echow '-U, --upgrade'
+ echo "${EPACE}${EPACE}Will upgrade web server to latest stable version"
+ echow '-S, --serial [YOUR_SERIAL|TRIAL]'
+ echo "${EPACE}${EPACE}Will apply your serial number to LiteSpeed Web Server."
+ echow '-H, --help'
+ echo "${EPACE}${EPACE}Display help and exit."
+ exit 0
+}
+
+check_input(){
+ if [ -z "${1}" ]; then
+ help_message
+ exit 1
+ fi
+}
+
+lsws_restart(){
+ docker compose exec -T ${CONT_NAME} su -c '/usr/local/lsws/bin/lswsctrl restart >/dev/null'
+}
+
+apply_serial(){
+ docker compose exec ${CONT_NAME} su -c "serialctl.sh --serial ${1}"
+ lsws_restart
+}
+
+mod_secure(){
+ if [ "${1}" = 'enable' ] || [ "${1}" = 'Enable' ]; then
+ docker compose exec ${CONT_NAME} su -s /bin/bash root -c "owaspctl.sh --enable"
+ lsws_restart
+ elif [ "${1}" = 'disable' ] || [ "${1}" = 'Disable' ]; then
+ docker compose exec ${CONT_NAME} su -s /bin/bash root -c "owaspctl.sh --disable"
+ lsws_restart
+ else
+ help_message
+ fi
+}
+
+ls_upgrade(){
+ echo 'Upgrade web server to latest stable version.'
+ docker compose exec ${CONT_NAME} su -c '/usr/local/lsws/admin/misc/lsup.sh 2>/dev/null'
+}
+
+set_web_admin(){
+ echo 'Update web admin password.'
+ local LSADPATH='/usr/local/lsws/admin'
+ docker compose exec ${CONT_NAME} su -s /bin/bash lsadm -c \
+ 'if [ -e /usr/local/lsws/admin/fcgi-bin/admin_php ]; then \
+ echo "admin:$('${LSADPATH}'/fcgi-bin/admin_php -q '${LSADPATH}'/misc/htpasswd.php '${1}')" > '${LSADPATH}'/conf/htpasswd; \
+ else echo "admin:$('${LSADPATH}'/fcgi-bin/admin_php5 -q '${LSADPATH}'/misc/htpasswd.php '${1}')" > '${LSADPATH}'/conf/htpasswd; \
+ fi';
+}
+
+main(){
+ set_web_admin ${1}
+}
+
+check_input ${1}
+while [ ! -z "${1}" ]; do
+ case ${1} in
+ -[hH] | -help | --help)
+ help_message
+ ;;
+ -[rR] | -restart | --restart)
+ lsws_restart
+ ;;
+ -M | -mode-secure | --mod-secure) shift
+ mod_secure ${1}
+ ;;
+ -lsup | --lsup | --upgrade | -U) shift
+ ls_upgrade
+ ;;
+ -[sS] | -serial | --serial) shift
+ apply_serial ${1}
+ ;;
+ *)
+ main ${1}
+ ;;
+ esac
+ shift
+done
\ No newline at end of file
diff --git a/apps/openlitespeed/1.7.17-lsphp81/data/sites/.gitignore b/apps/openlitespeed/1.7.17-lsphp81/data/sites/.gitignore
new file mode 100644
index 00000000..d6b7ef32
--- /dev/null
+++ b/apps/openlitespeed/1.7.17-lsphp81/data/sites/.gitignore
@@ -0,0 +1,2 @@
+*
+!.gitignore
diff --git a/apps/openlitespeed/1.7.17-lsphp81/docker-compose.yml b/apps/openlitespeed/1.7.17-lsphp81/docker-compose.yml
new file mode 100644
index 00000000..0d66d457
--- /dev/null
+++ b/apps/openlitespeed/1.7.17-lsphp81/docker-compose.yml
@@ -0,0 +1,30 @@
+version: '3'
+services:
+ litespeed:
+ container_name: ${CONTAINER_NAME}
+ restart: always
+ networks:
+ - 1panel-network
+ logging:
+ driver: none
+ volumes:
+ - ${DATA_PATH}/lsws/conf:/usr/local/lsws/conf
+ - ${DATA_PATH}/lsws/admin-conf:/usr/local/lsws/admin/conf
+ - ${DATA_PATH}/bin/container:/usr/local/bin
+ - ${DATA_PATH}/sites:/var/www/vhosts/
+ - ${DATA_PATH}/acme:/root/.acme.sh/
+ - ${DATA_PATH}/logs:/usr/local/lsws/logs/
+ ports:
+ - "${PANEL_APP_PORT_HTTP}:80"
+ - "${PANEL_APP_PORT_HTTPS}:443"
+ - "${PANEL_APP_PORT_HTTPS}:443/udp"
+ - "${PANEL_APP_PORT_CONSOLE}:7080"
+ environment:
+ - TZ=${TIME_ZONE}
+ image: litespeedtech/openlitespeed:1.7.17-lsphp81
+ labels:
+ createdBy: "Apps"
+
+networks:
+ 1panel-network:
+ external: true
diff --git a/apps/openlitespeed/README.md b/apps/openlitespeed/README.md
new file mode 100644
index 00000000..53c7ffab
--- /dev/null
+++ b/apps/openlitespeed/README.md
@@ -0,0 +1,43 @@
+# 使用说明
+
+安装完成后,在容器功能界面,连接容器终端,执行以下命令创建管理员账户密码
+
+```
+/usr/local/lsws/admin/misc/admpass.sh
+```
+
+# 原始相关
+
+OpenLiteSpeed Web Server
+========
+
+Description
+--------
+
+OpenLiteSpeed is a high-performance, lightweight, open source HTTP server developed and copyrighted by
+LiteSpeed Technologies. Users are free to download, use, distribute, and modify OpenLiteSpeed and its
+source code in accordance with the precepts of the GPLv3 license.
+
+This is the official repository for OpenLiteSpeed's source code. It is maintained by LiteSpeed
+Technologies.
+
+Documentation
+--------
+
+Users can find all OpenLiteSpeed documentation on the [OpenLiteSpeed site](https://openlitespeed.org),
+but here are some quick links to important parts of the site:
+
+[Installation](https://openlitespeed.org/kb/category/installation/)
+
+[Configuration](https://openlitespeed.org/kb/category/configuration/)
+
+[Road map](https://openlitespeed.org/mediawiki/index.php/Road_Map)
+
+[Release log](https://openlitespeed.org/release-log/)
+
+Get in Touch
+--------
+
+OpenLiteSpeed has a [Google Group](https://groups.google.com/forum/#!forum/openlitespeed-development). If
+you find a bug, want to request new features, or just want to talk about OpenLiteSpeed, this is the place
+to do it.
diff --git a/apps/openlitespeed/data.yml b/apps/openlitespeed/data.yml
new file mode 100644
index 00000000..48c3a649
--- /dev/null
+++ b/apps/openlitespeed/data.yml
@@ -0,0 +1,20 @@
+name: OpenLiteSpeed
+tags:
+ - Web 服务器
+title: 一个高性能、轻量级、开源 的HTTP 服务器
+type: Web 服务器
+description: 一个高性能、轻量级、开源 的HTTP 服务器
+additionalProperties:
+ key: openlitespeed
+ name: OpenLiteSpeed
+ tags:
+ - Server
+ shortDescZh: 一个高性能、轻量级、开源 的HTTP 服务器
+ shortDescEn: A high-performance, lightweight, open source HTTP server
+ type: runtime
+ crossVersionUpdate: true
+ limit: 1
+ recommend: 3
+ website: https://openlitespeed.org
+ github: https://github.com/litespeedtech/openlitespeed
+ document: https://openlitespeed.org/#install
\ No newline at end of file
diff --git a/apps/openlitespeed/latest/.env.sample b/apps/openlitespeed/latest/.env.sample
new file mode 100644
index 00000000..0902e645
--- /dev/null
+++ b/apps/openlitespeed/latest/.env.sample
@@ -0,0 +1,6 @@
+CONTAINER_NAME="openlitespeed"
+DATA_PATH="./data"
+PANEL_APP_PORT_CONSOLE="40113"
+PANEL_APP_PORT_HTTP="80"
+PANEL_APP_PORT_HTTPS="443"
+TIME_ZONE="Asia/Shanghai"
diff --git a/apps/openlitespeed/latest/data.yml b/apps/openlitespeed/latest/data.yml
new file mode 100644
index 00000000..2c19e306
--- /dev/null
+++ b/apps/openlitespeed/latest/data.yml
@@ -0,0 +1,40 @@
+additionalProperties:
+ formFields:
+ - default: 80
+ edit: true
+ envKey: PANEL_APP_PORT_HTTP
+ labelEn: HTTP Port
+ labelZh: HTTP端口
+ required: true
+ rule: paramPort
+ type: number
+ - default: 443
+ edit: true
+ envKey: PANEL_APP_PORT_HTTPS
+ labelEn: HTTPS Port
+ labelZh: HTTPS端口
+ required: true
+ rule: paramPort
+ type: number
+ - default: 40113
+ edit: true
+ envKey: PANEL_APP_PORT_CONSOLE
+ labelEn: Console Port
+ labelZh: 控制台端口
+ required: true
+ rule: paramPort
+ type: number
+ - default: ./data
+ disabled: true
+ envKey: DATA_PATH
+ labelEn: Data folder path
+ labelZh: 数据文件夹路径
+ required: true
+ type: text
+ - default: Asia/Shanghai
+ edit: true
+ envKey: TIME_ZONE
+ labelEn: Time zone
+ labelZh: 时区
+ required: true
+ type: text
diff --git a/apps/openlitespeed/latest/data/bin/acme.sh b/apps/openlitespeed/latest/data/bin/acme.sh
new file mode 100644
index 00000000..2804e217
--- /dev/null
+++ b/apps/openlitespeed/latest/data/bin/acme.sh
@@ -0,0 +1,293 @@
+#!/usr/bin/env bash
+EMAIL=''
+NO_EMAIL=''
+DOMAIN=''
+INSTALL=''
+UNINSTALL=''
+TYPE=0
+CONT_NAME='litespeed'
+ACME_SRC='https://raw.githubusercontent.com/Neilpang/acme.sh/master/acme.sh'
+EPACE=' '
+RENEW=''
+RENEW_ALL=''
+FORCE=''
+REVOKE=''
+REMOVE=''
+
+echow(){
+ FLAG=${1}
+ shift
+ echo -e "\033[1m${EPACE}${FLAG}\033[0m${@}"
+}
+
+help_message(){
+ case ${1} in
+ "1")
+ echo 'You will need to install acme script at the first time.'
+ echo 'Please run acme.sh --install --email example@example.com'
+ ;;
+ "2")
+ echo -e "\033[1mOPTIONS\033[0m"
+ echow '-D, --domain [DOMAIN_NAME]'
+ echo "${EPACE}${EPACE}Example: acme.sh --domain example.com"
+ echo "${EPACE}${EPACE}will auto detect and apply for both example.com and www.example.com domains."
+ echow '-H, --help'
+ echo "${EPACE}${EPACE}Display help and exit."
+ echo -e "\033[1m Only for the First time\033[0m"
+ echow '--install --email [EMAIL_ADDR]'
+ echo "${EPACE}${EPACE}Will install ACME with the Email provided"
+ echow '-r, --renew'
+ echo "${EPACE}${EPACE}Renew a specific domain with -D or --domain parameter if posibile. To force renew, use -f parameter."
+ echow '-R, --renew-all'
+ echo "${EPACE}${EPACE}Renew all domains if possible. To force renew, use -f parameter."
+ echow '-f, -F, --force'
+ echo "${EPACE}${EPACE}Force renew for a specific domain or all domains."
+ echow '-v, --revoke'
+ echo "${EPACE}${EPACE}Revoke a domain."
+ echow '-V, --remove'
+ echo "${EPACE}${EPACE}Remove a domain."
+ exit 0
+ ;;
+ "3")
+ echo 'Please run acme.sh --domain [DOMAIN_NAME] to apply certificate'
+ exit 0
+ ;;
+ esac
+}
+
+check_input(){
+ if [ -z "${1}" ]; then
+ help_message 2
+ fi
+}
+
+domain_filter(){
+ if [ -z "${1}" ]; then
+ help_message 3
+ fi
+ DOMAIN="${1}"
+ DOMAIN="${DOMAIN#http://}"
+ DOMAIN="${DOMAIN#https://}"
+ DOMAIN="${DOMAIN#ftp://}"
+ DOMAIN="${DOMAIN#scp://}"
+ DOMAIN="${DOMAIN#scp://}"
+ DOMAIN="${DOMAIN#sftp://}"
+ DOMAIN=${DOMAIN%%/*}
+}
+
+email_filter(){
+ CKREG="^[a-z0-9!#\$%&'*+/=?^_\`{|}~-]+(\.[a-z0-9!#$%&'*+/=?^_\`{|}~-]+)*@([a-z0-9]([a-z0-9-]*[a-z0-9])?\.)+[a-z0-9]([a-z0-9-]*[a-z0-9])?\$"
+ if [[ "${1}" =~ ${CKREG} ]] ; then
+ echo -e "[O] The E-mail \033[32m${1}\033[0m is valid."
+ else
+ echo -e "[X] The E-mail \e[31m${1}\e[39m is invalid"
+ exit 1
+ fi
+}
+
+cert_hook(){
+ echo '[Start] Adding ACME hook'
+ docker compose exec ${CONT_NAME} su -s /bin/bash -c "certhookctl.sh"
+ echo '[End] Adding ACME hook'
+}
+
+www_domain(){
+ CHECK_WWW=$(echo ${1} | cut -c1-4)
+ if [[ ${CHECK_WWW} == www. ]] ; then
+ DOMAIN=$(echo ${1} | cut -c 5-)
+ else
+ DOMAIN=${1}
+ fi
+ WWW_DOMAIN="www.${DOMAIN}"
+}
+
+domain_verify(){
+ curl -Is http://${DOMAIN}/ | grep -i LiteSpeed > /dev/null 2>&1
+ if [ ${?} = 0 ]; then
+ echo -e "[O] The domain name \033[32m${DOMAIN}\033[0m is accessible."
+ TYPE=1
+ curl -Is http://${WWW_DOMAIN}/ | grep -i LiteSpeed > /dev/null 2>&1
+ if [ ${?} = 0 ]; then
+ echo -e "[O] The domain name \033[32m${WWW_DOMAIN}\033[0m is accessible."
+ TYPE=2
+ else
+ echo -e "[!] The domain name ${WWW_DOMAIN} is inaccessible."
+ fi
+ else
+ echo -e "[X] The domain name \e[31m${DOMAIN}\e[39m is inaccessible, please verify."
+ exit 1
+ fi
+}
+
+install_acme(){
+ echo '[Start] Install ACME'
+ if [ "${1}" = 'true' ]; then
+ docker compose exec litespeed su -c "cd; wget ${ACME_SRC}; chmod 755 acme.sh; \
+ ./acme.sh --install --cert-home ~/.acme.sh/certs; \
+ rm ~/acme.sh"
+ elif [ "${2}" != '' ]; then
+ email_filter "${2}"
+ docker compose exec litespeed su -c "cd; wget ${ACME_SRC}; chmod 755 acme.sh; \
+ ./acme.sh --install --cert-home ~/.acme.sh/certs --accountemail ${2}; \
+ rm ~/acme.sh"
+ else
+ help_message 1
+ exit 1
+ fi
+ echo '[End] Install ACME'
+}
+
+uninstall_acme(){
+ echo '[Start] Uninstall ACME'
+ docker compose exec ${CONT_NAME} su -c "~/.acme.sh/acme.sh --uninstall"
+ echo '[End] Uninstall ACME'
+ exit 0
+}
+
+check_acme(){
+ echo '[Start] Checking ACME'
+ docker compose exec ${CONT_NAME} su -c "test -f /root/.acme.sh/acme.sh"
+ if [ ${?} != 0 ]; then
+ install_acme "${NO_EMAIL}" "${EMAIL}"
+ cert_hook
+ help_message 3
+ fi
+ echo '[End] Checking ACME'
+}
+
+lsws_restart(){
+ docker compose exec ${CONT_NAME} su -c '/usr/local/lsws/bin/lswsctrl restart >/dev/null'
+}
+
+doc_root_verify(){
+ if [ "${DOC_ROOT}" = '' ]; then
+ DOC_PATH="/var/www/vhosts/${1}/html"
+ else
+ DOC_PATH="${DOC_ROOT}"
+ fi
+ docker compose exec ${CONT_NAME} su -c "[ -e ${DOC_PATH} ]"
+ if [ ${?} -eq 0 ]; then
+ echo -e "[O] The document root folder \033[32m${DOC_PATH}\033[0m does exist."
+ else
+ echo -e "[X] The document root folder \e[31m${DOC_PATH}\e[39m does not exist!"
+ exit 1
+ fi
+}
+
+install_cert(){
+ echo '[Start] Apply Lets Encrypt Certificate'
+ if [ ${TYPE} = 1 ]; then
+ docker compose exec ${CONT_NAME} su -c "/root/.acme.sh/acme.sh --issue -d ${1} -w ${DOC_PATH}"
+ elif [ ${TYPE} = 2 ]; then
+ docker compose exec ${CONT_NAME} su -c "/root/.acme.sh/acme.sh --issue -d ${1} -d www.${1} -w ${DOC_PATH}"
+ else
+ echo 'unknown Type!'
+ exit 2
+ fi
+ echo '[End] Apply Lets Encrypt Certificate'
+}
+
+renew_acme(){
+ echo '[Start] Renew ACME'
+ if [ "${FORCE}" = 'true' ]; then
+ docker compose exec ${CONT_NAME} su -c "~/.acme.sh/acme.sh --renew --domain ${1} --force"
+ else
+ docker compose exec ${CONT_NAME} su -c "~/.acme.sh/acme.sh --renew --domain ${1}"
+ fi
+ echo '[End] Renew ACME'
+ lsws_restart
+}
+
+renew_all_acme(){
+ echo '[Start] Renew all ACME'
+ if [ "${FORCE}" = 'true' ]; then
+ docker compose exec ${CONT_NAME} su -c "~/.acme.sh/acme.sh --renew-all --force"
+ else
+ docker compose exec ${CONT_NAME} su -c "~/.acme.sh/acme.sh --renew-all"
+ fi
+ echo '[End] Renew all ACME'
+ lsws_restart
+}
+
+revoke(){
+ echo '[Start] Revoke a domain'
+ docker compose exec ${CONT_NAME} su -c "~/.acme.sh/acme.sh --revoke --domain ${1}"
+ echo '[End] Revoke a domain'
+ lsws_restart
+}
+
+remove(){
+ echo '[Start] Remove a domain'
+ docker compose exec ${CONT_NAME} su -c "~/.acme.sh/acme.sh --remove --domain ${1}"
+ echo '[End] Remove a domain'
+ lsws_restart
+}
+
+main(){
+ if [ "${RENEW_ALL}" = 'true' ]; then
+ renew_all_acme
+ exit 0
+ elif [ "${RENEW}" = 'true' ]; then
+ renew_acme ${DOMAIN}
+ exit 0
+ elif [ "${REVOKE}" = 'true' ]; then
+ revoke ${DOMAIN}
+ exit 0
+ elif [ "${REMOVE}" = 'true' ]; then
+ remove ${DOMAIN}
+ exit 0
+ fi
+
+ check_acme
+ domain_filter ${DOMAIN}
+ www_domain ${DOMAIN}
+ domain_verify
+ doc_root_verify ${DOMAIN}
+ install_cert ${DOMAIN}
+ lsws_restart
+}
+
+check_input ${1}
+while [ ! -z "${1}" ]; do
+ case ${1} in
+ -[hH] | -help | --help)
+ help_message 2
+ ;;
+ -[dD] | -domain | --domain) shift
+ check_input "${1}"
+ DOMAIN="${1}"
+ ;;
+ -[iI] | --install )
+ INSTALL=true
+ ;;
+ -[uU] | --uninstall )
+ UNINSTALL=true
+ uninstall_acme
+ ;;
+ -[fF] | --force )
+ FORCE=true
+ ;;
+ -[r] | --renew )
+ RENEW=true
+ ;;
+ -[R] | --renew-all )
+ RENEW_ALL=true
+ ;;
+ -[v] | --revoke )
+ REVOKE=true
+ ;;
+ -[V] | --remove )
+ REMOVE=true
+ ;;
+ -[eE] | --email ) shift
+ check_input "${1}"
+ EMAIL="${1}"
+ ;;
+ *)
+ help_message 2
+ ;;
+ esac
+ shift
+done
+
+main
\ No newline at end of file
diff --git a/apps/openlitespeed/latest/data/bin/appinstall.sh b/apps/openlitespeed/latest/data/bin/appinstall.sh
new file mode 100644
index 00000000..ba67d52d
--- /dev/null
+++ b/apps/openlitespeed/latest/data/bin/appinstall.sh
@@ -0,0 +1,60 @@
+#!/usr/bin/env bash
+APP_NAME=''
+DOMAIN=''
+EPACE=' '
+
+echow(){
+ FLAG=${1}
+ shift
+ echo -e "\033[1m${EPACE}${FLAG}\033[0m${@}"
+}
+
+help_message(){
+ echo -e "\033[1mOPTIONS\033[0m"
+ echow '-A, --app [app_name] -D, --domain [DOMAIN_NAME]'
+ echo "${EPACE}${EPACE}Example: appinstall.sh -A wordpress -D example.com"
+ echo "${EPACE}${EPACE}Will install WordPress CMS under the example.com domain"
+ echow '-H, --help'
+ echo "${EPACE}${EPACE}Display help and exit."
+ exit 0
+}
+
+check_input(){
+ if [ -z "${1}" ]; then
+ help_message
+ exit 1
+ fi
+}
+
+app_download(){
+ docker compose exec litespeed su -c "appinstallctl.sh --app ${1} --domain ${2}"
+ bash bin/webadmin.sh -r
+ exit 0
+}
+
+main(){
+ app_download ${APP_NAME} ${DOMAIN}
+}
+
+check_input ${1}
+while [ ! -z "${1}" ]; do
+ case ${1} in
+ -[hH] | -help | --help)
+ help_message
+ ;;
+ -[aA] | -app | --app) shift
+ check_input "${1}"
+ APP_NAME="${1}"
+ ;;
+ -[dD] | -domain | --domain) shift
+ check_input "${1}"
+ DOMAIN="${1}"
+ ;;
+ *)
+ help_message
+ ;;
+ esac
+ shift
+done
+
+main
\ No newline at end of file
diff --git a/apps/openlitespeed/latest/data/bin/container/appinstallctl.sh b/apps/openlitespeed/latest/data/bin/container/appinstallctl.sh
new file mode 100644
index 00000000..d79c63f1
--- /dev/null
+++ b/apps/openlitespeed/latest/data/bin/container/appinstallctl.sh
@@ -0,0 +1,660 @@
+#!/bin/bash
+DEFAULT_VH_ROOT='/var/www/vhosts'
+VH_DOC_ROOT=''
+VHNAME=''
+APP_NAME=''
+DOMAIN=''
+WWW_UID=''
+WWW_GID=''
+WP_CONST_CONF=''
+PUB_IP=$(curl -s http://checkip.amazonaws.com)
+DB_HOST='mysql'
+PLUGINLIST="litespeed-cache.zip"
+THEME='twentytwenty'
+EPACE=' '
+
+echow(){
+ FLAG=${1}
+ shift
+ echo -e "\033[1m${EPACE}${FLAG}\033[0m${@}"
+}
+
+help_message(){
+ echo -e "\033[1mOPTIONS\033[0m"
+ echow '-A, -app [wordpress] -D, --domain [DOMAIN_NAME]'
+ echo "${EPACE}${EPACE}Example: appinstallctl.sh --app wordpress --domain example.com"
+ echow '-H, --help'
+ echo "${EPACE}${EPACE}Display help and exit."
+ exit 0
+}
+
+check_input(){
+ if [ -z "${1}" ]; then
+ help_message
+ exit 1
+ fi
+}
+
+linechange(){
+ LINENUM=$(grep -n "${1}" ${2} | cut -d: -f 1)
+ if [ -n "${LINENUM}" ] && [ "${LINENUM}" -eq "${LINENUM}" ] 2>/dev/null; then
+ sed -i "${LINENUM}d" ${2}
+ sed -i "${LINENUM}i${3}" ${2}
+ fi
+}
+
+ck_ed(){
+ if [ ! -f /bin/ed ]; then
+ echo "Install ed package.."
+ apt-get install ed -y > /dev/null 2>&1
+ fi
+}
+
+ck_unzip(){
+ if [ ! -f /usr/bin/unzip ]; then
+ echo "Install unzip package.."
+ apt-get install unzip -y > /dev/null 2>&1
+ fi
+}
+
+get_owner(){
+ WWW_UID=$(stat -c "%u" ${DEFAULT_VH_ROOT})
+ WWW_GID=$(stat -c "%g" ${DEFAULT_VH_ROOT})
+ if [ ${WWW_UID} -eq 0 ] || [ ${WWW_GID} -eq 0 ]; then
+ WWW_UID=1000
+ WWW_GID=1000
+ echo "Set owner to ${WWW_UID}"
+ fi
+}
+
+get_db_pass(){
+ if [ -f ${DEFAULT_VH_ROOT}/${1}/.db_pass ]; then
+ SQL_DB=$(grep -i Database ${VH_ROOT}/.db_pass | awk -F ':' '{print $2}' | tr -d '"')
+ SQL_USER=$(grep -i Username ${VH_ROOT}/.db_pass | awk -F ':' '{print $2}' | tr -d '"')
+ SQL_PASS=$(grep -i Password ${VH_ROOT}/.db_pass | awk -F ':' '{print $2}' | tr -d '"')
+ else
+ echo 'db pass file can not locate, skip wp-config pre-config.'
+ fi
+}
+
+set_vh_docroot(){
+ if [ "${VHNAME}" != '' ]; then
+ VH_ROOT="${DEFAULT_VH_ROOT}/${VHNAME}"
+ VH_DOC_ROOT="${DEFAULT_VH_ROOT}/${VHNAME}/html"
+ WP_CONST_CONF="${VH_DOC_ROOT}/wp-content/plugins/litespeed-cache/data/const.default.ini"
+ elif [ -d ${DEFAULT_VH_ROOT}/${1}/html ]; then
+ VH_ROOT="${DEFAULT_VH_ROOT}/${1}"
+ VH_DOC_ROOT="${DEFAULT_VH_ROOT}/${1}/html"
+ WP_CONST_CONF="${VH_DOC_ROOT}/wp-content/plugins/litespeed-cache/data/const.default.ini"
+ else
+ echo "${DEFAULT_VH_ROOT}/${1}/html does not exist, please add domain first! Abort!"
+ exit 1
+ fi
+}
+
+check_sql_native(){
+ local COUNTER=0
+ local LIMIT_NUM=100
+ until [ "$(curl -v mysql:3306 2>&1 | grep -i 'native\|Connected')" ]; do
+ echo "Counter: ${COUNTER}/${LIMIT_NUM}"
+ COUNTER=$((COUNTER+1))
+ if [ ${COUNTER} = 10 ]; then
+ echo '--- MySQL is starting, please wait... ---'
+ elif [ ${COUNTER} = ${LIMIT_NUM} ]; then
+ echo '--- MySQL is timeout, exit! ---'
+ exit 1
+ fi
+ sleep 1
+ done
+}
+
+install_wp_plugin(){
+ for PLUGIN in ${PLUGINLIST}; do
+ wget -q -P ${VH_DOC_ROOT}/wp-content/plugins/ https://downloads.wordpress.org/plugin/${PLUGIN}
+ if [ ${?} = 0 ]; then
+ ck_unzip
+ unzip -qq -o ${VH_DOC_ROOT}/wp-content/plugins/${PLUGIN} -d ${VH_DOC_ROOT}/wp-content/plugins/
+ else
+ echo "${PLUGINLIST} FAILED to download"
+ fi
+ done
+ rm -f ${VH_DOC_ROOT}/wp-content/plugins/*.zip
+}
+
+set_htaccess(){
+ if [ ! -f ${VH_DOC_ROOT}/.htaccess ]; then
+ touch ${VH_DOC_ROOT}/.htaccess
+ fi
+ cat << EOM > ${VH_DOC_ROOT}/.htaccess
+# BEGIN WordPress
+
+RewriteEngine On
+RewriteBase /
+RewriteRule ^index\.php$ - [L]
+RewriteCond %{REQUEST_FILENAME} !-f
+RewriteCond %{REQUEST_FILENAME} !-d
+RewriteRule . /index.php [L]
+
+# END WordPress
+EOM
+}
+
+get_theme_name(){
+ THEME_NAME=$(grep WP_DEFAULT_THEME ${VH_DOC_ROOT}/wp-includes/default-constants.php | grep -v '!' | awk -F "'" '{print $4}')
+ echo "${THEME_NAME}" | grep 'twenty' >/dev/null 2>&1
+ if [ ${?} = 0 ]; then
+ THEME="${THEME_NAME}"
+ fi
+}
+
+set_lscache(){
+ cat << EOM > "${WP_CONST_CONF}"
+;
+; This is the predefined default LSCWP configuration file
+;
+; All the keys and values please refer \`src/const.cls.php\`
+;
+; Comments start with \`;\`
+;
+;; -------------------------------------------------- ;;
+;; -------------- General ----------------- ;;
+;; -------------------------------------------------- ;;
+; O_AUTO_UPGRADE
+auto_upgrade = false
+; O_API_KEY
+api_key = ''
+; O_SERVER_IP
+server_ip = ''
+; O_NEWS
+news = false
+;; -------------------------------------------------- ;;
+;; -------------- Cache ----------------- ;;
+;; -------------------------------------------------- ;;
+cache-priv = true
+cache-commenter = true
+cache-rest = true
+cache-page_login = true
+cache-favicon = true
+cache-resources = true
+cache-browser = false
+cache-mobile = false
+cache-mobile_rules = 'Mobile
+Android
+Silk/
+Kindle
+BlackBerry
+Opera Mini
+Opera Mobi'
+cache-exc_useragents = ''
+cache-exc_cookies = ''
+cache-exc_qs = ''
+cache-exc_cat = ''
+cache-exc_tag = ''
+cache-force_uri = ''
+cache-force_pub_uri = ''
+cache-priv_uri = ''
+cache-exc = ''
+cache-exc_roles = ''
+cache-drop_qs = 'fbclid
+gclid
+utm*
+_ga'
+cache-ttl_pub = 604800
+cache-ttl_priv = 1800
+cache-ttl_frontpage = 604800
+cache-ttl_feed = 604800
+; O_CACHE_TTL_REST
+cache-ttl_rest = 604800
+cache-ttl_browser = 31557600
+cache-login_cookie = ''
+cache-vary_group = ''
+cache-ttl_status = '403 3600
+404 3600
+500 3600'
+;; -------------------------------------------------- ;;
+;; -------------- Purge ----------------- ;;
+;; -------------------------------------------------- ;;
+; O_PURGE_ON_UPGRADE
+purge-upgrade = true
+; O_PURGE_STALE
+purge-stale = true
+purge-post_all = false
+purge-post_f = true
+purge-post_h = true
+purge-post_p = true
+purge-post_pwrp = true
+purge-post_a = true
+purge-post_y = false
+purge-post_m = true
+purge-post_d = false
+purge-post_t = true
+purge-post_pt = true
+purge-timed_urls = ''
+purge-timed_urls_time = ''
+purge-hook_all = 'switch_theme
+wp_create_nav_menu
+wp_update_nav_menu
+wp_delete_nav_menu
+create_term
+edit_terms
+delete_term
+add_link
+edit_link
+delete_link'
+;; -------------------------------------------------- ;;
+;; -------------- ESI ----------------- ;;
+;; -------------------------------------------------- ;;
+; O_ESI
+esi = false
+; O_ESI_CACHE_ADMBAR
+esi-cache_admbar = true
+; O_ESI_CACHE_COMMFORM
+esi-cache_commform = true
+; O_ESI_NONCE
+esi-nonce = 'stats_nonce
+subscribe_nonce'
+;; -------------------------------------------------- ;;
+;; -------------- Utilities ----------------- ;;
+;; -------------------------------------------------- ;;
+util-heartbeat = true
+util-instant_click = false
+util-check_advcache = true
+util-no_https_vary = false
+;; -------------------------------------------------- ;;
+;; -------------- Debug ----------------- ;;
+;; -------------------------------------------------- ;;
+; O_DEBUG_DISABLE_ALL
+debug-disable_all = false
+; O_DEBUG
+debug = false
+; O_DEBUG_IPS
+debug-ips = '127.0.0.1'
+; O_DEBUG_LEVEL
+debug-level = false
+; O_DEBUG_FILESIZE
+debug-filesize = 3
+; O_DEBUG_COOKIE
+debug-cookie = false
+; O_DEBUG_COLLAPS_QS
+debug-collaps_qs = false
+; O_DEBUG_INC
+debug-inc = ''
+; O_DEBUG_EXC
+debug-exc = ''
+;; -------------------------------------------------- ;;
+;; -------------- DB Optm ----------------- ;;
+;; -------------------------------------------------- ;;
+; O_DB_OPTM_REVISIONS_MAX
+db_optm-revisions_max = 0
+; O_DB_OPTM_REVISIONS_AGE
+db_optm-revisions_age = 0
+;; -------------------------------------------------- ;;
+;; -------------- HTML Optm ----------------- ;;
+;; -------------------------------------------------- ;;
+; O_OPTM_CSS_MIN
+optm-css_min = false
+optm-css_inline_min = false
+; O_OPTM_CSS_COMB
+optm-css_comb = false
+optm-css_comb_priority = false
+; O_OPTM_CSS_HTTP2
+optm-css_http2 = false
+optm-css_exc = ''
+; O_OPTM_JS_MIN
+optm-js_min = false
+optm-js_inline_min = false
+; O_OPTM_JS_COMB
+optm-js_comb = false
+optm-js_comb_priority = false
+; O_OPTM_JS_HTTP2
+optm-js_http2 = false
+; O_OPTM_EXC_JQ
+optm-js_exc = ''
+optm-ttl = 604800
+optm-html_min = false
+optm-qs_rm = false
+optm-ggfonts_rm = false
+; O_OPTM_CSS_ASYNC
+optm-css_async = false
+; O_OPTM_CCSS_GEN
+optm-ccss_gen = true
+; O_OPTM_CCSS_ASYNC
+optm-ccss_async = true
+; O_OPTM_CSS_ASYNC_INLINE
+optm-css_async_inline = true
+; O_OPTM_CSS_FONT_DISPLAY
+optm-css_font_display = false
+; O_OPTM_JS_DEFER
+optm-js_defer = false
+; O_OPTM_JS_INLINE_DEFER
+optm-js_inline_defer = false
+optm-emoji_rm = false
+optm-exc_jq = true
+optm-ggfonts_async = false
+optm-max_size = 2
+optm-rm_comment = false
+optm-exc_roles = ''
+optm-ccss_con = ''
+optm-js_defer_exc = ''
+; O_OPTM_DNS_PREFETCH
+optm-dns_prefetch = ''
+; O_OPTM_DNS_PREFETCH_CTRL
+optm-dns_prefetch_ctrl = false
+optm-exc = ''
+; O_OPTM_CCSS_SEP_POSTTYPE
+optm-ccss_sep_posttype = ''
+; O_OPTM_CCSS_SEP_URI
+optm-ccss_sep_uri = ''
+;; -------------------------------------------------- ;;
+;; -------------- Object Cache ----------------- ;;
+;; -------------------------------------------------- ;;
+object = true
+object-kind = false
+;object-host = 'localhost'
+object-host = '/var/www/memcached.sock'
+;object-port = 11211
+cache_object_port = ''
+object-life = 360
+object-persistent = true
+object-admin = true
+object-transients = true
+object-db_id = 0
+object-user = ''
+object-pswd = ''
+object-global_groups = 'users
+userlogins
+usermeta
+user_meta
+site-transient
+site-options
+site-lookup
+blog-lookup
+blog-details
+rss
+global-posts
+blog-id-cache'
+object-non_persistent_groups = 'comment
+counts
+plugins
+wc_session_id'
+;; -------------------------------------------------- ;;
+;; -------------- Discussion ----------------- ;;
+;; -------------------------------------------------- ;;
+; O_DISCUSS_AVATAR_CACHE
+discuss-avatar_cache = false
+; O_DISCUSS_AVATAR_CRON
+discuss-avatar_cron = false
+; O_DISCUSS_AVATAR_CACHE_TTL
+discuss-avatar_cache_ttl = 604800
+;; -------------------------------------------------- ;;
+;; -------------- Media ----------------- ;;
+;; -------------------------------------------------- ;;
+; O_MEDIA_LAZY
+media-lazy = false
+; O_MEDIA_LAZY_PLACEHOLDER
+media-lazy_placeholder = ''
+; O_MEDIA_PLACEHOLDER_RESP
+media-placeholder_resp = false
+; O_MEDIA_PLACEHOLDER_RESP_COLOR
+media-placeholder_resp_color = '#cfd4db'
+; O_MEDIA_PLACEHOLDER_RESP_GENERATOR
+media-placeholder_resp_generator = false
+; O_MEDIA_PLACEHOLDER_RESP_SVG
+media-placeholder_resp_svg = ''
+; O_MEDIA_PLACEHOLDER_LQIP
+media-placeholder_lqip = false
+; O_MEDIA_PLACEHOLDER_LQIP_QUAL
+media-placeholder_lqip_qual = 4
+; O_MEDIA_PLACEHOLDER_RESP_ASYNC
+media-placeholder_resp_async = true
+; O_MEDIA_IFRAME_LAZY
+media-iframe_lazy = false
+; O_MEDIA_LAZYJS_INLINE
+media-lazyjs_inline = false
+; O_MEDIA_LAZY_EXC
+media-lazy_exc = ''
+; O_MEDIA_LAZY_CLS_EXC
+media-lazy_cls_exc = ''
+; O_MEDIA_LAZY_PARENT_CLS_EXC
+media-lazy_parent_cls_exc = ''
+; O_MEDIA_IFRAME_LAZY_CLS_EXC
+media-iframe_lazy_cls_exc = ''
+; O_MEDIA_IFRAME_LAZY_PARENT_CLS_EXC
+media-iframe_lazy_parent_cls_exc = ''
+; O_MEDIA_LAZY_URI_EXC
+media-lazy_uri_exc = ''
+;; -------------------------------------------------- ;;
+;; -------------- Image Optm ----------------- ;;
+;; -------------------------------------------------- ;;
+img_optm-auto = false
+img_optm-cron = true
+img_optm-ori = true
+img_optm-rm_bkup = false
+img_optm-webp = false
+img_optm-lossless = false
+img_optm-exif = false
+img_optm-webp_replace = false
+img_optm-webp_attr = 'img.src
+div.data-thumb
+img.data-src
+div.data-large_image
+img.retina_logo_url
+div.data-parallax-image
+video.poster'
+img_optm-webp_replace_srcset = false
+img_optm-jpg_quality = 82
+;; -------------------------------------------------- ;;
+;; -------------- Crawler ----------------- ;;
+;; -------------------------------------------------- ;;
+crawler = false
+crawler-inc_posts = true
+crawler-inc_pages = true
+crawler-inc_cats = true
+crawler-inc_tags = true
+crawler-exc_cpt = ''
+crawler-order_links = 'date_desc'
+crawler-usleep = 500
+crawler-run_duration = 400
+crawler-run_interval = 600
+crawler-crawl_interval = 302400
+crawler-threads = 3
+crawler-timeout = 30
+crawler-load_limit = 1
+; O_CRAWLER_SITEMAP
+crawler-sitemap = ''
+; O_CRAWLER_DROP_DOMAIN
+crawler-drop_domain = true
+crawler-roles = ''
+crawler-cookies = ''
+;; -------------------------------------------------- ;;
+;; -------------- Misc ----------------- ;;
+;; -------------------------------------------------- ;;
+; O_MISC_HTACCESS_FRONT
+misc-htaccess_front = ''
+; O_MISC_HTACCESS_BACK
+misc-htaccess_back = ''
+; O_MISC_HEARTBEAT_FRONT
+misc-heartbeat_front = false
+; O_MISC_HEARTBEAT_FRONT_TTL
+misc-heartbeat_front_ttl = 60
+; O_MISC_HEARTBEAT_BACK
+misc-heartbeat_back = false
+; O_MISC_HEARTBEAT_BACK_TTL
+misc-heartbeat_back_ttl = 60
+; O_MISC_HEARTBEAT_EDITOR
+misc-heartbeat_editor = false
+; O_MISC_HEARTBEAT_EDITOR_TTL
+misc-heartbeat_editor_ttl = 15
+;; -------------------------------------------------- ;;
+;; -------------- CDN ----------------- ;;
+;; -------------------------------------------------- ;;
+cdn = false
+cdn-ori = ''
+cdn-ori_dir = ''
+cdn-exc = ''
+cdn-remote_jq = false
+cdn-quic = false
+cdn-quic_email = ''
+cdn-quic_key = ''
+cdn-cloudflare = false
+cdn-cloudflare_email = ''
+cdn-cloudflare_key = ''
+cdn-cloudflare_name = ''
+cdn-cloudflare_zone = ''
+; \`cdn-mapping\` needs to be put in the end with a section tag
+;; -------------------------------------------------- ;;
+;; -------------- CDN 2 ----------------- ;;
+;; -------------------------------------------------- ;;
+; <------------ CDN Mapping Example BEGIN -------------------->
+; Need to keep the section tag \`[cdn-mapping]\` before list.
+;
+; NOTE 1) Need to set all child options to make all resources to be replaced without missing.
+; NOTE 2) \`url[n]\` option must have to enable the row setting of \`n\`.
+; NOTE 3) This section needs to be put in the end of this .ini file
+;
+; To enable the 2nd mapping record by default, please remove the \`;;\` in the related lines.
+[cdn-mapping]
+url[0] = ''
+inc_js[0] = true
+inc_css[0] = true
+inc_img[0] = true
+filetype[0] = '.aac
+.css
+.eot
+.gif
+.jpeg
+.js
+.jpg
+.less
+.mp3
+.mp4
+.ogg
+.otf
+.pdf
+.png
+.svg
+.ttf
+.woff'
+;;url[1] = 'https://2nd_CDN_url.com/'
+;;filetype[1] = '.webm'
+; <------------ CDN Mapping Example END ------------------>
+EOM
+
+ THEME_PATH="${VH_DOC_ROOT}/wp-content/themes/${THEME}"
+ if [ ! -f ${THEME_PATH}/functions.php ]; then
+ cat >> "${THEME_PATH}/functions.php" <>/dev/null 2>&1
+2i
+require_once( WP_CONTENT_DIR.'/../wp-admin/includes/plugin.php' );
+\$path = 'litespeed-cache/litespeed-cache.php' ;
+if (!is_plugin_active( \$path )) {
+ activate_plugin( \$path ) ;
+ rename( __FILE__ . '.bk', __FILE__ );
+}
+.
+w
+q
+END
+ fi
+}
+
+preinstall_wordpress(){
+ if [ "${VHNAME}" != '' ]; then
+ get_db_pass ${VHNAME}
+ else
+ get_db_pass ${DOMAIN}
+ fi
+ if [ ! -f ${VH_DOC_ROOT}/wp-config.php ] && [ -f ${VH_DOC_ROOT}/wp-config-sample.php ]; then
+ cp ${VH_DOC_ROOT}/wp-config-sample.php ${VH_DOC_ROOT}/wp-config.php
+ NEWDBPWD="define('DB_PASSWORD', '${SQL_PASS}');"
+ linechange 'DB_PASSWORD' ${VH_DOC_ROOT}/wp-config.php "${NEWDBPWD}"
+ NEWDBPWD="define('DB_USER', '${SQL_USER}');"
+ linechange 'DB_USER' ${VH_DOC_ROOT}/wp-config.php "${NEWDBPWD}"
+ NEWDBPWD="define('DB_NAME', '${SQL_DB}');"
+ linechange 'DB_NAME' ${VH_DOC_ROOT}/wp-config.php "${NEWDBPWD}"
+ #NEWDBPWD="define('DB_HOST', '${PUB_IP}');"
+ NEWDBPWD="define('DB_HOST', '${DB_HOST}');"
+ linechange 'DB_HOST' ${VH_DOC_ROOT}/wp-config.php "${NEWDBPWD}"
+ elif [ -f ${VH_DOC_ROOT}/wp-config.php ]; then
+ echo "${VH_DOC_ROOT}/wp-config.php already exist, exit !"
+ exit 1
+ else
+ echo 'Skip!'
+ exit 2
+ fi
+}
+
+app_wordpress_dl(){
+ if [ ! -f "${VH_DOC_ROOT}/wp-config.php" ] && [ ! -f "${VH_DOC_ROOT}/wp-config-sample.php" ]; then
+ wp core download \
+ --allow-root \
+ --quiet
+ else
+ echo 'wordpress already exist, abort!'
+ exit 1
+ fi
+}
+
+change_owner(){
+ if [ "${VHNAME}" != '' ]; then
+ chown -R ${WWW_UID}:${WWW_GID} ${DEFAULT_VH_ROOT}/${VHNAME}
+ else
+ chown -R ${WWW_UID}:${WWW_GID} ${DEFAULT_VH_ROOT}/${DOMAIN}
+ fi
+}
+
+main(){
+ set_vh_docroot ${DOMAIN}
+ get_owner
+ cd ${VH_DOC_ROOT}
+ if [ "${APP_NAME}" = 'wordpress' ] || [ "${APP_NAME}" = 'wp' ]; then
+ check_sql_native
+ app_wordpress_dl
+ preinstall_wordpress
+ install_wp_plugin
+ set_htaccess
+ get_theme_name
+ set_lscache
+ change_owner
+ exit 0
+ else
+ echo "APP: ${APP_NAME} not support, exit!"
+ exit 1
+ fi
+}
+
+check_input ${1}
+while [ ! -z "${1}" ]; do
+ case ${1} in
+ -[hH] | -help | --help)
+ help_message
+ ;;
+ -[aA] | -app | --app) shift
+ check_input "${1}"
+ APP_NAME="${1}"
+ ;;
+ -[dD] | -domain | --domain) shift
+ check_input "${1}"
+ DOMAIN="${1}"
+ ;;
+ -vhname | --vhname) shift
+ VHNAME="${1}"
+ ;;
+ *)
+ help_message
+ ;;
+ esac
+ shift
+done
+main
diff --git a/apps/openlitespeed/latest/data/bin/container/certhookctl.sh b/apps/openlitespeed/latest/data/bin/container/certhookctl.sh
new file mode 100644
index 00000000..18be0965
--- /dev/null
+++ b/apps/openlitespeed/latest/data/bin/container/certhookctl.sh
@@ -0,0 +1,18 @@
+#!/bin/bash
+BOTCRON='/var/spool/cron/crontabs/root'
+
+cert_hook(){
+ grep 'acme' ${BOTCRON} >/dev/null
+ if [ ${?} = 0 ]; then
+ grep 'lswsctrl' ${BOTCRON} >/dev/null
+ if [ ${?} = 0 ]; then
+ echo 'Hook already exist, skip!'
+ else
+ sed -i 's/--cron/--cron --renew-hook "\/usr\/local\/lsws\/bin\/lswsctrl restart"/g' ${BOTCRON}
+ fi
+ else
+ echo "[X] ${BOTCRON} does not exist, please check it later!"
+ fi
+}
+
+cert_hook
\ No newline at end of file
diff --git a/apps/openlitespeed/latest/data/bin/container/domainctl.sh b/apps/openlitespeed/latest/data/bin/container/domainctl.sh
new file mode 100644
index 00000000..75539ef0
--- /dev/null
+++ b/apps/openlitespeed/latest/data/bin/container/domainctl.sh
@@ -0,0 +1,160 @@
+#!/usr/bin/env bash
+CK_RESULT=''
+LSDIR='/usr/local/lsws'
+LS_HTTPD_CONF="${LSDIR}/conf/httpd_config.xml"
+OLS_HTTPD_CONF="${LSDIR}/conf/httpd_config.conf"
+EPACE=' '
+
+echow(){
+ FLAG=${1}
+ shift
+ echo -e "\033[1m${EPACE}${FLAG}\033[0m${@}"
+}
+
+help_message(){
+ echo -e "\033[1mOPTIONS\033[0m"
+ echow '-A, --add [DOMAIN_NAME]'
+ echo "${EPACE}${EPACE}Will add domain to listener and creat a virtual host from template"
+ echow '-D, --del [DOMAIN_NAME]'
+ echo "${EPACE}${EPACE}Will delete domain from listener"
+ echow '-H, --help'
+ echo "${EPACE}${EPACE}Display help."
+}
+
+check_lsv(){
+ if [ -f ${LSDIR}/bin/openlitespeed ]; then
+ LSV='openlitespeed'
+ elif [ -f ${LSDIR}/bin/litespeed ]; then
+ LSV='lsws'
+ else
+ echo 'Version not exist, abort!'
+ exit 1
+ fi
+}
+
+dot_escape(){
+ ESCAPE=$(echo ${1} | sed 's/\./\\./g')
+}
+
+check_duplicate(){
+ CK_RESULT=$(grep -E "${1}" ${2})
+}
+
+fst_match_line(){
+ FIRST_LINE_NUM=$(grep -n -m 1 ${1} ${2} | awk -F ':' '{print $1}')
+}
+fst_match_after(){
+ FIRST_NUM_AFTER=$(tail -n +${1} ${2} | grep -n -m 1 ${3} | awk -F ':' '{print $1}')
+}
+lst_match_line(){
+ fst_match_after ${1} ${2} ${3}
+ LAST_LINE_NUM=$((${FIRST_LINE_NUM}+${FIRST_NUM_AFTER}-1))
+}
+
+check_input(){
+ if [ -z "${1}" ]; then
+ help_message
+ exit 1
+ fi
+}
+
+check_www(){
+ CHECK_WWW=$(echo ${1} | cut -c1-4)
+ if [[ ${CHECK_WWW} == www. ]] ; then
+ echo 'www domain shoudnt be passed!'
+ exit 1
+ fi
+}
+
+www_domain(){
+ check_www ${1}
+ WWW_DOMAIN=$(echo www.${1})
+}
+
+add_ls_domain(){
+ fst_match_line 'docker.xml' ${LS_HTTPD_CONF}
+ NEWNUM=$((FIRST_LINE_NUM+2))
+ sed -i "${NEWNUM}i \ \ \ \ \ \ \n \ \ \ \ \ \ \ ${DOMAIN}\n \ \ \ \ \ \ \ ${DOMAIN},${WWW_DOMAIN}\n \ \ \ \ \ \ " ${LS_HTTPD_CONF}
+}
+
+add_ols_domain(){
+ perl -0777 -p -i -e 's/(vhTemplate docker \{[^}]+)\}*(^.*listeners.*$)/\1$2
+ member '${DOMAIN}' {
+ vhDomain '${DOMAIN},${WWW_DOMAIN}'
+ }/gmi' ${OLS_HTTPD_CONF}
+}
+
+add_domain(){
+ check_lsv
+ dot_escape ${1}
+ DOMAIN=${ESCAPE}
+ www_domain ${1}
+ if [ "${LSV}" = 'lsws' ]; then
+ check_duplicate "vhDomain.*${DOMAIN}" ${LS_HTTPD_CONF}
+ if [ "${CK_RESULT}" != '' ]; then
+ echo "# It appears the domain already exist! Check the ${LS_HTTPD_CONF} if you believe this is a mistake!"
+ exit 1
+ fi
+ elif [ "${LSV}" = 'openlitespeed' ]; then
+ check_duplicate "member.*${DOMAIN}" ${OLS_HTTPD_CONF}
+ if [ "${CK_RESULT}" != '' ]; then
+ echo "# It appears the domain already exist! Check the ${OLS_HTTPD_CONF} if you believe this is a mistake!"
+ exit 1
+ fi
+ fi
+ add_ls_domain
+ add_ols_domain
+}
+
+del_ls_domain(){
+ fst_match_line "*${1}" ${LS_HTTPD_CONF}
+ FIRST_LINE_NUM=$((FIRST_LINE_NUM-1))
+ lst_match_line ${FIRST_LINE_NUM} ${LS_HTTPD_CONF} ''
+ sed -i "${FIRST_LINE_NUM},${LAST_LINE_NUM}d" ${LS_HTTPD_CONF}
+}
+
+del_ols_domain(){
+ fst_match_line ${1} ${OLS_HTTPD_CONF}
+ lst_match_line ${FIRST_LINE_NUM} ${OLS_HTTPD_CONF} '}'
+ sed -i "${FIRST_LINE_NUM},${LAST_LINE_NUM}d" ${OLS_HTTPD_CONF}
+}
+
+del_domain(){
+ check_lsv
+ dot_escape ${1}
+ DOMAIN=${ESCAPE}
+ if [ "${LSV}" = 'lsws' ]; then
+ check_duplicate "vhDomain.*${DOMAIN}" ${LS_HTTPD_CONF}
+ if [ "${CK_RESULT}" = '' ]; then
+ echo "# Domain non-exist! Check the ${LS_HTTPD_CONF} if you believe this is a mistake!"
+ exit 1
+ fi
+ elif [ "${LSV}" = 'openlitespeed' ]; then
+ check_duplicate "member.*${DOMAIN}" ${OLS_HTTPD_CONF}
+ if [ "${CK_RESULT}" = '' ]; then
+ echo "# Domain non-exist! Check the ${OLS_HTTPD_CONF} if you believe this is a mistake!"
+ exit 1
+ fi
+ fi
+ del_ls_domain ${1}
+ del_ols_domain ${1}
+}
+
+check_input ${1}
+while [ ! -z "${1}" ]; do
+ case ${1} in
+ -[hH] | -help | --help)
+ help_message
+ ;;
+ -[aA] | -add | --add) shift
+ add_domain ${1}
+ ;;
+ -[dD] | -del | --del | --delete) shift
+ del_domain ${1}
+ ;;
+ *)
+ help_message
+ ;;
+ esac
+ shift
+done
\ No newline at end of file
diff --git a/apps/openlitespeed/latest/data/bin/container/owaspctl.sh b/apps/openlitespeed/latest/data/bin/container/owaspctl.sh
new file mode 100644
index 00000000..72fd8e59
--- /dev/null
+++ b/apps/openlitespeed/latest/data/bin/container/owaspctl.sh
@@ -0,0 +1,236 @@
+#!/bin/bash
+LSDIR='/usr/local/lsws'
+OWASP_DIR="${LSDIR}/conf/owasp"
+RULE_FILE='modsec_includes.conf'
+LS_HTTPD_CONF="${LSDIR}/conf/httpd_config.xml"
+OLS_HTTPD_CONF="${LSDIR}/conf/httpd_config.conf"
+EPACE=' '
+OWASP_V='3.3.4'
+
+echow(){
+ FLAG=${1}
+ shift
+ echo -e "\033[1m${EPACE}${FLAG}\033[0m${@}"
+}
+
+help_message(){
+ echo -e "\033[1mOPTIONS\033[0m"
+ echow '-E, --enable'
+ echo "${EPACE}${EPACE}Will Enable mod_secure module with latest OWASP version of rules"
+ echow '-D, --disable'
+ echo "${EPACE}${EPACE}Will Disable mod_secure module with latest OWASP version of rules"
+ echow '-H, --help'
+ echo "${EPACE}${EPACE}Display help and exit."
+ exit 0
+}
+
+check_lsv(){
+ if [ -f ${LSDIR}/bin/openlitespeed ]; then
+ LSV='openlitespeed'
+ elif [ -f ${LSDIR}/bin/litespeed ]; then
+ LSV='lsws'
+ else
+ echo 'Version not exist, abort!'
+ exit 1
+ fi
+}
+
+check_input(){
+ if [ -z "${1}" ]; then
+ help_message
+ exit 1
+ fi
+}
+
+mk_owasp_dir(){
+ if [ -d ${OWASP_DIR} ] ; then
+ rm -rf ${OWASP_DIR}
+ fi
+ mkdir -p ${OWASP_DIR}
+ if [ ${?} -ne 0 ] ; then
+ echo "Unable to create directory: ${OWASP_DIR}, exit!"
+ exit 1
+ fi
+}
+
+fst_match_line(){
+ FIRST_LINE_NUM=$(grep -n -m 1 "${1}" ${2} | awk -F ':' '{print $1}')
+}
+fst_match_after(){
+ FIRST_NUM_AFTER=$(tail -n +${1} ${2} | grep -n -m 1 ${3} | awk -F ':' '{print $1}')
+}
+lst_match_line(){
+ fst_match_after ${1} ${2} ${3}
+ LAST_LINE_NUM=$((${FIRST_LINE_NUM}+${FIRST_NUM_AFTER}-1))
+}
+
+enable_ols_modsec(){
+ grep 'module mod_security {' ${OLS_HTTPD_CONF} >/dev/null 2>&1
+ if [ ${?} -eq 0 ] ; then
+ echo "Already configured for modsecurity."
+ else
+ echo 'Enable modsecurity'
+ sed -i "s=module cache=module mod_security {\nmodsecurity on\
+ \nmodsecurity_rules \`\nSecRuleEngine On\n\`\nmodsecurity_rules_file \
+ ${OWASP_DIR}/${RULE_FILE}\n ls_enabled 1\n}\
+ \n\nmodule cache=" ${OLS_HTTPD_CONF}
+ fi
+}
+
+enable_ls_modsec(){
+ grep '1' ${LS_HTTPD_CONF} >/dev/null 2>&1
+ if [ ${?} -eq 0 ] ; then
+ echo "LSWS already configured for modsecurity"
+ else
+ echo 'Enable modsecurity'
+ sed -i \
+ "s=0=1=" ${LS_HTTPD_CONF}
+ sed -i \
+ "s==\n\
+ \n\
+ ModSec\n\
+ 1\n\
+ include ${OWASP_DIR}/modsec_includes.conf\n\
+ =" ${LS_HTTPD_CONF}
+ fi
+}
+
+enable_modsec(){
+ if [ "${LSV}" = 'lsws' ]; then
+ enable_ls_modsec
+ elif [ "${LSV}" = 'openlitespeed' ]; then
+ enable_ols_modsec
+ fi
+}
+
+disable_ols_modesec(){
+ grep 'module mod_security {' ${OLS_HTTPD_CONF} >/dev/null 2>&1
+ if [ ${?} -eq 0 ] ; then
+ echo 'Disable modsecurity'
+ fst_match_line 'module mod_security' ${OLS_HTTPD_CONF}
+ lst_match_line ${FIRST_LINE_NUM} ${OLS_HTTPD_CONF} '}'
+ sed -i "${FIRST_LINE_NUM},${LAST_LINE_NUM}d" ${OLS_HTTPD_CONF}
+ else
+ echo 'Already disabled for modsecurity'
+ fi
+}
+
+disable_ls_modesec(){
+ grep '0' ${LS_HTTPD_CONF}
+ if [ ${?} -eq 0 ] ; then
+ echo 'Already disabled for modsecurity'
+ else
+ echo 'Disable modsecurity'
+ sed -i \
+ "s=1=0=" ${LS_HTTPD_CONF}
+ fst_match_line 'censorshipRuleSet' ${LS_HTTPD_CONF}
+ lst_match_line ${FIRST_LINE_NUM} ${LS_HTTPD_CONF} '/censorshipRuleSet'
+ sed -i "${FIRST_LINE_NUM},${LAST_LINE_NUM}d" ${LS_HTTPD_CONF}
+ fi
+}
+
+disable_modsec(){
+ check_lsv
+ if [ "${LSV}" = 'lsws' ]; then
+ disable_ls_modesec
+ elif [ "${LSV}" = 'openlitespeed' ]; then
+ disable_ols_modesec
+ fi
+}
+
+install_unzip(){
+ if [ ! -f /usr/bin/unzip ]; then
+ echo 'Install Unzip'
+ apt update >/dev/null 2>&1
+ apt-get install unzip -y >/dev/null 2>&1
+ fi
+}
+
+install_owasp(){
+ cd ${OWASP_DIR}
+ echo 'Download OWASP rules'
+ wget -q https://github.com/coreruleset/coreruleset/archive/refs/tags/v${OWASP_V}.zip
+ unzip -qq v${OWASP_V}.zip
+ rm -f v${OWASP_V}.zip
+ mv coreruleset-* owasp-modsecurity-crs
+}
+
+configure_owasp(){
+ echo 'Config OWASP rules.'
+ cd ${OWASP_DIR}
+ echo "include modsecurity.conf
+include owasp-modsecurity-crs/crs-setup.conf
+include owasp-modsecurity-crs/rules/REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf
+include owasp-modsecurity-crs/rules/REQUEST-901-INITIALIZATION.conf
+include owasp-modsecurity-crs/rules/REQUEST-903.9001-DRUPAL-EXCLUSION-RULES.conf
+include owasp-modsecurity-crs/rules/REQUEST-903.9002-WORDPRESS-EXCLUSION-RULES.conf
+include owasp-modsecurity-crs/rules/REQUEST-903.9003-NEXTCLOUD-EXCLUSION-RULES.conf
+include owasp-modsecurity-crs/rules/REQUEST-903.9004-DOKUWIKI-EXCLUSION-RULES.conf
+include owasp-modsecurity-crs/rules/REQUEST-903.9005-CPANEL-EXCLUSION-RULES.conf
+include owasp-modsecurity-crs/rules/REQUEST-903.9006-XENFORO-EXCLUSION-RULES.conf
+include owasp-modsecurity-crs/rules/REQUEST-905-COMMON-EXCEPTIONS.conf
+include owasp-modsecurity-crs/rules/REQUEST-910-IP-REPUTATION.conf
+include owasp-modsecurity-crs/rules/REQUEST-911-METHOD-ENFORCEMENT.conf
+include owasp-modsecurity-crs/rules/REQUEST-912-DOS-PROTECTION.conf
+include owasp-modsecurity-crs/rules/REQUEST-913-SCANNER-DETECTION.conf
+include owasp-modsecurity-crs/rules/REQUEST-920-PROTOCOL-ENFORCEMENT.conf
+include owasp-modsecurity-crs/rules/REQUEST-921-PROTOCOL-ATTACK.conf
+include owasp-modsecurity-crs/rules/REQUEST-930-APPLICATION-ATTACK-LFI.conf
+include owasp-modsecurity-crs/rules/REQUEST-931-APPLICATION-ATTACK-RFI.conf
+include owasp-modsecurity-crs/rules/REQUEST-932-APPLICATION-ATTACK-RCE.conf
+include owasp-modsecurity-crs/rules/REQUEST-933-APPLICATION-ATTACK-PHP.conf
+include owasp-modsecurity-crs/rules/REQUEST-934-APPLICATION-ATTACK-NODEJS.conf
+include owasp-modsecurity-crs/rules/REQUEST-941-APPLICATION-ATTACK-XSS.conf
+include owasp-modsecurity-crs/rules/REQUEST-942-APPLICATION-ATTACK-SQLI.conf
+include owasp-modsecurity-crs/rules/REQUEST-943-APPLICATION-ATTACK-SESSION-FIXATION.conf
+include owasp-modsecurity-crs/rules/REQUEST-944-APPLICATION-ATTACK-JAVA.conf
+include owasp-modsecurity-crs/rules/REQUEST-949-BLOCKING-EVALUATION.conf
+include owasp-modsecurity-crs/rules/RESPONSE-950-DATA-LEAKAGES.conf
+include owasp-modsecurity-crs/rules/RESPONSE-951-DATA-LEAKAGES-SQL.conf
+include owasp-modsecurity-crs/rules/RESPONSE-952-DATA-LEAKAGES-JAVA.conf
+include owasp-modsecurity-crs/rules/RESPONSE-953-DATA-LEAKAGES-PHP.conf
+include owasp-modsecurity-crs/rules/RESPONSE-954-DATA-LEAKAGES-IIS.conf
+include owasp-modsecurity-crs/rules/RESPONSE-959-BLOCKING-EVALUATION.conf
+include owasp-modsecurity-crs/rules/RESPONSE-980-CORRELATION.conf
+include owasp-modsecurity-crs/rules/RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf">modsec_includes.conf
+ echo "SecRuleEngine On">modsecurity.conf
+ cd ${OWASP_DIR}/owasp-modsecurity-crs
+ if [ -f crs-setup.conf.example ]; then
+ mv crs-setup.conf.example crs-setup.conf
+ fi
+ cd ${OWASP_DIR}/owasp-modsecurity-crs/rules
+ if [ -f REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf.example ]; then
+ mv REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf.example REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf
+ fi
+ if [ -f RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf.example ]; then
+ mv RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf.example RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf
+ fi
+}
+
+main_owasp(){
+ mk_owasp_dir
+ install_unzip
+ install_owasp
+ configure_owasp
+ check_lsv
+ enable_modsec
+}
+
+check_input ${1}
+while [ ! -z "${1}" ]; do
+ case ${1} in
+ -[hH] | -help | --help)
+ help_message
+ ;;
+ -[eE] | -enable | --enable)
+ main_owasp
+ ;;
+ -[dD] | -disable | --disable)
+ disable_modsec
+ ;;
+ *)
+ help_message
+ ;;
+ esac
+ shift
+done
\ No newline at end of file
diff --git a/apps/openlitespeed/latest/data/bin/container/serialctl.sh b/apps/openlitespeed/latest/data/bin/container/serialctl.sh
new file mode 100644
index 00000000..42e312dc
--- /dev/null
+++ b/apps/openlitespeed/latest/data/bin/container/serialctl.sh
@@ -0,0 +1,84 @@
+#!/bin/bash
+LSDIR='/usr/local/lsws'
+EPACE=' '
+
+echow(){
+ FLAG=${1}
+ shift
+ echo -e "\033[1m${EPACE}${FLAG}\033[0m${@}"
+}
+
+help_message(){
+ echo -e "\033[1mOPTIONS\033[0m"
+ echow '-S, --serial [YOUR_SERIAL|TRIAL]'
+ echo "${EPACE}${EPACE}Will apply and register the serial to LSWS."
+ echow '-H, --help'
+ echo "${EPACE}${EPACE}Display help and exit."
+ exit 0
+}
+
+check_input(){
+ if [ -z "${1}" ]; then
+ help_message
+ exit 1
+ fi
+}
+
+backup_old(){
+ if [ -f ${1} ] && [ ! -f ${1}_old ]; then
+ mv ${1} ${1}_old
+ fi
+}
+
+detect_ols(){
+ if [ -e ${LSDIR}/bin/openlitespeed ]; then
+ echo '[X] Detect OpenLiteSpeed, abort!'
+ exit 1
+ fi
+}
+
+apply_serial(){
+ detect_ols
+ check_input ${1}
+ echo ${1} | grep -i 'trial' >/dev/null
+ if [ ${?} = 0 ]; then
+ echo 'Apply Trial License'
+ if [ ! -e ${LSDIR}/conf/serial.no ] && [ ! -e ${LSDIR}/conf/license.key ]; then
+ rm -f ${LSDIR}/conf/trial.key*
+ wget -P ${LSDIR}/conf -q http://license.litespeedtech.com/reseller/trial.key
+ echo 'Apply trial finished'
+ else
+ echo "Please backup and remove your existing license, apply abort!"
+ exit 1
+ fi
+ else
+ echo "Apply Serial number: ${1}"
+ backup_old ${LSDIR}/conf/serial.no
+ backup_old ${LSDIR}/conf/license.key
+ backup_old ${LSDIR}/conf/trial.key
+ echo "${1}" > ${LSDIR}/conf/serial.no
+ ${LSDIR}/bin/lshttpd -r
+ if [ -f ${LSDIR}/conf/license.key ]; then
+ echo '[O] Apply success'
+ else
+ echo '[X] Apply failed, please check!'
+ exit 1
+ fi
+ fi
+}
+
+check_input ${1}
+while [ ! -z "${1}" ]; do
+ case ${1} in
+ -[hH] | -help | --help)
+ help_message
+ ;;
+ -[sS] | -serial | --serial) shift
+ apply_serial "${1}"
+ ;;
+ *)
+ help_message
+ ;;
+ esac
+ shift
+done
\ No newline at end of file
diff --git a/apps/openlitespeed/latest/data/bin/database.sh b/apps/openlitespeed/latest/data/bin/database.sh
new file mode 100644
index 00000000..ec1462d4
--- /dev/null
+++ b/apps/openlitespeed/latest/data/bin/database.sh
@@ -0,0 +1,158 @@
+#!/usr/bin/env bash
+source .env
+
+DOMAIN=''
+SQL_DB=''
+SQL_USER=''
+SQL_PASS=''
+ANY="'%'"
+SET_OK=0
+EPACE=' '
+
+echow(){
+ FLAG=${1}
+ shift
+ echo -e "\033[1m${EPACE}${FLAG}\033[0m${@}"
+}
+
+help_message(){
+ echo -e "\033[1mOPTIONS\033[0m"
+ echow '-D, --domain [DOMAIN_NAME]'
+ echo "${EPACE}${EPACE}Example: database.sh -D example.com"
+ echo "${EPACE}${EPACE}Will auto generate Database/username/password for the domain"
+ echow '-D, --domain [DOMAIN_NAME] -U, --user [xxx] -P, --password [xxx] -DB, --database [xxx]'
+ echo "${EPACE}${EPACE}Example: database.sh -D example.com -U USERNAME -P PASSWORD -DB DATABASENAME"
+ echo "${EPACE}${EPACE}Will create Database/username/password by given"
+ echow '-H, --help'
+ echo "${EPACE}${EPACE}Display help and exit."
+ exit 0
+}
+
+check_input(){
+ if [ -z "${1}" ]; then
+ help_message
+ exit 1
+ fi
+}
+
+specify_name(){
+ check_input ${SQL_USER}
+ check_input ${SQL_PASS}
+ check_input ${SQL_DB}
+}
+
+auto_name(){
+ SQL_DB="${TRANSNAME}"
+ SQL_USER="${TRANSNAME}"
+ SQL_PASS="'${RANDOM_PASS}'"
+}
+
+gen_pass(){
+ RANDOM_PASS="$(openssl rand -base64 12)"
+}
+
+trans_name(){
+ TRANSNAME=$(echo ${1} | tr -d '.&&-')
+}
+
+display_credential(){
+ if [ ${SET_OK} = 0 ]; then
+ echo "Database: ${SQL_DB}"
+ echo "Username: ${SQL_USER}"
+ echo "Password: $(echo ${SQL_PASS} | tr -d "'")"
+ fi
+}
+
+store_credential(){
+ if [ -d "./sites/${1}" ]; then
+ if [ -f ./sites/${1}/.db_pass ]; then
+ mv ./sites/${1}/.db_pass ./sites/${1}/.db_pass.bk
+ fi
+ cat > "./sites/${1}/.db_pass" << EOT
+"Database":"${SQL_DB}"
+"Username":"${SQL_USER}"
+"Password":"$(echo ${SQL_PASS} | tr -d "'")"
+EOT
+ else
+ echo "./sites/${1} not found, abort credential store!"
+ fi
+}
+
+check_db_access(){
+ docker compose exec -T mysql su -c "mysql -uroot -p${MYSQL_ROOT_PASSWORD} -e 'status'" >/dev/null 2>&1
+ if [ ${?} != 0 ]; then
+ echo '[X] DB access failed, please check!'
+ exit 1
+ fi
+}
+
+check_db_exist(){
+ docker compose exec -T mysql su -c "test -e /var/lib/mysql/${1}"
+ if [ ${?} = 0 ]; then
+ echo "Database ${1} already exist, skip DB creation!"
+ exit 0
+ fi
+}
+
+db_setup(){
+ docker compose exec -T mysql su -c 'mysql -uroot -p${MYSQL_ROOT_PASSWORD} \
+ -e "CREATE DATABASE '${SQL_DB}';" \
+ -e "GRANT ALL PRIVILEGES ON '${SQL_DB}'.* TO '${SQL_USER}'@'${ANY}' IDENTIFIED BY '${SQL_PASS}';" \
+ -e "FLUSH PRIVILEGES;"'
+ SET_OK=${?}
+}
+
+auto_setup_main(){
+ check_input ${DOMAIN}
+ gen_pass
+ trans_name ${DOMAIN}
+ auto_name
+ check_db_exist ${SQL_DB}
+ check_db_access
+ db_setup
+ display_credential
+ store_credential ${DOMAIN}
+}
+
+specify_setup_main(){
+ specify_name
+ check_db_exist ${SQL_DB}
+ check_db_access
+ db_setup
+ display_credential
+ store_credential ${DOMAIN}
+}
+
+main(){
+ if [ "${SQL_USER}" != '' ] && [ "${SQL_PASS}" != '' ] && [ "${SQL_DB}" != '' ]; then
+ specify_setup_main
+ else
+ auto_setup_main
+ fi
+}
+
+check_input ${1}
+while [ ! -z "${1}" ]; do
+ case ${1} in
+ -[hH] | -help | --help)
+ help_message
+ ;;
+ -[dD] | -domain| --domain) shift
+ DOMAIN="${1}"
+ ;;
+ -[uU] | -user | --user) shift
+ SQL_USER="${1}"
+ ;;
+ -[pP] | -password| --password) shift
+ SQL_PASS="'${1}'"
+ ;;
+ -db | -DB | -database| --database) shift
+ SQL_DB="${1}"
+ ;;
+ *)
+ help_message
+ ;;
+ esac
+ shift
+done
+main
diff --git a/apps/openlitespeed/latest/data/bin/demosite.sh b/apps/openlitespeed/latest/data/bin/demosite.sh
new file mode 100644
index 00000000..91474647
--- /dev/null
+++ b/apps/openlitespeed/latest/data/bin/demosite.sh
@@ -0,0 +1,102 @@
+#!/usr/bin/env bash
+source .env
+APP_NAME='wordpress'
+CONT_NAME='litespeed'
+DOC_FD=''
+
+echow(){
+ FLAG=${1}
+ shift
+ echo -e "\033[1m${EPACE}${FLAG}\033[0m${@}"
+}
+
+help_message(){
+ case ${1} in
+ "1")
+ echow "Script will get 'DOMAIN' and 'database' info from .env file, then auto setup virtual host and the wordpress site for you."
+ exit 0
+ ;;
+ "2")
+ echow 'Service finished, enjoy your accelarated LiteSpeed server!'
+ ;;
+ esac
+}
+
+domain_filter(){
+ if [ ! -n "${DOMAIN}" ]; then
+ echo "Parameters not supplied, please check!"
+ exit 1
+ fi
+ DOMAIN="${1}"
+ DOMAIN="${DOMAIN#http://}"
+ DOMAIN="${DOMAIN#https://}"
+ DOMAIN="${DOMAIN#ftp://}"
+ DOMAIN="${DOMAIN#scp://}"
+ DOMAIN="${DOMAIN#scp://}"
+ DOMAIN="${DOMAIN#sftp://}"
+ DOMAIN=${DOMAIN%%/*}
+}
+
+gen_root_fd(){
+ DOC_FD="./sites/${1}/"
+ if [ -d "./sites/${1}" ]; then
+ echo -e "[O] The root folder \033[32m${DOC_FD}\033[0m exist."
+ else
+ echo "Creating - document root."
+ bash bin/domain.sh -add ${1}
+ echo "Finished - document root."
+ fi
+}
+
+create_db(){
+ if [ ! -n "${MYSQL_DATABASE}" ] || [ ! -n "${MYSQL_USER}" ] || [ ! -n "${MYSQL_PASSWORD}" ]; then
+ echo "Parameters not supplied, please check!"
+ exit 1
+ else
+ bash bin/database.sh -D ${1} -U ${MYSQL_USER} -P ${MYSQL_PASSWORD} -DB ${MYSQL_DATABASE}
+ fi
+}
+
+store_credential(){
+ if [ -f ${DOC_FD}/.db_pass ]; then
+ echo '[O] db file exist!'
+ else
+ echo 'Storing database parameter'
+ cat > "${DOC_FD}/.db_pass" << EOT
+"Database":"${MYSQL_DATABASE}"
+"Username":"${MYSQL_USER}"
+"Password":"$(echo ${MYSQL_PASSWORD} | tr -d "'")"
+EOT
+ fi
+}
+
+app_download(){
+ docker compose exec -T ${CONT_NAME} su -c "appinstallctl.sh --app ${1} --domain ${2}"
+}
+
+lsws_restart(){
+ bash bin/webadmin.sh -r
+}
+
+main(){
+ domain_filter ${DOMAIN}
+ gen_root_fd ${DOMAIN}
+ create_db ${DOMAIN}
+ store_credential
+ app_download ${APP_NAME} ${DOMAIN}
+ lsws_restart
+ help_message 2
+}
+
+while [ ! -z "${1}" ]; do
+ case ${1} in
+ -[hH] | -help | --help)
+ help_message 1
+ ;;
+ *)
+ help_message 1
+ ;;
+ esac
+ shift
+done
+main
\ No newline at end of file
diff --git a/apps/openlitespeed/latest/data/bin/dev/list-flagged-files.sh b/apps/openlitespeed/latest/data/bin/dev/list-flagged-files.sh
new file mode 100644
index 00000000..bde4f25f
--- /dev/null
+++ b/apps/openlitespeed/latest/data/bin/dev/list-flagged-files.sh
@@ -0,0 +1,2 @@
+#!/usr/bin/env bash
+git ls-files -v|grep '^S'
diff --git a/apps/openlitespeed/latest/data/bin/dev/no-skip-worktree-conf.sh b/apps/openlitespeed/latest/data/bin/dev/no-skip-worktree-conf.sh
new file mode 100644
index 00000000..9bf83e7c
--- /dev/null
+++ b/apps/openlitespeed/latest/data/bin/dev/no-skip-worktree-conf.sh
@@ -0,0 +1,2 @@
+#!/usr/bin/env bash
+find conf -maxdepth 1 -type d \( ! -name . \) -exec bash -c "cd '{}' && pwd && git ls-files -z ${pwd} | xargs -0 git update-index --no-skip-worktree" \;
diff --git a/apps/openlitespeed/latest/data/bin/dev/skip-worktree-conf.sh b/apps/openlitespeed/latest/data/bin/dev/skip-worktree-conf.sh
new file mode 100644
index 00000000..18b16184
--- /dev/null
+++ b/apps/openlitespeed/latest/data/bin/dev/skip-worktree-conf.sh
@@ -0,0 +1,3 @@
+#!/usr/bin/env bash
+find conf -maxdepth 1 -type d \( ! -name . \) -exec bash -c "cd '{}' && pwd && git ls-files -z ${pwd} | xargs -0 git update-index --skip-worktree" \;
+
diff --git a/apps/openlitespeed/latest/data/bin/domain.sh b/apps/openlitespeed/latest/data/bin/domain.sh
new file mode 100644
index 00000000..d99dca40
--- /dev/null
+++ b/apps/openlitespeed/latest/data/bin/domain.sh
@@ -0,0 +1,61 @@
+#!/usr/bin/env bash
+CONT_NAME='litespeed'
+EPACE=' '
+
+echow(){
+ FLAG=${1}
+ shift
+ echo -e "\033[1m${EPACE}${FLAG}\033[0m${@}"
+}
+
+help_message(){
+ echo -e "\033[1mOPTIONS\033[0m"
+ echow "-A, --add [domain_name]"
+ echo "${EPACE}${EPACE}Example: domain.sh -A example.com, will add the domain to Listener and auto create a new virtual host."
+ echow "-D, --del [domain_name]"
+ echo "${EPACE}${EPACE}Example: domain.sh -D example.com, will delete the domain from Listener."
+ echow '-H, --help'
+ echo "${EPACE}${EPACE}Display help and exit."
+}
+
+check_input(){
+ if [ -z "${1}" ]; then
+ help_message
+ exit 1
+ fi
+}
+
+add_domain(){
+ check_input ${1}
+ docker compose exec ${CONT_NAME} su -s /bin/bash lsadm -c "cd /usr/local/lsws/conf && domainctl.sh --add ${1}"
+ if [ ! -d "./sites/${1}" ]; then
+ mkdir -p ./sites/${1}/{html,logs,certs}
+ fi
+ bash bin/webadmin.sh -r
+}
+
+del_domain(){
+ check_input ${1}
+ docker compose exec ${CONT_NAME} su -s /bin/bash lsadm -c "cd /usr/local/lsws/conf && domainctl.sh --del ${1}"
+ bash bin/webadmin.sh -r
+}
+
+check_input ${1}
+while [ ! -z "${1}" ]; do
+ case ${1} in
+ -[hH] | -help | --help)
+ help_message
+ ;;
+ -[aA] | -add | --add) shift
+ add_domain ${1}
+ ;;
+ -[dD] | -del | --del | --delete) shift
+ del_domain ${1}
+ ;;
+ *)
+ help_message
+ ;;
+ esac
+ shift
+done
+
\ No newline at end of file
diff --git a/apps/openlitespeed/latest/data/bin/webadmin.sh b/apps/openlitespeed/latest/data/bin/webadmin.sh
new file mode 100644
index 00000000..18e108ec
--- /dev/null
+++ b/apps/openlitespeed/latest/data/bin/webadmin.sh
@@ -0,0 +1,98 @@
+#!/usr/bin/env bash
+CONT_NAME='litespeed'
+EPACE=' '
+
+echow(){
+ FLAG=${1}
+ shift
+ echo -e "\033[1m${EPACE}${FLAG}\033[0m${@}"
+}
+
+help_message(){
+ echo -e "\033[1mOPTIONS\033[0m"
+ echow '[Enter Your PASSWORD]'
+ echo "${EPACE}${EPACE}Example: webadmin.sh MY_SECURE_PASS, to update web admin password immediatly."
+ echow '-R, --restart'
+ echo "${EPACE}${EPACE}Will gracefully restart LiteSpeed Web Server."
+ echow '-M, --mod-secure [enable|disable]'
+ echo "${EPACE}${EPACE}Example: webadmin.sh -M enable, will enable and apply Mod_Secure OWASP rules on server"
+ echow '-U, --upgrade'
+ echo "${EPACE}${EPACE}Will upgrade web server to latest stable version"
+ echow '-S, --serial [YOUR_SERIAL|TRIAL]'
+ echo "${EPACE}${EPACE}Will apply your serial number to LiteSpeed Web Server."
+ echow '-H, --help'
+ echo "${EPACE}${EPACE}Display help and exit."
+ exit 0
+}
+
+check_input(){
+ if [ -z "${1}" ]; then
+ help_message
+ exit 1
+ fi
+}
+
+lsws_restart(){
+ docker compose exec -T ${CONT_NAME} su -c '/usr/local/lsws/bin/lswsctrl restart >/dev/null'
+}
+
+apply_serial(){
+ docker compose exec ${CONT_NAME} su -c "serialctl.sh --serial ${1}"
+ lsws_restart
+}
+
+mod_secure(){
+ if [ "${1}" = 'enable' ] || [ "${1}" = 'Enable' ]; then
+ docker compose exec ${CONT_NAME} su -s /bin/bash root -c "owaspctl.sh --enable"
+ lsws_restart
+ elif [ "${1}" = 'disable' ] || [ "${1}" = 'Disable' ]; then
+ docker compose exec ${CONT_NAME} su -s /bin/bash root -c "owaspctl.sh --disable"
+ lsws_restart
+ else
+ help_message
+ fi
+}
+
+ls_upgrade(){
+ echo 'Upgrade web server to latest stable version.'
+ docker compose exec ${CONT_NAME} su -c '/usr/local/lsws/admin/misc/lsup.sh 2>/dev/null'
+}
+
+set_web_admin(){
+ echo 'Update web admin password.'
+ local LSADPATH='/usr/local/lsws/admin'
+ docker compose exec ${CONT_NAME} su -s /bin/bash lsadm -c \
+ 'if [ -e /usr/local/lsws/admin/fcgi-bin/admin_php ]; then \
+ echo "admin:$('${LSADPATH}'/fcgi-bin/admin_php -q '${LSADPATH}'/misc/htpasswd.php '${1}')" > '${LSADPATH}'/conf/htpasswd; \
+ else echo "admin:$('${LSADPATH}'/fcgi-bin/admin_php5 -q '${LSADPATH}'/misc/htpasswd.php '${1}')" > '${LSADPATH}'/conf/htpasswd; \
+ fi';
+}
+
+main(){
+ set_web_admin ${1}
+}
+
+check_input ${1}
+while [ ! -z "${1}" ]; do
+ case ${1} in
+ -[hH] | -help | --help)
+ help_message
+ ;;
+ -[rR] | -restart | --restart)
+ lsws_restart
+ ;;
+ -M | -mode-secure | --mod-secure) shift
+ mod_secure ${1}
+ ;;
+ -lsup | --lsup | --upgrade | -U) shift
+ ls_upgrade
+ ;;
+ -[sS] | -serial | --serial) shift
+ apply_serial ${1}
+ ;;
+ *)
+ main ${1}
+ ;;
+ esac
+ shift
+done
\ No newline at end of file
diff --git a/apps/openlitespeed/latest/data/sites/.gitignore b/apps/openlitespeed/latest/data/sites/.gitignore
new file mode 100644
index 00000000..d6b7ef32
--- /dev/null
+++ b/apps/openlitespeed/latest/data/sites/.gitignore
@@ -0,0 +1,2 @@
+*
+!.gitignore
diff --git a/apps/openlitespeed/latest/docker-compose.yml b/apps/openlitespeed/latest/docker-compose.yml
new file mode 100644
index 00000000..d7977118
--- /dev/null
+++ b/apps/openlitespeed/latest/docker-compose.yml
@@ -0,0 +1,30 @@
+version: '3'
+services:
+ litespeed:
+ container_name: ${CONTAINER_NAME}
+ restart: always
+ networks:
+ - 1panel-network
+ logging:
+ driver: none
+ volumes:
+ - ${DATA_PATH}/lsws/conf:/usr/local/lsws/conf
+ - ${DATA_PATH}/lsws/admin-conf:/usr/local/lsws/admin/conf
+ - ${DATA_PATH}/bin/container:/usr/local/bin
+ - ${DATA_PATH}/sites:/var/www/vhosts/
+ - ${DATA_PATH}/acme:/root/.acme.sh/
+ - ${DATA_PATH}/logs:/usr/local/lsws/logs/
+ ports:
+ - "${PANEL_APP_PORT_HTTP}:80"
+ - "${PANEL_APP_PORT_HTTPS}:443"
+ - "${PANEL_APP_PORT_HTTPS}:443/udp"
+ - "${PANEL_APP_PORT_CONSOLE}:7080"
+ environment:
+ - TZ=${TIME_ZONE}
+ image: litespeedtech/openlitespeed:latest
+ labels:
+ createdBy: "Apps"
+
+networks:
+ 1panel-network:
+ external: true
diff --git a/apps/openlitespeed/logo.png b/apps/openlitespeed/logo.png
new file mode 100644
index 00000000..dba1cecc
Binary files /dev/null and b/apps/openlitespeed/logo.png differ
diff --git a/docs/app-list.png b/docs/app-list.png
index 406696ce..f46b1850 100644
Binary files a/docs/app-list.png and b/docs/app-list.png differ
diff --git a/docs/applist-20230810-2.png:Zone.Identifier b/docs/applist-20230810-2.png:Zone.Identifier
deleted file mode 100644
index a45e1ac4..00000000
--- a/docs/applist-20230810-2.png:Zone.Identifier
+++ /dev/null
@@ -1,2 +0,0 @@
-[ZoneTransfer]
-ZoneId=3