server/bin/init.py
2025-02-28 16:46:45 -05:00

181 lines
5.1 KiB
Python
Executable File

#!/usr/bin/python3
## A simple tool to configure services and create sql users/databases
import os
import subprocess
import secrets
import argparse
# only allow aA-zZ, 0-9 and below "special" characters in passwords
ALLOWED_CHARS = [ord('!'), ord('@'), ord('#'), ord('%'), ord('&')]\
+ list(range(48,58))\
+ list(range(64,90))\
+ list(range(97,123))
# convert ints back to characters
ALLOWED_CHARS = [chr(c) for c in ALLOWED_CHARS]
def gen_pass(l):
"""gen_pass returns a password of length l from ALLOWED_CHARS"""
password = ""
for i in range(int(l)):
password += secrets.choice(ALLOWED_CHARS)
return password
def get_var(key):
"""get_var() gets user input to confirm/replace empty keys"""
while True:
user_input = input(
f"{key} is empty, enter value (leave blank for password):\n")
if user_input == "":
prompt = "provided value is empty, choose an option below\n"\
+ "\t1) generate password\n"\
+ "\t2) leave blank\n"\
+ "\t3) back\n"
choice = input(prompt)
while choice not in ["1","2","3"]:
print(f"error: {choice} unrecognized, please enter 1,2 or 3")
choice = input(prompt)
if choice == "1":
return gen_pass(32)
if choice == "2":
return user_input
if choice == "3":
continue
else:
# run until user confirms input
confirm = input(f"set {key} to {user_input} (y/N)?")
if confirm in ["y", "Y"]:
return user_input
def gen_sql_user(username, password, *databases):
"""gen_sql_user creates the sql queries to add a user and db with perms"""
sql = f"CREATE USER IF NOT EXISTS {username} IDENTIFIED BY '{password}';\n"
for db in databases:
sql += f"CREATE DATABASE {db};\n"
sql += f"GRANT ALL PRIVILEGES ON {db} TO {username};\n"
print(sql)
return sql
def gen_sql(sql):
"""gen_sql creates an init.sql file to be run by the database on first launch"""
if sql == "":
return
subprocess.run(["mkdir", "-p", "mariadb/priv/initdb.d"])
f = open(f"mariadb/priv/initdb.d/init.sql", 'w')
f.write(sql)
f.close()
def parse_env(env_file):
"""parse_env returns a dictionary of env vars parsed from the base file
:service: is a string of the service name to parse, must match folder name
"""
f = open(env_file)
c = dict()
for line in f:
# skip comments
if line[0] == "#":
continue
if line.isspace():
continue
kv = line.strip('\n').split("=")
key = kv[0]
val = kv[1].strip('"')
if val == "":
val = get_var(key)
c[key] = val
return c
def gen_env(kv):
"""gen_env takes in a dictionary and returns the formatted env file text as a string
:kv: is a dictionary of strings mapping env var to value
ex) "HOME": "/home/user/"
"""
env = ""
for key, value in kv.items():
env += f"{key}=\"{value}\"\n"
return env
def config_service(service, force=False):
"""config_service processes service env vars to generate private .env file
:service: is a string of the name of the folder to write the .env to
also requires file service/env to source base config from
:force: is an optional parameter to overwrite existing file
default is False
"""
# setup directories
subprocess.run(["mkdir", "-p", f"{service}/priv"])
priv_file = f"{service}/.env"
# prevent overwrite
if os.path.isfile(priv_file) and not force:
print(f"{priv_file} already exists... skipping")
return parse_env(priv_file)
c = parse_env(f"{service}/env")
f = open(priv_file, 'w')
f.write(gen_env(c))
f.close()
return c
def main():
parser = argparse.ArgumentParser(
description="takes in a service to configure a private .env file and generate sql users/databases")
parser.add_argument('service', nargs="+", help="service to configure env for")
parser.add_argument('-f', '--force', action="store_true", help="overwrite existing files")
args = parser.parse_args()
sql = ""
for service in args.service:
print(f"\nsetting up {service}...")
c = config_service(service, args.force)
# create mariadb users/dbs
if service == "gitea":
username = c["GITEA__database__USER"]
password = c["GITEA__database__PASSWD"]
db = c["GITEA__database__NAME"]
sql += gen_sql_user(username, password, db)
elif service == "seafile":
username = c["SEAFILE_MYSQL_DB_USER"]
password = c["SEAFILE_MYSQL_DB_PASSWORD"]
dbs = ["ccnet_db", "seafile_db", "seahub_db"]
sql += gen_sql_user(username, password, *dbs)
gen_sql(sql)
# finalize sql
print("running mariadb to initialize users/dbs. Ctrl+c to cancel after database is setup")
subprocess.run(["docker", "compose", "-f", f"mariadb/compose.yml", "up"])
print("success")
main()