mirror of
https://github.com/wisdgod/cursor-api.git
synced 2025-10-23 14:43:10 +08:00
v0.1.3-rc.1
This commit is contained in:
@@ -1,158 +1,126 @@
|
||||
# <EFBFBD><EFBFBD>ɫ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
function Write-Info { Write-Host "[INFO] $args" -ForegroundColor Blue }
|
||||
function Write-Warn { Write-Host "[WARN] $args" -ForegroundColor Yellow }
|
||||
function Write-Error { Write-Host "[ERROR] $args" -ForegroundColor Red; exit 1 }
|
||||
# 参数处理
|
||||
param(
|
||||
[switch]$Static,
|
||||
[switch]$Help,
|
||||
[ValidateSet("x86_64", "aarch64", "i686")]
|
||||
[string]$Architecture
|
||||
)
|
||||
|
||||
# <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ҫ<EFBFBD>Ĺ<EFBFBD><EFBFBD><EFBFBD>
|
||||
function Test-Requirements {
|
||||
$tools = @("cargo", "protoc", "npm", "node")
|
||||
$missing = @()
|
||||
# 设置错误时停止执行
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
foreach ($tool in $tools) {
|
||||
if (!(Get-Command $tool -ErrorAction SilentlyContinue)) {
|
||||
$missing += $tool
|
||||
}
|
||||
}
|
||||
# 颜色输出函数
|
||||
function Write-Info { param($Message) Write-Host "[INFO] $Message" -ForegroundColor Blue }
|
||||
function Write-Warn { param($Message) Write-Host "[WARN] $Message" -ForegroundColor Yellow }
|
||||
function Write-Error { param($Message) Write-Host "[ERROR] $Message" -ForegroundColor Red; exit 1 }
|
||||
|
||||
if ($missing.Count -gt 0) {
|
||||
Write-Error "ȱ<EFBFBD>ٱ<EFBFBD>Ҫ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>: $($missing -join ', ')"
|
||||
}
|
||||
}
|
||||
# 检查必要的工具
|
||||
function Check-Requirements {
|
||||
$tools = @("cargo", "protoc", "npm", "node")
|
||||
$missing = @()
|
||||
|
||||
# <20><> Test-Requirements <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>º<EFBFBD><C2BA><EFBFBD>
|
||||
function Initialize-VSEnvironment {
|
||||
Write-Info "<EFBFBD><EFBFBD><EFBFBD>ڳ<EFBFBD>ʼ<EFBFBD><EFBFBD> Visual Studio <20><><EFBFBD><EFBFBD>..."
|
||||
|
||||
# ֱ<><D6B1>ʹ<EFBFBD><CAB9><EFBFBD><EFBFBD>֪<EFBFBD><D6AA> vcvarsall.bat ·<><C2B7>
|
||||
$vcvarsallPath = "E:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Auxiliary\Build\vcvarsall.bat"
|
||||
|
||||
if (-not (Test-Path $vcvarsallPath)) {
|
||||
Write-Error "δ<EFBFBD>ҵ<EFBFBD> vcvarsall.bat: $vcvarsallPath"
|
||||
return
|
||||
}
|
||||
|
||||
Write-Info "ʹ<EFBFBD><EFBFBD> vcvarsall.bat ·<><C2B7>: $vcvarsallPath"
|
||||
|
||||
# <20><>ȡ<EFBFBD><C8A1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
$archArg = "x64"
|
||||
$command = "`"$vcvarsallPath`" $archArg && set"
|
||||
|
||||
try {
|
||||
$output = cmd /c "$command" 2>&1
|
||||
|
||||
# <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ƿ<EFBFBD><C7B7>ɹ<EFBFBD>ִ<EFBFBD><D6B4>
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Error "vcvarsall.bat ִ<><D6B4>ʧ<EFBFBD>ܣ<EFBFBD><DCA3>˳<EFBFBD><CBB3><EFBFBD>: $LASTEXITCODE"
|
||||
return
|
||||
foreach ($tool in $tools) {
|
||||
if (-not (Get-Command $tool -ErrorAction SilentlyContinue)) {
|
||||
$missing += $tool
|
||||
}
|
||||
|
||||
# <20><><EFBFBD>µ<EFBFBD>ǰ PowerShell <20>Ự<EFBFBD>Ļ<EFBFBD><C4BB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
foreach ($line in $output) {
|
||||
if ($line -match "^([^=]+)=(.*)$") {
|
||||
$name = $matches[1]
|
||||
$value = $matches[2]
|
||||
if (![string]::IsNullOrEmpty($name)) {
|
||||
Set-Item -Path "env:$name" -Value $value -ErrorAction SilentlyContinue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Write-Info "Visual Studio <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʼ<EFBFBD><CABC><EFBFBD><EFBFBD><EFBFBD><EFBFBD>"
|
||||
}
|
||||
catch {
|
||||
Write-Error "<EFBFBD><EFBFBD>ʼ<EFBFBD><EFBFBD> Visual Studio <20><><EFBFBD><EFBFBD>ʱ<EFBFBD><CAB1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>: $_"
|
||||
|
||||
if ($missing.Count -gt 0) {
|
||||
Write-Error "缺少必要工具: $($missing -join ', ')"
|
||||
}
|
||||
}
|
||||
|
||||
# <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϣ
|
||||
# 帮助信息
|
||||
function Show-Help {
|
||||
Write-Host @"
|
||||
<EFBFBD>÷<EFBFBD>: $(Split-Path $MyInvocation.MyCommand.Path -Leaf) [ѡ<EFBFBD><EFBFBD>]
|
||||
Write-Host @"
|
||||
用法: $(Split-Path $MyInvocation.ScriptName -Leaf) [选项]
|
||||
|
||||
ѡ<EFBFBD><EFBFBD>:
|
||||
--static ʹ<EFBFBD>þ<EFBFBD>̬<EFBFBD><EFBFBD><EFBFBD>ӣ<EFBFBD>Ĭ<EFBFBD>϶<EFBFBD>̬<EFBFBD><EFBFBD><EFBFBD>ӣ<EFBFBD>
|
||||
--help <EFBFBD><EFBFBD>ʾ<EFBFBD>˰<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϣ
|
||||
选项:
|
||||
-Static 使用静态链接(默认动态链接)
|
||||
-Help 显示此帮助信息
|
||||
|
||||
Ĭ<EFBFBD>ϱ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> Windows ֧<EFBFBD>ֵļܹ<EFBFBD> (x64 <EFBFBD><EFBFBD> arm64)
|
||||
不带参数时使用默认配置构建
|
||||
"@
|
||||
}
|
||||
|
||||
# <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
function New-Target {
|
||||
param (
|
||||
[string]$target,
|
||||
[string]$rustflags
|
||||
)
|
||||
# 构建函数
|
||||
function Build-Target {
|
||||
param (
|
||||
[string]$Target,
|
||||
[string]$RustFlags
|
||||
)
|
||||
|
||||
Write-Info "<EFBFBD><EFBFBD><EFBFBD>ڹ<EFBFBD><EFBFBD><EFBFBD> $target..."
|
||||
Write-Info "正在构建 $Target..."
|
||||
|
||||
# <EFBFBD><EFBFBD><EFBFBD>û<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ִ<EFBFBD>й<EFBFBD><EFBFBD><EFBFBD>
|
||||
$env:RUSTFLAGS = $rustflags
|
||||
cargo build --target $target --release
|
||||
# 设置环境变量
|
||||
$env:RUSTFLAGS = $RustFlags
|
||||
|
||||
# <EFBFBD>ƶ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
$binaryName = "cursor-api"
|
||||
if ($UseStatic) {
|
||||
$binaryName += "-static"
|
||||
}
|
||||
# 构建
|
||||
if ($Target -ne (rustc -Vv | Select-String "host: (.*)" | ForEach-Object { $_.Matches.Groups[1].Value })) {
|
||||
cargo build --target $Target --release
|
||||
} else {
|
||||
cargo build --release
|
||||
}
|
||||
|
||||
$sourcePath = "target/$target/release/cursor-api.exe"
|
||||
$targetPath = "release/${binaryName}-${target}.exe"
|
||||
# 移动编译产物到 release 目录
|
||||
$binaryName = "cursor-api"
|
||||
if ($Static) {
|
||||
$binaryName += "-static"
|
||||
}
|
||||
|
||||
if (Test-Path $sourcePath) {
|
||||
Copy-Item $sourcePath $targetPath -Force
|
||||
Write-Info "<EFBFBD><EFBFBD><EFBFBD>ɹ<EFBFBD><EFBFBD><EFBFBD> $target"
|
||||
}
|
||||
else {
|
||||
Write-Warn "<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>δ<EFBFBD>ҵ<EFBFBD>: $target"
|
||||
return $false
|
||||
}
|
||||
return $true
|
||||
$binaryPath = if ($Target -eq (rustc -Vv | Select-String "host: (.*)" | ForEach-Object { $_.Matches.Groups[1].Value })) {
|
||||
"target/release/cursor-api.exe"
|
||||
} else {
|
||||
"target/$Target/release/cursor-api.exe"
|
||||
}
|
||||
|
||||
if (Test-Path $binaryPath) {
|
||||
Copy-Item $binaryPath "release/$binaryName-$Target.exe"
|
||||
Write-Info "完成构建 $Target"
|
||||
} else {
|
||||
Write-Warn "构建产物未找到: $Target"
|
||||
Write-Warn "查找路径: $binaryPath"
|
||||
Write-Warn "当前目录内容:"
|
||||
Get-ChildItem -Recurse target/
|
||||
return $false
|
||||
}
|
||||
|
||||
return $true
|
||||
}
|
||||
|
||||
# <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
$UseStatic = $false
|
||||
|
||||
foreach ($arg in $args) {
|
||||
switch ($arg) {
|
||||
"--static" { $UseStatic = $true }
|
||||
"--help" { Show-Help; exit 0 }
|
||||
default { Write-Error "δ֪<EFBFBD><EFBFBD><EFBFBD><EFBFBD>: $arg" }
|
||||
}
|
||||
if ($Help) {
|
||||
Show-Help
|
||||
exit 0
|
||||
}
|
||||
|
||||
# <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
try {
|
||||
# <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
Test-Requirements
|
||||
# 检查依赖
|
||||
Check-Requirements
|
||||
|
||||
# <EFBFBD><EFBFBD>ʼ<EFBFBD><EFBFBD> Visual Studio <20><><EFBFBD><EFBFBD>
|
||||
Initialize-VSEnvironment
|
||||
# 创建 release 目录
|
||||
New-Item -ItemType Directory -Force -Path release | Out-Null
|
||||
|
||||
# <EFBFBD><EFBFBD><EFBFBD><EFBFBD> release Ŀ¼
|
||||
New-Item -ItemType Directory -Force -Path "release" | Out-Null
|
||||
|
||||
# <20><><EFBFBD><EFBFBD>Ŀ<EFBFBD><C4BF>ƽ̨
|
||||
$targets = @(
|
||||
"x86_64-pc-windows-msvc",
|
||||
"aarch64-pc-windows-msvc"
|
||||
)
|
||||
|
||||
# <20><><EFBFBD>þ<EFBFBD>̬<EFBFBD><CCAC><EFBFBD>ӱ<EFBFBD>־
|
||||
$rustflags = ""
|
||||
if ($UseStatic) {
|
||||
$rustflags = "-C target-feature=+crt-static"
|
||||
}
|
||||
|
||||
Write-Info "<EFBFBD><EFBFBD>ʼ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>..."
|
||||
|
||||
# <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ŀ<EFBFBD><C4BF>
|
||||
foreach ($target in $targets) {
|
||||
New-Target -target $target -rustflags $rustflags
|
||||
}
|
||||
|
||||
Write-Info "<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ɣ<EFBFBD>"
|
||||
# 设置静态链接标志
|
||||
$rustFlags = ""
|
||||
if ($Static) {
|
||||
$rustFlags = "-C target-feature=+crt-static"
|
||||
}
|
||||
catch {
|
||||
Write-Error "<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>з<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>: $_"
|
||||
}
|
||||
|
||||
# 获取目标架构
|
||||
$arch = if ($Architecture) {
|
||||
$Architecture
|
||||
} else {
|
||||
switch ($env:PROCESSOR_ARCHITECTURE) {
|
||||
"AMD64" { "x86_64" }
|
||||
"ARM64" { "aarch64" }
|
||||
"X86" { "i686" }
|
||||
default { Write-Error "不支持的架构: $env:PROCESSOR_ARCHITECTURE" }
|
||||
}
|
||||
}
|
||||
$target = "$arch-pc-windows-msvc"
|
||||
|
||||
Write-Info "开始构建..."
|
||||
if (-not (Build-Target -Target $target -RustFlags $rustFlags)) {
|
||||
Write-Error "构建失败"
|
||||
}
|
||||
|
||||
Write-Info "构建完成!"
|
@@ -6,11 +6,6 @@ info() { echo -e "\033[1;34m[INFO]\033[0m $*"; }
|
||||
warn() { echo -e "\033[1;33m[WARN]\033[0m $*"; }
|
||||
error() { echo -e "\033[1;31m[ERROR]\033[0m $*" >&2; exit 1; }
|
||||
|
||||
# 检查是否在 Linux 环境
|
||||
is_linux() {
|
||||
[ "$(uname -s)" = "Linux" ]
|
||||
}
|
||||
|
||||
# 检查必要的工具
|
||||
check_requirements() {
|
||||
local missing_tools=()
|
||||
@@ -22,23 +17,29 @@ check_requirements() {
|
||||
fi
|
||||
done
|
||||
|
||||
# cross 工具检查(仅在 Linux 上需要)
|
||||
if [[ "$OS" == "Linux" ]] && ! command -v cross &>/dev/null; then
|
||||
missing_tools+=("cross")
|
||||
fi
|
||||
|
||||
if [[ ${#missing_tools[@]} -gt 0 ]]; then
|
||||
error "缺少必要工具: ${missing_tools[*]}"
|
||||
fi
|
||||
}
|
||||
|
||||
# 解析参数
|
||||
USE_STATIC=false
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--static) USE_STATIC=true ;;
|
||||
--help) show_help; exit 0 ;;
|
||||
*) error "未知参数: $1" ;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
# 帮助信息
|
||||
show_help() {
|
||||
cat << EOF
|
||||
用法: $(basename "$0") [选项]
|
||||
|
||||
选项:
|
||||
--cross 使用 cross 进行交叉编译(仅在 Linux 上有效)
|
||||
--static 使用静态链接(默认动态链接)
|
||||
--help 显示此帮助信息
|
||||
|
||||
@@ -46,22 +47,6 @@ show_help() {
|
||||
EOF
|
||||
}
|
||||
|
||||
# 判断是否使用 cross
|
||||
should_use_cross() {
|
||||
local target=$1
|
||||
# 如果不是 Linux 环境,直接返回 false
|
||||
if [[ "$OS" != "Linux" ]]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 在 Linux 环境下,以下目标不使用 cross:
|
||||
# 1. Linux 上的 x86_64-unknown-linux-gnu
|
||||
if [[ "$target" == "x86_64-unknown-linux-gnu" ]]; then
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# 并行构建函数
|
||||
build_target() {
|
||||
local target=$1
|
||||
@@ -73,15 +58,11 @@ build_target() {
|
||||
# 确定文件后缀
|
||||
[[ $target == *"windows"* ]] && extension=".exe"
|
||||
|
||||
# 判断是否使用 cross
|
||||
if should_use_cross "$target"; then
|
||||
env RUSTFLAGS="$rustflags" cross build --target "$target" --release
|
||||
# 构建
|
||||
if [[ $target != "$CURRENT_TARGET" ]]; then
|
||||
env RUSTFLAGS="$rustflags" cargo build --target "$target" --release
|
||||
else
|
||||
if [[ $target != "$CURRENT_TARGET" ]]; then
|
||||
env RUSTFLAGS="$rustflags" cargo build --target "$target" --release
|
||||
else
|
||||
env RUSTFLAGS="$rustflags" cargo build --release
|
||||
fi
|
||||
env RUSTFLAGS="$rustflags" cargo build --release
|
||||
fi
|
||||
|
||||
# 移动编译产物到 release 目录
|
||||
@@ -117,7 +98,13 @@ get_target() {
|
||||
local os=$2
|
||||
case "$os" in
|
||||
"Darwin") echo "${arch}-apple-darwin" ;;
|
||||
"Linux") echo "${arch}-unknown-linux-gnu" ;;
|
||||
"Linux")
|
||||
if [[ $USE_STATIC == true ]]; then
|
||||
echo "${arch}-unknown-linux-musl"
|
||||
else
|
||||
echo "${arch}-unknown-linux-gnu"
|
||||
fi
|
||||
;;
|
||||
"MINGW"*|"MSYS"*|"CYGWIN"*|"Windows_NT") echo "${arch}-pc-windows-msvc" ;;
|
||||
"FreeBSD") echo "${arch}-unknown-freebsd" ;;
|
||||
*) error "不支持的系统: $os" ;;
|
||||
@@ -134,16 +121,16 @@ CURRENT_TARGET=$(get_target "$ARCH" "$OS")
|
||||
get_targets() {
|
||||
case "$1" in
|
||||
"linux")
|
||||
# Linux 构建所有 Linux 目标和 FreeBSD 目标
|
||||
echo "x86_64-unknown-linux-gnu x86_64-unknown-freebsd"
|
||||
# Linux 只构建当前架构
|
||||
echo "$CURRENT_TARGET"
|
||||
;;
|
||||
"freebsd")
|
||||
# FreeBSD 只构建当前架构的 FreeBSD 目标
|
||||
echo "${ARCH}-unknown-freebsd"
|
||||
# FreeBSD 只构建当前架构
|
||||
echo "$CURRENT_TARGET"
|
||||
;;
|
||||
"windows")
|
||||
# Windows 构建所有 Windows 目标
|
||||
echo "x86_64-pc-windows-msvc"
|
||||
# Windows 只构建当前架构
|
||||
echo "$CURRENT_TARGET"
|
||||
;;
|
||||
"macos")
|
||||
# macOS 构建所有 macOS 目标
|
||||
@@ -153,33 +140,21 @@ get_targets() {
|
||||
esac
|
||||
}
|
||||
|
||||
# 解析参数
|
||||
USE_STATIC=false
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--static) USE_STATIC=true ;;
|
||||
--help) show_help; exit 0 ;;
|
||||
*) error "未知参数: $1" ;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
# 检查依赖
|
||||
check_requirements
|
||||
|
||||
# 确定要构建的目标
|
||||
case "$OS" in
|
||||
"Darwin")
|
||||
Darwin)
|
||||
TARGETS=($(get_targets "macos"))
|
||||
;;
|
||||
"Linux")
|
||||
Linux)
|
||||
TARGETS=($(get_targets "linux"))
|
||||
;;
|
||||
"FreeBSD")
|
||||
FreeBSD)
|
||||
TARGETS=($(get_targets "freebsd"))
|
||||
;;
|
||||
"MINGW"*|"MSYS"*|"CYGWIN"*|"Windows_NT")
|
||||
MINGW*|MSYS*|CYGWIN*|Windows_NT)
|
||||
TARGETS=($(get_targets "windows"))
|
||||
;;
|
||||
*) error "不支持的系统: $OS" ;;
|
||||
@@ -189,8 +164,8 @@ esac
|
||||
mkdir -p release
|
||||
|
||||
# 设置静态链接标志
|
||||
RUSTFLAGS=""
|
||||
[[ $USE_STATIC == true ]] && RUSTFLAGS="-C target-feature=+crt-static"
|
||||
RUSTFLAGS="-C link-arg=-s"
|
||||
[[ $USE_STATIC == true ]] && RUSTFLAGS="-C target-feature=+crt-static -C link-arg=-s"
|
||||
|
||||
# 并行构建所有目标
|
||||
info "开始构建..."
|
||||
|
@@ -1,49 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const { minify } = require('html-minifier-terser');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
// 配置选项
|
||||
const options = {
|
||||
collapseWhitespace: true,
|
||||
removeComments: true,
|
||||
removeEmptyAttributes: true,
|
||||
removeOptionalTags: true,
|
||||
removeRedundantAttributes: true,
|
||||
removeScriptTypeAttributes: true,
|
||||
removeStyleLinkTypeAttributes: true,
|
||||
minifyCSS: true,
|
||||
minifyJS: true,
|
||||
processScripts: ['application/json'],
|
||||
};
|
||||
|
||||
// 处理文件
|
||||
async function minifyFile(inputPath, outputPath) {
|
||||
try {
|
||||
const html = fs.readFileSync(inputPath, 'utf8');
|
||||
const minified = await minify(html, options);
|
||||
fs.writeFileSync(outputPath, minified);
|
||||
console.log(`✓ Minified ${path.basename(inputPath)} -> ${path.basename(outputPath)}`);
|
||||
} catch (err) {
|
||||
console.error(`✗ Error processing ${inputPath}:`, err);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// 主函数
|
||||
async function main() {
|
||||
const staticDir = path.join(__dirname, '..', 'static');
|
||||
const files = [
|
||||
['tokeninfo.html', 'tokeninfo.min.html'],
|
||||
];
|
||||
|
||||
for (const [input, output] of files) {
|
||||
await minifyFile(
|
||||
path.join(staticDir, input),
|
||||
path.join(staticDir, output)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
81
scripts/minify.js
Normal file
81
scripts/minify.js
Normal file
@@ -0,0 +1,81 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const { minify: minifyHtml } = require('html-minifier-terser');
|
||||
const { minify: minifyJs } = require('terser');
|
||||
const CleanCSS = require('clean-css');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
// 配置选项
|
||||
const options = {
|
||||
collapseWhitespace: true,
|
||||
removeComments: true,
|
||||
removeEmptyAttributes: true,
|
||||
removeOptionalTags: true,
|
||||
removeRedundantAttributes: true,
|
||||
removeScriptTypeAttributes: true,
|
||||
removeStyleLinkTypeAttributes: true,
|
||||
minifyCSS: true,
|
||||
minifyJS: true,
|
||||
processScripts: ['application/json'],
|
||||
};
|
||||
|
||||
// CSS 压缩选项
|
||||
const cssOptions = {
|
||||
level: 2
|
||||
};
|
||||
|
||||
// 处理文件
|
||||
async function minifyFile(inputPath, outputPath) {
|
||||
try {
|
||||
const ext = path.extname(inputPath).toLowerCase();
|
||||
const content = fs.readFileSync(inputPath, 'utf8');
|
||||
let minified;
|
||||
|
||||
switch (ext) {
|
||||
case '.html':
|
||||
minified = await minifyHtml(content, options);
|
||||
break;
|
||||
case '.js':
|
||||
const result = await minifyJs(content);
|
||||
minified = result.code;
|
||||
break;
|
||||
case '.css':
|
||||
minified = new CleanCSS(cssOptions).minify(content).styles;
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unsupported file type: ${ext}`);
|
||||
}
|
||||
|
||||
fs.writeFileSync(outputPath, minified);
|
||||
console.log(`✓ Minified ${path.basename(inputPath)} -> ${path.basename(outputPath)}`);
|
||||
} catch (err) {
|
||||
console.error(`✗ Error processing ${inputPath}:`, err);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// 主函数
|
||||
async function main() {
|
||||
// 获取命令行参数,跳过前两个参数(node和脚本路径)
|
||||
const files = process.argv.slice(2);
|
||||
|
||||
if (files.length === 0) {
|
||||
console.error('No input files specified');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const staticDir = path.join(__dirname, '..', 'static');
|
||||
|
||||
for (const file of files) {
|
||||
const inputPath = path.join(staticDir, file);
|
||||
const ext = path.extname(file);
|
||||
const outputPath = path.join(
|
||||
staticDir,
|
||||
file.replace(ext, `.min${ext}`)
|
||||
);
|
||||
await minifyFile(inputPath, outputPath);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
4
scripts/package-lock.json
generated
4
scripts/package-lock.json
generated
@@ -8,7 +8,9 @@
|
||||
"name": "html-minifier-scripts",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"html-minifier-terser": "^7.2.0"
|
||||
"clean-css": "^5.3.3",
|
||||
"html-minifier-terser": "^7.2.0",
|
||||
"terser": "^5.37.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
|
@@ -6,6 +6,8 @@
|
||||
"node": ">=14.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"html-minifier-terser": "^7.2.0"
|
||||
"clean-css": "^5.3.3",
|
||||
"html-minifier-terser": "^7.2.0",
|
||||
"terser": "^5.37.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,31 +0,0 @@
|
||||
# <20><><EFBFBD><EFBFBD> PowerShell <20><><EFBFBD><EFBFBD>Ϊ UTF-8
|
||||
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
||||
$OutputEncoding = [System.Text.Encoding]::UTF8
|
||||
|
||||
# <20><><EFBFBD><EFBFBD><EFBFBD>Ƿ<EFBFBD><C7B7>Թ<EFBFBD><D4B9><EFBFBD>ԱȨ<D4B1><C8A8><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
if (-NOT ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
|
||||
Write-Warning "<EFBFBD><EFBFBD><EFBFBD>Թ<EFBFBD><EFBFBD><EFBFBD>ԱȨ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>д˽ű<EFBFBD>"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# <20><><EFBFBD>鲢<EFBFBD><E9B2A2>װ Chocolatey
|
||||
if (!(Get-Command choco -ErrorAction SilentlyContinue)) {
|
||||
Write-Output "<EFBFBD><EFBFBD><EFBFBD>ڰ<EFBFBD>װ Chocolatey..."
|
||||
Set-ExecutionPolicy Bypass -Scope Process -Force
|
||||
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072
|
||||
iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))
|
||||
}
|
||||
|
||||
# <20><>װ<EFBFBD><D7B0>Ҫ<EFBFBD>Ĺ<EFBFBD><C4B9><EFBFBD>
|
||||
Write-Output "<EFBFBD><EFBFBD><EFBFBD>ڰ<EFBFBD>װ<EFBFBD><EFBFBD>Ҫ<EFBFBD>Ĺ<EFBFBD><EFBFBD><EFBFBD>..."
|
||||
choco install -y mingw
|
||||
choco install -y protoc
|
||||
choco install -y git
|
||||
|
||||
# <20><>װ Rust <20><><EFBFBD><EFBFBD>
|
||||
Write-Output "<EFBFBD><EFBFBD><EFBFBD>ڰ<EFBFBD>װ Rust <20><><EFBFBD><EFBFBD>..."
|
||||
rustup target add x86_64-pc-windows-msvc
|
||||
rustup target add x86_64-unknown-linux-gnu
|
||||
cargo install cross
|
||||
|
||||
Write-Output "<EFBFBD><EFBFBD>װ<EFBFBD><EFBFBD><EFBFBD>ɣ<EFBFBD>"
|
179
scripts/setup.ps1
Normal file
179
scripts/setup.ps1
Normal file
@@ -0,0 +1,179 @@
|
||||
# <20><><EFBFBD>ô<EFBFBD><C3B4><EFBFBD>ʱִֹͣ<D6B9><D6B4>
|
||||
$ErrorActionPreference = "Stop"
|
||||
$ProgressPreference = "SilentlyContinue" # <20>ӿ<EFBFBD><D3BF><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ٶ<EFBFBD>
|
||||
|
||||
# <20><>ɫ<EFBFBD><C9AB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
function Write-Info { param($Message) Write-Host "[INFO] $Message" -ForegroundColor Blue }
|
||||
function Write-Warn { param($Message) Write-Host "[WARN] $Message" -ForegroundColor Yellow }
|
||||
function Write-Success { param($Message) Write-Host "[SUCCESS] $Message" -ForegroundColor Green }
|
||||
function Write-Error { param($Message) Write-Host "[ERROR] $Message" -ForegroundColor Red; exit 1 }
|
||||
|
||||
# <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ԱȨ<D4B1><C8A8>
|
||||
function Test-Administrator {
|
||||
$user = [Security.Principal.WindowsIdentity]::GetCurrent()
|
||||
$principal = New-Object Security.Principal.WindowsPrincipal $user
|
||||
return $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
|
||||
}
|
||||
|
||||
if (-not (Test-Administrator)) {
|
||||
Write-Error "<EFBFBD><EFBFBD><EFBFBD>Թ<EFBFBD><EFBFBD><EFBFBD>ԱȨ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>д˽ű<EFBFBD>"
|
||||
}
|
||||
|
||||
# <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϣ
|
||||
function Show-Help {
|
||||
Write-Host @"
|
||||
<EFBFBD>÷<EFBFBD>: $(Split-Path $MyInvocation.ScriptName -Leaf) [ѡ<EFBFBD><EFBFBD>]
|
||||
|
||||
ѡ<EFBFBD><EFBFBD>:
|
||||
-NoVS <EFBFBD><EFBFBD><EFBFBD><EFBFBD>װ Visual Studio Build Tools
|
||||
-NoRust <EFBFBD><EFBFBD><EFBFBD><EFBFBD>װ Rust
|
||||
-NoNode <EFBFBD><EFBFBD><EFBFBD><EFBFBD>װ Node.js
|
||||
-Help <EFBFBD><EFBFBD>ʾ<EFBFBD>˰<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϣ
|
||||
|
||||
ʾ<EFBFBD><EFBFBD>:
|
||||
.\setup.ps1
|
||||
.\setup.ps1 -NoVS
|
||||
.\setup.ps1 -NoRust -NoNode
|
||||
"@
|
||||
}
|
||||
|
||||
# <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
param(
|
||||
[switch]$NoVS,
|
||||
[switch]$NoRust,
|
||||
[switch]$NoNode,
|
||||
[switch]$Help
|
||||
)
|
||||
|
||||
if ($Help) {
|
||||
Show-Help
|
||||
exit 0
|
||||
}
|
||||
|
||||
# <20><><EFBFBD>鲢<EFBFBD><E9B2A2>װ Chocolatey
|
||||
function Install-Chocolatey {
|
||||
Write-Info "<EFBFBD><EFBFBD><EFBFBD><EFBFBD> Chocolatey..."
|
||||
if (-not (Get-Command choco -ErrorAction SilentlyContinue)) {
|
||||
Write-Info "<EFBFBD><EFBFBD>װ Chocolatey..."
|
||||
Set-ExecutionPolicy Bypass -Scope Process -Force
|
||||
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072
|
||||
try {
|
||||
Invoke-Expression ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))
|
||||
}
|
||||
catch {
|
||||
Write-Error "<EFBFBD><EFBFBD>װ Chocolatey ʧ<><CAA7>: $_"
|
||||
}
|
||||
# ˢ<>»<EFBFBD><C2BB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
$env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User")
|
||||
}
|
||||
}
|
||||
|
||||
# <20><>װ Visual Studio Build Tools
|
||||
function Install-VSBuildTools {
|
||||
if ($NoVS) {
|
||||
Write-Info "<EFBFBD><EFBFBD><EFBFBD><EFBFBD> Visual Studio Build Tools <20><>װ"
|
||||
return
|
||||
}
|
||||
|
||||
Write-Info "<EFBFBD><EFBFBD><EFBFBD><EFBFBD> Visual Studio Build Tools..."
|
||||
$vsPath = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe"
|
||||
if (-not (Test-Path $vsPath)) {
|
||||
Write-Info "<EFBFBD><EFBFBD>װ Visual Studio Build Tools..."
|
||||
try {
|
||||
# <20><><EFBFBD>ذ<EFBFBD>װ<EFBFBD><D7B0><EFBFBD><EFBFBD>
|
||||
$vsInstallerUrl = "https://aka.ms/vs/17/release/vs_BuildTools.exe"
|
||||
$vsInstallerPath = "$env:TEMP\vs_BuildTools.exe"
|
||||
Invoke-WebRequest -Uri $vsInstallerUrl -OutFile $vsInstallerPath
|
||||
|
||||
# <20><>װ
|
||||
$process = Start-Process -FilePath $vsInstallerPath -ArgumentList `
|
||||
"--quiet", "--wait", "--norestart", "--nocache", `
|
||||
"--installPath", "${env:ProgramFiles(x86)}\Microsoft Visual Studio\2022\BuildTools", `
|
||||
"--add", "Microsoft.VisualStudio.Workload.VCTools" `
|
||||
-NoNewWindow -Wait -PassThru
|
||||
|
||||
if ($process.ExitCode -ne 0) {
|
||||
Write-Error "Visual Studio Build Tools <20><>װʧ<D7B0><CAA7>"
|
||||
}
|
||||
|
||||
Remove-Item $vsInstallerPath -Force
|
||||
}
|
||||
catch {
|
||||
Write-Error "<EFBFBD><EFBFBD>װ Visual Studio Build Tools ʧ<><CAA7>: $_"
|
||||
}
|
||||
}
|
||||
else {
|
||||
Write-Info "Visual Studio Build Tools <20>Ѱ<EFBFBD>װ"
|
||||
}
|
||||
}
|
||||
|
||||
# <20><>װ Rust
|
||||
function Install-Rust {
|
||||
if ($NoRust) {
|
||||
Write-Info "<EFBFBD><EFBFBD><EFBFBD><EFBFBD> Rust <20><>װ"
|
||||
return
|
||||
}
|
||||
|
||||
Write-Info "<EFBFBD><EFBFBD><EFBFBD><EFBFBD> Rust..."
|
||||
if (-not (Get-Command rustc -ErrorAction SilentlyContinue)) {
|
||||
Write-Info "<EFBFBD><EFBFBD>װ Rust..."
|
||||
try {
|
||||
$rustupInit = "$env:TEMP\rustup-init.exe"
|
||||
Invoke-WebRequest -Uri "https://win.rustup.rs" -OutFile $rustupInit
|
||||
Start-Process -FilePath $rustupInit -ArgumentList "-y" -Wait
|
||||
Remove-Item $rustupInit -Force
|
||||
|
||||
# ˢ<>»<EFBFBD><C2BB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
$env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User")
|
||||
}
|
||||
catch {
|
||||
Write-Error "<EFBFBD><EFBFBD>װ Rust ʧ<><CAA7>: $_"
|
||||
}
|
||||
}
|
||||
|
||||
# <20><><EFBFBD><EFBFBD>Ŀ<EFBFBD><C4BF>ƽ̨
|
||||
Write-Info "<EFBFBD><EFBFBD><EFBFBD><EFBFBD> Rust Ŀ<><C4BF>ƽ̨..."
|
||||
$arch = if ([Environment]::Is64BitOperatingSystem) { "x86_64" } else { "i686" }
|
||||
rustup target add "$arch-pc-windows-msvc"
|
||||
}
|
||||
|
||||
# <20><>װ<EFBFBD><D7B0><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
function Install-Tools {
|
||||
Write-Info "<EFBFBD><EFBFBD>װ<EFBFBD><EFBFBD>Ҫ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>..."
|
||||
|
||||
# <20><>װ protoc
|
||||
if (-not (Get-Command protoc -ErrorAction SilentlyContinue)) {
|
||||
Write-Info "<EFBFBD><EFBFBD>װ Protocol Buffers..."
|
||||
choco install -y protoc
|
||||
}
|
||||
|
||||
# <20><>װ Git
|
||||
if (-not (Get-Command git -ErrorAction SilentlyContinue)) {
|
||||
Write-Info "<EFBFBD><EFBFBD>װ Git..."
|
||||
choco install -y git
|
||||
}
|
||||
|
||||
# <20><>װ Node.js
|
||||
if (-not $NoNode -and -not (Get-Command node -ErrorAction SilentlyContinue)) {
|
||||
Write-Info "<EFBFBD><EFBFBD>װ Node.js..."
|
||||
choco install -y nodejs
|
||||
}
|
||||
|
||||
# ˢ<>»<EFBFBD><C2BB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
$env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User")
|
||||
}
|
||||
|
||||
# <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
try {
|
||||
Write-Info "<EFBFBD><EFBFBD>ʼ<EFBFBD><EFBFBD>װ<EFBFBD><EFBFBD>Ҫ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>..."
|
||||
|
||||
Install-Chocolatey
|
||||
Install-VSBuildTools
|
||||
Install-Rust
|
||||
Install-Tools
|
||||
|
||||
Write-Success "<EFBFBD><EFBFBD>װ<EFBFBD><EFBFBD><EFBFBD>ɣ<EFBFBD>"
|
||||
}
|
||||
catch {
|
||||
Write-Error "<EFBFBD><EFBFBD>װ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>г<EFBFBD><EFBFBD>ִ<EFBFBD><EFBFBD><EFBFBD>: $_"
|
||||
}
|
157
scripts/setup.sh
Normal file
157
scripts/setup.sh
Normal file
@@ -0,0 +1,157 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 设置错误时退出
|
||||
set -e
|
||||
|
||||
# 颜色输出
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
info() {
|
||||
echo -e "${BLUE}[INFO] $1${NC}"
|
||||
}
|
||||
|
||||
error() {
|
||||
echo -e "${RED}[ERROR] $1${NC}"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# 检查是否为 root 用户(FreeBSD 和 Linux)
|
||||
if [ "$(uname)" != "Darwin" ] && [ "$EUID" -ne 0 ]; then
|
||||
error "请使用 root 权限运行此脚本 (sudo ./setup.sh)"
|
||||
fi
|
||||
|
||||
# 检测包管理器
|
||||
if command -v brew &> /dev/null; then
|
||||
PKG_MANAGER="brew"
|
||||
info "检测到 macOS/Homebrew 系统"
|
||||
elif command -v pkg &> /dev/null; then
|
||||
PKG_MANAGER="pkg"
|
||||
info "检测到 FreeBSD 系统"
|
||||
elif command -v apt-get &> /dev/null; then
|
||||
PKG_MANAGER="apt-get"
|
||||
info "检测到 Debian/Ubuntu 系统"
|
||||
elif command -v dnf &> /dev/null; then
|
||||
PKG_MANAGER="dnf"
|
||||
info "检测到 Fedora/RHEL 系统"
|
||||
elif command -v yum &> /dev/null; then
|
||||
PKG_MANAGER="yum"
|
||||
info "检测到 CentOS 系统"
|
||||
else
|
||||
error "未检测到支持的包管理器"
|
||||
fi
|
||||
|
||||
# 更新包管理器缓存
|
||||
info "更新包管理器缓存..."
|
||||
case $PKG_MANAGER in
|
||||
"brew")
|
||||
brew update
|
||||
;;
|
||||
"pkg")
|
||||
pkg update
|
||||
;;
|
||||
*)
|
||||
$PKG_MANAGER update -y
|
||||
;;
|
||||
esac
|
||||
|
||||
# 安装基础构建工具
|
||||
info "安装基础构建工具..."
|
||||
case $PKG_MANAGER in
|
||||
"brew")
|
||||
brew install \
|
||||
protobuf \
|
||||
pkg-config \
|
||||
openssl \
|
||||
curl \
|
||||
git \
|
||||
node
|
||||
;;
|
||||
"pkg")
|
||||
pkg install -y \
|
||||
gmake \
|
||||
protobuf \
|
||||
pkgconf \
|
||||
openssl \
|
||||
curl \
|
||||
git \
|
||||
node
|
||||
;;
|
||||
"apt-get")
|
||||
$PKG_MANAGER install -y --no-install-recommends \
|
||||
build-essential \
|
||||
protobuf-compiler \
|
||||
pkg-config \
|
||||
libssl-dev \
|
||||
ca-certificates \
|
||||
curl \
|
||||
tzdata \
|
||||
git
|
||||
;;
|
||||
*)
|
||||
$PKG_MANAGER install -y \
|
||||
gcc \
|
||||
gcc-c++ \
|
||||
make \
|
||||
protobuf-compiler \
|
||||
pkg-config \
|
||||
openssl-devel \
|
||||
ca-certificates \
|
||||
curl \
|
||||
tzdata \
|
||||
git
|
||||
;;
|
||||
esac
|
||||
|
||||
# 安装 Node.js 和 npm(如果还没有通过包管理器安装)
|
||||
if ! command -v node &> /dev/null && [ "$PKG_MANAGER" != "brew" ] && [ "$PKG_MANAGER" != "pkg" ]; then
|
||||
info "安装 Node.js 和 npm..."
|
||||
if [ "$PKG_MANAGER" = "apt-get" ]; then
|
||||
curl -fsSL https://deb.nodesource.com/setup_lts.x | bash -
|
||||
$PKG_MANAGER install -y nodejs
|
||||
else
|
||||
curl -fsSL https://rpm.nodesource.com/setup_lts.x | bash -
|
||||
$PKG_MANAGER install -y nodejs
|
||||
fi
|
||||
fi
|
||||
|
||||
# 安装 Rust(如果未安装)
|
||||
if ! command -v rustc &> /dev/null; then
|
||||
info "安装 Rust..."
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
||||
. "$HOME/.cargo/env"
|
||||
fi
|
||||
|
||||
# 添加目标平台
|
||||
info "添加 Rust 目标平台..."
|
||||
case "$(uname)" in
|
||||
"FreeBSD")
|
||||
rustup target add x86_64-unknown-freebsd
|
||||
;;
|
||||
"Darwin")
|
||||
rustup target add x86_64-apple-darwin aarch64-apple-darwin
|
||||
;;
|
||||
*)
|
||||
rustup target add x86_64-unknown-linux-gnu
|
||||
;;
|
||||
esac
|
||||
|
||||
# 清理包管理器缓存
|
||||
case $PKG_MANAGER in
|
||||
"apt-get")
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
;;
|
||||
"pkg")
|
||||
pkg clean -y
|
||||
;;
|
||||
esac
|
||||
|
||||
# 设置时区(除了 macOS)
|
||||
if [ "$(uname)" != "Darwin" ]; then
|
||||
info "设置时区为 Asia/Shanghai..."
|
||||
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}安装完成!${NC}"
|
Reference in New Issue
Block a user