修改启动脚本
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
# Rust 本地联调与远端发布脚本方案
|
||||
# Rust 本地联调与远端发布脚本方案
|
||||
|
||||
日期:`2026-04-22`
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
1. 本地一键联调脚本:同时启动本地 SpacetimeDB、Rust `api-server` 与 Web 前端,并通过现有 Vite 代理开关把运行时 API 指向 Rust。
|
||||
2. Ubuntu 发布包构建脚本:在仓库根目录生成 `build/<当前时间>/` 发布目录,内含前端 release、Linux `api-server`、SpacetimeDB wasm、启动脚本、停止脚本,以及从仓库根目录复制的 `.env` / `.env.local`,并默认通过 `scp` 上传到目标服务器。
|
||||
|
||||
脚本只做部署与联调编排,不改变 HTTP contract、SpacetimeDB schema 命名、对象存储键规划和前端默认 Node 开发入口。
|
||||
脚本只做部署与联调编排,不改变 HTTP contract、SpacetimeDB schema 命名和对象存储键规划。
|
||||
|
||||
## 2. 本地脚本
|
||||
|
||||
@@ -19,13 +19,7 @@
|
||||
npm run dev:rust
|
||||
```
|
||||
|
||||
跨平台 Bash 入口:
|
||||
|
||||
```bash
|
||||
npm run dev:rust:sh
|
||||
```
|
||||
|
||||
Windows 下 `dev:rust:sh`、`deploy:rust:remote` 与 `build:rust:ubuntu` 会通过 `scripts/run-bash-script.mjs` 优先查找 Git Bash;如安装路径不标准,可用 `GENARRATIVE_BASH` 指定 `bash` 可执行文件。
|
||||
默认入口直接执行 Bash 版 `scripts/dev-rust-stack.sh`。Windows 下 `dev:rust`、`dev:rust:logs`、`deploy:rust:remote` 与 `build:rust:ubuntu` 会通过 `scripts/run-bash-script.mjs` 优先查找 Git Bash;如安装路径不标准,可用 `GENARRATIVE_BASH` 指定 `bash` 可执行文件。
|
||||
|
||||
默认端口:
|
||||
|
||||
@@ -33,11 +27,12 @@ Windows 下 `dev:rust:sh`、`deploy:rust:remote` 与 `build:rust:ubuntu` 会通
|
||||
2. Rust `api-server`:`http://127.0.0.1:8082`
|
||||
3. SpacetimeDB standalone:`http://127.0.0.1:3101`
|
||||
4. SpacetimeDB database:优先读取仓库根目录 `spacetime.local.json` 的 `database` 字段;没有该字段时才回退到 `genarrative-dev`
|
||||
5. SpacetimeDB 本地数据与日志目录:`server-rs/.spacetimedb/local`
|
||||
|
||||
默认流程:
|
||||
|
||||
1. 检查 `cargo`、`node` 与 `spacetime` CLI。
|
||||
2. 启动 `spacetime --root-dir server-rs/.spacetimedb/local start --edition standalone --listen-addr 127.0.0.1:3101`。
|
||||
2. 启动 `spacetime --root-dir=server-rs/.spacetimedb/local start --edition standalone --listen-addr 127.0.0.1:3101`,确保本地数据库与 SpacetimeDB 内部日志不会落到开发者全局目录。
|
||||
3. 等待 `spacetime server ping http://127.0.0.1:3101` 可用。
|
||||
4. 执行 `spacetime publish <本地数据库名> --server http://127.0.0.1:3101 --module-path server-rs/crates/spacetime-module --yes`。
|
||||
5. 注入 `GENARRATIVE_API_*` 与 `GENARRATIVE_SPACETIME_*` 后启动 `cargo run -p api-server`;直接运行 `api-server` 时,如未显式设置 `GENARRATIVE_SPACETIME_DATABASE`,服务端也会向上查找 `spacetime.local.json` 作为本地默认库名。
|
||||
@@ -53,26 +48,35 @@ Vite 代理覆盖范围:
|
||||
安全边界:
|
||||
|
||||
1. 默认不执行 `--clear-database`。
|
||||
2. 只有显式传入 `-ClearDatabase` 或 `--clear-database` 才允许清库重发。
|
||||
3. 如需要复用已经启动的 SpacetimeDB,可传 `-SkipSpacetime` / `--skip-spacetime`。
|
||||
4. 如只想启动进程不发布模块,可传 `-SkipPublish` / `--skip-publish`。
|
||||
2. 只有显式传入 `--clear-database` 才允许清库重发。
|
||||
3. 如需要复用已经启动的 SpacetimeDB,可传 `--skip-spacetime`。
|
||||
4. 如只想启动进程不发布模块,可传 `--skip-publish`。
|
||||
|
||||
常用示例:
|
||||
|
||||
```powershell
|
||||
.\scripts\dev-rust-stack.ps1
|
||||
.\scripts\dev-rust-stack.ps1 -ApiPort 8090 -SpacetimePort 3110 -Database genarrative-dev
|
||||
.\scripts\dev-rust-stack.ps1 -SkipSpacetime -SkipPublish
|
||||
.\scripts\dev-rust-stack.ps1 -ClearDatabase
|
||||
```
|
||||
|
||||
```bash
|
||||
npm run dev:rust
|
||||
./scripts/dev-rust-stack.sh
|
||||
./scripts/dev-rust-stack.sh --api-port 8090 --spacetime-port 3110 --database genarrative-dev
|
||||
./scripts/dev-rust-stack.sh --skip-spacetime --skip-publish
|
||||
./scripts/dev-rust-stack.sh --clear-database
|
||||
```
|
||||
|
||||
日志提取:
|
||||
|
||||
```bash
|
||||
npm run dev:rust:logs
|
||||
npm run dev:rust:logs -- --follow
|
||||
./scripts/spacetime-logs-local.sh --lines 500 --output logs/spacetime/local.log
|
||||
```
|
||||
|
||||
日志提取规则:
|
||||
|
||||
1. SpacetimeDB 模块日志以 `spacetime logs <database>` 为唯一提取入口,脚本不直接读取内部日志文件结构。
|
||||
2. 默认读取 `spacetime.local.json` 的 `database` 字段,默认 server 为 `http://127.0.0.1:3101`。
|
||||
3. 默认输出到 `logs/spacetime/<database>-<timestamp>.log`,并通过 `tee` 同步显示在终端。
|
||||
4. `--follow` 仅用于本地追踪,会持续追加到同一个输出文件;停止时用 `Ctrl+C`。
|
||||
|
||||
联调排错补充:
|
||||
|
||||
1. 如果首页公开广场出现 `上游服务请求失败`,优先检查 `api-server` 错误详情里的 `ws://.../v1/database/<database>/subscribe` 是否指向了未发布的库。
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "node scripts/dev-node.mjs",
|
||||
"dev:rust": "powershell -ExecutionPolicy Bypass -File scripts/dev-rust-stack.ps1",
|
||||
"dev:rust:sh": "node scripts/run-bash-script.mjs scripts/dev-rust-stack.sh",
|
||||
"dev:rust": "node scripts/run-bash-script.mjs scripts/dev-rust-stack.sh",
|
||||
"dev:rust:logs": "node scripts/run-bash-script.mjs scripts/spacetime-logs-local.sh",
|
||||
"dev:web": "node scripts/vite-cli.mjs --port=3000 --host=0.0.0.0",
|
||||
"dev:node": "node scripts/dev-node.mjs",
|
||||
"deploy:rust:remote": "node scripts/run-bash-script.mjs scripts/deploy-rust-remote.sh",
|
||||
|
||||
@@ -1,342 +0,0 @@
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Alias("h")]
|
||||
[switch]$Help,
|
||||
[string]$ApiHost = "127.0.0.1",
|
||||
[int]$ApiPort = 8082,
|
||||
[string]$WebHost = "0.0.0.0",
|
||||
[int]$WebPort = 3000,
|
||||
[string]$SpacetimeHost = "127.0.0.1",
|
||||
[int]$SpacetimePort = 3101,
|
||||
[string]$SpacetimeRootDir = "",
|
||||
[string]$Database = "",
|
||||
[string]$Log = "info,tower_http=info",
|
||||
[int]$SpacetimeStartupTimeoutSeconds = 60,
|
||||
[switch]$SkipSpacetime,
|
||||
[switch]$SkipPublish,
|
||||
[switch]$ClearDatabase
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
function Write-Usage {
|
||||
@(
|
||||
'Usage:',
|
||||
' npm run dev:rust',
|
||||
' .\scripts\dev-rust-stack.ps1 -ApiPort 8090 -SpacetimePort 3110',
|
||||
' .\scripts\dev-rust-stack.ps1 -SkipSpacetime -SkipPublish',
|
||||
' .\scripts\dev-rust-stack.ps1 -ClearDatabase',
|
||||
'',
|
||||
'Notes:',
|
||||
' 1. Start SpacetimeDB standalone, Rust api-server, and Vite web together.',
|
||||
' 2. Publish server-rs/crates/spacetime-module by default, without clearing data.',
|
||||
' 3. Only -ClearDatabase appends spacetime publish --clear-database.',
|
||||
' 4. Web listens on 0.0.0.0:3000 by default; API listens on 127.0.0.1:8082.'
|
||||
) -join [Environment]::NewLine
|
||||
}
|
||||
|
||||
function Quote-ProcessArgument {
|
||||
param([string]$Value)
|
||||
|
||||
if ($null -eq $Value) {
|
||||
return '""'
|
||||
}
|
||||
|
||||
if ($Value -notmatch '[\s"]') {
|
||||
return $Value
|
||||
}
|
||||
|
||||
return '"' + $Value.Replace('"', '\"') + '"'
|
||||
}
|
||||
|
||||
function Join-ProcessArguments {
|
||||
param([string[]]$Arguments)
|
||||
|
||||
return (($Arguments | ForEach-Object { Quote-ProcessArgument $_ }) -join " ")
|
||||
}
|
||||
|
||||
function Resolve-ClientHost {
|
||||
param([string]$HostName)
|
||||
|
||||
if ($HostName -eq "0.0.0.0" -or $HostName -eq "::") {
|
||||
return "127.0.0.1"
|
||||
}
|
||||
|
||||
return $HostName
|
||||
}
|
||||
|
||||
function Read-LocalSpacetimeDatabase {
|
||||
param([string]$RepoRoot)
|
||||
|
||||
$localConfigPath = Join-Path $RepoRoot "spacetime.local.json"
|
||||
if (-not (Test-Path $localConfigPath)) {
|
||||
return ""
|
||||
}
|
||||
|
||||
try {
|
||||
$localConfig = Get-Content -Path $localConfigPath -Encoding UTF8 -Raw | ConvertFrom-Json
|
||||
$database = [string]$localConfig.database
|
||||
if (-not [string]::IsNullOrWhiteSpace($database)) {
|
||||
return $database.Trim()
|
||||
}
|
||||
}
|
||||
catch {
|
||||
Write-Host "[dev:rust] ignore invalid spacetime.local.json: $($_.Exception.Message)"
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
function Start-StackProcess {
|
||||
param(
|
||||
[string]$Name,
|
||||
[string]$FilePath,
|
||||
[string[]]$Arguments,
|
||||
[string]$WorkingDirectory,
|
||||
[hashtable]$Environment
|
||||
)
|
||||
|
||||
$argumentLine = Join-ProcessArguments -Arguments $Arguments
|
||||
Write-Host "[dev:rust] start ${Name}: $FilePath $argumentLine"
|
||||
|
||||
$startInfo = New-Object System.Diagnostics.ProcessStartInfo
|
||||
$startInfo.FileName = $FilePath
|
||||
$startInfo.Arguments = $argumentLine
|
||||
$startInfo.WorkingDirectory = $WorkingDirectory
|
||||
$startInfo.UseShellExecute = $false
|
||||
$startInfo.RedirectStandardOutput = $false
|
||||
$startInfo.RedirectStandardError = $false
|
||||
$startInfo.RedirectStandardInput = $false
|
||||
|
||||
foreach ($entry in $Environment.GetEnumerator()) {
|
||||
$startInfo.EnvironmentVariables[$entry.Key] = [string]$entry.Value
|
||||
}
|
||||
|
||||
$process = New-Object System.Diagnostics.Process
|
||||
$process.StartInfo = $startInfo
|
||||
|
||||
if (-not $process.Start()) {
|
||||
throw "Failed to start process: $Name"
|
||||
}
|
||||
|
||||
return [PSCustomObject]@{
|
||||
Name = $Name
|
||||
Process = $process
|
||||
}
|
||||
}
|
||||
|
||||
function Stop-StackProcesses {
|
||||
param([System.Collections.Generic.List[object]]$Processes)
|
||||
|
||||
for ($index = $Processes.Count - 1; $index -ge 0; $index--) {
|
||||
$item = $Processes[$index]
|
||||
$process = $item.Process
|
||||
|
||||
if ($null -ne $process -and -not $process.HasExited) {
|
||||
Write-Host "[dev:rust] stop $($item.Name) (pid=$($process.Id))"
|
||||
$taskkillCommand = Get-Command taskkill.exe -ErrorAction SilentlyContinue
|
||||
if ($null -ne $taskkillCommand) {
|
||||
& $taskkillCommand.Source /PID $process.Id /T /F *> $null
|
||||
}
|
||||
else {
|
||||
Stop-Process -Id $process.Id -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function Wait-ForSpacetimeServer {
|
||||
param(
|
||||
[string]$CommandPath,
|
||||
[string]$Server,
|
||||
[int]$TimeoutSeconds,
|
||||
$ProcessItem
|
||||
)
|
||||
|
||||
$deadline = (Get-Date).AddSeconds($TimeoutSeconds)
|
||||
|
||||
while ((Get-Date) -lt $deadline) {
|
||||
if ($null -ne $ProcessItem -and $ProcessItem.Process.HasExited) {
|
||||
throw "SpacetimeDB exited before readiness, exit code: $($ProcessItem.Process.ExitCode)"
|
||||
}
|
||||
|
||||
& $CommandPath server ping $Server *> $null
|
||||
if ($LASTEXITCODE -eq 0) {
|
||||
return
|
||||
}
|
||||
|
||||
Start-Sleep -Milliseconds 500
|
||||
}
|
||||
|
||||
throw "Timed out waiting for SpacetimeDB readiness: $Server"
|
||||
}
|
||||
|
||||
if ($Help) {
|
||||
Write-Usage
|
||||
exit 0
|
||||
}
|
||||
|
||||
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||||
$repoRoot = Split-Path -Parent $scriptDir
|
||||
$serverRsDir = Join-Path $repoRoot "server-rs"
|
||||
$manifestPath = Join-Path $serverRsDir "Cargo.toml"
|
||||
$modulePath = Join-Path $serverRsDir "crates\spacetime-module"
|
||||
$viteCliPath = Join-Path $repoRoot "scripts\vite-cli.mjs"
|
||||
|
||||
if ([string]::IsNullOrWhiteSpace($SpacetimeRootDir)) {
|
||||
$SpacetimeRootDir = Join-Path $serverRsDir ".spacetimedb\local"
|
||||
}
|
||||
|
||||
if ([string]::IsNullOrWhiteSpace($Database)) {
|
||||
$Database = Read-LocalSpacetimeDatabase -RepoRoot $repoRoot
|
||||
}
|
||||
|
||||
if ([string]::IsNullOrWhiteSpace($Database)) {
|
||||
$Database = "genarrative-dev"
|
||||
}
|
||||
|
||||
if (-not (Test-Path $manifestPath)) {
|
||||
throw "Missing server-rs/Cargo.toml, cannot start Rust local stack."
|
||||
}
|
||||
|
||||
if (-not (Test-Path (Join-Path $modulePath "Cargo.toml"))) {
|
||||
throw "Missing server-rs/crates/spacetime-module/Cargo.toml, cannot publish SpacetimeDB module."
|
||||
}
|
||||
|
||||
if (-not (Test-Path $viteCliPath)) {
|
||||
throw "Missing scripts/vite-cli.mjs, cannot start web frontend."
|
||||
}
|
||||
|
||||
$cargoCommand = Get-Command cargo -ErrorAction SilentlyContinue
|
||||
$nodeCommand = Get-Command node -ErrorAction SilentlyContinue
|
||||
$spacetimeCommand = Get-Command spacetime -ErrorAction SilentlyContinue
|
||||
|
||||
if ($null -eq $cargoCommand) {
|
||||
throw "Missing cargo. Install Rust toolchain first."
|
||||
}
|
||||
|
||||
if ($null -eq $nodeCommand) {
|
||||
throw "Missing node. Install Node.js or use the project bundled runtime first."
|
||||
}
|
||||
|
||||
if (-not $SkipSpacetime -or -not $SkipPublish) {
|
||||
if ($null -eq $spacetimeCommand) {
|
||||
throw "Missing spacetime CLI. Install guide: https://spacetimedb.com/install"
|
||||
}
|
||||
}
|
||||
|
||||
$spacetimeServer = "http://$SpacetimeHost`:$SpacetimePort"
|
||||
$apiTargetHost = Resolve-ClientHost -HostName $ApiHost
|
||||
$rustServerTarget = "http://$apiTargetHost`:$ApiPort"
|
||||
$stackProcesses = New-Object System.Collections.Generic.List[object]
|
||||
$exitCode = 0
|
||||
|
||||
Write-Host "[dev:rust] repo: $repoRoot"
|
||||
Write-Host "[dev:rust] web: http://127.0.0.1:$WebPort"
|
||||
Write-Host "[dev:rust] rust api: $rustServerTarget"
|
||||
Write-Host "[dev:rust] spacetime: $spacetimeServer"
|
||||
Write-Host "[dev:rust] database: $Database"
|
||||
|
||||
try {
|
||||
$spacetimeProcessItem = $null
|
||||
|
||||
if (-not $SkipSpacetime) {
|
||||
New-Item -ItemType Directory -Force -Path $SpacetimeRootDir | Out-Null
|
||||
$spacetimeProcessItem = Start-StackProcess `
|
||||
-Name "spacetimedb" `
|
||||
-FilePath $spacetimeCommand.Source `
|
||||
-Arguments @(
|
||||
"start",
|
||||
"--edition", "standalone",
|
||||
"--listen-addr", "$SpacetimeHost`:$SpacetimePort"
|
||||
) `
|
||||
-WorkingDirectory $serverRsDir `
|
||||
-Environment @{}
|
||||
$stackProcesses.Add($spacetimeProcessItem)
|
||||
}
|
||||
|
||||
if (-not $SkipPublish) {
|
||||
Write-Host "[dev:rust] wait for SpacetimeDB readiness"
|
||||
Wait-ForSpacetimeServer `
|
||||
-CommandPath $spacetimeCommand.Source `
|
||||
-Server $spacetimeServer `
|
||||
-TimeoutSeconds $SpacetimeStartupTimeoutSeconds `
|
||||
-ProcessItem $spacetimeProcessItem
|
||||
|
||||
$publishArgs = @(
|
||||
"publish",
|
||||
$Database,
|
||||
"--server", $spacetimeServer,
|
||||
"--module-path", $modulePath
|
||||
)
|
||||
|
||||
if ($ClearDatabase) {
|
||||
$publishArgs += "--clear-database"
|
||||
}
|
||||
|
||||
$publishArgs += "--yes"
|
||||
|
||||
Write-Host "[dev:rust] publish SpacetimeDB module: $Database"
|
||||
& $spacetimeCommand.Source @publishArgs
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "spacetime publish failed, exit code: $LASTEXITCODE"
|
||||
}
|
||||
}
|
||||
|
||||
$apiEnvironment = @{
|
||||
GENARRATIVE_API_HOST = $ApiHost
|
||||
GENARRATIVE_API_PORT = "$ApiPort"
|
||||
GENARRATIVE_API_LOG = $Log
|
||||
GENARRATIVE_SPACETIME_SERVER_URL = $spacetimeServer
|
||||
GENARRATIVE_SPACETIME_DATABASE = $Database
|
||||
}
|
||||
|
||||
$apiProcessItem = Start-StackProcess `
|
||||
-Name "api-server" `
|
||||
-FilePath $cargoCommand.Source `
|
||||
-Arguments @("run", "-p", "api-server", "--manifest-path", $manifestPath) `
|
||||
-WorkingDirectory $repoRoot `
|
||||
-Environment $apiEnvironment
|
||||
$stackProcesses.Add($apiProcessItem)
|
||||
|
||||
$webEnvironment = @{
|
||||
GENARRATIVE_BACKEND_STACK = "rust"
|
||||
RUST_SERVER_TARGET = $rustServerTarget
|
||||
GENARRATIVE_RUNTIME_SERVER_TARGET = $rustServerTarget
|
||||
VITE_DEV_HOST = $WebHost
|
||||
}
|
||||
|
||||
$webProcessItem = Start-StackProcess `
|
||||
-Name "vite" `
|
||||
-FilePath $nodeCommand.Source `
|
||||
-Arguments @($viteCliPath, "--port=$WebPort", "--host=$WebHost") `
|
||||
-WorkingDirectory $repoRoot `
|
||||
-Environment $webEnvironment
|
||||
$stackProcesses.Add($webProcessItem)
|
||||
|
||||
Write-Host "[dev:rust] local Rust stack is running. Press Ctrl+C to stop all child processes."
|
||||
|
||||
while ($true) {
|
||||
foreach ($item in $stackProcesses) {
|
||||
if ($item.Process.HasExited) {
|
||||
$exitCode = $item.Process.ExitCode
|
||||
Write-Host "[dev:rust] $($item.Name) exited, code: $exitCode"
|
||||
throw "Child process exited, shutting down Rust local stack."
|
||||
}
|
||||
}
|
||||
|
||||
Start-Sleep -Seconds 1
|
||||
}
|
||||
}
|
||||
catch {
|
||||
if ($exitCode -eq 0) {
|
||||
$exitCode = 1
|
||||
}
|
||||
|
||||
Write-Host "[dev:rust] $($_.Exception.Message)"
|
||||
}
|
||||
finally {
|
||||
Stop-StackProcesses -Processes $stackProcesses
|
||||
}
|
||||
|
||||
exit $exitCode
|
||||
@@ -5,15 +5,17 @@ set -euo pipefail
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
用法:
|
||||
npm run dev:rust:sh
|
||||
npm run dev:rust
|
||||
./scripts/dev-rust-stack.sh --api-port 8090 --spacetime-port 3110
|
||||
./scripts/dev-rust-stack.sh --skip-spacetime --skip-publish
|
||||
./scripts/dev-rust-stack.sh --clear-database
|
||||
npm run dev:rust:logs -- --follow
|
||||
|
||||
说明:
|
||||
1. 默认同时启动 SpacetimeDB standalone、Rust api-server 与 Vite 前端。
|
||||
2. 默认会 publish server-rs/crates/spacetime-module,但不会清空数据库。
|
||||
3. 只有显式传入 --clear-database 时,才会追加 spacetime publish --clear-database。
|
||||
4. SpacetimeDB 默认使用 server-rs/.spacetimedb/local 作为本地数据与日志目录。
|
||||
EOF
|
||||
}
|
||||
|
||||
@@ -236,6 +238,7 @@ echo "[dev:rust] web: http://127.0.0.1:${WEB_PORT}"
|
||||
echo "[dev:rust] rust api: ${RUST_SERVER_TARGET}"
|
||||
echo "[dev:rust] spacetime: ${SPACETIME_SERVER}"
|
||||
echo "[dev:rust] database: ${DATABASE}"
|
||||
echo "[dev:rust] spacetime root: ${SPACETIME_ROOT_DIR}"
|
||||
|
||||
if [[ "${SKIP_SPACETIME}" -ne 1 ]]; then
|
||||
mkdir -p "${SPACETIME_ROOT_DIR}"
|
||||
@@ -243,6 +246,7 @@ if [[ "${SKIP_SPACETIME}" -ne 1 ]]; then
|
||||
(
|
||||
cd "${SERVER_RS_DIR}"
|
||||
exec spacetime \
|
||||
--root-dir="${SPACETIME_ROOT_DIR}" \
|
||||
start \
|
||||
--edition standalone \
|
||||
--listen-addr "${SPACETIME_HOST}:${SPACETIME_PORT}"
|
||||
|
||||
Reference in New Issue
Block a user