mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-09-29 05:56:38 +08:00
feat: 更新配置,优化时间字段为毫秒,添加 Nginx 版本检测功能
This commit is contained in:
@@ -1,14 +1,10 @@
|
||||
{
|
||||
"server_url": "http://127.0.0.1:3000",
|
||||
"agent_token": "123",
|
||||
"node_name": "edge-01",
|
||||
"node_ip": "10.0.0.8",
|
||||
"agent_version": "0.1.0",
|
||||
"nginx_version": "1.25.5",
|
||||
"data_dir": "./data",
|
||||
"nginx_container_name": "atsflare-nginx",
|
||||
"nginx_docker_image": "nginx:stable-alpine",
|
||||
"heartbeat_interval": 30000000000,
|
||||
"sync_interval": 30000000000,
|
||||
"request_timeout": 10000000000
|
||||
"heartbeat_interval": 30000,
|
||||
"sync_interval": 30000,
|
||||
"request_timeout": 10000
|
||||
}
|
||||
|
||||
@@ -24,9 +24,21 @@ func main() {
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
cfg.NginxVersion = nginx.DetectVersion(
|
||||
context.Background(),
|
||||
nginx.ExecutorOptions{
|
||||
NginxPath: cfg.NginxPath,
|
||||
DockerBinary: cfg.DockerBinary,
|
||||
ContainerName: cfg.NginxContainerName,
|
||||
Image: cfg.NginxDockerImage,
|
||||
RouteConfigPath: cfg.RouteConfigPath,
|
||||
CertDir: cfg.CertDir,
|
||||
NginxCertDir: cfg.NginxCertDir,
|
||||
},
|
||||
)
|
||||
log.Printf("agent config loaded: server=%s node=%s ip=%s heartbeat_interval=%s sync_interval=%s route_config=%s cert_dir=%s", cfg.ServerURL, cfg.NodeName, cfg.NodeIP, cfg.HeartbeatInterval, cfg.SyncInterval, cfg.RouteConfigPath, cfg.CertDir)
|
||||
|
||||
client := httpclient.New(cfg.ServerURL, cfg.AgentToken, cfg.RequestTimeout)
|
||||
client := httpclient.New(cfg.ServerURL, cfg.AgentToken, cfg.RequestTimeout.Duration())
|
||||
stateStore := state.NewStore(cfg.StatePath)
|
||||
runner := &agent.Runner{
|
||||
Config: cfg,
|
||||
|
||||
@@ -52,9 +52,9 @@ func (r *Runner) Run(ctx context.Context) error {
|
||||
log.Printf("agent initial discovery register failed: %v", err)
|
||||
}
|
||||
|
||||
heartbeatTicker := time.NewTicker(r.Config.HeartbeatInterval)
|
||||
heartbeatTicker := time.NewTicker(r.Config.HeartbeatInterval.Duration())
|
||||
defer heartbeatTicker.Stop()
|
||||
syncTicker := time.NewTicker(r.Config.SyncInterval)
|
||||
syncTicker := time.NewTicker(r.Config.SyncInterval.Duration())
|
||||
defer syncTicker.Stop()
|
||||
|
||||
for {
|
||||
|
||||
@@ -102,10 +102,10 @@ func TestRunnerKeepsHeartbeatWhenStartupSyncFails(t *testing.T) {
|
||||
AgentToken: "agent-token",
|
||||
NodeName: "edge-01",
|
||||
NodeIP: "10.0.0.8",
|
||||
AgentVersion: "0.1.0",
|
||||
AgentVersion: config.AgentVersion,
|
||||
NginxVersion: "1.25.5",
|
||||
HeartbeatInterval: 10 * time.Millisecond,
|
||||
SyncInterval: 20 * time.Millisecond,
|
||||
HeartbeatInterval: config.MillisecondDuration(10 * time.Millisecond),
|
||||
SyncInterval: config.MillisecondDuration(20 * time.Millisecond),
|
||||
},
|
||||
StateStore: stateStore,
|
||||
HeartbeatService: heartbeatService,
|
||||
@@ -153,10 +153,10 @@ func TestRunnerDoesNotExitOnHeartbeatOrSyncError(t *testing.T) {
|
||||
AgentToken: "agent-token",
|
||||
NodeName: "edge-01",
|
||||
NodeIP: "10.0.0.8",
|
||||
AgentVersion: "0.1.0",
|
||||
AgentVersion: config.AgentVersion,
|
||||
NginxVersion: "1.25.5",
|
||||
HeartbeatInterval: 10 * time.Millisecond,
|
||||
SyncInterval: 10 * time.Millisecond,
|
||||
HeartbeatInterval: config.MillisecondDuration(10 * time.Millisecond),
|
||||
SyncInterval: config.MillisecondDuration(10 * time.Millisecond),
|
||||
},
|
||||
StateStore: stateStore,
|
||||
HeartbeatService: heartbeatService,
|
||||
@@ -214,20 +214,20 @@ func TestRunnerDiscoveryRegisterUpdatesTokenAndNodeID(t *testing.T) {
|
||||
DiscoveryToken: cfg.DiscoveryToken,
|
||||
NodeName: cfg.NodeName,
|
||||
NodeIP: cfg.NodeIP,
|
||||
AgentVersion: "0.1.0",
|
||||
AgentVersion: config.AgentVersion,
|
||||
NginxVersion: "1.25.5",
|
||||
HeartbeatInterval: 10 * time.Millisecond,
|
||||
SyncInterval: 20 * time.Millisecond,
|
||||
HeartbeatInterval: config.MillisecondDuration(10 * time.Millisecond),
|
||||
SyncInterval: config.MillisecondDuration(20 * time.Millisecond),
|
||||
},
|
||||
StateStore: stateStore,
|
||||
HeartbeatService: heartbeatService,
|
||||
SyncService: syncService,
|
||||
}
|
||||
runner.Config = cfg
|
||||
runner.Config.AgentVersion = "0.1.0"
|
||||
runner.Config.AgentVersion = config.AgentVersion
|
||||
runner.Config.NginxVersion = "1.25.5"
|
||||
runner.Config.HeartbeatInterval = 10 * time.Millisecond
|
||||
runner.Config.SyncInterval = 20 * time.Millisecond
|
||||
runner.Config.HeartbeatInterval = config.MillisecondDuration(10 * time.Millisecond)
|
||||
runner.Config.SyncInterval = config.MillisecondDuration(20 * time.Millisecond)
|
||||
|
||||
err = runner.Run(ctx)
|
||||
if !errors.Is(err, context.Canceled) {
|
||||
|
||||
@@ -19,25 +19,25 @@ const (
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
ServerURL string `json:"server_url"`
|
||||
AgentToken string `json:"agent_token"`
|
||||
DiscoveryToken string `json:"discovery_token"`
|
||||
NodeName string `json:"node_name"`
|
||||
NodeIP string `json:"node_ip"`
|
||||
AgentVersion string `json:"agent_version"`
|
||||
NginxVersion string `json:"nginx_version"`
|
||||
NginxPath string `json:"nginx_path"`
|
||||
NginxContainerName string `json:"nginx_container_name"`
|
||||
NginxDockerImage string `json:"nginx_docker_image"`
|
||||
DockerBinary string `json:"docker_binary"`
|
||||
DataDir string `json:"data_dir"`
|
||||
RouteConfigPath string `json:"route_config_path"`
|
||||
CertDir string `json:"cert_dir"`
|
||||
NginxCertDir string `json:"nginx_cert_dir"`
|
||||
StatePath string `json:"state_path"`
|
||||
HeartbeatInterval time.Duration `json:"heartbeat_interval"`
|
||||
SyncInterval time.Duration `json:"sync_interval"`
|
||||
RequestTimeout time.Duration `json:"request_timeout"`
|
||||
ServerURL string `json:"server_url"`
|
||||
AgentToken string `json:"agent_token"`
|
||||
DiscoveryToken string `json:"discovery_token"`
|
||||
NodeName string `json:"node_name"`
|
||||
NodeIP string `json:"node_ip"`
|
||||
AgentVersion string `json:"-"`
|
||||
NginxVersion string `json:"-"`
|
||||
NginxPath string `json:"nginx_path"`
|
||||
NginxContainerName string `json:"nginx_container_name"`
|
||||
NginxDockerImage string `json:"nginx_docker_image"`
|
||||
DockerBinary string `json:"docker_binary"`
|
||||
DataDir string `json:"data_dir"`
|
||||
RouteConfigPath string `json:"route_config_path"`
|
||||
CertDir string `json:"cert_dir"`
|
||||
NginxCertDir string `json:"nginx_cert_dir"`
|
||||
StatePath string `json:"state_path"`
|
||||
HeartbeatInterval MillisecondDuration `json:"heartbeat_interval"`
|
||||
SyncInterval MillisecondDuration `json:"sync_interval"`
|
||||
RequestTimeout MillisecondDuration `json:"request_timeout"`
|
||||
configPath string
|
||||
}
|
||||
|
||||
@@ -60,9 +60,7 @@ func Load(path string) (*Config, error) {
|
||||
|
||||
func applyDefaults(cfg *Config, baseDir string) {
|
||||
baseDir = filepath.Clean(baseDir)
|
||||
if cfg.AgentVersion == "" {
|
||||
cfg.AgentVersion = "dev"
|
||||
}
|
||||
cfg.AgentVersion = AgentVersion
|
||||
if cfg.NginxContainerName == "" {
|
||||
cfg.NginxContainerName = "atsflare-nginx"
|
||||
}
|
||||
@@ -103,13 +101,13 @@ func applyDefaults(cfg *Config, baseDir string) {
|
||||
}
|
||||
}
|
||||
if cfg.HeartbeatInterval <= 0 {
|
||||
cfg.HeartbeatInterval = 30 * time.Second
|
||||
cfg.HeartbeatInterval = MillisecondDuration(30 * time.Second)
|
||||
}
|
||||
if cfg.SyncInterval <= 0 {
|
||||
cfg.SyncInterval = 30 * time.Second
|
||||
cfg.SyncInterval = MillisecondDuration(30 * time.Second)
|
||||
}
|
||||
if cfg.RequestTimeout <= 0 {
|
||||
cfg.RequestTimeout = 10 * time.Second
|
||||
cfg.RequestTimeout = MillisecondDuration(10 * time.Second)
|
||||
}
|
||||
normalizeManagedPaths(cfg)
|
||||
}
|
||||
|
||||
@@ -5,17 +5,17 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestLoadDockerModeUsesManagedPaths(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
configPath := filepath.Join(dir, "agent.json")
|
||||
payload := map[string]any{
|
||||
"server_url": "http://127.0.0.1:3000",
|
||||
"agent_token": "token",
|
||||
"node_name": "edge-01",
|
||||
"node_ip": "10.0.0.8",
|
||||
"agent_version": "0.1.0",
|
||||
"server_url": "http://127.0.0.1:3000",
|
||||
"agent_token": "token",
|
||||
"node_name": "edge-01",
|
||||
"node_ip": "10.0.0.8",
|
||||
}
|
||||
data, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
@@ -87,12 +87,11 @@ func TestLoadUsesCustomDataDirForGeneratedFiles(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
configPath := filepath.Join(dir, "agent.json")
|
||||
payload := map[string]any{
|
||||
"server_url": "http://127.0.0.1:3000",
|
||||
"agent_token": "token",
|
||||
"node_name": "edge-01",
|
||||
"node_ip": "10.0.0.8",
|
||||
"data_dir": "/srv/atsflare",
|
||||
"agent_version": "0.1.0",
|
||||
"server_url": "http://127.0.0.1:3000",
|
||||
"agent_token": "token",
|
||||
"node_name": "edge-01",
|
||||
"node_ip": "10.0.0.8",
|
||||
"data_dir": "/srv/atsflare",
|
||||
}
|
||||
data, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
@@ -117,3 +116,84 @@ func TestLoadUsesCustomDataDirForGeneratedFiles(t *testing.T) {
|
||||
t.Fatalf("unexpected cert dir: %s", cfg.CertDir)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadUsesMillisecondsForIntervals(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
configPath := filepath.Join(dir, "agent.json")
|
||||
payload := map[string]any{
|
||||
"server_url": "http://127.0.0.1:3000",
|
||||
"agent_token": "token",
|
||||
"node_name": "edge-01",
|
||||
"node_ip": "10.0.0.8",
|
||||
"heartbeat_interval": 30000,
|
||||
"sync_interval": 45000,
|
||||
"request_timeout": 1500,
|
||||
}
|
||||
data, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to marshal config: %v", err)
|
||||
}
|
||||
if err = os.WriteFile(configPath, data, 0o644); err != nil {
|
||||
t.Fatalf("failed to write config: %v", err)
|
||||
}
|
||||
|
||||
cfg, err := Load(configPath)
|
||||
if err != nil {
|
||||
t.Fatalf("Load failed: %v", err)
|
||||
}
|
||||
|
||||
if cfg.HeartbeatInterval.Duration() != 30*time.Second {
|
||||
t.Fatalf("unexpected heartbeat interval: %s", cfg.HeartbeatInterval)
|
||||
}
|
||||
if cfg.SyncInterval.Duration() != 45*time.Second {
|
||||
t.Fatalf("unexpected sync interval: %s", cfg.SyncInterval)
|
||||
}
|
||||
if cfg.RequestTimeout.Duration() != 1500*time.Millisecond {
|
||||
t.Fatalf("unexpected request timeout: %s", cfg.RequestTimeout)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSavePersistsMillisecondsAndOmitsRuntimeVersions(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
configPath := filepath.Join(dir, "agent.json")
|
||||
if err := os.WriteFile(configPath, []byte(`{"server_url":"http://127.0.0.1:3000","agent_token":"token","node_name":"edge-01","node_ip":"10.0.0.8"}`), 0o644); err != nil {
|
||||
t.Fatalf("failed to write config: %v", err)
|
||||
}
|
||||
|
||||
cfg, err := Load(configPath)
|
||||
if err != nil {
|
||||
t.Fatalf("Load failed: %v", err)
|
||||
}
|
||||
cfg.NginxVersion = "1.25.5"
|
||||
cfg.HeartbeatInterval = MillisecondDuration(5 * time.Second)
|
||||
cfg.SyncInterval = MillisecondDuration(6 * time.Second)
|
||||
cfg.RequestTimeout = MillisecondDuration(7 * time.Second)
|
||||
|
||||
if err = cfg.Save(); err != nil {
|
||||
t.Fatalf("Save failed: %v", err)
|
||||
}
|
||||
|
||||
data, err := os.ReadFile(configPath)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read saved config: %v", err)
|
||||
}
|
||||
var decoded map[string]any
|
||||
if err = json.Unmarshal(data, &decoded); err != nil {
|
||||
t.Fatalf("failed to decode saved config: %v", err)
|
||||
}
|
||||
if _, ok := decoded["agent_version"]; ok {
|
||||
t.Fatal("agent_version should not be persisted")
|
||||
}
|
||||
if _, ok := decoded["nginx_version"]; ok {
|
||||
t.Fatal("nginx_version should not be persisted")
|
||||
}
|
||||
if decoded["heartbeat_interval"] != float64(5000) {
|
||||
t.Fatalf("unexpected heartbeat interval: %#v", decoded["heartbeat_interval"])
|
||||
}
|
||||
if decoded["sync_interval"] != float64(6000) {
|
||||
t.Fatalf("unexpected sync interval: %#v", decoded["sync_interval"])
|
||||
}
|
||||
if decoded["request_timeout"] != float64(7000) {
|
||||
t.Fatalf("unexpected request timeout: %#v", decoded["request_timeout"])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type MillisecondDuration time.Duration
|
||||
|
||||
func (d MillisecondDuration) Duration() time.Duration {
|
||||
return time.Duration(d)
|
||||
}
|
||||
|
||||
func (d MillisecondDuration) String() string {
|
||||
return time.Duration(d).String()
|
||||
}
|
||||
|
||||
func (d *MillisecondDuration) UnmarshalJSON(data []byte) error {
|
||||
raw := strings.TrimSpace(string(data))
|
||||
if raw == "" || raw == "null" {
|
||||
*d = 0
|
||||
return nil
|
||||
}
|
||||
if strings.HasPrefix(raw, "\"") {
|
||||
var text string
|
||||
if err := json.Unmarshal(data, &text); err != nil {
|
||||
return err
|
||||
}
|
||||
text = strings.TrimSpace(text)
|
||||
if text == "" {
|
||||
*d = 0
|
||||
return nil
|
||||
}
|
||||
parsed, err := time.ParseDuration(text)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid duration string %q: %w", text, err)
|
||||
}
|
||||
*d = MillisecondDuration(parsed)
|
||||
return nil
|
||||
}
|
||||
ms, err := strconv.ParseInt(raw, 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid duration milliseconds %q: %w", raw, err)
|
||||
}
|
||||
*d = MillisecondDuration(time.Duration(ms) * time.Millisecond)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d MillisecondDuration) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(time.Duration(d).Milliseconds())
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
package config
|
||||
|
||||
const AgentVersion = "0.1.0"
|
||||
@@ -263,6 +263,56 @@ func NewExecutor(options ExecutorOptions) Executor {
|
||||
}
|
||||
}
|
||||
|
||||
func DetectVersion(ctx context.Context, options ExecutorOptions) string {
|
||||
version, err := detectVersion(ctx, options, &OSCommandRunner{})
|
||||
if err != nil {
|
||||
log.Printf("detect nginx version failed: %v", err)
|
||||
return ""
|
||||
}
|
||||
log.Printf("detected nginx version: %s", version)
|
||||
return version
|
||||
}
|
||||
|
||||
func detectVersion(ctx context.Context, options ExecutorOptions, runner CommandRunner) (string, error) {
|
||||
if runner == nil {
|
||||
runner = &OSCommandRunner{}
|
||||
}
|
||||
if options.NginxPath != "" {
|
||||
output, err := runner.Run(ctx, options.NginxPath, "-v")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("run nginx -v failed: %w: %s", err, string(output))
|
||||
}
|
||||
version := parseNginxVersion(string(output))
|
||||
if version == "" {
|
||||
return "", errors.New("cannot parse nginx version from binary output")
|
||||
}
|
||||
return version, nil
|
||||
}
|
||||
output, err := runner.Run(ctx, options.DockerBinary, "run", "--rm", options.Image, "nginx", "-v")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("run docker nginx -v failed: %w: %s", err, string(output))
|
||||
}
|
||||
version := parseNginxVersion(string(output))
|
||||
if version == "" {
|
||||
return "", errors.New("cannot parse nginx version from docker output")
|
||||
}
|
||||
return version, nil
|
||||
}
|
||||
|
||||
func parseNginxVersion(output string) string {
|
||||
start := strings.Index(output, "nginx/")
|
||||
if start < 0 {
|
||||
return ""
|
||||
}
|
||||
version := output[start+len("nginx/"):]
|
||||
for i, r := range version {
|
||||
if r == ' ' || r == '\n' || r == '\r' || r == '\t' {
|
||||
return version[:i]
|
||||
}
|
||||
}
|
||||
return version
|
||||
}
|
||||
|
||||
type backupState struct {
|
||||
RouteExisted bool
|
||||
RouteData []byte
|
||||
|
||||
@@ -203,6 +203,47 @@ func TestNewExecutorUsesAbsoluteDockerMountPath(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestDetectVersionFromBinary(t *testing.T) {
|
||||
version, err := detectVersion(context.Background(), ExecutorOptions{
|
||||
NginxPath: "/opt/nginx/sbin/nginx",
|
||||
}, &fakeRunner{
|
||||
runFn: func(name string, args ...string) ([]byte, error) {
|
||||
return []byte("nginx version: nginx/1.25.5\n"), nil
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("detectVersion failed: %v", err)
|
||||
}
|
||||
if version != "1.25.5" {
|
||||
t.Fatalf("unexpected version: %s", version)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDetectVersionFromDockerImage(t *testing.T) {
|
||||
runner := &fakeRunner{
|
||||
runFn: func(name string, args ...string) ([]byte, error) {
|
||||
return []byte("nginx version: nginx/1.27.4\n"), nil
|
||||
},
|
||||
}
|
||||
version, err := detectVersion(context.Background(), ExecutorOptions{
|
||||
DockerBinary: "docker",
|
||||
Image: "nginx:stable-alpine",
|
||||
}, runner)
|
||||
if err != nil {
|
||||
t.Fatalf("detectVersion failed: %v", err)
|
||||
}
|
||||
if version != "1.27.4" {
|
||||
t.Fatalf("unexpected version: %s", version)
|
||||
}
|
||||
if len(runner.calls) != 1 {
|
||||
t.Fatalf("expected one command call, got %d", len(runner.calls))
|
||||
}
|
||||
expectedArgs := []string{"run", "--rm", "nginx:stable-alpine", "nginx", "-v"}
|
||||
if !reflect.DeepEqual(runner.calls[0].args, expectedArgs) {
|
||||
t.Fatalf("unexpected docker args: %#v", runner.calls[0].args)
|
||||
}
|
||||
}
|
||||
|
||||
func TestManagerApplyWritesSupportFilesAndReplacesPlaceholder(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
manager := &Manager{
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
|
||||
var StartTime = time.Now().Unix() // unit: second
|
||||
var Version = "v0.0.0" // this hard coding will be replaced automatically when building, no need to manually change
|
||||
var SystemName = "项目模板"
|
||||
var SystemName = "ATSFlare"
|
||||
var ServerAddress = "http://localhost:3000"
|
||||
var Footer = ""
|
||||
var HomePageLink = ""
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
name="description"
|
||||
content="Web site created using create-react-app"
|
||||
/>
|
||||
<title>项目模板</title>
|
||||
<title>ATSFlare</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
|
||||
@@ -56,12 +56,6 @@ const headerButtons = [
|
||||
icon: 'globe',
|
||||
admin: true,
|
||||
},
|
||||
{
|
||||
name: '文件',
|
||||
to: '/file',
|
||||
icon: 'file',
|
||||
admin: true,
|
||||
},
|
||||
{
|
||||
name: '用户',
|
||||
to: '/user',
|
||||
|
||||
@@ -17,7 +17,7 @@ export function isRoot() {
|
||||
|
||||
export function getSystemName() {
|
||||
let system_name = localStorage.getItem('system_name');
|
||||
if (!system_name) return '项目模板';
|
||||
if (!system_name) return 'ATSFlare';
|
||||
return system_name;
|
||||
}
|
||||
|
||||
|
||||
+39
-42
@@ -1,4 +1,4 @@
|
||||
# ATSFlare 部署与联调说明(当前基线)
|
||||
# ATSFlare 部署说明
|
||||
|
||||
本文档仅保留当前可用基线的最小部署方式,用于第三版开发前后的本地部署、联调与回归验证。
|
||||
|
||||
@@ -63,45 +63,43 @@ go run .
|
||||
|
||||
### 3.1 节点专属 `agent_token`
|
||||
|
||||
```json
|
||||
{
|
||||
"server_url": "http://127.0.0.1:3000",
|
||||
"agent_token": "replace-with-node-auth-token",
|
||||
"agent_version": "0.1.0",
|
||||
"nginx_version": "1.25.5",
|
||||
"data_dir": "./data",
|
||||
"nginx_container_name": "atsflare-nginx",
|
||||
"nginx_docker_image": "nginx:stable-alpine",
|
||||
"heartbeat_interval": 30000000000,
|
||||
"sync_interval": 30000000000,
|
||||
"request_timeout": 10000000000
|
||||
}
|
||||
```
|
||||
```json
|
||||
{
|
||||
"server_url": "http://127.0.0.1:3000",
|
||||
"agent_token": "replace-with-node-auth-token",
|
||||
"data_dir": "./data",
|
||||
"nginx_container_name": "atsflare-nginx",
|
||||
"nginx_docker_image": "nginx:stable-alpine",
|
||||
"heartbeat_interval": 30000,
|
||||
"sync_interval": 30000,
|
||||
"request_timeout": 10000
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2 全局 `discovery_token`
|
||||
|
||||
```json
|
||||
{
|
||||
"server_url": "http://127.0.0.1:3000",
|
||||
"discovery_token": "replace-with-global-discovery-token",
|
||||
"agent_version": "0.1.0",
|
||||
"nginx_version": "1.25.5",
|
||||
"data_dir": "./data",
|
||||
"nginx_container_name": "atsflare-nginx",
|
||||
"nginx_docker_image": "nginx:stable-alpine",
|
||||
"heartbeat_interval": 30000000000,
|
||||
"sync_interval": 30000000000,
|
||||
"request_timeout": 10000000000
|
||||
}
|
||||
```
|
||||
|
||||
说明:
|
||||
|
||||
* 时间字段当前仍使用纳秒整数
|
||||
* `agent_token` 与 `discovery_token` 至少填写一个
|
||||
* 若 `agent_token` 为空且 `discovery_token` 存在,Agent 会自动注册并写回新的专属 `agent_token`
|
||||
* `node_name` 与 `node_ip` 可省略,未填写时自动探测
|
||||
* 未配置 `nginx_path` 时,默认使用 Docker Nginx 容器
|
||||
```json
|
||||
{
|
||||
"server_url": "http://127.0.0.1:3000",
|
||||
"discovery_token": "replace-with-global-discovery-token",
|
||||
"data_dir": "./data",
|
||||
"nginx_container_name": "atsflare-nginx",
|
||||
"nginx_docker_image": "nginx:stable-alpine",
|
||||
"heartbeat_interval": 30000,
|
||||
"sync_interval": 30000,
|
||||
"request_timeout": 10000
|
||||
}
|
||||
```
|
||||
|
||||
说明:
|
||||
|
||||
* `agent_version` 由 Agent 代码内常量提供,升级时同步修改代码
|
||||
* `nginx_version` 由 Agent 启动时执行命令自动探测
|
||||
* 时间字段使用毫秒整数
|
||||
* `agent_token` 与 `discovery_token` 至少填写一个
|
||||
* 若 `agent_token` 为空且 `discovery_token` 存在,Agent 会自动注册并写回新的专属 `agent_token`
|
||||
* `node_name` 与 `node_ip` 可省略,未填写时自动探测
|
||||
* 未配置 `nginx_path` 时,默认使用 Docker Nginx 容器
|
||||
|
||||
---
|
||||
|
||||
@@ -197,11 +195,10 @@ npm run build
|
||||
|
||||
## 7. 当前已知限制
|
||||
|
||||
* 时间字段仍使用纳秒整数,不够友好
|
||||
* 暂未内置 systemd unit 文件
|
||||
* 暂未提供一键部署脚本
|
||||
* Docker 模式仍是 MVP 级封装
|
||||
* 联调以手工步骤为主
|
||||
* 暂未内置 systemd unit 文件
|
||||
* 暂未提供一键部署脚本
|
||||
* Docker 模式仍是 MVP 级封装
|
||||
* 联调以手工步骤为主
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user