442 lines
13 KiB
Bash
Executable File
442 lines
13 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
if [ -v $OSHIPKA_PATH ]; then
|
|
echo "You need to specify OSHIPKA_PATH env variable"
|
|
exit 1
|
|
fi
|
|
|
|
echo "oshipka is at: $OSHIPKA_PATH"
|
|
|
|
#!/usr/bin/env bash
|
|
HELP="
|
|
Usage $0 [ bootstrap | model | db_migrate | db_upgrade | db_populate | db_recreate | db_purge_recreate | init | worker | web | venv | install | link | cert ]
|
|
|
|
bootstrap [PROJECT_PATH] Create a new project in PROJECT_PATH
|
|
init Install dev env
|
|
download_sensitive Download sensitive
|
|
|
|
model Create or update a model from files in webapp/view_models/*.yaml
|
|
db_migrate DB migration
|
|
db_upgrade DB upgrade to last migration
|
|
db_populate Populate db with data from data_static/ and populate.py
|
|
db_recreate Delete the database, recreate to latest migration
|
|
db_recreate_populate Same as db_recreate and populate
|
|
db_purge_recreate_populate Same as db_recreate_populate but also purge the migrations
|
|
|
|
translate Translations subcommand
|
|
|
|
ca Create oshipka certificate authority (Oshipka CA)
|
|
cert_dev [DOMAIN] Generate dev certificate signed by Oshipka CA
|
|
|
|
worker Start worker
|
|
web Start webapp
|
|
|
|
venv Init venv
|
|
install Install requirements
|
|
link Link dev oshipka
|
|
|
|
prod Run in prod
|
|
prod_install Install in prod
|
|
cert [DOMAIN] Install certificate
|
|
"
|
|
|
|
HELP_TRANSLATION="
|
|
USAGE ./manage.sh translate [extract|gen {lang}|compile|update]
|
|
|
|
extract Extract strings in files as defined in translations/babel.cfg
|
|
gen {lang} Init translations for {lang}
|
|
compile Compile all translations
|
|
update Use after a new extract - it may mark strings as fuzzy.
|
|
"
|
|
|
|
command_translate() {
|
|
shift
|
|
TRANSLATE_COMMAND=$1
|
|
shift
|
|
source venv/bin/activate
|
|
case "$TRANSLATE_COMMAND" in
|
|
extract) pybabel extract -F translations/babel.cfg -o translations/messages.pot .
|
|
;;
|
|
gen) pybabel init -i translations/messages.pot -d translations -l "$@"
|
|
;;
|
|
compile) pybabel compile -d translations
|
|
;;
|
|
update) pybabel update -i translations/messages.pot -d translations
|
|
;;
|
|
*) >&2 echo -e "${HELP_TRANSLATION}"
|
|
;;
|
|
esac
|
|
return $?
|
|
}
|
|
|
|
ca () {
|
|
if [ -f ${OSHIPKA_PATH}/ssl/oshipka_ca.pem ]; then
|
|
echo "Oshipka CA already exists"
|
|
exit 1;
|
|
fi
|
|
echo "Creating Oshipka CA..."
|
|
|
|
mkdir -p ${OSHIPKA_PATH}/ssl
|
|
cd ${OSHIPKA_PATH}/ssl || exit
|
|
openssl genrsa -out oshipka_ca.key 2048
|
|
openssl req -x509 -new -nodes -key oshipka_ca.key -sha256 -days 1825 -out oshipka_ca.pem -subj "/C=/ST=/L=/O=Oshipka Web Development CA/OU=/CN=oshipka_web_development_ca"
|
|
}
|
|
|
|
cert_dev () {
|
|
shift
|
|
DOMAIN=$1
|
|
|
|
if [ -f webapp/ssl/cert.crt ]; then
|
|
echo "Certificate already exists"
|
|
exit 1;
|
|
elif [ ! -f ${OSHIPKA_PATH}/ssl/oshipka_ca.key ]; then
|
|
echo "Oshipka CA not found, generating..."
|
|
ca
|
|
fi
|
|
|
|
if [ -z "${DOMAIN}" ]; then
|
|
DOMAIN=$(basename `pwd`)
|
|
fi
|
|
|
|
mkdir -p webapp/ssl
|
|
echo "Create CSR for ${DOMAIN}"
|
|
openssl genrsa -out "webapp/ssl/cert.key" 2048
|
|
openssl req -new -key "webapp/ssl/cert.key" -out "webapp/ssl/cert.csr" -subj "/C=/ST=/L=/O=Oshipka Web Development/OU=/CN=${DOMAIN}.localhost"
|
|
|
|
cat > "webapp/ssl/cert.ext" << EOF
|
|
authorityKeyIdentifier=keyid,issuer
|
|
basicConstraints=CA:FALSE
|
|
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
|
|
subjectAltName = @alt_names
|
|
|
|
[alt_names]
|
|
DNS.1 = ${DOMAIN}.localhost
|
|
EOF
|
|
|
|
echo "Create self-signed certificate"
|
|
openssl x509 -req -in "webapp/ssl/cert.csr" -CA ${OSHIPKA_PATH}/ssl/oshipka_ca.pem -CAkey ${OSHIPKA_PATH}/ssl/oshipka_ca.key -CAcreateserial -out "webapp/ssl/cert.crt" -days 825 -sha256 -extfile "webapp/ssl/cert.ext"
|
|
|
|
rm "webapp/ssl/cert.ext" "webapp/ssl/cert.csr"
|
|
}
|
|
|
|
|
|
worker () {
|
|
source venv/bin/activate
|
|
python worker.py
|
|
}
|
|
|
|
web () {
|
|
source venv/bin/activate
|
|
python run.py
|
|
}
|
|
|
|
init_apt() {
|
|
sudo apt-get update
|
|
sudo apt-get install -y build-essential libssl-dev libffi-dev python3-dev
|
|
}
|
|
|
|
init_venv() {
|
|
virtualenv -p python3 venv
|
|
}
|
|
|
|
install_reqs() {
|
|
source venv/bin/activate
|
|
pip3 install --upgrade pip --trusted-host pypi.org --trusted-host files.pythonhosted.org
|
|
pip install -r requirements.txt
|
|
}
|
|
|
|
link_dev_oshipka() {
|
|
source venv/bin/activate
|
|
pip install -e ${OSHIPKA_PATH}
|
|
pip install -e ${TWW_PATH}
|
|
}
|
|
|
|
download_sensitive() {
|
|
if [ ! -f sensitive.py ]; then
|
|
echo "File sensitive.py NOT FOUND"
|
|
if [ -f sensitive_dev.py ]; then
|
|
echo "Copying sensitive_dev for dev env"
|
|
cp sensitive_dev.py sensitive.py
|
|
else
|
|
exit 1;
|
|
fi
|
|
fi
|
|
}
|
|
|
|
init() {
|
|
init_apt
|
|
init_venv
|
|
install_reqs
|
|
link_dev_oshipka
|
|
mkdir -p data
|
|
db_upgrade
|
|
download_sensitive
|
|
db_populate
|
|
}
|
|
|
|
install_cert() {
|
|
PROJECT_DOMAIN=$1
|
|
sudo apt install certbot
|
|
sudo certbot certonly --authenticator standalone --installer nginx --pre-hook "service nginx stop" --post-hook "service nginx start" --redirect --agree-tos --no-eff-email --email danieltcv@gmail.com -d ${PROJECT_DOMAIN} --no-bootstrap
|
|
}
|
|
|
|
bootstrap() {
|
|
shift
|
|
PROJECT_PATH=$1
|
|
if [[ -z "$PROJECT_PATH" ]]; then
|
|
read -p "Enter project path: " PROJECT_PATH
|
|
fi
|
|
if [[ -z "$PROJECT_PATH" ]]; then
|
|
echo "ERROR: Specify project path"
|
|
exit 1
|
|
else
|
|
echo "INFO: Project path: $PROJECT_PATH"
|
|
PROJECT_PATH=`realpath $PROJECT_PATH`
|
|
echo "INFO: Absolute project path: $PROJECT_PATH"
|
|
if [[ "$PROJECT_PATH" == $OSHIPKA_PATH* ]]; then
|
|
echo "ERROR: Project path can't be inside this directory. Exiting..."
|
|
exit 1
|
|
fi
|
|
if [ -d $PROJECT_PATH ]; then
|
|
echo "ERROR: Project path exists. Please remove or specify another. Exiting..."
|
|
exit 1
|
|
else
|
|
echo "INFO: Project path doesn't exist, creating..."
|
|
mkdir -p $PROJECT_PATH
|
|
fi
|
|
fi
|
|
PROJECT_NAME=$(basename $PROJECT_PATH)
|
|
echo "INFO: Bootstrapping project $PROJECT_NAME..."
|
|
mkdir -p ${PROJECT_PATH}
|
|
cp -r ${OSHIPKA_PATH}/bootstrap/* ${PROJECT_PATH}/
|
|
cp ${OSHIPKA_PATH}/bootstrap/.gitignore ${PROJECT_PATH}/.gitignore
|
|
mkdir ${PROJECT_PATH}/data
|
|
cd ${PROJECT_PATH}
|
|
sed -i "s/PROJECT_NAME.localhost:5000/${PROJECT_NAME}.localhost:5000/" config.py
|
|
cert_dev
|
|
init_venv
|
|
link_dev_oshipka
|
|
source venv/bin/activate
|
|
model
|
|
python manager.py db init
|
|
python manager.py db migrate -m "001"
|
|
_post_migrate
|
|
python manager.py db upgrade
|
|
|
|
command_translate "" extract
|
|
command_translate "" gen en
|
|
# TODO: sed acts on a line-by-line
|
|
# sed -i 's/msgid "Project"/msgstr ${PROJECT_NAME}/' translations/en/LC_MESSAGES/messages.po
|
|
command_translate "" compile
|
|
|
|
git init .
|
|
git add .
|
|
git commit -m "Initial commit"
|
|
}
|
|
|
|
run_in_prod() {
|
|
shift
|
|
PORT=$1
|
|
source venv/bin/activate
|
|
gunicorn -w 4 -b 0.0.0.0:${PORT} run:app
|
|
}
|
|
|
|
prod_install() {
|
|
shift
|
|
if [ ! -f ${OSHIPKA_PATH}/provision/auto_dns/sensitive.py ]; then
|
|
echo "File ${OSHIPKA_PATH}/provision/auto_dns/sensitive.py NOT FOUND"
|
|
exit 1;
|
|
fi
|
|
sudo apt install -y nginx dnsutils
|
|
source venv/bin/activate
|
|
PROJECT_NAME=$(basename `pwd`)
|
|
echo "1/6 Generating service and config files..."
|
|
python "${OSHIPKA_PATH}/provision/prod_mgmt.py"
|
|
if [ -f "/etc/systemd/system/${PROJECT_NAME}.service" ]; then
|
|
echo "Service gunicorn for ${PROJECT_NAME} service exists."
|
|
systemctl status ${PROJECT_NAME}
|
|
else
|
|
echo "Installing '$PROJECT_NAME' gunicorn service"
|
|
sudo cp "${OSHIPKA_PATH}/provision/tmp/${PROJECT_NAME}.service" /etc/systemd/system/
|
|
sudo systemctl enable "${PROJECT_NAME}"
|
|
sudo systemctl start "${PROJECT_NAME}"
|
|
fi
|
|
|
|
echo "2/6 Installing '$PROJECT_NAME' worker service"
|
|
if [ -f "/etc/systemd/system/${PROJECT_NAME}_worker.service" ]; then
|
|
echo "Service worker for ${PROJECT_NAME} service exists."
|
|
systemctl status "${PROJECT_NAME}_worker"
|
|
else
|
|
sudo cp "${OSHIPKA_PATH}/provision/tmp/${PROJECT_NAME}_worker.service" /etc/systemd/system/
|
|
sudo systemctl enable "${PROJECT_NAME}_worker"
|
|
sudo systemctl start "${PROJECT_NAME}_worker"
|
|
fi
|
|
|
|
NGINX_CONFIG_FILE=$(basename `find $OSHIPKA_PATH/provision/tmp -name *.conf`)
|
|
DOMAIN=$(basename -s .conf $NGINX_CONFIG_FILE)
|
|
echo "3/6 Installing '$DOMAIN' domain..."
|
|
python "${OSHIPKA_PATH}/provision/auto_dns/set_domain_ipv4.py" "$DOMAIN"
|
|
|
|
sudo systemctl start nginx
|
|
echo "Enabling firewall rule -> 80/tcp..."
|
|
sudo ufw allow proto tcp to any port 80
|
|
echo "4/6 Installing '$PROJECT_NAME' insecure nginx config..."
|
|
if [ -f "/etc/nginx/sites-available/${DOMAIN}.insecure" ]; then
|
|
echo "Insecure Nginx config for ${PROJECT_NAME} available."
|
|
if [ -f "/etc/nginx/sites-enabled/${DOMAIN}_insecure" ]; then
|
|
echo "Nginx config for ${PROJECT_NAME} enabled."
|
|
else
|
|
echo "Nginx config for ${PROJECT_NAME} NOT enabled."
|
|
fi
|
|
else
|
|
echo "Installing insecure nginx config for ${PROJECT_NAME} -> enabling + available."
|
|
sudo cp "${OSHIPKA_PATH}/provision/tmp/${DOMAIN}.insecure" /etc/nginx/sites-available/
|
|
sudo ln -s "/etc/nginx/sites-available/${DOMAIN}.insecure" "/etc/nginx/sites-enabled/${DOMAIN}.insecure"
|
|
sudo systemctl reload nginx
|
|
fi
|
|
|
|
echo "5/6 Installing '$PROJECT_NAME' certificate..."
|
|
install_cert $DOMAIN
|
|
|
|
echo "6/6 Installing '$PROJECT_NAME' secure nginx config..."
|
|
echo "Enabling firewall rule for 192.168.1.1 -> 443/tcp..."
|
|
sudo ufw allow proto tcp to any port 443
|
|
echo "Removing '$PROJECT_NAME' insecure nginx config..."
|
|
sudo rm "/etc/nginx/sites-available/${DOMAIN}.insecure" "/etc/nginx/sites-enabled/${DOMAIN}.insecure"
|
|
|
|
# PROBLEM: BIO_new_file("/etc/nginx/dhparam.pem") failed
|
|
# SOLUTION: sudo openssl dhparam -out /etc/nginx/dhparam.pem 2048
|
|
if [ ! -f "/etc/nginx/dhparam.pem" ]; then
|
|
sudo openssl dhparam -out /etc/nginx/dhparam.pem 2048
|
|
fi
|
|
|
|
if [ -f "/etc/nginx/sites-available/${NGINX_CONFIG_FILE}" ]; then
|
|
echo "Nginx config for ${PROJECT_NAME} available."
|
|
if [ -f "/etc/nginx/sites-enabled/${NGINX_CONFIG_FILE}" ]; then
|
|
echo "Nginx config for ${PROJECT_NAME} enabled."
|
|
else
|
|
echo "Nginx config for ${PROJECT_NAME} NOT enabled."
|
|
fi
|
|
else
|
|
echo "Installing nginx config for ${PROJECT_NAME} -> enabling + available."
|
|
sudo cp "${OSHIPKA_PATH}/provision/tmp/${NGINX_CONFIG_FILE}" /etc/nginx/sites-available/
|
|
sudo ln -s "/etc/nginx/sites-available/${NGINX_CONFIG_FILE}" "/etc/nginx/sites-enabled/${NGINX_CONFIG_FILE}"
|
|
sudo systemctl reload nginx
|
|
fi
|
|
|
|
# PROBLEM : Certificates missing
|
|
# SOLUTION: rm /etc/ssl/certs/ca-certificates.crt
|
|
# sudo update-ca-certificates in virtual environment.
|
|
}
|
|
|
|
model() {
|
|
shift
|
|
source venv/bin/activate
|
|
python "${OSHIPKA_PATH}/vm_gen/vm_gen.py" "`pwd`"
|
|
}
|
|
|
|
db_migrate() {
|
|
shift
|
|
source venv/bin/activate
|
|
next_id=$(printf "%03d" $(($(ls -la migrations/versions/*.py | wc -l)+1)))
|
|
python manager.py db migrate -m "${next_id}"
|
|
_post_migrate
|
|
}
|
|
|
|
db_upgrade() {
|
|
shift
|
|
source venv/bin/activate
|
|
mkdir -p data
|
|
python manager.py db upgrade
|
|
}
|
|
|
|
db_purge_recreate_populate() {
|
|
shift
|
|
source venv/bin/activate
|
|
rm -rf data/db.sqlite data/search_index migrations/ data/media
|
|
python manager.py db init
|
|
model
|
|
db_migrate
|
|
db_upgrade
|
|
db_populate
|
|
}
|
|
|
|
db_populate() {
|
|
shift
|
|
source venv/bin/activate
|
|
python init_populate.py
|
|
}
|
|
|
|
db_recreate() {
|
|
shift
|
|
source venv/bin/activate
|
|
rm -rf data/db.sqlite data/search_index
|
|
db_upgrade
|
|
}
|
|
|
|
db_recreate_populate() {
|
|
shift
|
|
db_recreate
|
|
db_populate
|
|
}
|
|
|
|
_post_migrate() {
|
|
for i in migrations/versions/*.py; do
|
|
sed -i "s/sqlalchemy_utils.types.choice.ChoiceType(length=255), /sa.String(), / " "$i";
|
|
sed -i "s/oshipka.persistance.LiberalBoolean(), /sa.Boolean(), / " "$i";
|
|
done
|
|
}
|
|
|
|
command_main() {
|
|
INITIAL_COMMAND=$1
|
|
case "$INITIAL_COMMAND" in
|
|
bootstrap) bootstrap "$@"
|
|
;;
|
|
model) model "$@"
|
|
;;
|
|
db_migrate) db_migrate "$@"
|
|
;;
|
|
db_upgrade) db_upgrade "$@"
|
|
;;
|
|
db_populate) db_populate "$@"
|
|
;;
|
|
db_recreate) db_recreate "$@"
|
|
;;
|
|
db_recreate_populate) db_recreate_populate "$@"
|
|
;;
|
|
db_purge_recreate_populate) db_purge_recreate_populate "$@"
|
|
;;
|
|
translate) command_translate "$@"
|
|
;;
|
|
init) init "$@"
|
|
;;
|
|
download_sensitive) download_sensitive "$@"
|
|
;;
|
|
worker) worker "$@"
|
|
;;
|
|
web) web "$@"
|
|
;;
|
|
venv) init_venv "$@"
|
|
;;
|
|
ca) ca "$@"
|
|
;;
|
|
cert_dev) cert_dev "$@"
|
|
;;
|
|
install) install_reqs "$@"
|
|
;;
|
|
link) link_dev_oshipka "$@"
|
|
;;
|
|
prod) run_in_prod "$@"
|
|
;;
|
|
prod_install) prod_install "$@"
|
|
;;
|
|
cert) shift && install_cert "$@"
|
|
;;
|
|
*) >&2 echo -e "${HELP}"
|
|
return 1
|
|
;;
|
|
esac
|
|
return $?
|
|
}
|
|
|
|
command_main "$@"
|