Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
D
db-tools
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
yzy
db-tools
Commits
09a35038
Commit
09a35038
authored
Sep 24, 2026
by
wangteng
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
整理优化完善
parent
8cb592c8
Changes
7
Show whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
281 additions
and
79 deletions
+281
-79
README.md
README.md
+5
-6
deploy/README.md
deploy/README.md
+29
-7
deploy/build_release.py
deploy/build_release.py
+28
-1
deploy/db-tool.service
deploy/db-tool.service
+1
-0
deploy/deploy_server.py
deploy/deploy_server.py
+201
-63
deploy/remote_shell.py
deploy/remote_shell.py
+4
-0
web/backend/core/db_adapter.py
web/backend/core/db_adapter.py
+13
-2
No files found.
README.md
View file @
09a35038
...
@@ -29,11 +29,10 @@ npm run dev
...
@@ -29,11 +29,10 @@ npm run dev
## 验证与发布
## 验证与发布
```
powershell
```
powershell
cd
web
# 正式发布:自动构建、上传、重启并检查前后端和 Oracle
npm
run
build
python
deploy/deploy_server.py
cd
..
node
scripts/verify/verify_tree.cjs
python
deploy/build_release.py
```
```
发布产物生成到
`release/`
,部署步骤见
[
deploy/README.md
](
deploy/README.md
)
。
最后一条命令会自动构建发布包、读取
`deploy/server.local.md`
中的本机私有发布配置、上传并重启正式服务,同时执行健康检查。只构建不发布时仍可执行
`python deploy/build_release.py`
;完整说明见
[
deploy/README.md
](
deploy/README.md
)
。
发布前如需单独运行本地前端树校验,可执行
`node scripts/verify/verify_tree.cjs`
。
deploy/README.md
View file @
09a35038
...
@@ -2,14 +2,36 @@
...
@@ -2,14 +2,36 @@
本目录只保存部署相关文件,业务代码不应放入此处。
本目录只保存部署相关文件,业务代码不应放入此处。
1.
在项目根目录执行
`python deploy/build_release.py`
,构建产物生成到
`release/`
。
## 正式发布(一条命令)
2.
设置
`DBTOOL_PW`
后执行
`python deploy/deploy_server.py`
,上传发布包并重启服务。
3.
使用
`python deploy/remote_shell.py "命令"`
执行临时远程检查;密码不会写入脚本或仓库。
`db-tool.service`
是 systemd 服务定义,
`db-tool-nginx.conf`
是 Nginx 反向代理示例。
在项目根目录执行:
```
powershell
python
deploy/deploy_server.py
```
脚本会自动完成以下步骤:
1.
从
`deploy/server.local.md`
读取本机私有服务器地址、用户、密码和 Oracle 测试数据源 ID。
2.
执行
`build_release.py`
构建前后端发布包。
3.
上传前检查后端入口、正式配置、前端
`index.html/assets/`
,并检查 Oracle
`libclntsh.so`
符号链接和 ELF 实体库。
4.
备份线上版本和 SQLite 快照,上传并解压包,安装 systemd 配置,迁移数据后重启服务。
5.
检查 systemd、后端本机接口、Nginx
`/db-tool/api/health`
,并默认测试 Oracle 数据源连接。
`server.local.md`
已加入
`.gitignore`
,只允许保存在本机,禁止提交 Git 或打入发布包。环境变量
`DBTOOL_HOST`
、
`DBTOOL_USER`
、
`DBTOOL_PW`
和
`DBTOOL_ORACLE_SOURCE_ID`
可临时覆盖文档中的值。
部署脚本默认使用现有服务器地址和
`root`
用户,也可通过
`DBTOOL_HOST`
、
`DBTOOL_USER`
覆盖;SSH 密码只从
`DBTOOL_PW`
读取。
## 特殊场景
本机正式服务器凭据记录在被 Git 忽略的
`deploy/server.local.md`
中。发布前从该文件读取密码并设置
`DBTOOL_PW`
,不要将凭据写入可提交文件或发布包。
```
powershell
# 只使用已有 release/,不重新构建
python
deploy/deploy_server.py
--skip-build
# 临时跳过 Oracle 测试(不建议正式发布使用)
python
deploy/deploy_server.py
--skip-oracle-check
```
如需只构建不发布,可执行
`python deploy/build_release.py`
。临时远程检查使用
`python deploy/remote_shell.py "命令"`
;该脚本仍从
`DBTOOL_PW`
读取密码。
`db-tool.service`
是 systemd 服务定义,
`db-tool-nginx.conf`
是 Nginx 反向代理示例。
如生产环境需要 Oracle Thick 模式,请
在构建前从 Oracle 官方获取 Linux Instant Client,并解压到项目根目录的
`oracle_client/linux/`
。该目录是本机构建依赖,不纳入版本控制
。
如生产环境需要 Oracle Thick 模式,请
从 Oracle 官方获取 Linux Instant Client,并解压到项目根目录的
`oracle_client/linux/`
。该目录是本机构建依赖,不纳入版本控制。Linux 服务通过
`db-tool.service`
中的
`LD_LIBRARY_PATH`
加载 Instant Client;不要改成运行时传
`lib_dir`
,CentOS 7 与 python-oracledb 3.x 的组合可能因此触发原生段错误。构建脚本会把 Windows 解压产生的伪符号链接恢复为 Linux tar 符号链接
。
deploy/build_release.py
View file @
09a35038
...
@@ -8,6 +8,7 @@ from __future__ import annotations
...
@@ -8,6 +8,7 @@ from __future__ import annotations
import
os
import
os
import
subprocess
import
subprocess
import
tarfile
import
tarfile
import
time
from
pathlib
import
Path
from
pathlib
import
Path
ROOT
=
Path
(
__file__
).
resolve
().
parent
.
parent
ROOT
=
Path
(
__file__
).
resolve
().
parent
.
parent
...
@@ -28,7 +29,33 @@ def _add_tree(archive: tarfile.TarFile, root: Path, arcname: str) -> None:
...
@@ -28,7 +29,33 @@ def _add_tree(archive: tarfile.TarFile, root: Path, arcname: str) -> None:
for
path
in
root
.
rglob
(
"*"
):
for
path
in
root
.
rglob
(
"*"
):
if
_skip
(
path
)
or
not
path
.
is_file
():
if
_skip
(
path
)
or
not
path
.
is_file
():
continue
continue
archive
.
add
(
path
,
arcname
=
f"
{
arcname
}
/
{
path
.
relative_to
(
root
).
as_posix
()
}
"
)
archive_name
=
f"
{
arcname
}
/
{
path
.
relative_to
(
root
).
as_posix
()
}
"
# Windows 解压 Oracle 官方 ZIP 时,Linux 符号链接会变成内容仅为
# 目标文件名的小文本文件。恢复为 tar 符号链接,否则 Linux 加载
# libclntsh.so 时会把文本当动态库,严重时直接导致进程 SIGSEGV。
if
"oracle_client"
in
path
.
parts
and
path
.
stat
().
st_size
<
256
:
try
:
link_target
=
path
.
read_text
(
encoding
=
"ascii"
).
strip
()
except
(
OSError
,
UnicodeDecodeError
):
link_target
=
""
if
link_target
and
(
path
.
parent
/
link_target
).
is_file
():
info
=
tarfile
.
TarInfo
(
archive_name
)
info
.
type
=
tarfile
.
SYMTYPE
info
.
linkname
=
link_target
info
.
mode
=
0o777
# Windows 伪链接常带 1970 时间戳,避免 Linux 解包时出现
# "unknown date" 警告,使用当前构建时间作为链接时间。
info
.
mtime
=
int
(
time
.
time
())
archive
.
addfile
(
info
)
continue
info
=
archive
.
gettarinfo
(
str
(
path
),
arcname
=
archive_name
)
if
"oracle_client"
in
path
.
parts
:
# Windows 文件系统不保存 Unix 可执行位,发布时统一补齐。
info
.
mode
=
0o755
with
path
.
open
(
"rb"
)
as
source
:
archive
.
addfile
(
info
,
source
)
def
main
()
->
None
:
def
main
()
->
None
:
...
...
deploy/db-tool.service
View file @
09a35038
...
@@ -10,6 +10,7 @@ Environment=PYTHONPATH=/opt/app/db-tool
...
@@ -10,6 +10,7 @@ Environment=PYTHONPATH=/opt/app/db-tool
Environment=APP_ENV=production
Environment=APP_ENV=production
Environment=WEB_HOST=127.0.0.1
Environment=WEB_HOST=127.0.0.1
Environment=WEB_PORT=8767
Environment=WEB_PORT=8767
Environment=LD_LIBRARY_PATH=/opt/app/db-tool/web/oracle_client/linux/instantclient_19_32
ExecStart=/opt/app/db-tool/.venv/bin/python -m web.backend.start
ExecStart=/opt/app/db-tool/.venv/bin/python -m web.backend.start
Restart=always
Restart=always
RestartSec=5
RestartSec=5
...
...
deploy/deploy_server.py
View file @
09a35038
#!/usr/bin/env python3
#!/usr/bin/env python3
"""db-tools 正式发版:上传、迁移旧 SQLite、切换 MySQL、重启并验证。"""
"""一键构建并发布 db-tools 正式版本。
默认流程:读取本机私有服务器文档、构建发布包、校验产物、上传、备份、
重启 systemd 服务,并检查后端、Nginx 和可选 Oracle 数据源连接。
"""
from
__future__
import
annotations
from
__future__
import
annotations
import
argparse
import
json
import
os
import
os
import
posixpath
import
re
import
subprocess
import
sys
import
sys
import
tarfile
from
pathlib
import
Path
from
pathlib
import
Path
import
paramiko
import
paramiko
HOST
=
os
.
environ
.
get
(
"DBTOOL_HOST"
,
"192.168.20.175"
)
USER
=
os
.
environ
.
get
(
"DBTOOL_USER"
,
"root"
)
ROOT
=
Path
(
__file__
).
resolve
().
parent
.
parent
ROOT
=
Path
(
__file__
).
resolve
().
parent
.
parent
DEPLOY_DIR
=
ROOT
/
"deploy"
RELEASE_DIR
=
ROOT
/
"release"
RELEASE_DIR
=
ROOT
/
"release"
SERVICE_FILE
=
Path
(
__file__
).
with_name
(
"db-tool.service"
)
SERVICE_FILE
=
DEPLOY_DIR
/
"db-tool.service"
pw
=
os
.
environ
.
get
(
"DBTOOL_PW"
)
SERVER_FILE
=
DEPLOY_DIR
/
"server.local.md"
if
not
pw
:
sys
.
exit
(
"set DBTOOL_PW first"
)
c
=
paramiko
.
SSHClient
()
c
.
set_missing_host_key_policy
(
paramiko
.
AutoAddPolicy
())
c
.
connect
(
HOST
,
username
=
USER
,
password
=
pw
,
timeout
=
10
,
look_for_keys
=
False
,
allow_agent
=
False
)
def
run
(
cmd
,
must
=
True
):
_
,
out
,
err
=
c
.
exec_command
(
cmd
,
timeout
=
120
)
o
,
e
=
out
.
read
().
decode
(
"utf-8"
,
"replace"
),
err
.
read
().
decode
(
"utf-8"
,
"replace"
)
rc
=
out
.
channel
.
recv_exit_status
()
print
(
f"$
{
cmd
}\
n
{
o
}{
(
'[stderr] '
+
e
)
if
e
.
strip
()
else
''
}
rc=
{
rc
}\
n
"
)
if
must
and
rc
!=
0
:
sys
.
exit
(
f"FAILED rc=
{
rc
}
:
{
cmd
}
"
)
return
o
sftp
=
c
.
open_sftp
()
sftp
.
put
(
str
(
RELEASE_DIR
/
"backend-deploy.tgz"
),
"/tmp/dbtool-backend.tgz"
)
sftp
.
put
(
str
(
RELEASE_DIR
/
"frontend-deploy.tgz"
),
"/tmp/dbtool-frontend.tgz"
)
sftp
.
put
(
str
(
SERVICE_FILE
),
"/tmp/db-tool.service"
)
sftp
.
close
()
print
(
"uploaded both deployment packages
\
n
"
)
# 0) 部署前快照(旧 SQLite 只作为首次 MySQL 迁移源)
run
(
"test -f /opt/app/db-tool/web/data/web.db && md5sum /opt/app/db-tool/web/data/web.db || true"
)
# 1) 备份当前版本
run
(
"TS=$(date +%Y%m%d-%H%M%S) && mkdir -p /opt/app/db-tool/releases/$TS && "
"cp -a /opt/app/db-tool/web/backend /opt/app/db-tool/releases/$TS/backend && "
"cp -a /opt/web/db-tool /opt/app/db-tool/releases/$TS/frontend && echo backup at $TS"
)
# 2) 停止旧服务并备份其 SQLite,保证迁移读取一致快照
run
(
"systemctl stop db-tool && TS=$(date +%Y%m%d-%H%M%S) && "
"mkdir -p /opt/app/db-tool/releases/$TS && "
"cp -a /opt/app/db-tool/web/data/web.db /opt/app/db-tool/releases/$TS/web.db"
)
# 3) 解压后端(不动 data/、logs/),并锁定私有配置权限
def
_markdown_value
(
text
:
str
,
label
:
str
)
->
str
|
None
:
run
(
"tar xzf /tmp/dbtool-backend.tgz -C /opt/app/db-tool/web/ && "
"""读取 ``- 标签:`值` `` 形式的私有配置,兼容无反引号写法。"""
"find /opt/app/db-tool/web/backend -name __pycache__ -type d -exec rm -rf {} + ; "
match
=
re
.
search
(
"chmod 600 /opt/app/db-tool/web/backend/configs/llm.yaml /opt/app/db-tool/web/backend/configs/database.production.yaml"
)
rf"^\
s*-
\s*
{
re
.
escape
(
label
)
}
\
s*[::]
\s*`?([^`\r\n]+?)`?\
s*$
",
text,
flags=re.MULTILINE,
)
return match.group(1).strip() if match else None
# 4) 首次发布时迁移旧 SQLite 到正式 MySQL;已有 MySQL 数据时不覆盖
run
(
"cd /opt/app/db-tool && APP_ENV=production PYTHONPATH=/opt/app/db-tool "
"/opt/app/db-tool/.venv/bin/python -m web.backend.db.migrate_sqlite_to_mysql "
"--source /opt/app/db-tool/web/data/web.db --skip-if-populated"
)
# 5) 安装正式 systemd 配置并解压前端
def load_server_settings() -> tuple[str, str, str, int | None]:
run
(
"install -m 644 /tmp/db-tool.service /etc/systemd/system/db-tool.service && systemctl daemon-reload"
)
"""加载服务器配置,环境变量优先,私有 Markdown 作为默认来源。"""
run
(
"rm -rf /opt/web/db-tool/assets && tar xzf /tmp/dbtool-frontend.tgz -C /opt/web/db-tool/ && ls /opt/web/db-tool/ /opt/web/db-tool/assets/"
)
private_text = SERVER_FILE.read_text(encoding="
utf
-
8
") if SERVER_FILE.is_file() else ""
host = os.environ.get("
DBTOOL_HOST
") or _markdown_value(private_text, "
主机
") or "
192.168
.
20.175
"
user = os.environ.get("
DBTOOL_USER
") or _markdown_value(private_text, "
SSH
用户
") or "
root
"
password = os.environ.get("
DBTOOL_PW
") or _markdown_value(private_text, "
SSH
密码
")
source_id = os.environ.get("
DBTOOL_ORACLE_SOURCE_ID
") or _markdown_value(
private_text, "
Oracle
测试数据源
ID
"
)
if not password:
raise SystemExit(
f"
未找到
SSH
密码,请填写
{
SERVER_FILE
}
,或设置
DBTOOL_PW
环境变量。
"
)
try:
oracle_source_id = int(source_id) if source_id else None
except ValueError as exc:
raise SystemExit("
Oracle
测试数据源
ID
必须是数字。
") from exc
return host, user, password, oracle_source_id
def _archive_members(path: Path) -> list[tarfile.TarInfo]:
if not path.is_file() or path.stat().st_size == 0:
raise SystemExit(f"
发布包不存在或为空:
{
path
}
")
try:
with tarfile.open(path, "
r
:
gz
") as archive:
return archive.getmembers()
except (tarfile.TarError, OSError) as exc:
raise SystemExit(f"
发布包无法读取:
{
path
}:
{
exc
}
") from exc
def validate_release_packages(require_oracle: bool = False) -> None:
"""上传前校验关键文件,避免坏包把线上服务停在半途。"""
backend = RELEASE_DIR / "
backend
-
deploy
.
tgz
"
frontend = RELEASE_DIR / "
frontend
-
deploy
.
tgz
"
backend_members = _archive_members(backend)
frontend_members = _archive_members(frontend)
backend_names = {member.name for member in backend_members}
# tarfile 可能保留 ``./`` 前缀(前端归档的根目录就是 ``.``)。
frontend_names = {member.name.lstrip("
.
/
").rstrip("
/
") for member in frontend_members}
missing = sorted(
{"
backend
/
app
.
py
", "
backend
/
configs
/
database
.
production
.
yaml
"} - backend_names
)
if missing:
raise SystemExit(f"
后端发布包缺少:
{
', '
.
join
(
missing
)}
")
if "
index
.
html
" not in frontend_names or not any(
name.startswith("
assets
/
") for name in frontend_names
):
raise SystemExit("
前端发布包缺少
index
.
html
或
assets
/
。
")
oracle_links = [member for member in backend_members if member.name.endswith("
/
libclntsh
.
so
")]
if oracle_links or require_oracle:
if not oracle_links:
raise SystemExit("
已配置
Oracle
测试数据源,但后端发布包未包含
Oracle
Instant
Client
。
")
if not any(member.issym() for member in oracle_links):
raise SystemExit("
Oracle
发布包中的
libclntsh
.
so
不是符号链接。
")
link = next(member for member in oracle_links if member.issym())
target_name = posixpath.normpath(
posixpath.join(posixpath.dirname(link.name), link.linkname)
)
target = next(
(member for member in backend_members if member.name == target_name and not member.issym()),
None,
)
if target is None:
raise SystemExit(f"
Oracle
符号链接目标不存在:
{
link
.
name
}
->
{
link
.
linkname
}
")
with tarfile.open(backend, "
r
:
gz
") as archive:
content = archive.extractfile(target)
if content is None or content.read(4) != b"
\
x7fELF
":
raise SystemExit("
Oracle
目标库不是
ELF
文件。
")
print("
发布包预检查通过:后端、前端和
Oracle
文件结构正常。
")
# 6) 重启
def build_release() -> None:
run
(
"systemctl restart db-tool && sleep 4 && systemctl is-active db-tool && ss -tlnp | grep 8767"
)
print("
开始构建发布包……
")
subprocess.run([sys.executable, str(DEPLOY_DIR / "
build_release
.
py
")], cwd=ROOT, check=True)
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description="
构建并发布
db
-
tools
正式版本
")
parser.add_argument("
--
skip
-
build
", action="
store_true
", help="
使用现有
release
/
发布包
")
parser.add_argument(
"
--
skip
-
oracle
-
check
", action="
store_true
", help="
跳过发布后的
Oracle
数据源连接测试
"
)
return parser.parse_args()
def main() -> None:
args = parse_args()
if not args.skip_build:
build_release()
host, user, password, oracle_source_id = load_server_settings()
validate_release_packages(require_oracle=oracle_source_id is not None)
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
try:
client.connect(
host,
username=user,
password=password,
timeout=10,
look_for_keys=False,
allow_agent=False,
)
def run(command: str, must: bool = True) -> str:
_, stdout, stderr = client.exec_command(command, timeout=120)
output = stdout.read().decode("
utf
-
8
", "
replace
")
error = stderr.read().decode("
utf
-
8
", "
replace
")
code = stdout.channel.recv_exit_status()
print(f"
$
{
command
}
\
n
{
output
}{(
'[stderr] '
+
error
)
if
error
.
strip
()
else
''
}
rc
=
{
code
}
\
n
")
if must and code != 0:
raise SystemExit(f"
远程命令失败
rc
=
{
code
}:
{
command
}
")
return output
sftp = client.open_sftp()
try:
sftp.put(str(RELEASE_DIR / "
backend
-
deploy
.
tgz
"), "
/
tmp
/
dbtool
-
backend
.
tgz
")
sftp.put(str(RELEASE_DIR / "
frontend
-
deploy
.
tgz
"), "
/
tmp
/
dbtool
-
frontend
.
tgz
")
sftp.put(str(SERVICE_FILE), "
/
tmp
/
db
-
tool
.
service
")
finally:
sftp.close()
print("
发布包和
systemd
配置上传完成。
")
# 部署前确认目标目录存在,并保留旧 SQLite 作为迁移快照。
run("
test
-
d
/
opt
/
app
/
db
-
tool
/
web
/
backend
&&
test
-
d
/
opt
/
web
/
db
-
tool
")
run("
test
-
f
/
opt
/
app
/
db
-
tool
/
web
/
data
/
web
.
db
&&
md5sum
/
opt
/
app
/
db
-
tool
/
web
/
data
/
web
.
db
||
true
")
run(
"
TS
=
$
(
date
+%
Y
%
m
%
d
-%
H
%
M
%
S
)
&&
mkdir
-
p
/
opt
/
app
/
db
-
tool
/
releases
/
$
TS
&&
"
"
cp
-
a
/
opt
/
app
/
db
-
tool
/
web
/
backend
/
opt
/
app
/
db
-
tool
/
releases
/
$
TS
/
backend
&&
"
"
cp
-
a
/
opt
/
web
/
db
-
tool
/
opt
/
app
/
db
-
tool
/
releases
/
$
TS
/
frontend
&&
echo
backup
at
$
TS
"
)
run(
"
systemctl
stop
db
-
tool
&&
TS
=
$
(
date
+%
Y
%
m
%
d
-%
H
%
M
%
S
)
&&
"
"
mkdir
-
p
/
opt
/
app
/
db
-
tool
/
releases
/
$
TS
&&
"
"
cp
-
a
/
opt
/
app
/
db
-
tool
/
web
/
data
/
web
.
db
/
opt
/
app
/
db
-
tool
/
releases
/
$
TS
/
web
.
db
"
)
run(
"
tar
xzf
/
tmp
/
dbtool
-
backend
.
tgz
-
C
/
opt
/
app
/
db
-
tool
/
web
/
&&
"
"
find
/
opt
/
app
/
db
-
tool
/
web
/
backend
-
name
__pycache__
-
type
d
-
exec
rm
-
rf
{}
+
;
"
"
chmod
600
/
opt
/
app
/
db
-
tool
/
web
/
backend
/
configs
/
llm
.
yaml
"
"
/
opt
/
app
/
db
-
tool
/
web
/
backend
/
configs
/
database
.
production
.
yaml
"
)
run(
"
cd
/
opt
/
app
/
db
-
tool
&&
APP_ENV
=
production
PYTHONPATH
=/
opt
/
app
/
db
-
tool
"
"
/
opt
/
app
/
db
-
tool
/
.
venv
/
bin
/
python
-
m
web
.
backend
.
db
.
migrate_sqlite_to_mysql
"
"
--
source
/
opt
/
app
/
db
-
tool
/
web
/
data
/
web
.
db
--
skip
-
if
-
populated
"
)
run("
install
-
m
644
/
tmp
/
db
-
tool
.
service
/
etc
/
systemd
/
system
/
db
-
tool
.
service
&&
systemctl
daemon
-
reload
")
run(
"
rm
-
rf
/
opt
/
web
/
db
-
tool
/
assets
&&
tar
xzf
/
tmp
/
dbtool
-
frontend
.
tgz
-
C
/
opt
/
web
/
db
-
tool
/
"
"
&&
test
-
f
/
opt
/
web
/
db
-
tool
/
index
.
html
"
)
run("
systemctl
restart
db
-
tool
&&
sleep
4
&&
systemctl
is
-
active
db
-
tool
&&
ss
-
tlnp
|
grep
8767
")
run("
curl
-
fsS
http
:
//
127.0
.
0.1
:
8767
/
api
/
health
")
run("
curl
-
fsS
http
:
//
127.0
.
0.1
/
db
-
tool
/
api
/
health
")
if oracle_source_id is not None and not args.skip_oracle_check:
result = run(
f"
curl
-
fsS
-
X
POST
http
:
//
127.0
.
0.1
/
db
-
tool
/
api
/
data
-
sources
/
{
oracle_source_id
}
/
test
"
"
-
H
'Content-Type: application/json'
--
data
'{}'"
)
try:
if json.loads(result).get("
ok
") is not True:
raise SystemExit("
Oracle
数据源连接测试未返回
ok
=
true
。
")
except json.JSONDecodeError as exc:
raise SystemExit("
Oracle
数据源连接测试返回了非
JSON
内容。
") from exc
run("
journalctl
-
u
db
-
tool
-
n
12
--
no
-
pager
|
tail
-
12
")
print("
正式发布完成,服务和健康检查均通过。
")
finally:
client.close()
# 7) 部署后核对
run
(
"curl -fsS http://127.0.0.1:8767/api/health && journalctl -u db-tool -n 12 --no-pager | tail -12"
)
c
.
close
()
if __name__ == "
__main__
":
print
(
"DEPLOY DONE"
)
main(
)
deploy/remote_shell.py
View file @
09a35038
...
@@ -7,6 +7,10 @@ import sys
...
@@ -7,6 +7,10 @@ import sys
import
paramiko
import
paramiko
for
stream
in
(
sys
.
stdout
,
sys
.
stderr
):
if
hasattr
(
stream
,
"reconfigure"
):
stream
.
reconfigure
(
encoding
=
"utf-8"
,
errors
=
"replace"
)
HOST
=
os
.
environ
.
get
(
"DBTOOL_HOST"
,
"192.168.20.175"
)
HOST
=
os
.
environ
.
get
(
"DBTOOL_HOST"
,
"192.168.20.175"
)
USER
=
os
.
environ
.
get
(
"DBTOOL_USER"
,
"root"
)
USER
=
os
.
environ
.
get
(
"DBTOOL_USER"
,
"root"
)
password
=
os
.
environ
.
get
(
"DBTOOL_PW"
)
password
=
os
.
environ
.
get
(
"DBTOOL_PW"
)
...
...
web/backend/core/db_adapter.py
View file @
09a35038
...
@@ -338,6 +338,18 @@ def _ensure_oracle_client(lib_dir: Optional[str] = None) -> str:
...
@@ -338,6 +338,18 @@ def _ensure_oracle_client(lib_dir: Optional[str] = None) -> str:
import
oracledb
import
oracledb
bundled_dir
=
_bundled_oracle_client_dir
(
lib_dir
)
bundled_dir
=
_bundled_oracle_client_dir
(
lib_dir
)
if
bundled_dir
:
if
bundled_dir
:
if
sys
.
platform
.
startswith
(
"linux"
):
# CentOS 7 + python-oracledb 3.x 使用 lib_dir 动态加载时可能在
# ODPI-C 内部触发 SIGSEGV。Linux 应在进程启动前通过
# LD_LIBRARY_PATH 暴露客户端,再使用无参数初始化。
library_paths
=
os
.
environ
.
get
(
"LD_LIBRARY_PATH"
,
""
).
split
(
os
.
pathsep
)
if
bundled_dir
not
in
library_paths
:
raise
RuntimeError
(
"Linux Oracle Thick 模式要求在进程启动前将 Instant Client "
f"加入 LD_LIBRARY_PATH:
{
bundled_dir
}
"
)
oracledb
.
init_oracle_client
()
else
:
oracledb
.
init_oracle_client
(
lib_dir
=
bundled_dir
)
oracledb
.
init_oracle_client
(
lib_dir
=
bundled_dir
)
logger
.
info
(
f"[DB] Oracle Instant Client 已加载(项目内置)lib_dir=
{
bundled_dir
}
"
)
logger
.
info
(
f"[DB] Oracle Instant Client 已加载(项目内置)lib_dir=
{
bundled_dir
}
"
)
else
:
else
:
...
@@ -351,7 +363,6 @@ def _ensure_oracle_client(lib_dir: Optional[str] = None) -> str:
...
@@ -351,7 +363,6 @@ def _ensure_oracle_client(lib_dir: Optional[str] = None) -> str:
# (Instant Client 12.x 是用 MSVC 2013 编译的,依赖 MSVCR120.dll;
# (Instant Client 12.x 是用 MSVC 2013 编译的,依赖 MSVCR120.dll;
# 装 vcredist 2015-2022 不够,必须装 VC++ 2013 Redist)
# 装 vcredist 2015-2022 不够,必须装 VC++ 2013 Redist)
if
"DPI-1047"
in
err_str
:
if
"DPI-1047"
in
err_str
:
import
os
oci_in_lib_dir
=
False
oci_in_lib_dir
=
False
if
lib_dir
:
if
lib_dir
:
oci_in_lib_dir
=
os
.
path
.
exists
(
os
.
path
.
join
(
lib_dir
,
"oci.dll"
))
oci_in_lib_dir
=
os
.
path
.
exists
(
os
.
path
.
join
(
lib_dir
,
"oci.dll"
))
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment