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
## 验证与发布
```
powershell
cd
web
npm
run
build
cd
..
node
scripts/verify/verify_tree.cjs
python
deploy/build_release.py
# 正式发布:自动构建、上传、重启并检查前后端和 Oracle
python
deploy/deploy_server.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 @@
本目录只保存部署相关文件,业务代码不应放入此处。
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
import
os
import
subprocess
import
tarfile
import
time
from
pathlib
import
Path
ROOT
=
Path
(
__file__
).
resolve
().
parent
.
parent
...
...
@@ -28,7 +29,33 @@ def _add_tree(archive: tarfile.TarFile, root: Path, arcname: str) -> None:
for
path
in
root
.
rglob
(
"*"
):
if
_skip
(
path
)
or
not
path
.
is_file
():
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
:
...
...
deploy/db-tool.service
View file @
09a35038
...
...
@@ -10,6 +10,7 @@ Environment=PYTHONPATH=/opt/app/db-tool
Environment=APP_ENV=production
Environment=WEB_HOST=127.0.0.1
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
Restart=always
RestartSec=5
...
...
deploy/deploy_server.py
View file @
09a35038
#!/usr/bin/env python3
"""db-tools 正式发版:上传、迁移旧 SQLite、切换 MySQL、重启并验证。"""
"""一键构建并发布 db-tools 正式版本。
默认流程:读取本机私有服务器文档、构建发布包、校验产物、上传、备份、
重启 systemd 服务,并检查后端、Nginx 和可选 Oracle 数据源连接。
"""
from
__future__
import
annotations
import
argparse
import
json
import
os
import
posixpath
import
re
import
subprocess
import
sys
import
tarfile
from
pathlib
import
Path
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
DEPLOY_DIR
=
ROOT
/
"deploy"
RELEASE_DIR
=
ROOT
/
"release"
SERVICE_FILE
=
Path
(
__file__
).
with_name
(
"db-tool.service"
)
pw
=
os
.
environ
.
get
(
"DBTOOL_PW"
)
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"
)
SERVICE_FILE
=
DEPLOY_DIR
/
"db-tool.service"
SERVER_FILE
=
DEPLOY_DIR
/
"server.local.md"
# 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/),并锁定私有配置权限
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"
)
def
_markdown_value
(
text
:
str
,
label
:
str
)
->
str
|
None
:
"""读取 ``- 标签:`值` `` 形式的私有配置,兼容无反引号写法。"""
match
=
re
.
search
(
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 配置并解压前端
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/ && ls /opt/web/db-tool/ /opt/web/db-tool/assets/"
)
def load_server_settings() -> tuple[str, str, str, int | None]:
"""加载服务器配置,环境变量优先,私有 Markdown 作为默认来源。"""
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) 重启
run
(
"systemctl restart db-tool && sleep 4 && systemctl is-active db-tool && ss -tlnp | grep 8767"
)
def build_release() -> None:
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
()
print
(
"DEPLOY DONE"
)
if __name__ == "
__main__
":
main(
)
deploy/remote_shell.py
View file @
09a35038
...
...
@@ -7,6 +7,10 @@ import sys
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"
)
USER
=
os
.
environ
.
get
(
"DBTOOL_USER"
,
"root"
)
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:
import
oracledb
bundled_dir
=
_bundled_oracle_client_dir
(
lib_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
)
logger
.
info
(
f"[DB] Oracle Instant Client 已加载(项目内置)lib_dir=
{
bundled_dir
}
"
)
else
:
...
...
@@ -351,7 +363,6 @@ def _ensure_oracle_client(lib_dir: Optional[str] = None) -> str:
# (Instant Client 12.x 是用 MSVC 2013 编译的,依赖 MSVCR120.dll;
# 装 vcredist 2015-2022 不够,必须装 VC++ 2013 Redist)
if
"DPI-1047"
in
err_str
:
import
os
oci_in_lib_dir
=
False
if
lib_dir
:
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