Files
Bridge-and-Join-s/deploy/linux/sbin/bj-quikexport-admin

122 lines
7.1 KiB
Bash
Executable File

#!/bin/bash
# bj-quikexport-admin — привилегированные операции модуля «Экспорт биржевой
# информации» (ARQA quikexport). Вызывается root-воркером bj-keymedia-worker
# по запросам bj-server. Печатает JSON {"ok":true,...} или {"ok":false,"error":...}.
set -u
ACTION="${1:-}"; STAGING="${2:-}"; EXTRA="${BJ_EXTRA:-}"
Q=/opt/arqasrv/quik/utils/quikexport; SVC=quikexport; OWNER=arqasrv
json(){ python3 -c 'import json,sys;print(json.dumps(json.loads(sys.argv[1]),ensure_ascii=False))' "$1"; }
err(){ python3 -c 'import json,sys;print(json.dumps({"ok":False,"error":sys.argv[1]},ensure_ascii=False))' "$1"; exit 0; }
b64(){ base64 -w0 "$1" 2>/dev/null || true; }
case "$ACTION" in
dump)
[ -d "$Q" ] || err "каталог экспортёра $Q не найден"
python3 - "$Q" <<'PY'
import json,sys,base64,os
q=sys.argv[1]; out={"ok":True,"dir":q}
for n in ("export.ini","info.ini"):
p=os.path.join(q,n)
out[n]=base64.b64encode(open(p,'rb').read()).decode() if os.path.exists(p) else ""
out["keys"]=[n for n in ("pubring.txk","secring.txk") if os.path.exists(os.path.join(q,n))]
out["keyfiles"]={n:base64.b64encode(open(os.path.join(q,n),'rb').read()).decode() for n in out["keys"]}
print(json.dumps(out,ensure_ascii=False))
PY
;;
apply)
case "$STAGING" in /var/lib/bj/quik-export-apply/*) [ -d "$STAGING" ] || err "нет папки staging" ;; *) err "staging вне зоны" ;; esac
[ -d "$Q" ] || err "каталог экспортёра $Q не найден — сначала установите модуль"
TS=$(date +%Y%m%d-%H%M%S); BK="$Q/bj-backup-$TS"; mkdir -p "$BK"
for f in export.ini info.ini pubring.txk secring.txk; do [ -f "$Q/$f" ] && cp -p "$Q/$f" "$BK/"; done
applied=()
for f in export.ini info.ini pubring.txk secring.txk; do
if [ -f "$STAGING/$f" ]; then install -o $OWNER -g $OWNER -m 640 "$STAGING/$f" "$Q/$f" && applied+=("$f"); fi
done
if [ -f "$STAGING/odbc.ini" ]; then
python3 - "$STAGING/odbc.ini" <<'PY'
import sys,re
new=open(sys.argv[1]).read().strip()+"\n"
p="/etc/odbc.ini"
try: cur=open(p).read()
except FileNotFoundError: cur=""
# заменяем/добавляем секцию [quik_export]
parts=re.split(r'(?m)^(?=\[)',cur); keep=[s for s in parts if s.strip() and not s.startswith('[quik_export]')]
open(p,'w').write("".join(keep).rstrip()+("\n\n" if keep else "")+new)
PY
applied+=("odbc.ini")
fi
# ротация логов экспортёра (quikapi.log растёт ~1.8 ГБ/сутки)
cat > /etc/logrotate.d/quikexport <<EOF
$Q/logs/*.log $Q/logs/*.err {
daily
maxsize 200M
rotate 5
compress
delaycompress
missingok
notifempty
copytruncate
su $OWNER $OWNER
}
EOF
# ограничиваем старые бэкапы (оставляем 10)
ls -1dt "$Q"/bj-backup-* 2>/dev/null | tail -n +11 | xargs -r rm -rf
svc="none"
case "$EXTRA" in
restart) systemctl daemon-reload 2>/dev/null; systemctl restart $SVC >/dev/null 2>&1 && svc="restarted" || svc="restart-failed" ;;
stop) systemctl stop $SVC >/dev/null 2>&1 && svc="stopped" ;;
esac
python3 -c 'import json,sys;print(json.dumps({"ok":True,"applied":sys.argv[1].split(),"backup":sys.argv[2],"service":sys.argv[3]},ensure_ascii=False))' "${applied[*]:-}" "$BK" "$svc"
;;
service)
case "$EXTRA" in
start|stop|restart) systemctl daemon-reload 2>/dev/null; out=$(systemctl "$EXTRA" $SVC 2>&1 | grep -v '^Warning:') ; systemctl is-active --quiet $SVC || [ "$EXTRA" = stop ] && json "{\"ok\":true,\"op\":\"$EXTRA\"}" || err "systemctl $EXTRA: $out" ;;
enable) systemctl enable $SVC >/dev/null 2>&1; json '{"ok":true,"op":"enable"}' ;;
*) err "неизвестная операция службы" ;;
esac
;;
db-init)
case "$STAGING" in /var/lib/bj/quik-export-apply/*) [ -f "$STAGING/db.json" ] || err "нет db.json" ;; *) err "staging вне зоны" ;; esac
python3 - "$STAGING" <<'PY'
import json,sys,subprocess,os
st=sys.argv[1]; d=json.load(open(os.path.join(st,"db.json")))
db,u,p,ru,rp=d["db"],d["user"],d["password"],d["ro_user"],d["ro_password"]
def psql(sql,dbname="postgres"):
r=subprocess.run(["sudo","-u","postgres","psql","-v","ON_ERROR_STOP=1","-Atq","-d",dbname,"-c",sql],capture_output=True,text=True)
if r.returncode: raise SystemExit(json.dumps({"ok":False,"error":r.stderr.strip()[-400:]},ensure_ascii=False))
return r.stdout.strip()
q=lambda s: "'"+s.replace("'","''")+"'"
steps=[]
if psql(f"select 1 from pg_roles where rolname={q(u)}")!="1": psql(f"create role {u} login password {q(p)}"); steps.append("роль "+u)
else: psql(f"alter role {u} with password {q(p)}")
if psql(f"select 1 from pg_roles where rolname={q(ru)}")!="1": psql(f"create role {ru} login password {q(rp)}"); steps.append("роль "+ru)
else: psql(f"alter role {ru} with password {q(rp)}")
psql(f"alter role {u} set log_min_messages = fatal")
created=False
if psql(f"select 1 from pg_database where datname={q(db)}")!="1":
psql(f"create database {db} owner {u} encoding 'UTF8' template template0 lc_collate 'C.UTF-8' lc_ctype 'C.UTF-8'"); steps.append("база "+db); created=True
if created or psql("select count(*) from pg_tables where schemaname='public' and tablename='Portfolio'",db)!="1":
env=dict(os.environ,PGPASSWORD=p)
for f in ("exporter_pg.sql","npr_migration.sql"):
r=subprocess.run(["psql","-v","ON_ERROR_STOP=1","-q","-h","localhost","-U",u,"-d",db,"-f",os.path.join(st,f)],capture_output=True,text=True,env=env)
if r.returncode: raise SystemExit(json.dumps({"ok":False,"error":f+": "+r.stderr.strip()[-400:]},ensure_ascii=False))
steps.append("схема "+f)
psql(f"grant connect on database {db} to {ru}",)
for s in ("public","npr"):
psql(f"grant usage on schema {s} to {ru}; grant select on all tables in schema {s} to {ru}; alter default privileges for role {u} in schema {s} grant select on tables to {ru}",db)
print(json.dumps({"ok":True,"steps":steps or ["база уже существует, права обновлены"]},ensure_ascii=False))
PY
;;
install)
case "$STAGING" in /var/lib/bj/quik-export-apply/*) : ;; *) err "staging вне зоны" ;; esac
[ -d "$Q" ] && err "экспортёр уже установлен в $Q"
RUN=$(ls "$STAGING"/install_quikexport-*.run 2>/dev/null | head -1); [ -n "$RUN" ] || err "в дистрибутиве нет install_quikexport-*.run"
chmod +x "$RUN"; out=$(cd "$STAGING" && "$RUN" -i 2>&1) || err "установщик: ${out: -300}"
apt-get install -y -q libjemalloc2 unixodbc odbc-postgresql libpq5 >/dev/null 2>&1 || true
mkdir -p /etc/systemd/system/$SVC.service.d
printf '[Unit]\nAfter=network-online.target postgresql.service\nWants=network-online.target\n[Service]\nRestart=on-failure\nRestartSec=15\nMemoryMax=3G\n[Install]\nWantedBy=multi-user.target\n' > /etc/systemd/system/$SVC.service.d/override.conf
systemctl daemon-reload; systemctl enable $SVC >/dev/null 2>&1
json '{"ok":true,"installed":true}'
;;
*) err "неизвестное действие $ACTION" ;;
esac