manage.py: show commands before confirms, fold schema/fn into step 1, nginx guard
- Show exact commands that will be run before each confirm prompt - Step 1 dialog now offers schema and function deployment after writing .env - Steps 2/3 relabeled as 'Redeploy only' for standalone use - Option 5 (nginx) detects existing config and warns before overwriting - Option 1 menu label clarified as 'Database configuration and deployment dialog' Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
a3c7be61d0
commit
1edb998487
98
manage.py
98
manage.py
@ -59,6 +59,13 @@ def confirm(label, default_yes=True):
|
||||
return default_yes
|
||||
return val.startswith('y')
|
||||
|
||||
def show_commands(cmds):
|
||||
label = 'Command that will be run:' if len(cmds) == 1 else 'Commands that will be run:'
|
||||
print(f' {label}')
|
||||
for cmd in cmds:
|
||||
print(f' {dim(" ".join(str(c) for c in cmd))}')
|
||||
print()
|
||||
|
||||
def pause():
|
||||
input(f'\n {dim("Press Enter to continue...")}')
|
||||
|
||||
@ -226,10 +233,10 @@ def show_status(cfg):
|
||||
def action_configure(cfg):
|
||||
"""Write or update .env with database connection details."""
|
||||
if cfg:
|
||||
header(f'Edit database connection settings in {ENV_FILE}')
|
||||
header(f'Database configuration and deployment dialog — editing {ENV_FILE}')
|
||||
print(f' Current settings will be shown as defaults.')
|
||||
else:
|
||||
header(f'Create {ENV_FILE} with database connection settings')
|
||||
header(f'Database configuration and deployment dialog — creating {ENV_FILE}')
|
||||
print(f' {ENV_FILE} does not exist yet.')
|
||||
|
||||
print(f' If the target database does not exist or the user cannot connect,')
|
||||
@ -255,6 +262,10 @@ def action_configure(cfg):
|
||||
ok(f'Successfully connected to database "{new_cfg["DB_NAME"]}" on {new_cfg["DB_HOST"]}')
|
||||
else:
|
||||
warn(f'Cannot connect to database "{new_cfg["DB_NAME"]}" on {new_cfg["DB_HOST"]} with the provided credentials.')
|
||||
show_commands([
|
||||
['psql', '-U', '<admin>', '-h', new_cfg['DB_HOST'], '-p', new_cfg['DB_PORT'], '-d', 'postgres', '-c', f"CREATE USER {new_cfg['DB_USER']} ... (if user does not exist)"],
|
||||
['psql', '-U', '<admin>', '-h', new_cfg['DB_HOST'], '-p', new_cfg['DB_PORT'], '-d', 'postgres', '-c', f"CREATE DATABASE {new_cfg['DB_NAME']} ... (if database does not exist)"],
|
||||
])
|
||||
if not confirm(f'Use PostgreSQL admin credentials to create the database user and/or database?', default_yes=False):
|
||||
info(f'{ENV_FILE} was not written — no changes made')
|
||||
return cfg
|
||||
@ -310,6 +321,46 @@ def action_configure(cfg):
|
||||
print()
|
||||
write_env(new_cfg)
|
||||
ok(f'Settings written to {ENV_FILE}')
|
||||
|
||||
db_location = f'database "{new_cfg["DB_NAME"]}" on {new_cfg["DB_HOST"]}:{new_cfg["DB_PORT"]}'
|
||||
schema_file = ROOT / 'database' / 'schema.sql'
|
||||
functions_file = ROOT / 'database' / 'functions.sql'
|
||||
|
||||
# Offer schema deployment
|
||||
print()
|
||||
sd = schema_deployed(new_cfg)
|
||||
if sd:
|
||||
warn(f'"dataflow" schema already exists in {db_location}.')
|
||||
show_commands([['psql', '-U', new_cfg['DB_USER'], '-h', new_cfg['DB_HOST'], '-p', new_cfg['DB_PORT'], '-d', new_cfg['DB_NAME'], '-f', str(schema_file)]])
|
||||
if confirm(f'Redeploy "dataflow" schema? (will reset all data)', default_yes=False):
|
||||
print(f' Running {schema_file} against {db_location}...')
|
||||
r = psql_file(new_cfg, schema_file)
|
||||
if r.returncode == 0:
|
||||
ok(f'"dataflow" schema redeployed into {db_location}')
|
||||
else:
|
||||
err(f'Schema deployment failed:\n{r.stderr}')
|
||||
else:
|
||||
show_commands([['psql', '-U', new_cfg['DB_USER'], '-h', new_cfg['DB_HOST'], '-p', new_cfg['DB_PORT'], '-d', new_cfg['DB_NAME'], '-f', str(schema_file)]])
|
||||
if confirm(f'Deploy "dataflow" schema into {db_location}?', default_yes=False):
|
||||
print(f' Running {schema_file} against {db_location}...')
|
||||
r = psql_file(new_cfg, schema_file)
|
||||
if r.returncode == 0:
|
||||
ok(f'"dataflow" schema deployed into {db_location}')
|
||||
else:
|
||||
err(f'Schema deployment failed:\n{r.stderr}')
|
||||
return new_cfg
|
||||
|
||||
# Offer function deployment
|
||||
print()
|
||||
show_commands([['psql', '-U', new_cfg['DB_USER'], '-h', new_cfg['DB_HOST'], '-p', new_cfg['DB_PORT'], '-d', new_cfg['DB_NAME'], '-f', str(functions_file)]])
|
||||
if confirm(f'Deploy SQL functions into {db_location}?', default_yes=False):
|
||||
print(f' Running {functions_file} against {db_location}...')
|
||||
r = psql_file(new_cfg, functions_file)
|
||||
if r.returncode == 0:
|
||||
ok(f'SQL functions deployed into {db_location}')
|
||||
else:
|
||||
err(f'Function deployment failed:\n{r.stderr}')
|
||||
|
||||
return new_cfg
|
||||
|
||||
|
||||
@ -324,12 +375,17 @@ def action_deploy_schema(cfg):
|
||||
|
||||
print(f' Source file : {schema_file}')
|
||||
print(f' Target : "dataflow" schema in {db_location}')
|
||||
print(f' Scope : creates the "dataflow" schema and tables inside the existing')
|
||||
print(f' database — does NOT create the database or PostgreSQL user.')
|
||||
print(f' Run option 1 first if the database or user does not exist yet.')
|
||||
print()
|
||||
|
||||
if not can_connect(cfg):
|
||||
err(f'Cannot connect to {db_location} — check credentials in {ENV_FILE}')
|
||||
return
|
||||
|
||||
show_commands([['psql', '-U', cfg['DB_USER'], '-h', cfg['DB_HOST'], '-p', cfg['DB_PORT'], '-d', cfg['DB_NAME'], '-q', '-f', str(schema_file)]])
|
||||
|
||||
if schema_deployed(cfg):
|
||||
warn(f'"dataflow" schema already exists in {db_location}.')
|
||||
warn(f'Redeploying will DROP and recreate the schema, deleting all data.')
|
||||
@ -337,7 +393,7 @@ def action_deploy_schema(cfg):
|
||||
info('Cancelled — no changes made')
|
||||
return
|
||||
else:
|
||||
if not confirm(f'Deploy "dataflow" schema from {schema_file} into {db_location}?', default_yes=False):
|
||||
if not confirm(f'Deploy "dataflow" schema into {db_location}?', default_yes=False):
|
||||
info('Cancelled — no changes made')
|
||||
return
|
||||
|
||||
@ -372,7 +428,8 @@ def action_deploy_functions(cfg):
|
||||
info('Cancelled — no changes made')
|
||||
return
|
||||
|
||||
if not confirm(f'Deploy SQL functions from {functions_file} into {db_location}?', default_yes=False):
|
||||
show_commands([['psql', '-U', cfg['DB_USER'], '-h', cfg['DB_HOST'], '-p', cfg['DB_PORT'], '-d', cfg['DB_NAME'], '-q', '-f', str(functions_file)]])
|
||||
if not confirm(f'Deploy SQL functions into {db_location}?', default_yes=False):
|
||||
info('Cancelled — no changes made')
|
||||
return
|
||||
|
||||
@ -397,6 +454,7 @@ def action_build_ui():
|
||||
err(f'{ui_dir}/package.json not found — is the ui directory present?')
|
||||
return
|
||||
|
||||
show_commands([['npm', 'run', 'build', f' (in {ui_dir})']])
|
||||
if not confirm(f'Build UI from {ui_dir} into {out_dir}?', default_yes=False):
|
||||
info('Cancelled — no changes made')
|
||||
return
|
||||
@ -417,6 +475,16 @@ def action_setup_nginx(cfg):
|
||||
|
||||
port = cfg.get('API_PORT', '3020') if cfg else '3020'
|
||||
|
||||
existing_domain = nginx_domain(port)
|
||||
if existing_domain:
|
||||
warn(f'nginx is already configured for this service.')
|
||||
info(f' Current config: {existing_domain} → localhost:{port}')
|
||||
print()
|
||||
if not confirm(f'Reconfigure nginx (overwrite existing config for {existing_domain})?', default_yes=False):
|
||||
info('Cancelled — no changes made')
|
||||
return
|
||||
print()
|
||||
|
||||
print(f' This will write an nginx site config and reload nginx (requires sudo).')
|
||||
print(f' The site will proxy incoming HTTP requests to the dataflow API on localhost:{port}.')
|
||||
print()
|
||||
@ -485,6 +553,12 @@ server {{
|
||||
f.write(conf)
|
||||
tmp = f.name
|
||||
|
||||
show_commands([
|
||||
['sudo', 'cp', '<config>', str(conf_path)],
|
||||
['sudo', 'chmod', '644', str(conf_path)],
|
||||
['sudo', 'nginx', '-t'],
|
||||
['sudo', 'systemctl', 'reload', 'nginx'],
|
||||
])
|
||||
if not confirm(f'Write nginx config to {conf_path} and reload nginx (requires sudo)?', default_yes=False):
|
||||
os.unlink(tmp)
|
||||
info('Cancelled — no changes made')
|
||||
@ -512,6 +586,7 @@ server {{
|
||||
if not cert_exists:
|
||||
warn(f'No SSL certificate found for {domain} — site is HTTP only.')
|
||||
if confirm(f'Run certbot to obtain an SSL certificate for {domain} and switch to HTTPS?'):
|
||||
show_commands([['sudo', 'certbot', '--nginx', '-d', domain, '--non-interactive', '--agree-tos', '--redirect', '-m', f'admin@{domain}']])
|
||||
print(f' Running certbot for {domain}...')
|
||||
r = sudo_run(['certbot', '--nginx', '-d', domain,
|
||||
'--non-interactive', '--agree-tos', '--redirect',
|
||||
@ -538,6 +613,11 @@ def action_install_service():
|
||||
err(f'Service unit file not found: {SERVICE_SRC}')
|
||||
return
|
||||
|
||||
show_commands([
|
||||
['sudo', 'cp', str(SERVICE_SRC), str(SERVICE_FILE)],
|
||||
['sudo', 'systemctl', 'daemon-reload'],
|
||||
['sudo', 'systemctl', 'enable', 'dataflow'],
|
||||
])
|
||||
if not confirm(f'Copy {SERVICE_SRC.name} to {SERVICE_FILE} and enable it with systemd (requires sudo)?', default_yes=False):
|
||||
info('Cancelled — no changes made')
|
||||
return
|
||||
@ -573,9 +653,9 @@ def action_restart_service():
|
||||
|
||||
print(f' Service file : {SERVICE_FILE}')
|
||||
print(f' Current state : {current_state}')
|
||||
print(f' Action : sudo systemctl {action} dataflow')
|
||||
print()
|
||||
|
||||
show_commands([['sudo', 'systemctl', action, 'dataflow']])
|
||||
if not confirm(f'{action.capitalize()} dataflow.service?', default_yes=False):
|
||||
info('Cancelled — no changes made')
|
||||
return
|
||||
@ -597,13 +677,13 @@ def action_stop_service():
|
||||
header('Stop dataflow.service')
|
||||
|
||||
print(f' Service file : {SERVICE_FILE}')
|
||||
print(f' Action : sudo systemctl stop dataflow')
|
||||
print()
|
||||
|
||||
if not service_running():
|
||||
info('dataflow.service is not currently running — nothing to stop')
|
||||
return
|
||||
|
||||
show_commands([['sudo', 'systemctl', 'stop', 'dataflow']])
|
||||
if not confirm('Stop dataflow.service?', default_yes=False):
|
||||
info('Cancelled — no changes made')
|
||||
return
|
||||
@ -616,9 +696,9 @@ def action_stop_service():
|
||||
# ── Main menu ─────────────────────────────────────────────────────────────────
|
||||
|
||||
MENU = [
|
||||
('Configure database connection settings (.env)', action_configure),
|
||||
('Deploy "dataflow" schema (database/schema.sql)', action_deploy_schema),
|
||||
('Deploy SQL functions (database/functions.sql)', action_deploy_functions),
|
||||
('Database configuration and deployment dialog (.env)', action_configure),
|
||||
('Redeploy "dataflow" schema only (database/schema.sql)', action_deploy_schema),
|
||||
('Redeploy SQL functions only (database/functions.sql)', action_deploy_functions),
|
||||
('Build UI (ui/ → public/)', action_build_ui),
|
||||
('Set up nginx reverse proxy', action_setup_nginx),
|
||||
('Install dataflow systemd service unit', action_install_service),
|
||||
|
||||
Loading…
Reference in New Issue
Block a user