大更新:

1、完善SQL语法
2、移除node前端,只保留静态资源
3、写好安装脚本
4、做好linux适配
This commit is contained in:
LIm1tZz7 committed 2026-10-01 20:48:38 +08:00
1 parent 687c012841
commit df6d348995
166 files changed
+16317 -10763

No files matched your search

+55
View File
@@ -0,0 +1,55 @@
# ChuSQL 全局配置。
#
# 整条链路共用这一份文件,按分区各管各的,互不影响:
# 存储进程:[page] [btree] [buffer] [storage] [server] [log]
# Web / 命令行:[web](账号口令、会话、分页上限、静态目录、存储进程路径)
# 端点也在这里:[server] pipe_name 是管名,[storage] data_dir 是数据目录,
# 谁需要谁读,不再靠环境变量传递。data_dir 这行是安装时会替换的占位:
# 不写就按平台惯例(Windows %LOCALAPPDATA%\ChuSQL\data,
# Unix ${XDG_DATA_HOME:-$HOME/.local/share}/chusql/data)。
# 每一层只读自己那几段,读完就关,不长期占着文件句柄,别的层照样能读。
# 只有这一份文件说了算:没有任何环境变量覆盖。
# 位置是固定的:Windows 在 %APPDATA%\ChuSQL\chusql.toml,其它平台在
# $XDG_CONFIG_HOME/ChuSQL/chusql.toml(没设就是 ~/.config/ChuSQL/chusql.toml)。
# 想临时读别处,三个可执行文件都认命令行 --config <path>。
[page]
size = 4096
[btree]
order = 4
[buffer]
pool_size = 1024
[storage]
# 安装时被替换成实际数据目录(--data-dir / -DataDir);不写就按平台惯例:
# Windows %LOCALAPPDATA%\ChuSQL\data,Unix ${XDG_DATA_HOME:-$HOME/.local/share}/chusql/data
data_dir = "../localdata"
[server]
pipe_name = "chusql-joint"
[log]
level = "info"
[web]
host = "127.0.0.1"
port = 7777
static_dir = "static"
# 存储进程的可执行文件;留空表示存储已在别处运行,只按 [server] pipe_name 去连
storage_server = ""
# 管理员账号与口令。口令留空表示"只允许管理员免密登录",普通账号一律不能登录。
user = "root"
password = ""
# 会话与登录限流
session_idle = 28800
session_max = 86400
login_max_attempts = 5
login_window = 300
# 请求与分页上限
body_limit = 65536
rows_per_page = 25
max_page_size = 500
max_rows = 1000
max_sql_length = 20000
+58
View File
@@ -0,0 +1,58 @@
# csql-web:只负责启动 Web 前端(先拉起存储进程,再起 Web 服务)。
# 配置走 chusql.toml,两层各读各的分区;这里只取 [web] 的 host/port 拼地址。
$ErrorActionPreference = 'Stop'
function Fail([string]$msg) { Write-Host "!! $msg" -ForegroundColor Red; exit 1 }
function Say([string]$msg) { Write-Host $msg }
$home_dir = Split-Path -Parent $MyInvocation.MyCommand.Path
$bin = Join-Path $home_dir 'bin'
$config_dir = if ($env:APPDATA) { Join-Path $env:APPDATA 'ChuSQL' } else { Join-Path $home_dir 'config' }
$config = Join-Path $config_dir 'chusql.toml'
if (-not (Test-Path $config)) { Fail "config not found: $config" }
function Get-WebSetting([string]$Key, [string]$Default) {
$section = ''
$pattern = '^\s*' + [regex]::Escape($Key) + '\s*='
foreach ($raw in (Get-Content -Path $config -Encoding UTF8)) {
if ($raw -match '^\s*\[') { $section = ($raw -replace '[\[\]\s]', ''); continue }
if ($section -ne 'web') { continue }
$line = $raw -replace '#.*$', ''
if ($line -match $pattern) {
return (($line -replace '^[^=]*=\s*', '').Trim().Trim('"'))
}
}
return $Default
}
$storage_exe = Join-Path $bin 'chusql-storage.exe'
$web_exe = Join-Path $bin 'chusql-web.exe'
if (-not (Test-Path $storage_exe)) { Fail "storage binary not found in $bin" }
if (-not (Test-Path $web_exe)) { Fail "web binary not found in $bin" }
$host_addr = Get-WebSetting 'host' '127.0.0.1'
$port = Get-WebSetting 'port' '7777'
$log_dir = Join-Path $home_dir 'logs'
New-Item -ItemType Directory -Force -Path $log_dir | Out-Null
$storage = $null
$web = $null
try {
$storage = Start-Process -FilePath $storage_exe -WorkingDirectory $home_dir -PassThru -RedirectStandardOutput (Join-Path $log_dir 'storage.log') -RedirectStandardError (Join-Path $log_dir 'storage.err.log')
$web = Start-Process -FilePath $web_exe -WorkingDirectory $home_dir -PassThru -RedirectStandardOutput (Join-Path $log_dir 'web.log') -RedirectStandardError (Join-Path $log_dir 'web.err.log')
Say 'starting web ...'
Say "ready: http://${host_addr}:${port}/"
Say "logs: $log_dir"
while (-not $web.HasExited) { Start-Sleep -Milliseconds 300 }
Say "!! web exited with code $($web.ExitCode)"
}
finally {
foreach ($proc in @($web, $storage)) {
if ($proc -and -not $proc.HasExited) { Stop-Process -Id $proc.Id -Force -ErrorAction SilentlyContinue }
}
Say 'stopped.'
}
+62
View File
@@ -0,0 +1,62 @@
#!/bin/sh
# csql-web:只负责启动 Web 前端(先拉起存储进程,再起 Web 服务)。
# 配置走 chusql.toml,两层各读各的分区;这里只取 [web] 的 host/port 拼地址。
set -eu
home_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
bin="$home_dir/bin"
# 和在 Windows 的 csql-web.ps1(Start-Process -WorkingDirectory)保持一致:
# 先切到安装目录,配置里写相对的 static_dir / data_dir 才有确定的解析基准。
cd "$home_dir"
config_dir="${XDG_CONFIG_HOME:-$HOME/.config}/ChuSQL"
config="$config_dir/chusql.toml"
[ -f "$config" ] || { echo "!! config not found: $config" >&2; exit 1; }
web_setting() {
awk -v key="$1" -v def="$2" '
/^[[:space:]]*\[/ { section = $0; gsub(/[][[:space:]]/, "", section); next }
section != "web" { next }
{
line = $0
sub(/#.*/, "", line)
if (line ~ "^[[:space:]]*" key "[[:space:]]*=") {
sub(/^[^=]*=[[:space:]]*/, "", line)
gsub(/^[[:space:]]+|[[:space:]]+$/, "", line)
gsub(/^"|"$/, "", line)
print line
found = 1
exit
}
}
END { if (!found) print def }
' "$config"
}
storage_exe="$bin/chusql-storage"
web_exe="$bin/chusql-web"
[ -x "$storage_exe" ] || { echo "!! storage binary not found in $bin" >&2; exit 1; }
[ -x "$web_exe" ] || { echo "!! web binary not found in $bin" >&2; exit 1; }
host=$(web_setting host 127.0.0.1)
port=$(web_setting port 7777)
log_dir="$home_dir/logs"
mkdir -p "$log_dir"
"$storage_exe" >>"$log_dir/storage.log" 2>>"$log_dir/storage.err.log" &
storage_pid=$!
"$web_exe" >>"$log_dir/web.log" 2>>"$log_dir/web.err.log" &
web_pid=$!
cleanup() {
kill "$web_pid" "$storage_pid" 2>/dev/null || true
wait "$web_pid" "$storage_pid" 2>/dev/null || true
echo stopped.
}
trap cleanup INT TERM EXIT
echo "starting web ..."
echo "ready: http://$host:$port/"
echo "logs: $log_dir"
wait "$web_pid"
+16
View File
@@ -0,0 +1,16 @@
-- ChuSQL 系统库 schema。
--
-- 系统库(system)不存在就自动建,里面的表都是系统表;这份脚本是它们唯一的定义,
-- 存储进程启动时按它声明表结构(见 chusql-storage/src/initsql.rs)。
-- 这里只描述"应该长什么样",不包含任何历史数据搬迁。
CREATE TABLE __chusql_users (
id int NOT NULL AUTO_INCREMENT PRIMARY KEY,
user varchar(64) NOT NULL UNIQUE,
password_hash varchar(256) NOT NULL,
registered_at timestamp NOT NULL,
last_login_at timestamp,
revision int NOT NULL
);
CREATE INDEX __chusql_users (user);
+124
View File
@@ -0,0 +1,124 @@
# ChuSQL 安装脚本(Windows)。
#
# 包目录结构(解压后就是这个样子):
# bin/ chusql-storage.exe(必装)、chusql-web.exe 或 csql.exe(二选一)
# static/ Web 前端静态资源(装 web 组件时带)
# scripts/ chusql.toml、init.sql
# csql-web.ps1
# install.ps1
#
# 安装 = 释放到目标目录 + 配好环境变量 + 写全局配置,装完把包目录删掉。
# storage/engine 一定装;web 与 cli 二选一。
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)][ValidateSet('web', 'cli')][string]$Component,
[string]$InstallDir = '',
[string]$DataDir = '',
[string]$RootUser = 'root',
[string]$RootPassword = '',
[switch]$KeepPackage
)
$ErrorActionPreference = 'Stop'
function Fail([string]$msg) { Write-Host "!! $msg" -ForegroundColor Red; exit 1 }
function Step([string]$msg) { Write-Host ''; Write-Host "==> $msg" -ForegroundColor Cyan }
function Say([string]$msg) { Write-Host $msg }
$pack = Split-Path -Parent $MyInvocation.MyCommand.Path
if (-not $InstallDir) { $InstallDir = Join-Path $env:LOCALAPPDATA 'ChuSQL' }
if (-not $DataDir) { $DataDir = Join-Path $InstallDir 'data' }
$InstallDir = [System.IO.Path]::GetFullPath($InstallDir)
$DataDir = [System.IO.Path]::GetFullPath($DataDir)
# ---- 校验包内容 ----
$storageExe = Join-Path $pack 'bin\chusql-storage.exe'
if (-not (Test-Path $storageExe)) { Fail "package is incomplete: bin\chusql-storage.exe not found in $pack" }
if ($Component -eq 'web') {
if (-not (Test-Path (Join-Path $pack 'bin\chusql-web.exe'))) { Fail 'package is incomplete: bin\chusql-web.exe not found' }
if (-not (Test-Path (Join-Path $pack 'static'))) { Fail 'package is incomplete: static\ not found' }
} else {
if (-not (Test-Path (Join-Path $pack 'bin\csql.exe'))) { Fail 'package is incomplete: bin\csql.exe not found' }
}
$template = Join-Path $pack 'scripts\chusql.toml'
if (-not (Test-Path $template)) { Fail 'package is incomplete: scripts\chusql.toml not found' }
Say "ChuSQL installer"
Say (" component {0}" -f $Component)
Say (" install to {0}" -f $InstallDir)
Say (" data dir {0}" -f $DataDir)
Say (" root user {0}{1}" -f $RootUser, $(if ($RootPassword) { '' } else { ' (no password: administrator-only sign-in)' }))
# ---- 释放文件 ----
Step 'Installing files'
New-Item -ItemType Directory -Force -Path (Join-Path $InstallDir 'bin'), (Join-Path $InstallDir 'logs'), $DataDir | Out-Null
Copy-Item (Join-Path $pack 'bin\*') (Join-Path $InstallDir 'bin') -Recurse -Force
if ($Component -eq 'web') {
New-Item -ItemType Directory -Force -Path (Join-Path $InstallDir 'static') | Out-Null
Copy-Item (Join-Path $pack 'static\*') (Join-Path $InstallDir 'static') -Recurse -Force
$launcher = Join-Path $pack 'csql-web.ps1'
if (Test-Path $launcher) {
Copy-Item $launcher $InstallDir -Force
} else {
Say ' note: csql-web.ps1 is not bundled yet; until it is, start bin\chusql-web.exe yourself'
}
}
# ---- 写全局配置 ----
Step 'Writing chusql.toml'
$configDir = if ($env:APPDATA) { Join-Path $env:APPDATA 'ChuSQL' } else { Join-Path $InstallDir 'config' }
$configFile = Join-Path $configDir 'chusql.toml'
$toml = Get-Content -Path $template -Raw -Encoding UTF8
$toml = $toml -replace '(?m)^(\s*user\s*=\s*).*$', "`$1`"$RootUser`""
$toml = $toml -replace '(?m)^(\s*password\s*=\s*).*$', "`$1`"$RootPassword`""
$toml = $toml -replace '(?m)^(\s*data_dir\s*=\s*).*$', "`$1`"$($DataDir -replace '\\', '/')`""
New-Item -ItemType Directory -Force -Path $configDir | Out-Null
Set-Content -Path $configFile -Value $toml -Encoding UTF8
Say (" config file {0}" -f $configFile)
Copy-Item (Join-Path $pack 'scripts\init.sql') $InstallDir -Force -ErrorAction SilentlyContinue
# ---- 命令入口 ----
Step 'Creating commands'
if ($Component -eq 'web') {
Set-Content -Path (Join-Path $InstallDir 'csql-web.cmd') -Encoding ASCII -Value @'
@echo off
powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0csql-web.ps1" %*
'@
} else {
Set-Content -Path (Join-Path $InstallDir 'csql.cmd') -Encoding ASCII -Value @'
@echo off
"%~dp0bin\csql.exe" %*
'@
}
# ---- 环境变量 ----
Step 'Updating PATH'
$userPath = [Environment]::GetEnvironmentVariable('Path', 'User')
if (-not $userPath) { $userPath = '' }
$entries = $userPath -split ';' | Where-Object { $_ }
if ($entries -notcontains $InstallDir) {
$updated = (@($entries) + $InstallDir) -join ';'
[Environment]::SetEnvironmentVariable('Path', $updated, 'User')
Say ("added {0} to the user PATH (open a new terminal to pick it up)" -f $InstallDir)
} else {
Say 'PATH already has the install directory'
}
# ---- 删掉安装包 ----
if (-not $KeepPackage) {
Step 'Removing the package'
$marker = Join-Path $pack 'bin\chusql-storage.exe'
$safe = (Test-Path $marker) -and ($pack -ne $InstallDir) -and (-not $InstallDir.StartsWith($pack, [StringComparison]::OrdinalIgnoreCase))
if ($safe) {
Remove-Item -Recurse -Force $pack
Say 'package removed'
} else {
Say ("kept the package (not safe to delete automatically): {0}" -f $pack)
}
}
Step 'Done'
Say (" chusql.toml {0}" -f $configFile)
Say (" data {0}" -f $DataDir)
if ($Component -eq 'web') { Say ' start with csql-web' } else { Say ' start with csql' }
+406
View File
@@ -0,0 +1,406 @@
#!/bin/sh
# ChuSQL 安装脚本(Linux / macOS / POSIX sh)。
#
# 三种来源,脚本自己判断走哪条:
#
# 1) 包内安装:解压一个发行包,脚本旁边就是 bin/,直接装本地文件
# ./install.sh --component web
#
# 2) 在线安装:脚本从 GitHub 取发行包(也可用 --url 指任意 URL 或本地归档)
# curl -fsSL https://raw.githubusercontent.com/TheBadRoger/ChuSQL/main/scripts/install.sh \
# | sh -s -- --component web
# sh install.sh --component web --url dist/chusql-web-linux-x86_64.tar.gz
#
# 3) 源码安装:拉仓库、本地编译(要有 git + cargo + stack + ghc)
# ./install.sh --component web --from-source
# ./install.sh --component web --from-source --source-dir ../ChuSQL
#
# 安装 = 释放到目标目录 + 配好环境变量 + 写全局配置,装完把包目录删掉。
# storage/engine 一定装;web 与 cli 二选一。
#
# 包目录结构(解压后就是这个样子):
# bin/ chusql-storage(必装)、chusql-web 或 csql(二选一)
# static/ Web 前端静态资源(装 web 组件时带)
# scripts/ chusql.toml、init.sql
# csql-web.sh
# install.sh
set -eu
default_repo='TheBadRoger/ChuSQL'
usage() {
cat <<'EOF'
usage: ./install.sh --component web|cli [options]
--component web|cli which front end to install (required)
--install-dir DIR where to install (default: $HOME/.local/share/chusql)
--data-dir DIR where to keep data (default: <install-dir>/data)
--user NAME administrator name (default: root)
--password PW administrator password, empty means no password
(administrator-only sign-in)
--keep-package do not delete the downloaded/extracted package
where the files come from:
(default) a package next to this script, else the newest GitHub release
--url URL|PATH install from this archive (URL, file:// URL or local path)
--repo OWNER/NAME GitHub repository (default: TheBadRoger/ChuSQL)
--version TAG release tag to download / git ref to build (default: latest)
--from-source clone and build locally (needs git, cargo, stack, ghc)
--source-dir DIR build from this checkout instead of cloning
-h, --help this text
EOF
}
component=''
install_dir=''
data_dir=''
root_user='root'
root_password=''
keep_package='no'
repo="$default_repo"
version='latest'
url_arg=''
from_source='no'
source_dir=''
while [ $# -gt 0 ]; do
case "$1" in
--component) component="${2:-}"; shift 2 ;;
--install-dir) install_dir="${2:-}"; shift 2 ;;
--data-dir) data_dir="${2:-}"; shift 2 ;;
--user) root_user="${2:-}"; shift 2 ;;
--password) root_password="${2:-}"; shift 2 ;;
--keep-package) keep_package='yes'; shift ;;
--url) url_arg="${2:-}"; shift 2 ;;
--repo) repo="${2:-}"; shift 2 ;;
--version) version="${2:-}"; shift 2 ;;
--from-source) from_source='yes'; shift ;;
--source-dir) source_dir="${2:-}"; shift 2 ;;
-h|--help) usage; exit 0 ;;
*) echo "!! unknown option: $1" >&2; usage >&2; exit 1 ;;
esac
done
fail() { echo "!! $1" >&2; exit 1; }
step() { echo; echo "==> $1"; }
note() { echo " $1"; }
case "$component" in
web|cli) ;;
'') fail '--component is required (web or cli)' ;;
*) fail "--component must be web or cli, got: $component" ;;
esac
# ---- 本脚本在哪(管道进来时 $0 是 sh,这时没有本地包)----
self=$0
script_dir=''
case "$self" in
sh|bash|dash|ash|ksh|'-sh'|*/sh|*/bash|*/dash|*/ash|*/ksh) script_dir='' ;;
*) script_dir=$(CDPATH= cd -- "$(dirname -- "$self")" 2>/dev/null && pwd) || script_dir='' ;;
esac
[ -n "$install_dir" ] || install_dir="$HOME/.local/share/chusql"
[ -n "$data_dir" ] || data_dir="$install_dir/data"
tmp=''
cleanup() { if [ -n "$tmp" ]; then rm -rf "$tmp"; fi; }
new_tmp() {
tmp=$(mktemp -d "${TMPDIR:-/tmp}/chusql-install.XXXXXX") || fail 'cannot create a temporary directory'
# --keep-package 时不注册清理,临时目录就留着
if [ "$keep_package" = 'no' ]; then trap cleanup EXIT INT TERM HUP; fi
}
download() {
# download URL FILE
if command -v curl >/dev/null 2>&1; then
curl -fsSL -o "$2" "$1" || return 1
elif command -v wget >/dev/null 2>&1; then
wget -q -O "$2" "$1" || return 1
else
fail 'need curl or wget to download the package'
fi
}
verify_checksum() {
# verify_checksum ASSET_URL FILE —— 发布带 .sha256 就校验,没带就说明一下
sums="$tmp/asset.sha256"
if ! download "$1.sha256" "$sums" 2>/dev/null; then
note 'checksum no .sha256 published for this asset (skipped)'
return 0
fi
expected=$(awk 'NR==1 { print $1 }' "$sums")
if command -v sha256sum >/dev/null 2>&1; then
actual=$(sha256sum "$2" | awk '{ print $1 }')
elif command -v shasum >/dev/null 2>&1; then
actual=$(shasum -a 256 "$2" | awk '{ print $1 }')
else
fail 'no sha256sum/shasum available to verify the checksum'
fi
[ "$actual" = "$expected" ] || fail "checksum mismatch: expected $expected, got $actual"
note "checksum $expected (ok)"
}
unpack() {
# unpack ARCHIVE DEST
mkdir -p "$2"
case "$1" in
*.zip)
command -v unzip >/dev/null 2>&1 || fail 'unzip is required to open a .zip package'
unzip -q "$1" -d "$2"
;;
*)
tar -xzf "$1" -C "$2"
;;
esac
}
# 平台标签(全局:os、arch、ext、labels)
detect_platform() {
os=$(uname -s 2>/dev/null || echo unknown)
case "$os" in
Linux) os='linux' ;;
Darwin) os='macos' ;;
MINGW*|MSYS*|CYGWIN*) os='windows' ;;
esac
arch=$(uname -m 2>/dev/null || echo unknown)
case "$arch" in
x86_64|amd64) arch='x86_64' ;;
aarch64|arm64) arch='arm64' ;;
esac
if [ "$os" = 'windows' ]; then ext='zip'; else ext='tar.gz'; fi
labels="$os-$arch $os"
}
# 发行资源(全局:url、archive),成功返回 0
fetch_asset() {
asset="$1"
if [ "$version" = 'latest' ]; then
url="https://github.com/$repo/releases/latest/download/$asset"
else
url="https://github.com/$repo/releases/download/$version/$asset"
fi
archive="$tmp/$asset"
if download "$url" "$archive" 2>/dev/null; then
note "asset $asset"
return 0
fi
return 1
}
pack=''
url=''
archive=''
if [ "$from_source" = 'yes' ]; then
# ---------- 源码安装 ----------
if [ -z "$source_dir" ]; then
command -v git >/dev/null 2>&1 || fail 'git is required to clone the repository'
fi
command -v cargo >/dev/null 2>&1 || fail 'cargo (Rust) is required for --from-source'
command -v stack >/dev/null 2>&1 || fail 'stack (Haskell) is required for --from-source'
new_tmp
if [ -n "$source_dir" ]; then
src=$(CDPATH= cd -- "$source_dir" 2>/dev/null && pwd) || fail "no such directory: $source_dir"
note "source $src (using this checkout as is)"
else
src="$tmp/src"
if [ "$version" = 'latest' ]; then
step "Cloning https://github.com/$repo.git"
git clone --depth 1 "https://github.com/$repo.git" "$src" >/dev/null 2>&1 ||
fail "cannot clone https://github.com/$repo.git"
else
step "Cloning https://github.com/$repo.git at $version"
git clone --depth 1 --branch "$version" "https://github.com/$repo.git" "$src" >/dev/null 2>&1 ||
fail "cannot clone $repo at $version"
fi
note "source $src"
fi
[ -f "$src/chusql-storage/Cargo.toml" ] || fail "not a ChuSQL checkout (chusql-storage/Cargo.toml missing): $src"
[ -f "$src/scripts/chusql.toml" ] || fail "not a ChuSQL checkout (scripts/chusql.toml missing): $src"
if command -v pkg-config >/dev/null 2>&1 && ! pkg-config --exists zlib 2>/dev/null; then
note 'hint the Haskell zlib binding needs the C library: apt-get install zlib1g-dev (or dnf install zlib-devel)'
fi
step 'Building the Rust storage (cargo build --release)'
( cd "$src/chusql-storage" && cargo build --release ) || fail 'cargo build --release failed'
storage_bin="$src/chusql-storage/target/release/chusql-storage"
[ -f "$storage_bin" ] || fail "storage binary not found after the build: $storage_bin"
if [ "$component" = 'web' ]; then
comp_dir='chusql-web'; front_name='chusql-web'
else
comp_dir='chusql-cli'; front_name='csql'
fi
step "Building the Haskell binaries (stack build --fast in $comp_dir)"
note 'the first build downloads the Hackage index (about 139 MB)'
( cd "$src/$comp_dir" && stack build --fast ) || fail "stack build failed in $comp_dir"
stack_root=$( cd "$src/$comp_dir" && stack path --local-install-root 2>/dev/null | tr -d '\r' )
[ -n "$stack_root" ] || fail 'cannot ask stack for --local-install-root'
front_bin="$stack_root/bin/$front_name"
[ -f "$front_bin" ] || fail "front end binary not found after the build: $front_bin"
step 'Staging the package'
pack="$tmp/stage"
mkdir -p "$pack/bin" "$pack/scripts"
cp "$storage_bin" "$pack/bin/"
cp "$front_bin" "$pack/bin/"
for extra in "$stack_root/bin/"*.so "$stack_root/bin/"*.dll; do
if [ -f "$extra" ]; then cp "$extra" "$pack/bin/"; fi
done
cp "$src/scripts/chusql.toml" "$src/scripts/init.sql" "$pack/scripts/"
cp "$src/scripts/install.sh" "$pack/"
if [ "$component" = 'web' ]; then
[ -d "$src/chusql-web/static" ] || fail "static assets not found: $src/chusql-web/static"
mkdir -p "$pack/static"
cp -R "$src/chusql-web/static/." "$pack/static/"
cp "$src/scripts/csql-web.sh" "$pack/"
fi
elif [ -n "$url_arg" ]; then
# ---------- 指定 URL / 本地归档 ----------
new_tmp
archive="$tmp/package"
case "$url_arg" in
http://*|https://*|file://*) url="$url_arg" ;;
*)
[ -f "$url_arg" ] || fail "no such package: $url_arg"
url="file://$(CDPATH= cd -- "$(dirname -- "$url_arg")" && pwd)/$(basename -- "$url_arg")"
;;
esac
step "Downloading $url"
download "$url" "$archive" || fail "cannot download $url"
case "$url_arg" in
http://*|https://*|file://*) verify_checksum "$url" "$archive" ;;
esac
step 'Unpacking'
unpack "$archive" "$tmp/pkg"
pack="$tmp/pkg"
elif [ -n "$script_dir" ] && [ -f "$script_dir/bin/chusql-storage" ]; then
# ---------- 包内安装 ----------
pack="$script_dir"
else
# ---------- 从 GitHub 发行版下载 ----------
new_tmp
detect_platform
step "Looking for a ChuSQL package for $os-$arch (component $component) in $repo"
found=''
for label in $labels; do
if fetch_asset "chusql-$component-$label.$ext"; then
found="$asset"
break
fi
done
if [ -z "$found" ]; then
if fetch_asset "chusql-$component.$ext"; then found="$asset"; fi
fi
[ -n "$found" ] || fail "no package for $os-$arch in $repo ($version); try --url or --from-source"
verify_checksum "$url" "$archive"
step 'Unpacking'
unpack "$archive" "$tmp/pkg"
pack="$tmp/pkg"
fi
# ---- 校验包内容 ----
[ -f "$pack/bin/chusql-storage" ] || fail "package is incomplete: bin/chusql-storage not found in $pack"
if [ "$component" = 'web' ]; then
[ -f "$pack/bin/chusql-web" ] || fail 'package is incomplete: bin/chusql-web not found'
[ -d "$pack/static" ] || fail 'package is incomplete: static/ not found'
else
[ -f "$pack/bin/csql" ] || fail 'package is incomplete: bin/csql not found'
fi
[ -f "$pack/scripts/chusql.toml" ] || fail 'package is incomplete: scripts/chusql.toml not found'
echo 'ChuSQL installer'
echo " component $component"
echo " install to $install_dir"
echo " data dir $data_dir"
if [ -n "$root_password" ]; then
echo " root user $root_user"
else
echo " root user $root_user (no password: administrator-only sign-in)"
fi
# ---- 释放文件 ----
step 'Installing files'
mkdir -p "$install_dir/bin" "$install_dir/logs" "$data_dir"
cp -R "$pack/bin/." "$install_dir/bin/"
chmod +x "$install_dir/bin/"* 2>/dev/null || true
if [ "$component" = 'web' ]; then
mkdir -p "$install_dir/static"
cp -R "$pack/static/." "$install_dir/static/"
if [ -f "$pack/csql-web.sh" ]; then
cp "$pack/csql-web.sh" "$install_dir/"
chmod +x "$install_dir/csql-web.sh"
fi
fi
# ---- 写全局配置 ----
step 'Writing chusql.toml'
config_dir="${XDG_CONFIG_HOME:-$HOME/.config}/ChuSQL"
config_file="$config_dir/chusql.toml"
mkdir -p "$config_dir"
sed -e "s|^\(user[[:space:]]*=[[:space:]]*\).*|\1\"$root_user\"|" \
-e "s|^\(password[[:space:]]*=[[:space:]]*\).*|\1\"$root_password\"|" \
-e "s|^\(data_dir[[:space:]]*=[[:space:]]*\).*|\1\"$data_dir\"|" \
"$pack/scripts/chusql.toml" > "$config_file"
echo " config file $config_file"
if [ -f "$pack/scripts/init.sql" ]; then cp "$pack/scripts/init.sql" "$install_dir/"; fi
# ---- 命令入口 ----
step 'Creating commands'
if [ "$component" = 'web' ]; then
cat > "$install_dir/csql-web" <<'EOF'
#!/bin/sh
exec "$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)/csql-web.sh" "$@"
EOF
chmod +x "$install_dir/csql-web"
else
cat > "$install_dir/csql" <<'EOF'
#!/bin/sh
exec "$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)/bin/csql" "$@"
EOF
chmod +x "$install_dir/csql"
fi
# ---- 环境变量 ----
step 'Updating PATH'
profile="$HOME/.profile"
marker='# ChuSQL'
if [ -f "$profile" ] && grep -q "^$marker" "$profile"; then
echo "PATH entry already present in $profile"
else
{
echo ''
echo "$marker"
echo "PATH=\"\$PATH:$install_dir\""
echo "export PATH"
} >> "$profile"
echo "added $install_dir to PATH in $profile (login again or: . \"$profile\")"
fi
# ---- 删掉安装包 ----
if [ "$keep_package" = 'no' ]; then
step 'Removing the package'
case "$install_dir" in
"$pack"*|"$pack") echo "kept the package (install dir is inside it): $pack" ;;
*)
if [ -f "$pack/bin/chusql-storage" ]; then
( cd / && rm -rf "$pack" )
echo 'package removed'
else
echo "kept the package (not safe to delete automatically): $pack"
fi
;;
esac
else
step 'Keeping the package'
echo " package kept at $pack"
fi
step 'Done'
echo " chusql.toml $config_file"
echo " data $data_dir"
if [ "$component" = 'web' ]; then echo ' start with csql-web'; else echo ' start with csql'; fi