Files
WPIQ/wo/cli/plugins/site.py
VirtuBox 01ee8c0a13 v3.11.0 (#211)
- PHP 7.4 support
- Improved Webp images support with Cloudflare (Issue [#95](https://github.com/WordOps/WordOps/issues/95)). Nginx will not serve webp images alternative with Cloudflare IP ranges.
- Stack upgrade for adminer
- Check acme.sh installation and setup acme.sh if needed before issuing certificate
- Add `--ufw` to `wo stack status`
- Add Nginx directive `gzip_static on;` to serve precompressed assets with Cache-Enabler or WP-Rocket. (Issue [#207](https://github.com/WordOps/WordOps/issues/207))
- Previous `--php73` & `--php73=off` flags are replaced by `--php72`, `--php73`, `--php74` to switch site's php version
- phpMyAdmin updated to v4.9.2
- Adminer updated to v4.7.5
- Replace dot and dashes by underscores in database names (Issue [#206](https://github.com/WordOps/WordOps/issues/206))
- Increased database name length to 32 characters from domain name + 8 random characters
- typo error in motd-news script (Issue [#204](https://github.com/WordOps/WordOps/issues/204))
- Install Nginx before ngxblocker
- WordOps install/update script text color
- Issue with MySQL stack on Raspbian 9/10
- Typo error  (PR [#205](https://github.com/WordOps/WordOps/pull/205))
- php version in `wo debug` (PR [#209](https://github.com/WordOps/WordOps/pull/209))
- SSL certificates expiration display with shared wildcard certificates
2019-12-03 19:48:18 +01:00

491 lines
20 KiB
Python

import glob
import os
import subprocess
from cement.core.controller import CementBaseController, expose
from wo.cli.plugins.site_functions import *
from wo.cli.plugins.sitedb import (addNewSite, deleteSiteInfo, getAllsites,
getSiteInfo, updateSiteInfo)
from wo.cli.plugins.site_create import WOSiteCreateController
from wo.cli.plugins.site_update import WOSiteUpdateController
from wo.core.domainvalidate import WODomain
from wo.core.fileutils import WOFileUtils
from wo.core.git import WOGit
from wo.core.logging import Log
from wo.core.services import WOService
from wo.core.shellexec import WOShellExec, CommandExecutionError
from wo.core.sslutils import SSL
from wo.core.variables import WOVar
def wo_site_hook(app):
from wo.core.database import init_db
import wo.cli.plugins.models
init_db(app)
class WOSiteController(CementBaseController):
class Meta:
label = 'site'
stacked_on = 'base'
stacked_type = 'nested'
description = ('Performs website specific operations')
arguments = [
(['site_name'],
dict(help='Website name', nargs='?')),
]
usage = "wo site (command) <site_name> [options]"
@expose(hide=True)
def default(self):
self.app.args.print_help()
@expose(help="Enable site example.com")
def enable(self):
pargs = self.app.pargs
if not pargs.site_name:
try:
while not pargs.site_name:
pargs.site_name = (input('Enter site name : ')
.strip())
except IOError as e:
Log.debug(self, str(e))
Log.error(self, 'could not input site name')
pargs.site_name = pargs.site_name.strip()
# validate domain name
wo_domain = WODomain.validate(self, pargs.site_name)
# check if site exists
if not check_domain_exists(self, wo_domain):
Log.error(self, "site {0} does not exist".format(wo_domain))
if os.path.isfile('/etc/nginx/sites-available/{0}'
.format(wo_domain)):
Log.info(self, "Enable domain {0:10} \t".format(wo_domain), end='')
WOFileUtils.create_symlink(self,
['/etc/nginx/sites-available/{0}'
.format(wo_domain),
'/etc/nginx/sites-enabled/{0}'
.format(wo_domain)])
WOGit.add(self, ["/etc/nginx"],
msg="Enabled {0} "
.format(wo_domain))
updateSiteInfo(self, wo_domain, enabled=True)
Log.info(self, "[" + Log.ENDC + "OK" + Log.OKBLUE + "]")
if not WOService.reload_service(self, 'nginx'):
Log.error(self, "service nginx reload failed. "
"check issues with `nginx -t` command")
else:
Log.error(self, 'nginx configuration file does not exist')
@expose(help="Disable site example.com")
def disable(self):
pargs = self.app.pargs
if not pargs.site_name:
try:
while not pargs.site_name:
pargs.site_name = (input('Enter site name : ')
.strip())
except IOError as e:
Log.debug(self, str(e))
Log.error(self, 'could not input site name')
pargs.site_name = pargs.site_name.strip()
wo_domain = WODomain.validate(self, pargs.site_name)
# check if site exists
if not check_domain_exists(self, wo_domain):
Log.error(self, "site {0} does not exist".format(wo_domain))
if os.path.isfile('/etc/nginx/sites-available/{0}'
.format(wo_domain)):
Log.info(self, "Disable domain {0:10} \t"
.format(wo_domain), end='')
if not os.path.isfile('/etc/nginx/sites-enabled/{0}'
.format(wo_domain)):
Log.debug(self, "Site {0} already disabled".format(wo_domain))
Log.info(self, "[" + Log.FAIL + "Failed" + Log.OKBLUE + "]")
else:
WOFileUtils.remove_symlink(self,
'/etc/nginx/sites-enabled/{0}'
.format(wo_domain))
WOGit.add(self, ["/etc/nginx"],
msg="Disabled {0} "
.format(wo_domain))
updateSiteInfo(self, wo_domain, enabled=False)
Log.info(self, "[" + Log.ENDC + "OK" + Log.OKBLUE + "]")
if not WOService.reload_service(self, 'nginx'):
Log.error(self, "service nginx reload failed. "
"check issues with `nginx -t` command")
else:
Log.error(self, "nginx configuration file does not exist")
@expose(help="Get example.com information")
def info(self):
pargs = self.app.pargs
if not pargs.site_name:
try:
while not pargs.site_name:
pargs.site_name = (input('Enter site name : ')
.strip())
except IOError as e:
Log.debug(self, str(e))
Log.error(self, 'could not input site name')
pargs.site_name = pargs.site_name.strip()
wo_domain = WODomain.validate(self, pargs.site_name)
wo_www_domain = "www.{0}".format(wo_domain)
(wo_domain_type, wo_root_domain) = WODomain.getlevel(
self, wo_domain)
wo_db_name = ''
wo_db_user = ''
wo_db_pass = ''
if not check_domain_exists(self, wo_domain):
Log.error(self, "site {0} does not exist".format(wo_domain))
if os.path.isfile('/etc/nginx/sites-available/{0}'
.format(wo_domain)):
siteinfo = getSiteInfo(self, wo_domain)
sitetype = siteinfo.site_type
cachetype = siteinfo.cache_type
wo_site_webroot = siteinfo.site_path
access_log = (wo_site_webroot + '/logs/access.log')
error_log = (wo_site_webroot + '/logs/error.log')
wo_db_name = siteinfo.db_name
wo_db_user = siteinfo.db_user
wo_db_pass = siteinfo.db_password
wo_db_host = siteinfo.db_host
php_version = siteinfo.php_version
ssl = ("enabled" if siteinfo.is_ssl else "disabled")
if (ssl == "enabled"):
sslprovider = "Lets Encrypt"
sslexpiry = str(SSL.getexpirationdays(self, wo_domain))
else:
sslprovider = ''
sslexpiry = ''
data = dict(domain=wo_domain, webroot=wo_site_webroot,
accesslog=access_log, errorlog=error_log,
dbname=wo_db_name, dbuser=wo_db_user,
php_version=php_version,
dbpass=wo_db_pass,
ssl=ssl, sslprovider=sslprovider, sslexpiry=sslexpiry,
type=sitetype + " " + cachetype + " ({0})"
.format("enabled" if siteinfo.is_enabled else
"disabled"))
self.app.render((data), 'siteinfo.mustache')
else:
Log.error(self, "nginx configuration file does not exist")
@expose(help="Monitor example.com logs")
def log(self):
pargs = self.app.pargs
pargs.site_name = pargs.site_name.strip()
wo_domain = WODomain.validate(self, pargs.site_name)
wo_site_webroot = getSiteInfo(self, wo_domain).site_path
if not check_domain_exists(self, wo_domain):
Log.error(self, "site {0} does not exist".format(wo_domain))
logfiles = glob.glob(wo_site_webroot + '/logs/*.log')
if logfiles:
logwatch(self, logfiles)
@expose(help="Display Nginx configuration of example.com")
def show(self):
pargs = self.app.pargs
if not pargs.site_name:
try:
while not pargs.site_name:
pargs.site_name = (input('Enter site name : ')
.strip())
except IOError as e:
Log.debug(self, str(e))
Log.error(self, 'could not input site name')
# TODO Write code for wo site edit command here
pargs.site_name = pargs.site_name.strip()
wo_domain = WODomain.validate(self, pargs.site_name)
wo_www_domain = "www.{0}".format(wo_domain)
if not check_domain_exists(self, wo_domain):
Log.error(self, "site {0} does not exist".format(wo_domain))
if os.path.isfile('/etc/nginx/sites-available/{0}'
.format(wo_domain)):
Log.info(self, "Display NGINX configuration for {0}"
.format(wo_domain))
f = open('/etc/nginx/sites-available/{0}'.format(wo_domain),
encoding='utf-8', mode='r')
text = f.read()
Log.info(self, Log.ENDC + text)
f.close()
else:
Log.error(self, "nginx configuration file does not exists")
@expose(help="Change directory to site webroot")
def cd(self):
pargs = self.app.pargs
if not pargs.site_name:
try:
while not pargs.site_name:
pargs.site_name = (input('Enter site name : ')
.strip())
except IOError as e:
Log.debug(self, str(e))
Log.error(self, 'Unable to read input, please try again')
pargs.site_name = pargs.site_name.strip()
wo_domain = WODomain.validate(self, pargs.site_name)
if not check_domain_exists(self, wo_domain):
Log.error(self, "site {0} does not exist".format(wo_domain))
wo_site_webroot = getSiteInfo(self, wo_domain).site_path
if os.path.isdir(wo_site_webroot):
WOFileUtils.chdir(self, wo_site_webroot)
else:
Log.error(self, "unable to change directory")
class WOSiteEditController(CementBaseController):
class Meta:
label = 'edit'
stacked_on = 'site'
stacked_type = 'nested'
description = ('Edit Nginx configuration of site')
arguments = [
(['site_name'],
dict(help='domain name for the site',
nargs='?')),
]
@expose(hide=True)
def default(self):
pargs = self.app.pargs
if not pargs.site_name:
try:
while not pargs.site_name:
pargs.site_name = (input('Enter site name : ')
.strip())
except IOError as e:
Log.debug(self, str(e))
Log.error(self, 'Unable to read input, Please try again')
pargs.site_name = pargs.site_name.strip()
wo_domain = WODomain.validate(self, pargs.site_name)
wo_www_domain = "www.{0}".format(wo_domain)
if not check_domain_exists(self, wo_domain):
Log.error(self, "site {0} does not exist".format(wo_domain))
wo_site_webroot = WOVar.wo_webroot + wo_domain
if os.path.isfile('/etc/nginx/sites-available/{0}'
.format(wo_domain)):
try:
WOShellExec.invoke_editor(self, '/etc/nginx/sites-availa'
'ble/{0}'.format(wo_domain))
except CommandExecutionError as e:
Log.debug(self, str(e))
Log.error(self, "Failed invoke editor")
if (WOGit.checkfilestatus(self, "/etc/nginx",
'/etc/nginx/sites-available/{0}'
.format(wo_domain))):
WOGit.add(self, ["/etc/nginx"], msg="Edit website: {0}"
.format(wo_domain))
# Reload NGINX
if not WOService.reload_service(self, 'nginx'):
Log.error(self, "service nginx reload failed. "
"check issues with `nginx -t` command")
else:
Log.error(self, "nginx configuration file does not exists")
class WOSiteDeleteController(CementBaseController):
class Meta:
label = 'delete'
stacked_on = 'site'
stacked_type = 'nested'
description = 'delete an existing website'
arguments = [
(['site_name'],
dict(help='domain name to be deleted', nargs='?')),
(['--no-prompt'],
dict(help="doesnt ask permission for delete",
action='store_true')),
(['-f', '--force'],
dict(help="forcefully delete site and configuration",
action='store_true')),
(['--all'],
dict(help="delete files & db", action='store_true')),
(['--db'],
dict(help="delete db only", action='store_true')),
(['--files'],
dict(help="delete webroot only", action='store_true')),
]
@expose(help="Delete website configuration and files")
@expose(hide=True)
def default(self):
pargs = self.app.pargs
if not pargs.site_name and not pargs.all:
try:
while not pargs.site_name:
pargs.site_name = (input('Enter site name : ')
.strip())
except IOError as e:
Log.debug(self, str(e))
Log.error(self, 'could not input site name')
pargs.site_name = pargs.site_name.strip()
wo_domain = WODomain.validate(self, pargs.site_name)
wo_www_domain = "www.{0}".format(wo_domain)
wo_db_name = ''
wo_prompt = ''
wo_nginx_prompt = ''
mark_db_delete_prompt = False
mark_webroot_delete_prompt = False
mark_db_deleted = False
mark_webroot_deleted = False
if not check_domain_exists(self, wo_domain):
Log.error(self, "site {0} does not exist".format(wo_domain))
if ((not pargs.db) and (not pargs.files) and
(not pargs.all)):
pargs.all = True
if pargs.force:
pargs.no_prompt = True
# Gather information from wo-db for wo_domain
check_site = getSiteInfo(self, wo_domain)
wo_site_type = check_site.site_type
wo_site_webroot = check_site.site_path
if wo_site_webroot == 'deleted':
mark_webroot_deleted = True
if wo_site_type in ['mysql', 'wp', 'wpsubdir', 'wpsubdomain']:
wo_db_name = check_site.db_name
wo_db_user = check_site.db_user
if self.app.config.has_section('mysql'):
wo_mysql_grant_host = self.app.config.get(
'mysql', 'grant-host')
else:
wo_mysql_grant_host = 'localhost'
if wo_db_name == 'deleted':
mark_db_deleted = True
if pargs.all:
pargs.db = True
pargs.files = True
else:
if pargs.all:
mark_db_deleted = True
pargs.files = True
# Delete website database
if pargs.db:
if wo_db_name != 'deleted' and wo_db_name != '':
if not pargs.no_prompt:
wo_db_prompt = input('Are you sure, you want to delete'
' database [y/N]: ')
else:
wo_db_prompt = 'Y'
mark_db_delete_prompt = True
if wo_db_prompt == 'Y' or wo_db_prompt == 'y':
mark_db_delete_prompt = True
Log.info(self, "Deleting Database, {0}, user {1}"
.format(wo_db_name, wo_db_user))
deleteDB(self, wo_db_name, wo_db_user,
wo_mysql_grant_host, False)
updateSiteInfo(self, wo_domain,
db_name='deleted',
db_user='deleted',
db_password='deleted')
mark_db_deleted = True
Log.info(self, "Deleted Database successfully.")
else:
mark_db_deleted = True
Log.info(self, "Does not seems to have database for this site."
)
# Delete webroot
if pargs.files:
if wo_site_webroot != 'deleted':
if not pargs.no_prompt:
wo_web_prompt = input('Are you sure, you want to delete '
'webroot [y/N]: ')
else:
wo_web_prompt = 'Y'
mark_webroot_delete_prompt = True
if wo_web_prompt == 'Y' or wo_web_prompt == 'y':
mark_webroot_delete_prompt = True
Log.info(self, "Deleting Webroot, {0}"
.format(wo_site_webroot))
deleteWebRoot(self, wo_site_webroot)
updateSiteInfo(self, wo_domain, webroot='deleted')
mark_webroot_deleted = True
Log.info(self, "Deleted webroot successfully")
else:
mark_webroot_deleted = True
Log.info(self, "Webroot seems to be already deleted")
if not pargs.force:
if (mark_webroot_deleted and mark_db_deleted):
# TODO Delete nginx conf
removeNginxConf(self, wo_domain)
deleteSiteInfo(self, wo_domain)
Log.info(self, "Deleted site {0}".format(wo_domain))
# else:
# Log.error(self, " site {0} does
# not exists".format(wo_domain))
else:
if (mark_db_delete_prompt or mark_webroot_delete_prompt or
(mark_webroot_deleted and mark_db_deleted)):
# TODO Delete nginx conf
removeNginxConf(self, wo_domain)
deleteSiteInfo(self, wo_domain)
Log.info(self, "Deleted site {0}".format(wo_domain))
class WOSiteListController(CementBaseController):
class Meta:
label = 'list'
stacked_on = 'site'
stacked_type = 'nested'
description = 'List websites'
arguments = [
(['--enabled'],
dict(help='List enabled websites', action='store_true')),
(['--disabled'],
dict(help="List disabled websites", action='store_true')),
]
@expose(help="Lists websites")
def default(self):
pargs = self.app.pargs
sites = getAllsites(self)
if not sites:
pass
if pargs.enabled:
for site in sites:
if site.is_enabled:
Log.info(self, "{0}".format(site.sitename))
elif pargs.disabled:
for site in sites:
if not site.is_enabled:
Log.info(self, "{0}".format(site.sitename))
else:
for site in sites:
Log.info(self, "{0}".format(site.sitename))
def load(app):
# register the plugin class.. this only happens if the plugin is enabled
app.handler.register(WOSiteController)
app.handler.register(WOSiteDeleteController)
app.handler.register(WOSiteUpdateController)
app.handler.register(WOSiteCreateController)
app.handler.register(WOSiteListController)
app.handler.register(WOSiteEditController)
# register a hook (function) to run after arguments are parsed.
app.hook.register('post_argument_parsing', wo_site_hook)