81 lines
5.2 KiB
Python
81 lines
5.2 KiB
Python
#!/usr/bin/env python3
|
|
"""Local-only runtime; credentials and backups are kept in ignored .local/."""
|
|
import json, os, pathlib, secrets, signal, subprocess, sys, time, socket
|
|
ROOT=pathlib.Path(__file__).resolve().parent.parent
|
|
STATE=ROOT/'.local'; DATA=pathlib.Path('/tmp/nurinda-rec-mysql'); SOCKET=DATA/'mysql.sock'
|
|
STATE.mkdir(exist_ok=True)
|
|
def run(args, **kwargs): return subprocess.run(args,check=True,**kwargs)
|
|
def wait_port(port):
|
|
for _ in range(100):
|
|
try:
|
|
with socket.create_connection(('127.0.0.1',port),timeout=.2): return
|
|
except OSError: time.sleep(.1)
|
|
raise RuntimeError('Local service did not start: '+str(port))
|
|
def sql(text):
|
|
run(['mysql','--defaults-extra-file='+str(STATE/'mysql-client.cnf')],input=text.encode(),stdout=subprocess.DEVNULL)
|
|
def launch(name,args,env=None):
|
|
pidfile=STATE/(name+'.pid')
|
|
if pidfile.exists():
|
|
try: os.kill(int(pidfile.read_text()),0); return
|
|
except ProcessLookupError: pass
|
|
with open(STATE/(name+'.log'),'ab') as log:
|
|
proc=subprocess.Popen(args,cwd=ROOT,env=env,stdin=subprocess.DEVNULL,stdout=log,stderr=log,start_new_session=True)
|
|
pidfile.write_text(str(proc.pid))
|
|
if sys.argv[1]=='stop':
|
|
cfg=json.loads((STATE/'credentials.json').read_text())
|
|
with open(STATE/'local-backup.sql','wb') as out:
|
|
run(['mysqldump','--defaults-extra-file='+str(STATE/'mysql-client.cnf'),'--single-transaction','--no-tablespaces','recruitment_local'],stdout=out)
|
|
for name in ['web','phpmyadmin']:
|
|
p=STATE/(name+'.pid')
|
|
if p.exists():
|
|
try: os.kill(int(p.read_text()),signal.SIGTERM)
|
|
except ProcessLookupError: pass
|
|
p.unlink()
|
|
run(['mysqladmin','--defaults-extra-file='+str(STATE/'mysql-client.cnf'),'shutdown'])
|
|
print('Local services stopped. Database backup: .local/local-backup.sql')
|
|
sys.exit()
|
|
if '.local/db.php' not in (ROOT/'includes/conn.php').read_text():
|
|
raise SystemExit('Preview lokal dibatalkan: arahkan includes/conn.php ke ../.local/db.php terlebih dahulu. Konfigurasi server tidak diubah.')
|
|
credentials=STATE/'credentials.json'
|
|
if not credentials.exists():
|
|
credentials.write_text(json.dumps({'db_user':'rec_local','db_password':secrets.token_urlsafe(20),'root_password':secrets.token_urlsafe(24),'admin_email':'admin@nurinda.local','admin_password':secrets.token_urlsafe(15),'blowfish':secrets.token_hex(16)},indent=2)); credentials.chmod(0o600)
|
|
cfg=json.loads(credentials.read_text())
|
|
fresh=not (DATA/'mysql').exists()
|
|
if fresh:
|
|
DATA.mkdir(exist_ok=True)
|
|
run(['mysqld','--no-defaults','--initialize-insecure','--datadir='+str(DATA),'--log-error='+str(DATA/'init.log')])
|
|
if not SOCKET.exists():
|
|
run(['mysqld','--no-defaults','--daemonize','--datadir='+str(DATA),'--socket='+str(SOCKET),'--bind-address=127.0.0.1','--port=33317','--mysqlx=0','--pid-file='+str(DATA/'mysql.pid'),'--log-error='+str(DATA/'server.log')])
|
|
wait_port(33317)
|
|
client=STATE/'mysql-client.cnf'
|
|
client.write_text('[client]\nuser=root\npassword='+('' if fresh else cfg['root_password'])+'\nsocket='+str(SOCKET)+'\n'); client.chmod(0o600)
|
|
if fresh:
|
|
sql("ALTER USER 'root'@'localhost' IDENTIFIED BY '"+cfg['root_password']+"';")
|
|
client.write_text('[client]\nuser=root\npassword='+cfg['root_password']+'\nsocket='+str(SOCKET)+'\n')
|
|
sql("CREATE DATABASE IF NOT EXISTS recruitment_local CHARACTER SET utf8mb4; CREATE USER IF NOT EXISTS 'rec_local'@'127.0.0.1' IDENTIFIED BY '"+cfg['db_password']+"'; GRANT ALL ON recruitment_local.* TO 'rec_local'@'127.0.0.1';")
|
|
if fresh:
|
|
dump=STATE/'local-backup.sql'
|
|
if not dump.exists():
|
|
exports=sorted((ROOT/'DB').glob('*.sql'))
|
|
if not exports: raise RuntimeError('No database export found in DB/')
|
|
dump=exports[-1]
|
|
with open(dump,'rb') as source: run(['mysql','--defaults-extra-file='+str(client),'recruitment_local'],stdin=source,stdout=subprocess.DEVNULL)
|
|
env=os.environ.copy()
|
|
for key in list(env):
|
|
if key.startswith('REC_DB_'): env.pop(key)
|
|
env.update(LOCAL_ADMIN_EMAIL=cfg['admin_email'],LOCAL_ADMIN_PASSWORD=cfg['admin_password'])
|
|
run(['php','dev/setup-local.php'],env=env)
|
|
# Use the system phpMyAdmin distribution without touching its global configuration.
|
|
pma=STATE/'phpmyadmin'
|
|
if not pma.exists():
|
|
import shutil
|
|
shutil.copytree('/usr/share/phpmyadmin',pma)
|
|
vendor=pma/'libraries/vendor_config.php'
|
|
vendor.write_text(vendor.read_text().replace("'/etc/phpmyadmin/config.inc.php'", "ROOT_PATH . 'config.inc.php'").replace("'/var/lib/phpmyadmin/tmp/'", "ROOT_PATH . 'tmp/'"))
|
|
(pma/'tmp').mkdir(exist_ok=True)
|
|
(pma/'config.inc.php').write_text("<?php\n$cfg['blowfish_secret']='"+cfg['blowfish']+"';\n$cfg['Servers'][1]=['auth_type'=>'cookie','host'=>'127.0.0.1','port'=>'33317','AllowNoPassword'=>false,'only_db'=>'recruitment_local'];\n$cfg['TempDir']=__DIR__.'/tmp';\n$cfg['VersionCheck']=false;\n$cfg['SendErrorReports']='never';\n")
|
|
launch('web',['php','-S','127.0.0.1:8086','-t',str(ROOT),str(ROOT/'dev/router.php')],env)
|
|
launch('phpmyadmin',['php','-d','session.name=rec_phpmyadmin','-S','127.0.0.1:8087','-t',str(pma)],env)
|
|
wait_port(8086); wait_port(8087)
|
|
print('Dashboard: http://127.0.0.1:8086/dashboard/login.php\nPeserta: http://127.0.0.1:8086/\nphpMyAdmin: http://127.0.0.1:8087/\nCredentials: .local/credentials.json')
|